-- 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, title VARCHAR(128) NULL, conversation_type TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-direct 2-group', member_signature CHAR(64) NOT NULL COMMENT 'sha256 of sorted member user_ids, e.g. sha256("1,2")', 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_type_member_signature (conversation_type, member_signature), KEY idx_last_message_at (last_message_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS chat_conversation_member ( conversation_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, member_role TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT '1-owner 2-member', joined_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), left_at DATETIME(3) NULL, PRIMARY KEY (conversation_id, user_id), KEY idx_user_conversation (user_id, conversation_id), CONSTRAINT fk_member_conv FOREIGN KEY (conversation_id) REFERENCES chat_conversation(id) ON DELETE CASCADE, CONSTRAINT fk_member_user FOREIGN KEY (user_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, title, conversation_type, member_signature, status, last_seq, message_count, last_message_preview, last_message_at, created_at, updated_at ) VALUES ( 1, 'Direct Chat', 1, '17f8af97ad4a7f7639a4c9171d5185cbafb85462877a4746c21bdb0a4f940ca0', 1, 4, 4, 'user: Great, explain how to solve it.', '2026-03-25 10:00:30.000', '2026-03-25 10:00:00.000', '2026-03-25 10:00:30.000' ), ( 2, 'Family Group', 2, '8a6ae15122001229edb8866f56e342af12ae8187203c3e3b33931743e7c0c48d', 1, 3, 3, 'user: Okay, we will wait in the living room.', '2026-03-25 21:16:10.000', '2026-03-25 21:15:00.000', '2026-03-25 21:16:10.000' ); INSERT INTO chat_conversation_member ( conversation_id, user_id, member_role, joined_at, left_at ) VALUES (1, 1, 1, '2026-03-25 10:00:00.000', NULL), (1, 2, 2, '2026-03-25 10:00:00.000', NULL), (2, 1, 1, '2026-03-25 21:15:00.000', NULL), (2, 2, 2, '2026-03-25 21:15:00.000', NULL), (2, 3, 2, '2026-03-25 21:15:00.000', NULL); 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' ), ( 2001, 2, 1, 1, 1, 1, 'Dinner at 7?', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c2-m1', '2026-03-25 21:15:08.000' ), ( 2002, 2, 2, 3, 1, 2, NULL, JSON_OBJECT('note', 'voice message'), 'audios/family-group-001.mp3', 95000, 'c2-m2', '2026-03-25 21:15:30.000' ), ( 2003, 2, 3, 2, 1, 1, 'Okay, we will wait in the living room.', JSON_OBJECT('lang', 'en'), NULL, NULL, 'c2-m3', '2026-03-25 21:16:10.000' );