From 7a617ce1f9aa776a48562777412e3708e027df0d Mon Sep 17 00:00:00 2001 From: Wang Zhuoxuan Date: Mon, 11 May 2026 18:06:19 +0800 Subject: [PATCH] Add daily progress schema --- docs/gamification-server-plan.md | 2 +- src/db/schema.ts | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/gamification-server-plan.md b/docs/gamification-server-plan.md index 1180277..c70385b 100644 --- a/docs/gamification-server-plan.md +++ b/docs/gamification-server-plan.md @@ -33,7 +33,7 @@ | G0-2 | 新增挑战组数据模型 | [x] | 支持 challenge session、session answers、组状态、正确数、完成时间、幂等提交 | | G0-3 | 新增钱包和道具库存模型 | [x] | 支持金币余额、道具库存、道具获得/消耗流水 | | G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 | -| G0-5 | 新增每日任务或每日进度模型 | [ ] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 | +| G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 | | G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [ ] | 可按自然周统计 XP,支持每周一刷新和历史快照 | | G0-7 | 生成并提交数据库迁移 | [ ] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 | diff --git a/src/db/schema.ts b/src/db/schema.ts index 8ce810a..7045274 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -287,6 +287,58 @@ export const rewardLedger = mysqlTable('reward_ledger', { 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>(), // 每日宝箱、降级倍率等扩展状态。 + 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>(), // 任务奖励快照。 + 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 ────────────────────────────────────────────── // 用户对题目的好坏反馈数据。