-- Chat schema and seed data for mini-program -- Target: MySQL 8+ CREATE DATABASE IF NOT EXISTS mini_program DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE mini_program; SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS chat_message; DROP TABLE IF EXISTS chat_conversation_member; DROP TABLE IF EXISTS chat_conversation; DROP TABLE IF EXISTS chat_user_auth; DROP TABLE IF EXISTS chat_user; SET FOREIGN_KEY_CHECKS=1; CREATE TABLE IF NOT EXISTS chat_user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, nickname VARCHAR(64) NULL, avatar_file_key VARCHAR(255) NULL COMMENT 'Stored file key, not URL', status TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-active 2-disabled 3-deleted', last_login_at DATETIME(3) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id), KEY idx_status (status), KEY idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS chat_user_auth ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL, auth_type TINYINT UNSIGNED NOT NULL COMMENT '1-wechat_miniapp_openid 2-wechat_unionid 3-phone_sms 4-email_magic_link 5-oauth 6-username_password', auth_identifier VARCHAR(191) NOT NULL COMMENT 'openid/unionid/phone/email/oauth-sub/username', password_hash CHAR(64) NULL COMMENT 'sha256 hex for demo only; production should use bcrypt/argon2', credential_json JSON NULL COMMENT 'Extra auth payload, e.g. app_id/provider/phone_area', is_primary TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-primary login identity 0-secondary', status TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-active 2-disabled 3-revoked', last_login_at DATETIME(3) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id), UNIQUE KEY uk_auth_type_identifier (auth_type, auth_identifier), KEY idx_user_status (user_id, status), KEY idx_last_login_at (last_login_at), CONSTRAINT fk_user_auth_user FOREIGN KEY (user_id) REFERENCES chat_user(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS chat_conversation ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_low_id BIGINT UNSIGNED NOT NULL COMMENT 'Smaller user_id in direct chat pair', user_high_id BIGINT UNSIGNED NOT NULL COMMENT 'Larger user_id in direct chat pair', status TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-active 2-archived', last_seq BIGINT UNSIGNED NOT NULL DEFAULT 0, message_count BIGINT UNSIGNED NOT NULL DEFAULT 0, last_message_preview VARCHAR(255) NULL, last_message_at DATETIME(3) NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), PRIMARY KEY (id), UNIQUE KEY uk_direct_pair (user_low_id, user_high_id), KEY idx_last_message_at (last_message_at), KEY idx_user_low_id (user_low_id), KEY idx_user_high_id (user_high_id), CONSTRAINT fk_conv_user_low FOREIGN KEY (user_low_id) REFERENCES chat_user(id) ON DELETE CASCADE, CONSTRAINT fk_conv_user_high FOREIGN KEY (user_high_id) REFERENCES chat_user(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS chat_message ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, conversation_id BIGINT UNSIGNED NOT NULL, seq BIGINT UNSIGNED NOT NULL COMMENT 'Conversation-local incremental sequence', sender_user_id BIGINT UNSIGNED NULL COMMENT 'Null means assistant/system sender', role TINYINT UNSIGNED NOT NULL COMMENT '1-user 2-assistant 3-system 4-tool', content_type TINYINT UNSIGNED NOT NULL COMMENT '1-text 2-audio 3-image 4-json', content_text MEDIUMTEXT NULL, content_json JSON NULL, media_file_key VARCHAR(255) NULL COMMENT 'Stored file key, not URL', media_duration_ms INT UNSIGNED NULL, client_msg_id VARCHAR(64) NULL COMMENT 'Idempotency key from client', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), deleted_at DATETIME(3) NULL, PRIMARY KEY (id), UNIQUE KEY uk_conv_seq (conversation_id, seq), UNIQUE KEY uk_conv_client_msg (conversation_id, client_msg_id), KEY idx_conv_created (conversation_id, created_at), KEY idx_sender_created (sender_user_id, created_at), CONSTRAINT fk_msg_conv FOREIGN KEY (conversation_id) REFERENCES chat_conversation(id) ON DELETE CASCADE, CONSTRAINT fk_msg_sender_user FOREIGN KEY (sender_user_id) REFERENCES chat_user(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO chat_user ( id, nickname, avatar_file_key, status, last_login_at, created_at, updated_at ) VALUES ( 1, 'Parent A', 'avatars/parent-a.png', 1, '2026-03-25 09:58:00.000', '2026-03-25 09:00:00.000', '2026-03-25 09:58:00.000' ), ( 2, 'Parent B', 'avatars/parent-b.png', 1, '2026-03-25 10:01:00.000', '2026-03-25 09:20:00.000', '2026-03-25 10:01:00.000' ), ( 3, 'Parent C', 'avatars/parent-c.png', 1, '2026-03-25 21:11:00.000', '2026-03-25 20:30:00.000', '2026-03-25 21:11:00.000' ); INSERT INTO chat_user_auth ( id, user_id, auth_type, auth_identifier, password_hash, credential_json, is_primary, status, last_login_at, created_at, updated_at ) VALUES ( 1, 1, 6, 'test1', SHA2('123', 256), JSON_OBJECT('provider', 'local_account'), 1, 1, '2026-03-25 09:58:00.000', '2026-03-25 09:00:00.000', '2026-03-25 09:58:00.000' ), ( 2, 2, 6, 'test2', SHA2('123', 256), JSON_OBJECT('provider', 'local_account'), 1, 1, '2026-03-25 10:01:00.000', '2026-03-25 09:20:00.000', '2026-03-25 10:01:00.000' ), ( 3, 3, 6, 'test3', SHA2('123', 256), JSON_OBJECT('provider', 'local_account'), 1, 1, '2026-03-25 21:11:00.000', '2026-03-25 20:30:00.000', '2026-03-25 21:11:00.000' ); INSERT INTO chat_conversation ( id, user_low_id, user_high_id, status, last_seq, message_count, last_message_preview, last_message_at, created_at, updated_at ) VALUES ( 1, 1, 2, 1, 5, 5, 'user: Thanks, I understand now.', '2026-03-25 10:00:40.000', '2026-03-25 10:00:00.000', '2026-03-25 10:00:40.000' ); INSERT INTO chat_message ( id, conversation_id, seq, sender_user_id, role, content_type, content_text, content_json, media_file_key, media_duration_ms, client_msg_id, created_at ) VALUES ( 1001, 1, 1, 1, 1, 1, 'Hi, did you finish homework?', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c1-m1', '2026-03-25 10:00:05.000' ), ( 1002, 1, 2, 2, 1, 1, 'Almost done, one question left.', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c1-m2', '2026-03-25 10:00:12.000' ), ( 1003, 1, 3, NULL, 2, 1, 'I can help explain the last question.', JSON_OBJECT('tone', 'assistant'), NULL, NULL, 'c1-m3', '2026-03-25 10:00:20.000' ), ( 1004, 1, 4, 1, 1, 1, 'Great, explain how to solve it.', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c1-m4', '2026-03-25 10:00:30.000' ), ( 1005, 1, 5, 2, 1, 1, 'Thanks, I understand now.', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c1-m5', '2026-03-25 10:00:40.000' );