1aaef71f52
Document-driven MVP with FastAPI backend, Vue H5, WeChat mini shell, product demo, and Docker dev stack. Co-authored-by: Cursor <cursoragent@cursor.com>
317 lines
12 KiB
SQL
317 lines
12 KiB
SQL
-- Kids AI Posture Platform V1.0 database schema
|
|
-- MySQL 8.x
|
|
-- 目标:满足审计、幂等、治理、隐私与可追溯性要求
|
|
|
|
CREATE TABLE tenants (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
name VARCHAR(128) NOT NULL,
|
|
type VARCHAR(32) NOT NULL DEFAULT 'organization',
|
|
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
|
retention_days INT NOT NULL DEFAULT 1095,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
CHECK (type IN ('organization','school','clinic','mall','other')),
|
|
CHECK (status IN ('active','inactive','frozen','closed')),
|
|
CHECK (retention_days BETWEEN 30 AND 3650)
|
|
);
|
|
|
|
CREATE TABLE users (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NULL,
|
|
phone VARCHAR(32),
|
|
phone_hash VARCHAR(128),
|
|
wechat_openid VARCHAR(128),
|
|
role VARCHAR(32) NOT NULL DEFAULT 'parent',
|
|
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
|
consent_signed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
privacy_version VARCHAR(16) DEFAULT 'v1',
|
|
last_login_at TIMESTAMP NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_users_tenant_role (tenant_id, role),
|
|
UNIQUE KEY uk_users_phone_hash (phone_hash),
|
|
UNIQUE KEY uk_users_wechat_openid (wechat_openid),
|
|
CHECK (role IN ('parent','coach','org_admin','platform_admin')),
|
|
CHECK (status IN ('active','inactive','blocked'))
|
|
);
|
|
|
|
CREATE TABLE users_passwords (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
user_id BIGINT NOT NULL,
|
|
password_hash VARCHAR(256) NOT NULL,
|
|
salt VARCHAR(64) NOT NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
KEY idx_user_password (user_id)
|
|
);
|
|
|
|
CREATE TABLE children (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NULL,
|
|
parent_user_id BIGINT NOT NULL,
|
|
coach_user_id BIGINT NULL,
|
|
name VARCHAR(64) NOT NULL,
|
|
gender VARCHAR(16) NOT NULL DEFAULT 'unknown',
|
|
birthday DATE NOT NULL,
|
|
height DECIMAL(5,2),
|
|
weight DECIMAL(5,2),
|
|
contraindications TEXT,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
|
deleted_at TIMESTAMP NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_children_parent (parent_user_id),
|
|
KEY idx_children_tenant_coach (tenant_id, coach_user_id),
|
|
KEY idx_children_status (status),
|
|
CHECK (gender IN ('male','female','unknown')),
|
|
CHECK (status IN ('active','inactive','archived')),
|
|
CHECK (height IS NULL OR (height > 20 AND height < 240)),
|
|
CHECK (weight IS NULL OR (weight > 5 AND weight < 250))
|
|
);
|
|
|
|
CREATE TABLE child_measurements (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
child_id BIGINT NOT NULL,
|
|
height DECIMAL(5,2),
|
|
weight DECIMAL(5,2),
|
|
source VARCHAR(32) NOT NULL DEFAULT 'manual',
|
|
measured_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_measurements_child_time (child_id, measured_at),
|
|
CHECK (source IN ('manual','device','coach','report')),
|
|
CHECK (height IS NULL OR (height > 20 AND height < 240)),
|
|
CHECK (weight IS NULL OR (weight > 5 AND weight < 250))
|
|
);
|
|
|
|
CREATE TABLE videos (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
child_id BIGINT NOT NULL,
|
|
uploaded_by BIGINT NOT NULL,
|
|
scene VARCHAR(64) NOT NULL,
|
|
object_key VARCHAR(512) NOT NULL,
|
|
duration_seconds INT,
|
|
width INT,
|
|
height INT,
|
|
size_bytes BIGINT,
|
|
codec VARCHAR(32),
|
|
quality_result JSON,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'uploaded',
|
|
archive_until_date DATE NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
KEY idx_videos_child_scene (child_id, scene),
|
|
KEY idx_videos_status (status),
|
|
UNIQUE KEY uk_videos_object_key (object_key),
|
|
CHECK (scene IN ('front_posture','side_posture','squat','balance','gait','custom')),
|
|
CHECK (status IN ('uploaded','transcoding','failed','ready','archived')),
|
|
CHECK (duration_seconds IS NULL OR duration_seconds BETWEEN 1 AND 1800),
|
|
CHECK (size_bytes IS NULL OR size_bytes BETWEEN 1 AND 10737418240)
|
|
);
|
|
|
|
CREATE TABLE analysis_tasks (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
child_id BIGINT NOT NULL,
|
|
video_id BIGINT NOT NULL,
|
|
task_type VARCHAR(64) NOT NULL,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'CREATED',
|
|
progress INT NOT NULL DEFAULT 0,
|
|
model_version VARCHAR(64),
|
|
error_code VARCHAR(64),
|
|
error_message VARCHAR(512),
|
|
result JSON,
|
|
idempotency_key VARCHAR(128),
|
|
retry_count INT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
started_at TIMESTAMP NULL,
|
|
finished_at TIMESTAMP NULL,
|
|
UNIQUE KEY uk_analysis_idempotency (child_id, video_id, task_type, idempotency_key),
|
|
KEY idx_analysis_child_status (child_id, status),
|
|
KEY idx_analysis_video (video_id),
|
|
KEY idx_analysis_created (created_at),
|
|
CHECK (status IN ('CREATED','QUEUED','PROCESSING','SUCCEEDED','FAILED','CANCELLED')),
|
|
CHECK (task_type IN ('posture_screening','movement_scoring','reassessment')),
|
|
CHECK (progress BETWEEN 0 AND 100),
|
|
CHECK (retry_count BETWEEN 0 AND 20)
|
|
);
|
|
|
|
CREATE TABLE reports (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
child_id BIGINT NOT NULL,
|
|
task_id BIGINT NOT NULL,
|
|
report_type VARCHAR(64) NOT NULL DEFAULT 'posture_screening',
|
|
risk_level VARCHAR(32) NOT NULL,
|
|
summary TEXT NOT NULL,
|
|
metrics JSON NOT NULL,
|
|
recommendations JSON NOT NULL,
|
|
disclaimer TEXT NOT NULL,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'draft',
|
|
reviewed_by BIGINT NULL,
|
|
reviewed_at TIMESTAMP NULL,
|
|
published_at TIMESTAMP NULL,
|
|
expires_at DATE NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
UNIQUE KEY uk_reports_task (task_id),
|
|
KEY idx_reports_child_status (child_id, status),
|
|
CHECK (risk_level IN ('low','medium','high','review_required')),
|
|
CHECK (status IN ('draft','published','retracted','archived'))
|
|
);
|
|
|
|
CREATE TABLE exercises (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
name VARCHAR(128) NOT NULL,
|
|
category VARCHAR(64) NOT NULL,
|
|
target_issue VARCHAR(128),
|
|
difficulty VARCHAR(32) NOT NULL DEFAULT 'basic',
|
|
duration_seconds INT NOT NULL DEFAULT 60,
|
|
media_url VARCHAR(512),
|
|
thumbnail_url VARCHAR(512),
|
|
rules JSON,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_exercises_category (category, status),
|
|
CHECK (difficulty IN ('basic','intermediate','advanced')),
|
|
CHECK (status IN ('active','offline','deprecated')),
|
|
CHECK (duration_seconds BETWEEN 20 AND 1800)
|
|
);
|
|
|
|
CREATE TABLE training_plans (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
child_id BIGINT NOT NULL,
|
|
report_id BIGINT NULL,
|
|
coach_user_id BIGINT NULL,
|
|
goal VARCHAR(256) NOT NULL,
|
|
cycle_days INT NOT NULL DEFAULT 28,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'draft',
|
|
plan_detail JSON NOT NULL,
|
|
started_at TIMESTAMP NULL,
|
|
ended_at TIMESTAMP NULL,
|
|
deleted_at TIMESTAMP NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_training_plans_child_status (child_id, status),
|
|
CHECK (cycle_days BETWEEN 7 AND 180),
|
|
CHECK (status IN ('draft','active','completed','paused','cancelled'))
|
|
);
|
|
|
|
CREATE TABLE training_records (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
plan_id BIGINT NOT NULL,
|
|
child_id BIGINT NOT NULL,
|
|
exercise_id BIGINT NOT NULL,
|
|
completed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
score INT,
|
|
duration_seconds INT,
|
|
feedback JSON,
|
|
note VARCHAR(512),
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
KEY idx_training_records_plan (plan_id),
|
|
KEY idx_training_records_child_time (child_id, created_at),
|
|
CHECK (score IS NULL OR score BETWEEN 0 AND 100),
|
|
CHECK (duration_seconds IS NULL OR duration_seconds BETWEEN 1 AND 7200)
|
|
);
|
|
|
|
CREATE TABLE products (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NULL,
|
|
name VARCHAR(128) NOT NULL,
|
|
product_type VARCHAR(32) NOT NULL,
|
|
price_cents INT NOT NULL,
|
|
duration_days INT NULL,
|
|
benefits JSON NOT NULL,
|
|
limit_child INTEGER NOT NULL DEFAULT 1,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CHECK (product_type IN ('video_pack','training_plan','membership','assessment_bundle','course_package')),
|
|
CHECK (status IN ('active','inactive','discontinued')),
|
|
CHECK (price_cents >= 0),
|
|
CHECK (duration_days IS NULL OR duration_days BETWEEN 1 AND 3650)
|
|
);
|
|
|
|
CREATE TABLE orders (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
user_id BIGINT NOT NULL,
|
|
child_id BIGINT NULL,
|
|
product_id BIGINT NOT NULL,
|
|
amount_cents INT NOT NULL,
|
|
pay_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
|
payment_channel VARCHAR(32),
|
|
paid_at TIMESTAMP NULL,
|
|
canceled_at TIMESTAMP NULL,
|
|
refund_status VARCHAR(32) DEFAULT 'none',
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_orders_user_status (user_id, pay_status),
|
|
KEY idx_orders_child (child_id),
|
|
CHECK (pay_status IN ('pending','paid','failed','closed','refunding','refunded')),
|
|
CHECK (refund_status IN ('none','requested','processing','completed','rejected')),
|
|
CHECK (amount_cents >= 0)
|
|
);
|
|
|
|
CREATE TABLE operation_leads (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NOT NULL,
|
|
child_id BIGINT NULL,
|
|
source VARCHAR(64) NOT NULL,
|
|
stage VARCHAR(32) NOT NULL DEFAULT 'new',
|
|
owner_user_id BIGINT NULL,
|
|
score INT DEFAULT 0,
|
|
priority TINYINT DEFAULT 3,
|
|
next_follow_at TIMESTAMP NULL,
|
|
note TEXT,
|
|
touched_at TIMESTAMP NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_leads_tenant_stage (tenant_id, stage),
|
|
KEY idx_leads_owner_next (owner_user_id, next_follow_at),
|
|
CHECK (source IN ('wechat','h5','miniapp','partner','referral','offline_campaign')),
|
|
CHECK (stage IN ('new','contacted','qualified','closed','lost','churned')),
|
|
CHECK (priority BETWEEN 1 AND 5),
|
|
CHECK (score BETWEEN 0 AND 100)
|
|
);
|
|
|
|
CREATE TABLE audit_logs (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NULL,
|
|
actor_user_id BIGINT NOT NULL,
|
|
action VARCHAR(128) NOT NULL,
|
|
resource_type VARCHAR(64) NOT NULL,
|
|
resource_id BIGINT NULL,
|
|
request_id VARCHAR(64),
|
|
ip VARCHAR(64),
|
|
user_agent VARCHAR(256),
|
|
payload JSON,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
KEY idx_audit_actor_time (actor_user_id, created_at),
|
|
KEY idx_audit_resource (resource_type, resource_id)
|
|
);
|
|
|
|
CREATE TABLE data_retention_rules (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
tenant_id BIGINT NULL,
|
|
resource_type VARCHAR(64) NOT NULL,
|
|
archive_after_days INT NOT NULL,
|
|
delete_after_days INT NOT NULL,
|
|
legal_basis VARCHAR(128),
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
UNIQUE KEY uk_retention (tenant_id, resource_type),
|
|
CHECK (delete_after_days >= archive_after_days),
|
|
CHECK (archive_after_days >= 30)
|
|
);
|
|
|
|
CREATE TABLE api_idempotency_keys (
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
scope VARCHAR(32) NOT NULL,
|
|
idempotency_key VARCHAR(128) NOT NULL,
|
|
request_path VARCHAR(255) NOT NULL,
|
|
request_hash CHAR(64) NOT NULL,
|
|
response_status INT,
|
|
response_body JSON,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE KEY uk_idempotency_scope_key (scope, idempotency_key),
|
|
KEY idx_idempotency_created (created_at)
|
|
);
|
|
|
|
-- 运行时可选索引补充说明(按需执行)
|
|
-- CREATE INDEX idx_analysis_status_updated ON analysis_tasks(status, updated_at);
|
|
-- CREATE INDEX idx_reports_risk_level ON reports(risk_level, created_at);
|
|
-- CREATE INDEX idx_orders_pay_status_updated ON orders(pay_status, updated_at);
|