Checkpoint: v3.0 - 新增训练视频教程库(分类浏览、自评系统)、训练提醒通知(多类型提醒、浏览器推送)、通知记录管理、去除冗余文字。65个测试全部通过。

这个提交包含在:
Manus
2026-03-14 08:28:57 -04:00
父节点 2c418b482e
当前提交 27083d5af9
修改 19 个文件,包含 2856 行新增32 行删除

查看文件

@@ -222,3 +222,82 @@ export const userBadges = mysqlTable("user_badges", {
export type UserBadge = typeof userBadges.$inferSelect;
export type InsertUserBadge = typeof userBadges.$inferInsert;
/**
* Tutorial video library - professional coaching reference videos
*/
export const tutorialVideos = mysqlTable("tutorial_videos", {
id: int("id").autoincrement().primaryKey(),
title: varchar("title", { length: 256 }).notNull(),
category: varchar("category", { length: 64 }).notNull(),
skillLevel: mysqlEnum("skillLevel", ["beginner", "intermediate", "advanced"]).default("beginner"),
description: text("description"),
keyPoints: json("keyPoints"),
commonMistakes: json("commonMistakes"),
videoUrl: text("videoUrl"),
thumbnailUrl: text("thumbnailUrl"),
duration: int("duration"),
sortOrder: int("sortOrder").default(0),
isPublished: int("isPublished").default(1),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type TutorialVideo = typeof tutorialVideos.$inferSelect;
export type InsertTutorialVideo = typeof tutorialVideos.$inferInsert;
/**
* User tutorial progress tracking
*/
export const tutorialProgress = mysqlTable("tutorial_progress", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
tutorialId: int("tutorialId").notNull(),
watched: int("watched").default(0),
comparisonVideoId: int("comparisonVideoId"),
selfScore: float("selfScore"),
notes: text("notes"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type TutorialProgress = typeof tutorialProgress.$inferSelect;
export type InsertTutorialProgress = typeof tutorialProgress.$inferInsert;
/**
* Training reminders
*/
export const trainingReminders = mysqlTable("training_reminders", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
reminderType: varchar("reminderType", { length: 32 }).notNull(),
title: varchar("title", { length: 256 }).notNull(),
message: text("message"),
timeOfDay: varchar("timeOfDay", { length: 5 }).notNull(),
daysOfWeek: json("daysOfWeek").notNull(),
isActive: int("isActive").default(1),
lastTriggered: timestamp("lastTriggered"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type TrainingReminder = typeof trainingReminders.$inferSelect;
export type InsertTrainingReminder = typeof trainingReminders.$inferInsert;
/**
* Notification log
*/
export const notificationLog = mysqlTable("notification_log", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
reminderId: int("reminderId"),
notificationType: varchar("notificationType", { length: 32 }).notNull(),
title: varchar("title", { length: 256 }).notNull(),
message: text("message"),
isRead: int("isRead").default(0),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type NotificationLogEntry = typeof notificationLog.$inferSelect;
export type InsertNotificationLog = typeof notificationLog.$inferInsert;