Checkpoint: v2.0完整版本:新增社区排行榜、每日打卡、24种成就徽章、实时摄像头姿势分析、在线录制(稳定压缩流/断线重连/自动剪辑)、移动端全面适配。47个测试通过。包含完整开发文档。
这个提交包含在:
@@ -412,6 +412,65 @@ ${recentScores.length > 0 ? `- 用户最近的分析数据: ${JSON.stringify(rec
|
||||
return { rating: user?.ntrpRating || 1.5 };
|
||||
}),
|
||||
}),
|
||||
|
||||
// Daily check-in system
|
||||
checkin: router({
|
||||
today: protectedProcedure.query(async ({ ctx }) => {
|
||||
return db.getTodayCheckin(ctx.user.id);
|
||||
}),
|
||||
do: protectedProcedure
|
||||
.input(z.object({
|
||||
notes: z.string().optional(),
|
||||
minutesTrained: z.number().optional(),
|
||||
}).optional())
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const result = await db.checkinToday(ctx.user.id, input?.notes, input?.minutesTrained);
|
||||
// Check for new badges after check-in
|
||||
const newBadges = await db.checkAndAwardBadges(ctx.user.id);
|
||||
return { ...result, newBadges };
|
||||
}),
|
||||
history: protectedProcedure
|
||||
.input(z.object({ limit: z.number().default(60) }).optional())
|
||||
.query(async ({ ctx, input }) => {
|
||||
return db.getUserCheckins(ctx.user.id, input?.limit || 60);
|
||||
}),
|
||||
}),
|
||||
|
||||
// Badge system
|
||||
badge: router({
|
||||
list: protectedProcedure.query(async ({ ctx }) => {
|
||||
const earned = await db.getUserBadges(ctx.user.id);
|
||||
const allBadges = Object.entries(db.BADGE_DEFINITIONS).map(([key, def]) => {
|
||||
const earnedBadge = earned.find(b => b.badgeKey === key);
|
||||
return {
|
||||
key,
|
||||
...def,
|
||||
earned: !!earnedBadge,
|
||||
earnedAt: earnedBadge?.earnedAt || null,
|
||||
};
|
||||
});
|
||||
return allBadges;
|
||||
}),
|
||||
check: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
const newBadges = await db.checkAndAwardBadges(ctx.user.id);
|
||||
return { newBadges: newBadges.map(key => ({ key, ...db.BADGE_DEFINITIONS[key] })) };
|
||||
}),
|
||||
definitions: publicProcedure.query(() => {
|
||||
return Object.entries(db.BADGE_DEFINITIONS).map(([key, def]) => ({ key, ...def }));
|
||||
}),
|
||||
}),
|
||||
|
||||
// Leaderboard
|
||||
leaderboard: router({
|
||||
get: protectedProcedure
|
||||
.input(z.object({
|
||||
sortBy: z.enum(["ntrpRating", "totalMinutes", "totalSessions", "totalShots"]).default("ntrpRating"),
|
||||
limit: z.number().default(50),
|
||||
}).optional())
|
||||
.query(async ({ input }) => {
|
||||
return db.getLeaderboard(input?.sortBy || "ntrpRating", input?.limit || 50);
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
// NTRP Rating calculation function
|
||||
|
||||
在新工单中引用
屏蔽一个用户