Checkpoint: v3.0 - 新增训练视频教程库(分类浏览、自评系统)、训练提醒通知(多类型提醒、浏览器推送)、通知记录管理、去除冗余文字。65个测试全部通过。
这个提交包含在:
57
drizzle/0004_exotic_randall.sql
普通文件
57
drizzle/0004_exotic_randall.sql
普通文件
@@ -0,0 +1,57 @@
|
||||
CREATE TABLE `notification_log` (
|
||||
`id` int AUTO_INCREMENT NOT NULL,
|
||||
`userId` int NOT NULL,
|
||||
`reminderId` int,
|
||||
`notificationType` varchar(32) NOT NULL,
|
||||
`title` varchar(256) NOT NULL,
|
||||
`message` text,
|
||||
`isRead` int DEFAULT 0,
|
||||
`createdAt` timestamp NOT NULL DEFAULT (now()),
|
||||
CONSTRAINT `notification_log_id` PRIMARY KEY(`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `training_reminders` (
|
||||
`id` int AUTO_INCREMENT NOT NULL,
|
||||
`userId` int NOT NULL,
|
||||
`reminderType` varchar(32) NOT NULL,
|
||||
`title` varchar(256) NOT NULL,
|
||||
`message` text,
|
||||
`timeOfDay` varchar(5) NOT NULL,
|
||||
`daysOfWeek` json NOT NULL,
|
||||
`isActive` int DEFAULT 1,
|
||||
`lastTriggered` timestamp,
|
||||
`createdAt` timestamp NOT NULL DEFAULT (now()),
|
||||
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT `training_reminders_id` PRIMARY KEY(`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `tutorial_progress` (
|
||||
`id` int AUTO_INCREMENT NOT NULL,
|
||||
`userId` int NOT NULL,
|
||||
`tutorialId` int NOT NULL,
|
||||
`watched` int DEFAULT 0,
|
||||
`comparisonVideoId` int,
|
||||
`selfScore` float,
|
||||
`notes` text,
|
||||
`createdAt` timestamp NOT NULL DEFAULT (now()),
|
||||
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT `tutorial_progress_id` PRIMARY KEY(`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `tutorial_videos` (
|
||||
`id` int AUTO_INCREMENT NOT NULL,
|
||||
`title` varchar(256) NOT NULL,
|
||||
`category` varchar(64) NOT NULL,
|
||||
`skillLevel` enum('beginner','intermediate','advanced') DEFAULT 'beginner',
|
||||
`description` text,
|
||||
`keyPoints` json,
|
||||
`commonMistakes` json,
|
||||
`videoUrl` text,
|
||||
`thumbnailUrl` text,
|
||||
`duration` int,
|
||||
`sortOrder` int DEFAULT 0,
|
||||
`isPublished` int DEFAULT 1,
|
||||
`createdAt` timestamp NOT NULL DEFAULT (now()),
|
||||
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT `tutorial_videos_id` PRIMARY KEY(`id`)
|
||||
);
|
||||
1233
drizzle/meta/0004_snapshot.json
普通文件
1233
drizzle/meta/0004_snapshot.json
普通文件
文件差异内容过多而无法显示
加载差异
@@ -29,6 +29,13 @@
|
||||
"when": 1773488765349,
|
||||
"tag": "0003_married_iron_lad",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "5",
|
||||
"when": 1773490358606,
|
||||
"tag": "0004_exotic_randall",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户