diff --git a/db/migrations/0003_lyrical_carnage.sql b/db/migrations/0003_lyrical_carnage.sql new file mode 100644 index 0000000..6ea2abe --- /dev/null +++ b/db/migrations/0003_lyrical_carnage.sql @@ -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`); \ No newline at end of file diff --git a/db/migrations/meta/0003_snapshot.json b/db/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000..70448f2 --- /dev/null +++ b/db/migrations/meta/0003_snapshot.json @@ -0,0 +1,3070 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "0c272a13-5ef4-40ac-adc8-5c10d9c351fb", + "prevId": "7f4322d5-fca2-43e2-8d99-d98ecef54b41", + "tables": { + "achievements": { + "name": "achievements", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "enum('knowledge','behavior')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(300)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "condition": { + "name": "condition", + "type": "json", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "achievements_id": { + "name": "achievements_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "ad_recovery_sessions": { + "name": "ad_recovery_sessions", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "enum('hearts','bonusAttempts','streakProtection')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "enum('pending','settling','completed','failed','expired')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "client_request_id": { + "name": "client_request_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "complete_request_id": { + "name": "complete_request_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "platform": { + "name": "platform", + "type": "enum('ios','android','harmony','web')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ad_provider": { + "name": "ad_provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ad_placement_id": { + "name": "ad_placement_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_reward_token": { + "name": "provider_reward_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_snapshot": { + "name": "reward_snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "progress_before": { + "name": "progress_before", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "progress_after": { + "name": "progress_after", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_error": { + "name": "provider_error", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duplicate_count": { + "name": "duplicate_count", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "expires_at": { + "name": "expires_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_ad_recovery_user_client_request": { + "name": "uk_ad_recovery_user_client_request", + "columns": [ + "user_id", + "client_request_id" + ], + "isUnique": true + }, + "idx_ad_recovery_user_type_status_created": { + "name": "idx_ad_recovery_user_type_status_created", + "columns": [ + "user_id", + "type", + "status", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "ad_recovery_sessions_user_id_users_id_fk": { + "name": "ad_recovery_sessions_user_id_users_id_fk", + "tableFrom": "ad_recovery_sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "ad_recovery_sessions_id": { + "name": "ad_recovery_sessions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "admin_audit_log": { + "name": "admin_audit_log", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "admin_id": { + "name": "admin_id", + "type": "varchar(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource": { + "name": "resource", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "admin_audit_log_id": { + "name": "admin_audit_log_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "admin_users": { + "name": "admin_users", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password_hash": { + "name": "password_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "enum('admin','super_admin')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'admin'" + }, + "is_active": { + "name": "is_active", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "last_login_at": { + "name": "last_login_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_admin_username": { + "name": "uk_admin_username", + "columns": [ + "username" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "admin_users_id": { + "name": "admin_users_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "categories": { + "name": "categories", + "columns": { + "id": { + "name": "id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_id": { + "name": "parent_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "question_count": { + "name": "question_count", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "enum('active','inactive')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_slug": { + "name": "uk_slug", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "categories_id": { + "name": "categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "challenge_session_answers": { + "name": "challenge_session_answers", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_id": { + "name": "question_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "submit_request_id": { + "name": "submit_request_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answer_order": { + "name": "answer_order", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answer": { + "name": "answer", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "correct": { + "name": "correct", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_ms": { + "name": "time_ms", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "combo_count": { + "name": "combo_count", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "result_snapshot": { + "name": "result_snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "submitted_at": { + "name": "submitted_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_challenge_answer_session_question": { + "name": "uk_challenge_answer_session_question", + "columns": [ + "session_id", + "question_id" + ], + "isUnique": true + }, + "uk_challenge_answer_session_request": { + "name": "uk_challenge_answer_session_request", + "columns": [ + "session_id", + "submit_request_id" + ], + "isUnique": true + }, + "idx_challenge_answer_user_submitted": { + "name": "idx_challenge_answer_user_submitted", + "columns": [ + "user_id", + "submitted_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "challenge_session_answers_session_id_challenge_sessions_id_fk": { + "name": "challenge_session_answers_session_id_challenge_sessions_id_fk", + "tableFrom": "challenge_session_answers", + "tableTo": "challenge_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "challenge_session_answers_user_id_users_id_fk": { + "name": "challenge_session_answers_user_id_users_id_fk", + "tableFrom": "challenge_session_answers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "challenge_session_answers_question_id_questions_id_fk": { + "name": "challenge_session_answers_question_id_questions_id_fk", + "tableFrom": "challenge_session_answers", + "tableTo": "questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "challenge_session_answers_id": { + "name": "challenge_session_answers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "challenge_sessions": { + "name": "challenge_sessions", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "track_id": { + "name": "track_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "chapter_id": { + "name": "chapter_id", + "type": "char(36)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "enum('pending','in_progress','completed','abandoned','expired')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "client_request_id": { + "name": "client_request_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "complete_request_id": { + "name": "complete_request_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "question_ids": { + "name": "question_ids", + "type": "json", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "total_questions": { + "name": "total_questions", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 5 + }, + "answered_count": { + "name": "answered_count", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "correct_count": { + "name": "correct_count", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "high_reward_eligible": { + "name": "high_reward_eligible", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "reward_snapshot": { + "name": "reward_snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "progress_before": { + "name": "progress_before", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "progress_after": { + "name": "progress_after", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_challenge_session_user_client_request": { + "name": "uk_challenge_session_user_client_request", + "columns": [ + "user_id", + "client_request_id" + ], + "isUnique": true + }, + "uk_challenge_session_user_complete_request": { + "name": "uk_challenge_session_user_complete_request", + "columns": [ + "user_id", + "complete_request_id" + ], + "isUnique": true + }, + "idx_challenge_session_user_status_created": { + "name": "idx_challenge_session_user_status_created", + "columns": [ + "user_id", + "status", + "created_at" + ], + "isUnique": false + }, + "idx_challenge_session_chapter_status": { + "name": "idx_challenge_session_chapter_status", + "columns": [ + "chapter_id", + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "challenge_sessions_user_id_users_id_fk": { + "name": "challenge_sessions_user_id_users_id_fk", + "tableFrom": "challenge_sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "challenge_sessions_category_id_categories_id_fk": { + "name": "challenge_sessions_category_id_categories_id_fk", + "tableFrom": "challenge_sessions", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "challenge_sessions_chapter_id_skill_tree_id_fk": { + "name": "challenge_sessions_chapter_id_skill_tree_id_fk", + "tableFrom": "challenge_sessions", + "tableTo": "skill_tree", + "columnsFrom": [ + "chapter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "challenge_sessions_id": { + "name": "challenge_sessions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "inventory_transactions": { + "name": "inventory_transactions", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inventory_item_id": { + "name": "inventory_item_id", + "type": "char(36)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "enum('coins','streak_shield','double_xp_potion','heart_supply','hint_feather','mascot_outfit')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "direction": { + "name": "direction", + "type": "enum('grant','consume','adjust')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity_delta": { + "name": "quantity_delta", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "balance_after": { + "name": "balance_after", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "enum('challenge','daily_task','level_up','theme_node','chest','shop_purchase','ad_recovery','subscription','admin_grant','system_adjust')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_id": { + "name": "source_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "varchar(160)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "snapshot": { + "name": "snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_inventory_transaction_idempotency": { + "name": "uk_inventory_transaction_idempotency", + "columns": [ + "user_id", + "idempotency_key" + ], + "isUnique": true + }, + "idx_inventory_transaction_user_created": { + "name": "idx_inventory_transaction_user_created", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + }, + "idx_inventory_transaction_source": { + "name": "idx_inventory_transaction_source", + "columns": [ + "source_type", + "source_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "inventory_transactions_user_id_users_id_fk": { + "name": "inventory_transactions_user_id_users_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_transactions_inventory_item_id_user_inventory_items_id_fk": { + "name": "inventory_transactions_inventory_item_id_user_inventory_items_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "user_inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "inventory_transactions_id": { + "name": "inventory_transactions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "knowledge_cards": { + "name": "knowledge_cards", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_id": { + "name": "question_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "varchar(300)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deep_dive": { + "name": "deep_dive", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source_ref": { + "name": "source_ref", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_question": { + "name": "uk_question", + "columns": [ + "question_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "knowledge_cards_question_id_questions_id_fk": { + "name": "knowledge_cards_question_id_questions_id_fk", + "tableFrom": "knowledge_cards", + "tableTo": "questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "knowledge_cards_id": { + "name": "knowledge_cards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "leaderboard_snapshots": { + "name": "leaderboard_snapshots", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "enum('bronze','silver','gold','platinum','diamond','master','grandmaster','champion','legend','mythic')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "weekly_xp": { + "name": "weekly_xp", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "rank": { + "name": "rank", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "group_id": { + "name": "group_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "league": { + "name": "league", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_snapshot": { + "name": "reward_snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "settled_at": { + "name": "settled_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "week_start": { + "name": "week_start", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "week_end": { + "name": "week_end", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_leaderboard_snapshot_user_week": { + "name": "uk_leaderboard_snapshot_user_week", + "columns": [ + "user_id", + "week_start" + ], + "isUnique": true + }, + "idx_leaderboard_snapshot_group_rank": { + "name": "idx_leaderboard_snapshot_group_rank", + "columns": [ + "group_id", + "week_start", + "rank" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "leaderboard_snapshots_id": { + "name": "leaderboard_snapshots_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "question_ratings": { + "name": "question_ratings", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_id": { + "name": "question_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating": { + "name": "rating", + "type": "enum('good','bad')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_user_question_rating": { + "name": "uk_user_question_rating", + "columns": [ + "user_id", + "question_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "question_ratings_id": { + "name": "question_ratings_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "questions": { + "name": "questions", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stem": { + "name": "stem", + "type": "json", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_type": { + "name": "content_type", + "type": "enum('text','image','video','audio')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "correct_answer": { + "name": "correct_answer", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "distractors": { + "name": "distractors", + "type": "json", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "difficulty": { + "name": "difficulty", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dynamic_difficulty": { + "name": "dynamic_difficulty", + "type": "decimal(3,1)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "enum('system','ugc')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'system'" + }, + "creator_id": { + "name": "creator_id", + "type": "char(36)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "enum('draft','reviewing','published','archived')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'draft'" + }, + "stats": { + "name": "stats", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "('{\"timesAnswered\":0,\"correctRate\":0,\"avgTimeMs\":0}')" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "questions_category_id_categories_id_fk": { + "name": "questions_category_id_categories_id_fk", + "tableFrom": "questions", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "questions_id": { + "name": "questions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "reward_ledger": { + "name": "reward_ledger", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "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')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_id": { + "name": "source_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "enum('pending','settling','completed','failed','reversed')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pending'" + }, + "reward_snapshot": { + "name": "reward_snapshot", + "type": "json", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_deltas": { + "name": "resource_deltas", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "state_before": { + "name": "state_before", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "state_after": { + "name": "state_after", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "settled_at": { + "name": "settled_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_reward_ledger_user_idempotency": { + "name": "uk_reward_ledger_user_idempotency", + "columns": [ + "user_id", + "idempotency_key" + ], + "isUnique": true + }, + "idx_reward_ledger_user_status_created": { + "name": "idx_reward_ledger_user_status_created", + "columns": [ + "user_id", + "status", + "created_at" + ], + "isUnique": false + }, + "idx_reward_ledger_source": { + "name": "idx_reward_ledger_source", + "columns": [ + "source_type", + "source_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "reward_ledger_user_id_users_id_fk": { + "name": "reward_ledger_user_id_users_id_fk", + "tableFrom": "reward_ledger", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reward_ledger_id": { + "name": "reward_ledger_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "skill_tree": { + "name": "skill_tree", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_id": { + "name": "parent_id", + "type": "char(36)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "questions_required": { + "name": "questions_required", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 4 + }, + "pass_threshold": { + "name": "pass_threshold", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 2 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "skill_tree_category_id_categories_id_fk": { + "name": "skill_tree_category_id_categories_id_fk", + "tableFrom": "skill_tree", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "skill_tree_id": { + "name": "skill_tree_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "subscriptions": { + "name": "subscriptions", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "enum('free','pro','proplus')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'free'" + }, + "platform": { + "name": "platform", + "type": "enum('huawei','apple','google')", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purchase_token": { + "name": "purchase_token", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auto_renew": { + "name": "auto_renew", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "enum('active','expired','cancelled')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_subscription_user": { + "name": "uk_subscription_user", + "columns": [ + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "subscriptions_id": { + "name": "subscriptions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_achievements": { + "name": "user_achievements", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "achievement_id": { + "name": "achievement_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unlocked_at": { + "name": "unlocked_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_user_achievement": { + "name": "uk_user_achievement", + "columns": [ + "user_id", + "achievement_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_achievements_id": { + "name": "user_achievements_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_chapter_progress": { + "name": "user_chapter_progress", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "chapter_id": { + "name": "chapter_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "enum('locked','unlocked','passed','perfect')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'locked'" + }, + "best_correct_count": { + "name": "best_correct_count", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "attempts": { + "name": "attempts", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "completed_at": { + "name": "completed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "uk_user_chapter": { + "name": "uk_user_chapter", + "columns": [ + "user_id", + "chapter_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "user_chapter_progress_user_id_users_id_fk": { + "name": "user_chapter_progress_user_id_users_id_fk", + "tableFrom": "user_chapter_progress", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_chapter_progress_chapter_id_skill_tree_id_fk": { + "name": "user_chapter_progress_chapter_id_skill_tree_id_fk", + "tableFrom": "user_chapter_progress", + "tableTo": "skill_tree", + "columnsFrom": [ + "chapter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_chapter_progress_id": { + "name": "user_chapter_progress_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_daily_progress": { + "name": "user_daily_progress", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "progress_date": { + "name": "progress_date", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timezone": { + "name": "timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'UTC'" + }, + "first_challenge_session_id": { + "name": "first_challenge_session_id", + "type": "char(36)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "first_challenge_completed_at": { + "name": "first_challenge_completed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "challenge_sessions_completed": { + "name": "challenge_sessions_completed", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "high_reward_sessions_max": { + "name": "high_reward_sessions_max", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 3 + }, + "high_reward_sessions_used": { + "name": "high_reward_sessions_used", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "high_reward_sessions_restored": { + "name": "high_reward_sessions_restored", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "daily_tasks_completed": { + "name": "daily_tasks_completed", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "daily_tasks_reward_claimed": { + "name": "daily_tasks_reward_claimed", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "xp_earned": { + "name": "xp_earned", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "coins_earned": { + "name": "coins_earned", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "streak_counted": { + "name": "streak_counted", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_daily_progress_user_date": { + "name": "uk_daily_progress_user_date", + "columns": [ + "user_id", + "progress_date" + ], + "isUnique": true + }, + "idx_daily_progress_date": { + "name": "idx_daily_progress_date", + "columns": [ + "progress_date" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_daily_progress_user_id_users_id_fk": { + "name": "user_daily_progress_user_id_users_id_fk", + "tableFrom": "user_daily_progress", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_daily_progress_first_challenge_session_id_challenge_sessions_id_fk": { + "name": "user_daily_progress_first_challenge_session_id_challenge_sessions_id_fk", + "tableFrom": "user_daily_progress", + "tableTo": "challenge_sessions", + "columnsFrom": [ + "first_challenge_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_daily_progress_id": { + "name": "user_daily_progress_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_daily_tasks": { + "name": "user_daily_tasks", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "daily_progress_id": { + "name": "daily_progress_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "task_date": { + "name": "task_date", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "task_id": { + "name": "task_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "task_type": { + "name": "task_type", + "type": "enum('complete_challenge','earn_xp','answer_correct','review_explanation','use_item','watch_ad')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_count": { + "name": "target_count", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "current_count": { + "name": "current_count", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "enum('active','completed','reward_claimed','expired')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'active'" + }, + "reward_snapshot": { + "name": "reward_snapshot", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_claimed_at": { + "name": "reward_claimed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_daily_task_user_date_task": { + "name": "uk_daily_task_user_date_task", + "columns": [ + "user_id", + "task_date", + "task_id" + ], + "isUnique": true + }, + "idx_daily_task_progress_status": { + "name": "idx_daily_task_progress_status", + "columns": [ + "daily_progress_id", + "status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_daily_tasks_daily_progress_id_user_daily_progress_id_fk": { + "name": "user_daily_tasks_daily_progress_id_user_daily_progress_id_fk", + "tableFrom": "user_daily_tasks", + "tableTo": "user_daily_progress", + "columnsFrom": [ + "daily_progress_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_daily_tasks_user_id_users_id_fk": { + "name": "user_daily_tasks_user_id_users_id_fk", + "tableFrom": "user_daily_tasks", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_daily_tasks_id": { + "name": "user_daily_tasks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_feedback": { + "name": "user_feedback", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "contact": { + "name": "contact", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_context": { + "name": "page_context", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_feedback_id": { + "name": "user_feedback_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_inventory_items": { + "name": "user_inventory_items", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "enum('streak_shield','double_xp_potion','heart_supply','hint_feather','mascot_outfit')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "active_until": { + "name": "active_until", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_inventory_user_item": { + "name": "uk_inventory_user_item", + "columns": [ + "user_id", + "item_id" + ], + "isUnique": true + }, + "idx_inventory_user_active": { + "name": "idx_inventory_user_active", + "columns": [ + "user_id", + "active_until" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_inventory_items_user_id_users_id_fk": { + "name": "user_inventory_items_user_id_users_id_fk", + "tableFrom": "user_inventory_items", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_inventory_items_id": { + "name": "user_inventory_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_progress": { + "name": "user_progress", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_id": { + "name": "question_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "correct": { + "name": "correct", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "time_ms": { + "name": "time_ms", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "answered_at": { + "name": "answered_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "idx_user_answered": { + "name": "idx_user_answered", + "columns": [ + "user_id", + "answered_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_progress_user_id_users_id_fk": { + "name": "user_progress_user_id_users_id_fk", + "tableFrom": "user_progress", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_progress_question_id_questions_id_fk": { + "name": "user_progress_question_id_questions_id_fk", + "tableFrom": "user_progress", + "tableTo": "questions", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_progress_id": { + "name": "user_progress_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_wallets": { + "name": "user_wallets", + "columns": { + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "coins_balance": { + "name": "coins_balance", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "lifetime_coins_earned": { + "name": "lifetime_coins_earned", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "lifetime_coins_spent": { + "name": "lifetime_coins_spent", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "user_wallets_user_id_users_id_fk": { + "name": "user_wallets_user_id_users_id_fk", + "tableFrom": "user_wallets", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_wallets_user_id": { + "name": "user_wallets_user_id", + "columns": [ + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_weekly_xp": { + "name": "user_weekly_xp", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "week_start": { + "name": "week_start", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "week_end": { + "name": "week_end", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timezone": { + "name": "timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'UTC'" + }, + "xp_earned": { + "name": "xp_earned", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "challenge_sessions_completed": { + "name": "challenge_sessions_completed", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "group_id": { + "name": "group_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rank": { + "name": "rank", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "settled": { + "name": "settled", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "settled_at": { + "name": "settled_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_xp_at": { + "name": "last_xp_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "next_refresh_at": { + "name": "next_refresh_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_weekly_xp_user_week": { + "name": "uk_weekly_xp_user_week", + "columns": [ + "user_id", + "week_start" + ], + "isUnique": true + }, + "idx_weekly_xp_group_rank": { + "name": "idx_weekly_xp_group_rank", + "columns": [ + "group_id", + "week_start", + "xp_earned" + ], + "isUnique": false + }, + "idx_weekly_xp_week_settled": { + "name": "idx_weekly_xp_week_settled", + "columns": [ + "week_start", + "settled" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_weekly_xp_user_id_users_id_fk": { + "name": "user_weekly_xp_user_id_users_id_fk", + "tableFrom": "user_weekly_xp", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_weekly_xp_id": { + "name": "user_weekly_xp_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "char(36)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "auth_type": { + "name": "auth_type", + "type": "enum('huawei','guest','phone','apple','google')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "auth_id": { + "name": "auth_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "nickname": { + "name": "nickname", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "enum('free','pro','proplus')", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'free'" + }, + "xp_total": { + "name": "xp_total", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "streak_days": { + "name": "streak_days", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "streak_last_date": { + "name": "streak_last_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hearts_remaining": { + "name": "hearts_remaining", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 5 + }, + "hearts_last_restore": { + "name": "hearts_last_restore", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "daily_xp_goal": { + "name": "daily_xp_goal", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 50 + }, + "daily_xp_earned": { + "name": "daily_xp_earned", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "daily_xp_date": { + "name": "daily_xp_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "current_theme": { + "name": "current_theme", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'inkTeal'" + }, + "active_track_id": { + "name": "active_track_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "daily_attempts_left": { + "name": "daily_attempts_left", + "type": "smallint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 5 + }, + "daily_attempts_date": { + "name": "daily_attempts_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "check_in_days": { + "name": "check_in_days", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "last_check_in_date": { + "name": "last_check_in_date", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "streak_protected_until": { + "name": "streak_protected_until", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" + } + }, + "indexes": { + "uk_auth": { + "name": "uk_auth", + "columns": [ + "auth_type", + "auth_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_id": { + "name": "users_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/_journal.json b/db/migrations/meta/_journal.json index 42a6bf7..e096b47 100644 --- a/db/migrations/meta/_journal.json +++ b/db/migrations/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1777965665440, "tag": "0002_foamy_rachel_grey", "breakpoints": true + }, + { + "idx": 3, + "version": "5", + "when": 1778494900458, + "tag": "0003_lyrical_carnage", + "breakpoints": true } ] } \ No newline at end of file diff --git a/docs/gamification-server-plan.md b/docs/gamification-server-plan.md index 569ff78..7ecd68c 100644 --- a/docs/gamification-server-plan.md +++ b/docs/gamification-server-plan.md @@ -35,7 +35,7 @@ | G0-4 | 新增奖励流水模型 | [x] | 记录奖励来源、幂等 key、奖励快照、发放前后状态 | | G0-5 | 新增每日任务或每日进度模型 | [x] | 可统计每日首组挑战、每日任务完成、每日高奖励次数 | | G0-6 | 新增周 XP 统计模型或扩展周榜快照 | [x] | 可按自然周统计 XP,支持每周一刷新和历史快照 | -| G0-7 | 生成并提交数据库迁移 | [ ] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 | +| G0-7 | 生成并提交数据库迁移 | [x] | `db/migrations/` 包含 schema 变更,迁移文件不被 `.gitignore` 遗漏 | ## Phase G1:挑战组与答题结算