Add weekly XP schema

This commit is contained in:
Wang Zhuoxuan 2026-05-11 18:18:33 +08:00
parent 7a617ce1f9
commit 6a655d0ce2
2 changed files with 32 additions and 4 deletions

View File

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

View File

@ -396,12 +396,40 @@ export const leaderboardSnapshots = mysqlTable('leaderboard_snapshots', {
tier: mysqlEnum('tier', ['bronze', 'silver', 'gold', 'platinum', 'diamond', 'master', 'grandmaster', 'champion', 'legend', 'mythic']).notNull(), // 排行榜段位。
weeklyXp: int('weekly_xp').default(0), // 本周累计经验值。
rank: int('rank'), // 当前排名。
groupId: varchar('group_id', { length: 80 }), // 周榜分组 ID。
league: varchar('league', { length: 50 }), // 所属联赛或榜单分组。
weekStart: date('week_start'), // 统计周开始日期。
weekEnd: date('week_end'), // 统计周结束日期。
rewardSnapshot: json('reward_snapshot').$type<Record<string, unknown>>(), // 周结算奖励预览或实际发放快照。
settledAt: datetime('settled_at'), // 周榜结算时间。
weekStart: date('week_start').notNull(), // 统计周开始日期,按 UTC 自然周一。
weekEnd: date('week_end').notNull(), // 统计周结束日期,按 UTC 自然周日。
createdAt: datetime('created_at').default(sql`CURRENT_TIMESTAMP`), // 创建时间。
}, (table) => [
index('idx_user_week').on(table.userId, table.weekStart),
uniqueIndex('uk_leaderboard_snapshot_user_week').on(table.userId, table.weekStart),
index('idx_leaderboard_snapshot_group_rank').on(table.groupId, table.weekStart, table.rank),
]);
// 用户每周 XP 统计,作为当前周排行榜和历史快照的累计数据源。
export const userWeeklyXp = mysqlTable('user_weekly_xp', {
id: char('id', { length: 36 }).primaryKey(),
userId: char('user_id', { length: 36 }).notNull(),
weekStart: date('week_start').notNull(), // UTC 自然周一。
weekEnd: date('week_end').notNull(), // UTC 自然周日。
timezone: varchar('timezone', { length: 50 }).default('UTC'), // 周期展示时区。
xpEarned: int('xp_earned').default(0), // 本周累计 XP。
challengeSessionsCompleted: int('challenge_sessions_completed').default(0), // 本周完成挑战组数。
groupId: varchar('group_id', { length: 80 }), // 分配到的周榜分组 ID。
rank: int('rank'), // 当前组内排名缓存。
settled: tinyint('settled').default(0), // 本周是否已完成结算。
settledAt: datetime('settled_at'), // 结算时间。
lastXpAt: datetime('last_xp_at'), // 最近一次 XP 累加时间。
nextRefreshAt: datetime('next_refresh_at'), // 下一次周榜刷新时间。
createdAt: datetime('created_at').default(sql`CURRENT_TIMESTAMP`), // 创建时间。
updatedAt: datetime('updated_at').default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`), // 更新时间。
}, (table) => [
uniqueIndex('uk_weekly_xp_user_week').on(table.userId, table.weekStart),
index('idx_weekly_xp_group_rank').on(table.groupId, table.weekStart, table.xpEarned),
index('idx_weekly_xp_week_settled').on(table.weekStart, table.settled),
foreignKey({ columns: [table.userId], foreignColumns: [users.id] }),
]);
// ── Subscriptions ──────────────────────────────────────────────────