Add daily progress schema

This commit is contained in:
Wang Zhuoxuan 2026-05-11 18:06:19 +08:00
parent 51395bf5ec
commit 7a617ce1f9
2 changed files with 53 additions and 1 deletions

View File

@ -33,7 +33,7 @@
| G0-2 | 新增挑战组数据模型 | [x] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 | | G0-2 | 新增挑战组数据模型 | [x] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 |
| G0-3 | 新增钱包和道具库存模型 | [x] | 支持金币余额、道具库存、道具获得/消耗流水 | | G0-3 | 新增钱包和道具库存模型 | [x] | 支持金币余额、道具库存、道具获得/消耗流水 |
| G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 | | G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
| G0-5 | 新增每日任务或每日进度模型 | [ ] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 | | G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
| G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [ ] | 可按自然周统计 XP支持每周一刷新和历史快照 | | G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [ ] | 可按自然周统计 XP支持每周一刷新和历史快照 |
| G0-7 | 生成并提交数据库迁移 | [ ] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 | | G0-7 | 生成并提交数据库迁移 | [ ] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 |

View File

@ -287,6 +287,58 @@ export const rewardLedger = mysqlTable('reward_ledger', {
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }), foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
]); ]);
// ── Daily Progress ────────────────────────────────────────────────
// 用户每日游戏化进度,聚合首组挑战、每日任务和高奖励次数。
export const userDailyProgress = mysqlTable('user_daily_progress', {
id: char('id', { length: 36 }).primaryKey(),
userId: char('user_id', { length: 36 }).notNull(),
progressDate: date('progress_date').notNull(), // 统计日期,服务端按 UTC 自然日写入。
timezone: varchar('timezone', { length: 50 }).default('UTC'), // 客户端或服务端用于展示的时区。
firstChallengeSessionId: char('first_challenge_session_id', { length: 36 }), // 当日首组完成挑战。
firstChallengeCompletedAt: datetime('first_challenge_completed_at'), // 当日首组挑战完成时间。
challengeSessionsCompleted: smallint('challenge_sessions_completed').default(0), // 当日完成挑战组数。
highRewardSessionsMax: smallint('high_reward_sessions_max').default(3), // 当日高奖励挑战次数上限。
highRewardSessionsUsed: smallint('high_reward_sessions_used').default(0), // 当日已消耗高奖励次数。
highRewardSessionsRestored: smallint('high_reward_sessions_restored').default(0), // 当日通过广告等方式恢复的高奖励次数。
dailyTasksCompleted: smallint('daily_tasks_completed').default(0), // 当日已完成任务数。
dailyTasksRewardClaimed: smallint('daily_tasks_reward_claimed').default(0), // 当日已领取任务奖励数。
xpEarned: int('xp_earned').default(0), // 当日获得 XP。
coinsEarned: int('coins_earned').default(0), // 当日获得金币。
streakCounted: tinyint('streak_counted').default(0), // 当日是否已计入连续学习。
metadata: json('metadata').$type<Record<string, unknown>>(), // 每日宝箱、降级倍率等扩展状态。
createdAt: datetime('created_at').default(sql`CURRENT_TIMESTAMP`), // 创建时间。
updatedAt: datetime('updated_at').default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`), // 更新时间。
}, (table) => [
uniqueIndex('uk_daily_progress_user_date').on(table.userId, table.progressDate),
index('idx_daily_progress_date').on(table.progressDate),
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
foreignKey({ columns: [table.firstChallengeSessionId], foreignColumns: [challengeSessions.id] }),
]);
// 用户每日任务进度,用于幂等记录任务完成和奖励领取。
export const userDailyTasks = mysqlTable('user_daily_tasks', {
id: char('id', { length: 36 }).primaryKey(),
dailyProgressId: char('daily_progress_id', { length: 36 }).notNull(),
userId: char('user_id', { length: 36 }).notNull(),
taskDate: date('task_date').notNull(), // 任务归属日期,服务端按 UTC 自然日写入。
taskId: varchar('task_id', { length: 80 }).notNull(), // 每日任务配置标识。
taskType: mysqlEnum('task_type', ['complete_challenge', 'earn_xp', 'answer_correct', 'review_explanation', 'use_item', 'watch_ad']).notNull(), // 任务类型。
targetCount: smallint('target_count').default(1), // 任务目标次数。
currentCount: smallint('current_count').default(0), // 当前完成次数。
status: mysqlEnum('status', ['active', 'completed', 'reward_claimed', 'expired']).default('active'), // 任务状态。
rewardSnapshot: json('reward_snapshot').$type<Record<string, unknown>>(), // 任务奖励快照。
completedAt: datetime('completed_at'), // 任务完成时间。
rewardClaimedAt: datetime('reward_claimed_at'), // 奖励领取时间。
createdAt: datetime('created_at').default(sql`CURRENT_TIMESTAMP`), // 创建时间。
updatedAt: datetime('updated_at').default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`), // 更新时间。
}, (table) => [
uniqueIndex('uk_daily_task_user_date_task').on(table.userId, table.taskDate, table.taskId),
index('idx_daily_task_progress_status').on(table.dailyProgressId, table.status),
foreignKey({ columns: [table.dailyProgressId], foreignColumns: [userDailyProgress.id] }),
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
]);
// ── Question Ratings ────────────────────────────────────────────── // ── Question Ratings ──────────────────────────────────────────────
// 用户对题目的好坏反馈数据。 // 用户对题目的好坏反馈数据。