306 lines
7.5 KiB
SQL
306 lines
7.5 KiB
SQL
-- 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'
|
|
);
|