Add challenge session schema
This commit is contained in:
parent
8382183ee5
commit
5570973f74
@ -30,7 +30,7 @@
|
|||||||
| # | 任务 | 状态 | 验收标准 |
|
| # | 任务 | 状态 | 验收标准 |
|
||||||
|---|------|------|----------|
|
|---|------|------|----------|
|
||||||
| G0-1 | 梳理游戏化规则常量模块 | [x] | 新增集中规则定义,覆盖红心、挑战组、XP、等级、金币、道具、广告恢复、周榜周期 |
|
| G0-1 | 梳理游戏化规则常量模块 | [x] | 新增集中规则定义,覆盖红心、挑战组、XP、等级、金币、道具、广告恢复、周榜周期 |
|
||||||
| G0-2 | 新增挑战组数据模型 | [ ] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 |
|
| G0-2 | 新增挑战组数据模型 | [x] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 |
|
||||||
| G0-3 | 新增钱包和道具库存模型 | [ ] | 支持金币余额、道具库存、道具获得/消耗流水 |
|
| G0-3 | 新增钱包和道具库存模型 | [ ] | 支持金币余额、道具库存、道具获得/消耗流水 |
|
||||||
| G0-4 | 新增奖励流水模型 | [ ] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
|
| G0-4 | 新增奖励流水模型 | [ ] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
|
||||||
| G0-5 | 新增每日任务或每日进度模型 | [ ] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
|
| G0-5 | 新增每日任务或每日进度模型 | [ ] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
|
||||||
|
|||||||
@ -153,6 +153,63 @@ export const userChapterProgress = mysqlTable('user_chapter_progress', {
|
|||||||
foreignKey({ columns: [table.chapterId], foreignColumns: [skillTree.id] }),
|
foreignKey({ columns: [table.chapterId], foreignColumns: [skillTree.id] }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// ── Challenge Sessions ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
// 用户挑战组会话数据,服务端以 5 题为一组裁决进度和奖励。
|
||||||
|
export const challengeSessions = mysqlTable('challenge_sessions', {
|
||||||
|
id: char('id', { length: 36 }).primaryKey(),
|
||||||
|
userId: char('user_id', { length: 36 }).notNull(),
|
||||||
|
trackId: varchar('track_id', { length: 50 }).notNull(), // 客户端选择的学习路线。
|
||||||
|
categoryId: varchar('category_id', { length: 50 }).notNull(), // 本组题目所属主题分类。
|
||||||
|
chapterId: char('chapter_id', { length: 36 }), // 绑定的技能树节点或章节。
|
||||||
|
status: mysqlEnum('status', ['pending', 'in_progress', 'completed', 'abandoned', 'expired']).default('pending'), // 挑战组状态。
|
||||||
|
clientRequestId: varchar('client_request_id', { length: 80 }).notNull(), // 创建挑战组的客户端幂等请求号。
|
||||||
|
completeRequestId: varchar('complete_request_id', { length: 80 }), // 完成结算的客户端幂等请求号。
|
||||||
|
questionIds: json('question_ids').$type<readonly string[]>().notNull(), // 本组题目 ID 快照,不包含正确答案。
|
||||||
|
totalQuestions: tinyint('total_questions').default(5), // 本组题目总数。
|
||||||
|
answeredCount: tinyint('answered_count').default(0), // 已提交答案数量。
|
||||||
|
correctCount: tinyint('correct_count').default(0), // 当前正确数量。
|
||||||
|
highRewardEligible: tinyint('high_reward_eligible').default(1), // 是否消耗并享受每日高奖励次数。
|
||||||
|
rewardSnapshot: json('reward_snapshot').$type<Record<string, unknown>>(), // 完成结算后的奖励快照。
|
||||||
|
progressBefore: json('progress_before').$type<Record<string, unknown>>(), // 创建或结算前的资源快照。
|
||||||
|
progressAfter: json('progress_after').$type<Record<string, unknown>>(), // 完成结算后的资源快照。
|
||||||
|
expiresAt: datetime('expires_at'), // 会话过期时间。
|
||||||
|
completedAt: datetime('completed_at'), // 组内题目完成并结算的时间。
|
||||||
|
createdAt: datetime('created_at').default(sql`CURRENT_TIMESTAMP`), // 创建时间。
|
||||||
|
updatedAt: datetime('updated_at').default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`), // 更新时间。
|
||||||
|
}, (table) => [
|
||||||
|
uniqueIndex('uk_challenge_session_user_client_request').on(table.userId, table.clientRequestId),
|
||||||
|
uniqueIndex('uk_challenge_session_user_complete_request').on(table.userId, table.completeRequestId),
|
||||||
|
index('idx_challenge_session_user_status_created').on(table.userId, table.status, table.createdAt),
|
||||||
|
index('idx_challenge_session_chapter_status').on(table.chapterId, table.status),
|
||||||
|
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
|
||||||
|
foreignKey({ columns: [table.categoryId], foreignColumns: [categories.id] }),
|
||||||
|
foreignKey({ columns: [table.chapterId], foreignColumns: [skillTree.id] }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 用户挑战组内的单题提交记录,支撑重复提交幂等和组完成结算。
|
||||||
|
export const challengeSessionAnswers = mysqlTable('challenge_session_answers', {
|
||||||
|
id: char('id', { length: 36 }).primaryKey(),
|
||||||
|
sessionId: char('session_id', { length: 36 }).notNull(),
|
||||||
|
userId: char('user_id', { length: 36 }).notNull(),
|
||||||
|
questionId: char('question_id', { length: 36 }).notNull(),
|
||||||
|
submitRequestId: varchar('submit_request_id', { length: 80 }).notNull(), // 单题提交的客户端幂等请求号。
|
||||||
|
answerOrder: tinyint('answer_order').notNull(), // 本题在挑战组中的顺序。
|
||||||
|
answer: varchar('answer', { length: 500 }), // 用户提交答案。
|
||||||
|
correct: tinyint('correct').notNull(), // 本次提交是否正确。
|
||||||
|
timeMs: int('time_ms'), // 答题耗时,单位毫秒。
|
||||||
|
comboCount: tinyint('combo_count').default(0), // 提交后组内连续答对数。
|
||||||
|
resultSnapshot: json('result_snapshot').$type<Record<string, unknown>>(), // 返回客户端的本题裁决快照。
|
||||||
|
submittedAt: datetime('submitted_at').default(sql`CURRENT_TIMESTAMP`), // 提交时间。
|
||||||
|
}, (table) => [
|
||||||
|
uniqueIndex('uk_challenge_answer_session_question').on(table.sessionId, table.questionId),
|
||||||
|
uniqueIndex('uk_challenge_answer_session_request').on(table.sessionId, table.submitRequestId),
|
||||||
|
index('idx_challenge_answer_user_submitted').on(table.userId, table.submittedAt),
|
||||||
|
foreignKey({ columns: [table.sessionId], foreignColumns: [challengeSessions.id] }),
|
||||||
|
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
|
||||||
|
foreignKey({ columns: [table.questionId], foreignColumns: [questions.id] }),
|
||||||
|
]);
|
||||||
|
|
||||||
// ── Question Ratings ──────────────────────────────────────────────
|
// ── Question Ratings ──────────────────────────────────────────────
|
||||||
|
|
||||||
// 用户对题目的好坏反馈数据。
|
// 用户对题目的好坏反馈数据。
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user