Generate game economy migrations
This commit is contained in:
parent
6a655d0ce2
commit
fd4c2b6361
201
db/migrations/0003_lyrical_carnage.sql
Normal file
201
db/migrations/0003_lyrical_carnage.sql
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
CREATE TABLE `challenge_session_answers` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`session_id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`question_id` char(36) NOT NULL,
|
||||||
|
`submit_request_id` varchar(80) NOT NULL,
|
||||||
|
`answer_order` tinyint NOT NULL,
|
||||||
|
`answer` varchar(500),
|
||||||
|
`correct` tinyint NOT NULL,
|
||||||
|
`time_ms` int,
|
||||||
|
`combo_count` tinyint DEFAULT 0,
|
||||||
|
`result_snapshot` json,
|
||||||
|
`submitted_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `challenge_session_answers_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_challenge_answer_session_question` UNIQUE(`session_id`,`question_id`),
|
||||||
|
CONSTRAINT `uk_challenge_answer_session_request` UNIQUE(`session_id`,`submit_request_id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `challenge_sessions` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`track_id` varchar(50) NOT NULL,
|
||||||
|
`category_id` varchar(50) NOT NULL,
|
||||||
|
`chapter_id` char(36),
|
||||||
|
`status` enum('pending','in_progress','completed','abandoned','expired') DEFAULT 'pending',
|
||||||
|
`client_request_id` varchar(80) NOT NULL,
|
||||||
|
`complete_request_id` varchar(80),
|
||||||
|
`question_ids` json NOT NULL,
|
||||||
|
`total_questions` tinyint DEFAULT 5,
|
||||||
|
`answered_count` tinyint DEFAULT 0,
|
||||||
|
`correct_count` tinyint DEFAULT 0,
|
||||||
|
`high_reward_eligible` tinyint DEFAULT 1,
|
||||||
|
`reward_snapshot` json,
|
||||||
|
`progress_before` json,
|
||||||
|
`progress_after` json,
|
||||||
|
`expires_at` datetime,
|
||||||
|
`completed_at` datetime,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `challenge_sessions_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_challenge_session_user_client_request` UNIQUE(`user_id`,`client_request_id`),
|
||||||
|
CONSTRAINT `uk_challenge_session_user_complete_request` UNIQUE(`user_id`,`complete_request_id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `inventory_transactions` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`inventory_item_id` char(36),
|
||||||
|
`item_id` enum('coins','streak_shield','double_xp_potion','heart_supply','hint_feather','mascot_outfit') NOT NULL,
|
||||||
|
`direction` enum('grant','consume','adjust') NOT NULL,
|
||||||
|
`quantity_delta` int NOT NULL,
|
||||||
|
`balance_after` int,
|
||||||
|
`source_type` enum('challenge','daily_task','level_up','theme_node','chest','shop_purchase','ad_recovery','subscription','admin_grant','system_adjust') NOT NULL,
|
||||||
|
`source_id` varchar(120),
|
||||||
|
`idempotency_key` varchar(160),
|
||||||
|
`snapshot` json,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `inventory_transactions_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_inventory_transaction_idempotency` UNIQUE(`user_id`,`idempotency_key`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `reward_ledger` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`source_type` enum('challenge_answer','challenge_completion','daily_task','streak_milestone','level_up','theme_node','knowledge_card','chest','shop_purchase','ad_recovery','leaderboard_settlement','subscription','admin_grant','system_adjust') NOT NULL,
|
||||||
|
`source_id` varchar(120),
|
||||||
|
`idempotency_key` varchar(160) NOT NULL,
|
||||||
|
`status` enum('pending','settling','completed','failed','reversed') DEFAULT 'pending',
|
||||||
|
`reward_snapshot` json NOT NULL,
|
||||||
|
`resource_deltas` json,
|
||||||
|
`state_before` json,
|
||||||
|
`state_after` json,
|
||||||
|
`failure_reason` varchar(120),
|
||||||
|
`settled_at` datetime,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `reward_ledger_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_reward_ledger_user_idempotency` UNIQUE(`user_id`,`idempotency_key`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `user_daily_progress` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`progress_date` date NOT NULL,
|
||||||
|
`timezone` varchar(50) DEFAULT 'UTC',
|
||||||
|
`first_challenge_session_id` char(36),
|
||||||
|
`first_challenge_completed_at` datetime,
|
||||||
|
`challenge_sessions_completed` smallint DEFAULT 0,
|
||||||
|
`high_reward_sessions_max` smallint DEFAULT 3,
|
||||||
|
`high_reward_sessions_used` smallint DEFAULT 0,
|
||||||
|
`high_reward_sessions_restored` smallint DEFAULT 0,
|
||||||
|
`daily_tasks_completed` smallint DEFAULT 0,
|
||||||
|
`daily_tasks_reward_claimed` smallint DEFAULT 0,
|
||||||
|
`xp_earned` int DEFAULT 0,
|
||||||
|
`coins_earned` int DEFAULT 0,
|
||||||
|
`streak_counted` tinyint DEFAULT 0,
|
||||||
|
`metadata` json,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `user_daily_progress_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_daily_progress_user_date` UNIQUE(`user_id`,`progress_date`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `user_daily_tasks` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`daily_progress_id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`task_date` date NOT NULL,
|
||||||
|
`task_id` varchar(80) NOT NULL,
|
||||||
|
`task_type` enum('complete_challenge','earn_xp','answer_correct','review_explanation','use_item','watch_ad') NOT NULL,
|
||||||
|
`target_count` smallint DEFAULT 1,
|
||||||
|
`current_count` smallint DEFAULT 0,
|
||||||
|
`status` enum('active','completed','reward_claimed','expired') DEFAULT 'active',
|
||||||
|
`reward_snapshot` json,
|
||||||
|
`completed_at` datetime,
|
||||||
|
`reward_claimed_at` datetime,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `user_daily_tasks_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_daily_task_user_date_task` UNIQUE(`user_id`,`task_date`,`task_id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `user_inventory_items` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`item_id` enum('streak_shield','double_xp_potion','heart_supply','hint_feather','mascot_outfit') NOT NULL,
|
||||||
|
`quantity` int DEFAULT 0,
|
||||||
|
`active_until` datetime,
|
||||||
|
`metadata` json,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `user_inventory_items_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_inventory_user_item` UNIQUE(`user_id`,`item_id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `user_wallets` (
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`coins_balance` int DEFAULT 0,
|
||||||
|
`lifetime_coins_earned` int DEFAULT 0,
|
||||||
|
`lifetime_coins_spent` int DEFAULT 0,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `user_wallets_user_id` PRIMARY KEY(`user_id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `user_weekly_xp` (
|
||||||
|
`id` char(36) NOT NULL,
|
||||||
|
`user_id` char(36) NOT NULL,
|
||||||
|
`week_start` date NOT NULL,
|
||||||
|
`week_end` date NOT NULL,
|
||||||
|
`timezone` varchar(50) DEFAULT 'UTC',
|
||||||
|
`xp_earned` int DEFAULT 0,
|
||||||
|
`challenge_sessions_completed` int DEFAULT 0,
|
||||||
|
`group_id` varchar(80),
|
||||||
|
`rank` int,
|
||||||
|
`settled` tinyint DEFAULT 0,
|
||||||
|
`settled_at` datetime,
|
||||||
|
`last_xp_at` datetime,
|
||||||
|
`next_refresh_at` datetime,
|
||||||
|
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT `user_weekly_xp_id` PRIMARY KEY(`id`),
|
||||||
|
CONSTRAINT `uk_weekly_xp_user_week` UNIQUE(`user_id`,`week_start`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
DROP INDEX `idx_user_week` ON `leaderboard_snapshots`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` MODIFY COLUMN `week_start` date NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` MODIFY COLUMN `week_end` date NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` ADD `group_id` varchar(80);--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` ADD `reward_snapshot` json;--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` ADD `settled_at` datetime;--> statement-breakpoint
|
||||||
|
ALTER TABLE `leaderboard_snapshots` ADD CONSTRAINT `uk_leaderboard_snapshot_user_week` UNIQUE(`user_id`,`week_start`);--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_session_answers` ADD CONSTRAINT `challenge_session_answers_session_id_challenge_sessions_id_fk` FOREIGN KEY (`session_id`) REFERENCES `challenge_sessions`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_session_answers` ADD CONSTRAINT `challenge_session_answers_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_session_answers` ADD CONSTRAINT `challenge_session_answers_question_id_questions_id_fk` FOREIGN KEY (`question_id`) REFERENCES `questions`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_sessions` ADD CONSTRAINT `challenge_sessions_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_sessions` ADD CONSTRAINT `challenge_sessions_category_id_categories_id_fk` FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `challenge_sessions` ADD CONSTRAINT `challenge_sessions_chapter_id_skill_tree_id_fk` FOREIGN KEY (`chapter_id`) REFERENCES `skill_tree`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `inventory_transactions` ADD CONSTRAINT `inventory_transactions_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `inventory_transactions` ADD CONSTRAINT `inventory_transactions_inventory_item_id_user_inventory_items_id_fk` FOREIGN KEY (`inventory_item_id`) REFERENCES `user_inventory_items`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `reward_ledger` ADD CONSTRAINT `reward_ledger_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_daily_progress` ADD CONSTRAINT `user_daily_progress_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_daily_progress` ADD CONSTRAINT `user_daily_progress_first_challenge_session_id_challenge_sessions_id_fk` FOREIGN KEY (`first_challenge_session_id`) REFERENCES `challenge_sessions`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_daily_tasks` ADD CONSTRAINT `user_daily_tasks_daily_progress_id_user_daily_progress_id_fk` FOREIGN KEY (`daily_progress_id`) REFERENCES `user_daily_progress`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_daily_tasks` ADD CONSTRAINT `user_daily_tasks_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_inventory_items` ADD CONSTRAINT `user_inventory_items_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_wallets` ADD CONSTRAINT `user_wallets_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE `user_weekly_xp` ADD CONSTRAINT `user_weekly_xp_user_id_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_challenge_answer_user_submitted` ON `challenge_session_answers` (`user_id`,`submitted_at`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_challenge_session_user_status_created` ON `challenge_sessions` (`user_id`,`status`,`created_at`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_challenge_session_chapter_status` ON `challenge_sessions` (`chapter_id`,`status`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_inventory_transaction_user_created` ON `inventory_transactions` (`user_id`,`created_at`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_inventory_transaction_source` ON `inventory_transactions` (`source_type`,`source_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_reward_ledger_user_status_created` ON `reward_ledger` (`user_id`,`status`,`created_at`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_reward_ledger_source` ON `reward_ledger` (`source_type`,`source_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_daily_progress_date` ON `user_daily_progress` (`progress_date`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_daily_task_progress_status` ON `user_daily_tasks` (`daily_progress_id`,`status`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_inventory_user_active` ON `user_inventory_items` (`user_id`,`active_until`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_weekly_xp_group_rank` ON `user_weekly_xp` (`group_id`,`week_start`,`xp_earned`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_weekly_xp_week_settled` ON `user_weekly_xp` (`week_start`,`settled`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `idx_leaderboard_snapshot_group_rank` ON `leaderboard_snapshots` (`group_id`,`week_start`,`rank`);
|
||||||
3070
db/migrations/meta/0003_snapshot.json
Normal file
3070
db/migrations/meta/0003_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,13 @@
|
|||||||
"when": 1777965665440,
|
"when": 1777965665440,
|
||||||
"tag": "0002_foamy_rachel_grey",
|
"tag": "0002_foamy_rachel_grey",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1778494900458,
|
||||||
|
"tag": "0003_lyrical_carnage",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@
|
|||||||
| G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
|
| G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 |
|
||||||
| G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
|
| G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 |
|
||||||
| G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [x] | 可按自然周统计 XP,支持每周一刷新和历史快照 |
|
| G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [x] | 可按自然周统计 XP,支持每周一刷新和历史快照 |
|
||||||
| G0-7 | 生成并提交数据库迁移 | [ ] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 |
|
| G0-7 | 生成并提交数据库迁移 | [x] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 |
|
||||||
|
|
||||||
## Phase G1:挑战组与答题结算
|
## Phase G1:挑战组与答题结算
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user