1041 lines
57 KiB
SQL
1041 lines
57 KiB
SQL
-- Author: fruit.com.cn
|
||
|
||
-- FruitShorts 短剧服务 · 第一阶段增量建表(PostgreSQL 18)
|
||
--
|
||
-- 对应 docs/prd-phase1.md 的 10 个模块,只做**增量**:不修改已有表结构,
|
||
-- 已有表缺的字段用 ALTER TABLE ... ADD COLUMN IF NOT EXISTS 追加。
|
||
-- 全部语句幂等,可重复执行。
|
||
--
|
||
-- 执行顺序:本脚本 → sql/partition-maintenance.sql
|
||
-- watch_history / playback_ticket / order_event 在本脚本里就定义成按月分区;
|
||
-- 具体的月分区由 sql/partition-maintenance.sql 创建(并把已存在的普通表转成分区表)。
|
||
--
|
||
-- 命名与类型约定(与 sql/schema.sql 保持一致):
|
||
-- BIGINT PRIMARY KEY
|
||
-- 布尔语义用 SMALLINT(1/0),时间用 TIMESTAMP(3),默认 CURRENT_TIMESTAMP
|
||
-- 逻辑删除 deleted SMALLINT NOT NULL DEFAULT 0
|
||
-- updated_at 由 set_updated_at() 触发器维护
|
||
-- 标注「预留」的字段第一阶段只建不写,为第二至四阶段留位
|
||
|
||
-- ============================================================
|
||
-- 一、已有表补字段(第一阶段必须 / 预留)
|
||
-- ============================================================
|
||
|
||
-- 短剧:内容分级、可播区域、版权窗口、标签、创作者归属
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS creator_id BIGINT NOT NULL DEFAULT 0;
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS tags VARCHAR(255) NOT NULL DEFAULT '';
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS rating VARCHAR(16) NOT NULL DEFAULT 'ALL'; -- ALL/R13/R17/R21,印尼内容分级
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS region_codes VARCHAR(255) NOT NULL DEFAULT ''; -- 空=全区域;否则逗号分隔 ISO 国家码
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS copyright_owner VARCHAR(128) NOT NULL DEFAULT '';
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS copyright_start_at TIMESTAMP(3);
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS copyright_end_at TIMESTAMP(3); -- 到期自动下架
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS subtitle_langs VARCHAR(255) NOT NULL DEFAULT ''; -- 预留:多语言字幕(第三阶段)
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS trailer_url VARCHAR(512) NOT NULL DEFAULT '';
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS recommend_weight INT NOT NULL DEFAULT 0; -- 预留:推荐权重(第三阶段)
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS is_vip_only SMALLINT NOT NULL DEFAULT 0; -- 预留:会员专属(第二阶段)
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS audit_status VARCHAR(16) NOT NULL DEFAULT 'approved'; -- 审核状态,创作者内容用
|
||
ALTER TABLE drama ADD COLUMN IF NOT EXISTS audit_note VARCHAR(500) NOT NULL DEFAULT '';
|
||
COMMENT ON COLUMN drama.region_codes IS '可播区域 ISO 国家码,逗号分隔,空=不限';
|
||
|
||
-- 剧集:转码产物、试看、字幕
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS transcode_status VARCHAR(16) NOT NULL DEFAULT 'none'; -- none/queued/running/success/failed
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS hls_url VARCHAR(512) NOT NULL DEFAULT ''; -- 多码率 master playlist
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS preview_seconds INT NOT NULL DEFAULT 0; -- 试看秒数,0=不限制
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS subtitle_url VARCHAR(512) NOT NULL DEFAULT '';
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS subtitle_lang VARCHAR(16) NOT NULL DEFAULT '';
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS is_vip_only SMALLINT NOT NULL DEFAULT 0; -- 预留:会员专属(第二阶段)
|
||
ALTER TABLE drama_episode ADD COLUMN IF NOT EXISTS like_count BIGINT NOT NULL DEFAULT 0; -- 本集点赞数
|
||
|
||
-- 媒资:CDN 地址与转码状态
|
||
ALTER TABLE media_asset ADD COLUMN IF NOT EXISTS cdn_url VARCHAR(512) NOT NULL DEFAULT '';
|
||
ALTER TABLE media_asset ADD COLUMN IF NOT EXISTS transcode_status VARCHAR(16) NOT NULL DEFAULT 'none';
|
||
ALTER TABLE media_asset ADD COLUMN IF NOT EXISTS storage_type VARCHAR(16) NOT NULL DEFAULT 'LOCAL'; -- LOCAL/OSS,预留对象存储切换
|
||
|
||
-- ============================================================
|
||
-- 二、用户域(PRD 02 用户账号与登录)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user (
|
||
id BIGINT PRIMARY KEY,
|
||
uid VARCHAR(32) NOT NULL, -- 对外展示的用户标识,避免暴露自增 ID
|
||
nickname VARCHAR(64) NOT NULL DEFAULT '',
|
||
avatar_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
gender VARCHAR(16) NOT NULL DEFAULT '', -- male/female/unknown
|
||
birthday DATE,
|
||
country_code VARCHAR(8) NOT NULL DEFAULT 'ID', -- 注册国家
|
||
region_code VARCHAR(16) NOT NULL DEFAULT 'ID', -- 印尼本地运营区域,预留到省级
|
||
language VARCHAR(16) NOT NULL DEFAULT 'id-ID',
|
||
timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Jakarta',
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'normal', -- normal/limited/banned/deactivated
|
||
register_source VARCHAR(32) NOT NULL DEFAULT 'APP', -- APP/H5/ADMIN
|
||
register_channel VARCHAR(64) NOT NULL DEFAULT '', -- 预留:投放渠道,归因用
|
||
register_device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
register_ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
last_login_at TIMESTAMP(3),
|
||
last_login_ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
age_verified SMALLINT NOT NULL DEFAULT 0,
|
||
agreement_version VARCHAR(32) NOT NULL DEFAULT '',
|
||
agreement_at TIMESTAMP(3),
|
||
vip_level SMALLINT NOT NULL DEFAULT 0, -- 预留:会员等级(第二阶段)
|
||
vip_expire_at TIMESTAMP(3), -- 预留:会员到期(第二阶段)
|
||
risk_level SMALLINT NOT NULL DEFAULT 0, -- 预留:风控等级
|
||
invite_code VARCHAR(32) NOT NULL DEFAULT '', -- 预留:邀请码
|
||
referrer_user_id BIGINT NOT NULL DEFAULT 0, -- 预留:推荐人
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_user_uid ON app_user (uid);
|
||
CREATE INDEX IF NOT EXISTS idx_app_user_status ON app_user (status, created_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_app_user_created ON app_user (created_at DESC);
|
||
COMMENT ON TABLE app_user IS 'C 端用户';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_credential (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
type VARCHAR(16) NOT NULL, -- PHONE/EMAIL/APPLE/GOOGLE
|
||
identifier VARCHAR(191) NOT NULL, -- E.164 手机号 / 邮箱 / 第三方唯一标识
|
||
federation_id VARCHAR(191) NOT NULL DEFAULT '', -- 预留:微信等第三方 unionid
|
||
app_id VARCHAR(64) NOT NULL DEFAULT '', -- 第三方登录的 appId
|
||
password_hash VARCHAR(100) NOT NULL DEFAULT '', -- 手机号/邮箱密码登录用,第三方为空
|
||
verified SMALLINT NOT NULL DEFAULT 0,
|
||
verified_at TIMESTAMP(3),
|
||
last_used_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_credential_identifier ON app_user_credential (type, identifier);
|
||
CREATE INDEX IF NOT EXISTS idx_app_credential_user ON app_user_credential (user_id);
|
||
COMMENT ON TABLE app_user_credential IS 'C 端登录凭证(手机号/邮箱/第三方)';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_session (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
refresh_token_hash VARCHAR(64) NOT NULL, -- SHA-256,不存明文
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
device_model VARCHAR(128) NOT NULL DEFAULT '',
|
||
os_version VARCHAR(64) NOT NULL DEFAULT '',
|
||
app_version VARCHAR(32) NOT NULL DEFAULT '',
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
user_agent VARCHAR(255) NOT NULL DEFAULT '',
|
||
platform VARCHAR(16) NOT NULL DEFAULT '', -- IOS/ANDROID
|
||
expire_at TIMESTAMP(3) NOT NULL,
|
||
revoked_at TIMESTAMP(3),
|
||
last_active_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_session_token ON app_user_session (refresh_token_hash);
|
||
CREATE INDEX IF NOT EXISTS idx_app_session_user ON app_user_session (user_id, revoked_at);
|
||
COMMENT ON TABLE app_user_session IS 'C 端会话(可多设备)';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_otp (
|
||
id BIGINT PRIMARY KEY,
|
||
scene VARCHAR(16) NOT NULL, -- REGISTER/LOGIN/RESET_PWD/BIND
|
||
target_type VARCHAR(16) NOT NULL, -- PHONE/EMAIL
|
||
target VARCHAR(191) NOT NULL,
|
||
code_hash VARCHAR(64) NOT NULL, -- SHA-256(验证码+盐),不存明文
|
||
expire_at TIMESTAMP(3) NOT NULL,
|
||
used_at TIMESTAMP(3),
|
||
send_ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
send_device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
send_count INT NOT NULL DEFAULT 1,
|
||
fail_count INT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_app_otp_target ON app_user_otp (target_type, target, created_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_app_otp_ip ON app_user_otp (send_ip, created_at DESC);
|
||
COMMENT ON TABLE app_user_otp IS '验证码发送与校验记录(同时用于反刷频控)';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_consent (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
scene VARCHAR(32) NOT NULL, -- PRIVACY/TERMS/MARKETING/PERSONALIZED_AD
|
||
version VARCHAR(32) NOT NULL DEFAULT '',
|
||
agreed SMALLINT NOT NULL DEFAULT 1,
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_app_consent_user ON app_user_consent (user_id, scene);
|
||
COMMENT ON TABLE app_user_consent IS '同意管理记录,商店审核与隐私合规要求可追溯';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_device (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
device_id VARCHAR(128) NOT NULL,
|
||
platform VARCHAR(16) NOT NULL DEFAULT '',
|
||
device_model VARCHAR(128) NOT NULL DEFAULT '',
|
||
first_seen_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
last_seen_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'active', -- active/removed
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_device ON app_user_device (user_id, device_id);
|
||
COMMENT ON TABLE app_user_device IS '用户设备,用于下载授权与并发播放数限制';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_user_ban_record (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
type VARCHAR(16) NOT NULL, -- BAN/LIMIT/MUTE
|
||
reason_code VARCHAR(32) NOT NULL DEFAULT '',
|
||
reason VARCHAR(500) NOT NULL DEFAULT '',
|
||
ref_type VARCHAR(32) NOT NULL DEFAULT '', -- 触发来源:REPORT/CONTENT/AUDIT
|
||
ref_id BIGINT NOT NULL DEFAULT 0,
|
||
start_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
end_at TIMESTAMP(3),
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
appeal_status VARCHAR(16) NOT NULL DEFAULT 'none', -- none/pending/approved/rejected
|
||
appeal_content VARCHAR(1000) NOT NULL DEFAULT '',
|
||
appeal_at TIMESTAMP(3),
|
||
handle_by BIGINT NOT NULL DEFAULT 0,
|
||
handle_at TIMESTAMP(3),
|
||
handle_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_app_ban_user ON app_user_ban_record (user_id, start_at DESC);
|
||
COMMENT ON TABLE app_user_ban_record IS '封禁/限制与申诉记录';
|
||
|
||
CREATE TABLE IF NOT EXISTS app_account_deletion (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending/cancelled/completed
|
||
reason VARCHAR(255) NOT NULL DEFAULT '',
|
||
requested_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
scheduled_at TIMESTAMP(3) NOT NULL, -- 冷静期结束时间,商店合规要求
|
||
cancelled_at TIMESTAMP(3),
|
||
completed_at TIMESTAMP(3),
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_app_deletion_user ON app_account_deletion (user_id) WHERE status = 'pending';
|
||
COMMENT ON TABLE app_account_deletion IS '账号注销申请与冷静期';
|
||
|
||
-- ============================================================
|
||
-- 三、播放与记录域(PRD 04 播放器 / 05 记录收藏下载)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS watch_progress (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL,
|
||
position_ms BIGINT NOT NULL DEFAULT 0,
|
||
duration_ms BIGINT NOT NULL DEFAULT 0,
|
||
finished SMALLINT NOT NULL DEFAULT 0,
|
||
play_count INT NOT NULL DEFAULT 1,
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
source VARCHAR(16) NOT NULL DEFAULT '', -- HOME/DETAIL/SEARCH/FAVORITE
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_watch_progress ON watch_progress (user_id, drama_id);
|
||
CREATE INDEX IF NOT EXISTS idx_watch_progress_user ON watch_progress (user_id, updated_at DESC);
|
||
COMMENT ON TABLE watch_progress IS '每用户每剧一条续看进度(跨端同步的权威数据)';
|
||
|
||
-- 观看流水:量大,按 watched_at 做声明式月分区,便于按期归档与清理
|
||
CREATE TABLE IF NOT EXISTS watch_history (
|
||
id BIGINT,
|
||
user_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL,
|
||
position_ms BIGINT NOT NULL DEFAULT 0,
|
||
duration_ms BIGINT NOT NULL DEFAULT 0,
|
||
source VARCHAR(16) NOT NULL DEFAULT '',
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
watched_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id, watched_at)
|
||
) PARTITION BY RANGE (watched_at);
|
||
CREATE TABLE IF NOT EXISTS watch_history_default PARTITION OF watch_history DEFAULT;
|
||
CREATE INDEX IF NOT EXISTS idx_watch_history_user ON watch_history (user_id, watched_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_watch_history_drama ON watch_history (drama_id, watched_at DESC);
|
||
COMMENT ON TABLE watch_history IS '观看流水(月分区,保留 12–24 个月)';
|
||
|
||
CREATE TABLE IF NOT EXISTS user_favorite (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
sort INT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_user_favorite ON user_favorite (user_id, drama_id);
|
||
CREATE INDEX IF NOT EXISTS idx_user_favorite_user ON user_favorite (user_id, created_at DESC);
|
||
COMMENT ON TABLE user_favorite IS '剧集收藏';
|
||
|
||
-- 播放票据:写入量最大的表之一,按 created_at 月分区。
|
||
-- 分区表的唯一索引必须包含分区键,所以主键是 (id, created_at)、
|
||
-- 唯一索引是 (ticket_no, created_at);ticket_no 是 UUID,实际唯一性不受影响。
|
||
-- 月分区本身由 sql/partition-maintenance.sql 建(含未来 3 个月)。
|
||
CREATE TABLE IF NOT EXISTS playback_ticket (
|
||
id BIGINT,
|
||
ticket_no VARCHAR(40) NOT NULL,
|
||
user_id BIGINT NOT NULL DEFAULT 0, -- 0 表示未登录的免费集
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL,
|
||
quality VARCHAR(16) NOT NULL DEFAULT 'AUTO',
|
||
play_url VARCHAR(1024) NOT NULL DEFAULT '',
|
||
expire_at TIMESTAMP(3) NOT NULL,
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ISSUED', -- ISSUED/USED/REVOKED
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||
PRIMARY KEY (id, created_at)
|
||
) PARTITION BY RANGE (created_at);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_playback_ticket_no
|
||
ON playback_ticket (ticket_no, created_at);
|
||
CREATE INDEX IF NOT EXISTS idx_playback_ticket_user
|
||
ON playback_ticket (user_id, created_at DESC);
|
||
COMMENT ON TABLE playback_ticket IS '播放票据签发审计;真正的鉴权在签名 URL 上(月分区)';
|
||
|
||
CREATE TABLE IF NOT EXISTS download_license (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL,
|
||
device_id VARCHAR(128) NOT NULL,
|
||
license_key VARCHAR(128) NOT NULL DEFAULT '', -- 本地解密密钥(加密后存)
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE', -- ACTIVE/EXPIRED/REVOKED
|
||
granted_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
expire_at TIMESTAMP(3),
|
||
revoked_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_download_license ON download_license (user_id, episode_id, device_id);
|
||
CREATE INDEX IF NOT EXISTS idx_download_license_user ON download_license (user_id, status);
|
||
COMMENT ON TABLE download_license IS '离线下载授权与设备绑定(换机/吊销)';
|
||
|
||
-- ============================================================
|
||
-- 四、互动域(PRD 06 评论、点赞与分享)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS content_comment (
|
||
id BIGINT PRIMARY KEY,
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL DEFAULT 0,
|
||
user_id BIGINT NOT NULL,
|
||
parent_id BIGINT NOT NULL DEFAULT 0,
|
||
root_id BIGINT NOT NULL DEFAULT 0,
|
||
content VARCHAR(1000) NOT NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending/published/rejected/deleted
|
||
audit_type VARCHAR(16) NOT NULL DEFAULT '', -- MACHINE/HUMAN
|
||
audit_reason VARCHAR(255) NOT NULL DEFAULT '',
|
||
like_count INT NOT NULL DEFAULT 0,
|
||
reply_count INT NOT NULL DEFAULT 0,
|
||
report_count INT NOT NULL DEFAULT 0,
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
country_code VARCHAR(8) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_comment_drama ON content_comment (drama_id, status, created_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_comment_root ON content_comment (root_id, created_at);
|
||
CREATE INDEX IF NOT EXISTS idx_comment_user ON content_comment (user_id, created_at DESC);
|
||
COMMENT ON TABLE content_comment IS '评论与回复(先审后发)';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_like (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
target_type VARCHAR(16) NOT NULL, -- DRAMA/EPISODE/COMMENT
|
||
target_id BIGINT NOT NULL,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_content_like ON content_like (user_id, target_type, target_id);
|
||
CREATE INDEX IF NOT EXISTS idx_content_like_target ON content_like (target_type, target_id);
|
||
COMMENT ON TABLE content_like IS '点赞关系;计数值异步聚合';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_report (
|
||
id BIGINT PRIMARY KEY,
|
||
reporter_id BIGINT NOT NULL,
|
||
target_type VARCHAR(16) NOT NULL, -- COMMENT/USER/DRAMA
|
||
target_id BIGINT NOT NULL,
|
||
reason_code VARCHAR(32) NOT NULL,
|
||
description VARCHAR(500) NOT NULL DEFAULT '',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending/handled/rejected
|
||
handle_by BIGINT NOT NULL DEFAULT 0,
|
||
handle_at TIMESTAMP(3),
|
||
handle_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_report_target ON content_report (target_type, target_id, status);
|
||
CREATE INDEX IF NOT EXISTS idx_report_status ON content_report (status, created_at DESC);
|
||
COMMENT ON TABLE content_report IS '举报工单';
|
||
|
||
CREATE TABLE IF NOT EXISTS user_blocklist (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
blocked_user_id BIGINT NOT NULL,
|
||
source VARCHAR(16) NOT NULL DEFAULT 'MANUAL', -- MANUAL/COMMENT
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_user_blocklist ON user_blocklist (user_id, blocked_user_id);
|
||
COMMENT ON TABLE user_blocklist IS '拉黑关系';
|
||
|
||
CREATE TABLE IF NOT EXISTS sensitive_word (
|
||
id BIGINT PRIMARY KEY,
|
||
word VARCHAR(64) NOT NULL,
|
||
category VARCHAR(32) NOT NULL DEFAULT '', -- POLITICS/PORN/ABUSE/AD/RELIGION
|
||
level VARCHAR(16) NOT NULL DEFAULT 'REVIEW', -- BLOCK/REVIEW/REPLACE
|
||
replace_text VARCHAR(64) NOT NULL DEFAULT '*',
|
||
language VARCHAR(16) NOT NULL DEFAULT 'id-ID',
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sensitive_word ON sensitive_word (language, word);
|
||
COMMENT ON TABLE sensitive_word IS '敏感词库';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_audit_record (
|
||
id BIGINT PRIMARY KEY,
|
||
biz_type VARCHAR(16) NOT NULL, -- COMMENT/DRAMA/AVATAR/CREATOR
|
||
biz_id BIGINT NOT NULL,
|
||
round INT NOT NULL DEFAULT 1,
|
||
auditor_type VARCHAR(16) NOT NULL DEFAULT 'MACHINE', -- MACHINE/HUMAN
|
||
auditor_id BIGINT NOT NULL DEFAULT 0,
|
||
result VARCHAR(16) NOT NULL, -- PASS/REJECT/NEED_MORE
|
||
reason_code VARCHAR(32) NOT NULL DEFAULT '',
|
||
reason_text VARCHAR(500) NOT NULL DEFAULT '',
|
||
snapshot TEXT, -- 审核时的内容快照,留证
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_audit_biz ON content_audit_record (biz_type, biz_id, round DESC);
|
||
COMMENT ON TABLE content_audit_record IS '审核留证(机审 + 人审都记)';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_share (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL DEFAULT 0,
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL DEFAULT 0,
|
||
channel VARCHAR(32) NOT NULL DEFAULT '', -- WHATSAPP/IG/TIKTOK/COPYLINK
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_share_drama ON content_share (drama_id, created_at DESC);
|
||
COMMENT ON TABLE content_share IS '分享事件,用于渠道效果与裂变分析';
|
||
|
||
-- ============================================================
|
||
-- 五、交易域(PRD 09 分集收费与支付)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS wallet (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
coin_balance BIGINT NOT NULL DEFAULT 0,
|
||
total_recharged_coin BIGINT NOT NULL DEFAULT 0,
|
||
total_consumed_coin BIGINT NOT NULL DEFAULT 0,
|
||
total_refunded_coin BIGINT NOT NULL DEFAULT 0,
|
||
version INT NOT NULL DEFAULT 0, -- 乐观锁
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wallet_user ON wallet (user_id);
|
||
COMMENT ON TABLE wallet IS '金币钱包;所有变动必须同时写 coin_ledger';
|
||
|
||
CREATE TABLE IF NOT EXISTS product (
|
||
id BIGINT PRIMARY KEY,
|
||
code VARCHAR(64) NOT NULL,
|
||
name VARCHAR(128) NOT NULL,
|
||
type VARCHAR(16) NOT NULL DEFAULT 'COIN_PACK', -- COIN_PACK/VIP/EPISODE_PACK(VIP 为第二阶段预留)
|
||
platform VARCHAR(16) NOT NULL DEFAULT 'ALL', -- ALL/APPLE/GOOGLE
|
||
store_product_id VARCHAR(128) NOT NULL DEFAULT '', -- 商店商品 ID
|
||
coin_amount BIGINT NOT NULL DEFAULT 0,
|
||
bonus_coin BIGINT NOT NULL DEFAULT 0, -- 赠送金币
|
||
price_amount NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
display_price VARCHAR(32) NOT NULL DEFAULT '', -- 商店返回的本地化价格
|
||
region_codes VARCHAR(255) NOT NULL DEFAULT '', -- 空=全区域
|
||
vip_days INT NOT NULL DEFAULT 0, -- 预留:会员天数
|
||
sort INT NOT NULL DEFAULT 0,
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
effective_at TIMESTAMP(3),
|
||
expire_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_product_code ON product (code);
|
||
CREATE INDEX IF NOT EXISTS idx_product_platform ON product (platform, status);
|
||
COMMENT ON TABLE product IS '商品配置(金币包;会员与活动价为后续阶段预留)';
|
||
|
||
CREATE TABLE IF NOT EXISTS orders (
|
||
id BIGINT PRIMARY KEY,
|
||
order_no VARCHAR(40) NOT NULL,
|
||
user_id BIGINT NOT NULL,
|
||
product_id BIGINT NOT NULL DEFAULT 0,
|
||
product_code VARCHAR(64) NOT NULL DEFAULT '',
|
||
product_name VARCHAR(128) NOT NULL DEFAULT '',
|
||
type VARCHAR(16) NOT NULL DEFAULT 'COIN_PACK',
|
||
platform VARCHAR(16) NOT NULL DEFAULT '', -- APPLE/GOOGLE
|
||
store_product_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
store_transaction_id VARCHAR(128) NOT NULL DEFAULT '', -- 幂等键之一
|
||
original_transaction_id VARCHAR(128) NOT NULL DEFAULT '', -- 订阅续期追溯(预留)
|
||
amount NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
coin_amount BIGINT NOT NULL DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'CREATED', -- CREATED/PAID/DELIVERED/FAILED/REFUNDING/REFUNDED/CLOSED
|
||
quantity INT NOT NULL DEFAULT 1,
|
||
region_code VARCHAR(16) NOT NULL DEFAULT '',
|
||
channel VARCHAR(64) NOT NULL DEFAULT '', -- 归因渠道
|
||
experiment_id VARCHAR(64) NOT NULL DEFAULT '', -- 预留:定价实验分组
|
||
receipt TEXT,
|
||
verify_at TIMESTAMP(3),
|
||
paid_at TIMESTAMP(3),
|
||
delivered_at TIMESTAMP(3),
|
||
fail_reason VARCHAR(500) NOT NULL DEFAULT '',
|
||
refund_at TIMESTAMP(3),
|
||
refund_reason VARCHAR(255) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_orders_no ON orders (order_no);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_orders_store_txn ON orders (platform, store_transaction_id)
|
||
WHERE store_transaction_id <> '';
|
||
CREATE INDEX IF NOT EXISTS idx_orders_user ON orders (user_id, created_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders (status, created_at DESC);
|
||
COMMENT ON TABLE orders IS '订单;orders 为保留字规避(order 是 SQL 关键字)';
|
||
|
||
-- 订单状态流转流水,同样按月分区(同 playback_ticket,主键带分区键)
|
||
CREATE TABLE IF NOT EXISTS order_event (
|
||
id BIGINT,
|
||
order_no VARCHAR(40) NOT NULL,
|
||
event VARCHAR(32) NOT NULL, -- CREATE/VERIFY_OK/VERIFY_FAIL/DELIVER/REFUND/NOTIFY
|
||
from_status VARCHAR(16) NOT NULL DEFAULT '',
|
||
to_status VARCHAR(16) NOT NULL DEFAULT '',
|
||
source VARCHAR(16) NOT NULL DEFAULT 'SERVER', -- SERVER/STORE/ADMIN/COMPENSATE
|
||
payload TEXT,
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id, created_at)
|
||
) PARTITION BY RANGE (created_at);
|
||
CREATE INDEX IF NOT EXISTS idx_order_event_no ON order_event (order_no, created_at);
|
||
COMMENT ON TABLE order_event IS '订单状态流转流水,支付争议时的证据链(月分区)';
|
||
|
||
CREATE TABLE IF NOT EXISTS store_receipt (
|
||
id BIGINT PRIMARY KEY,
|
||
platform VARCHAR(16) NOT NULL,
|
||
transaction_id VARCHAR(128) NOT NULL,
|
||
original_transaction_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
order_no VARCHAR(40) NOT NULL DEFAULT '',
|
||
user_id BIGINT NOT NULL DEFAULT 0,
|
||
product_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
purchase_at TIMESTAMP(3),
|
||
raw_payload TEXT, -- 商店原始回执,留证
|
||
verify_status VARCHAR(16) NOT NULL DEFAULT 'PENDING',-- PENDING/OK/FAIL
|
||
verify_response TEXT,
|
||
verify_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_store_receipt ON store_receipt (platform, transaction_id);
|
||
COMMENT ON TABLE store_receipt IS '商店票据去重与校验留证(重放防护)';
|
||
|
||
CREATE TABLE IF NOT EXISTS coin_ledger (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
change_amount BIGINT NOT NULL, -- 正数入账,负数出账
|
||
balance_after BIGINT NOT NULL,
|
||
biz_type VARCHAR(16) NOT NULL, -- RECHARGE/UNLOCK/REFUND/REWARD/ADMIN
|
||
biz_id VARCHAR(64) NOT NULL DEFAULT '', -- 订单号 / 剧集号
|
||
idempotent_key VARCHAR(64) NOT NULL, -- 幂等键,重复请求直接返回
|
||
remark VARCHAR(255) NOT NULL DEFAULT '',
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_coin_ledger_idem ON coin_ledger (idempotent_key);
|
||
CREATE INDEX IF NOT EXISTS idx_coin_ledger_user ON coin_ledger (user_id, created_at DESC);
|
||
COMMENT ON TABLE coin_ledger IS '金币流水,钱包余额的唯一事实来源';
|
||
|
||
CREATE TABLE IF NOT EXISTS entitlement (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL DEFAULT 0,
|
||
episode_id BIGINT NOT NULL DEFAULT 0, -- 0 表示整剧权益
|
||
type VARCHAR(16) NOT NULL DEFAULT 'EPISODE', -- EPISODE/DRAMA/VIP(预留)
|
||
source VARCHAR(16) NOT NULL DEFAULT 'ORDER', -- ORDER/ACTIVITY/ADMIN/TRIAL
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE', -- ACTIVE/EXPIRED/REVOKED
|
||
order_no VARCHAR(40) NOT NULL DEFAULT '',
|
||
start_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
expire_at TIMESTAMP(3),
|
||
revoke_reason VARCHAR(255) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_entitlement ON entitlement (user_id, type, drama_id, episode_id);
|
||
CREATE INDEX IF NOT EXISTS idx_entitlement_user ON entitlement (user_id, status);
|
||
COMMENT ON TABLE entitlement IS '权益;播放判定优先读这里';
|
||
|
||
CREATE TABLE IF NOT EXISTS refund_record (
|
||
id BIGINT PRIMARY KEY,
|
||
order_no VARCHAR(40) NOT NULL,
|
||
platform VARCHAR(16) NOT NULL DEFAULT '',
|
||
refund_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
amount NUMERIC(12,2) NOT NULL DEFAULT 0,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
reason VARCHAR(255) NOT NULL DEFAULT '',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING', -- PENDING/RECOVERED/IGNORED
|
||
notified_at TIMESTAMP(3),
|
||
handled_at TIMESTAMP(3),
|
||
handle_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_refund_order ON refund_record (order_no);
|
||
CREATE INDEX IF NOT EXISTS idx_refund_status ON refund_record (status, created_at DESC);
|
||
COMMENT ON TABLE refund_record IS '退款与撤销事件,触发权益回收';
|
||
|
||
CREATE TABLE IF NOT EXISTS reconcile_task (
|
||
id BIGINT PRIMARY KEY,
|
||
biz_date DATE NOT NULL,
|
||
platform VARCHAR(16) NOT NULL DEFAULT 'ALL',
|
||
total_count INT NOT NULL DEFAULT 0,
|
||
matched_count INT NOT NULL DEFAULT 0,
|
||
diff_count INT NOT NULL DEFAULT 0,
|
||
amount_sum NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'RUNNING', -- RUNNING/SUCCESS/FAILED
|
||
detail TEXT,
|
||
started_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
finished_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_reconcile_date ON reconcile_task (biz_date DESC, platform);
|
||
COMMENT ON TABLE reconcile_task IS '每日对账任务(与商店账单比对)';
|
||
|
||
-- ============================================================
|
||
-- 六、创作者域(PRD 07 创作者中心)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS creator (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL, -- 关联 C 端账号
|
||
code VARCHAR(32) NOT NULL,
|
||
name VARCHAR(128) NOT NULL,
|
||
type VARCHAR(16) NOT NULL DEFAULT 'INDIVIDUAL', -- INDIVIDUAL/COMPANY
|
||
contact_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
contact_mobile VARCHAR(32) NOT NULL DEFAULT '',
|
||
contact_email VARCHAR(128) NOT NULL DEFAULT '',
|
||
country_code VARCHAR(8) NOT NULL DEFAULT 'ID',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending/approved/rejected/frozen
|
||
audit_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
audit_by BIGINT NOT NULL DEFAULT 0,
|
||
audit_at TIMESTAMP(3),
|
||
settlement_currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
bank_account VARCHAR(255) NOT NULL DEFAULT '', -- 预留:加密存储(第二阶段结算)
|
||
tax_no VARCHAR(64) NOT NULL DEFAULT '', -- 预留:税号
|
||
kyc_status VARCHAR(16) NOT NULL DEFAULT 'none', -- 预留:KYC 状态
|
||
share_rate NUMERIC(5,4) NOT NULL DEFAULT 0, -- 预留:分成比例(第二阶段)
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_user ON creator (user_id);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_code ON creator (code);
|
||
CREATE INDEX IF NOT EXISTS idx_creator_status ON creator (status, created_at DESC);
|
||
COMMENT ON TABLE creator IS '创作者主体与资质';
|
||
|
||
CREATE TABLE IF NOT EXISTS creator_document (
|
||
id BIGINT PRIMARY KEY,
|
||
creator_id BIGINT NOT NULL,
|
||
type VARCHAR(32) NOT NULL, -- ID_CARD/BUSINESS_LICENSE/CONTRACT
|
||
file_url VARCHAR(512) NOT NULL,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||
audit_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
expire_at TIMESTAMP(3), -- 证件有效期
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_creator_doc ON creator_document (creator_id, type);
|
||
COMMENT ON TABLE creator_document IS '创作者资质文件';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_submission (
|
||
id BIGINT PRIMARY KEY,
|
||
creator_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
type VARCHAR(16) NOT NULL DEFAULT 'CREATE', -- CREATE/UPDATE/PUBLISH/OFFLINE
|
||
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT', -- DRAFT/SUBMITTED/REVIEWING/APPROVED/REJECTED/WITHDRAWN
|
||
version INT NOT NULL DEFAULT 1,
|
||
submit_at TIMESTAMP(3),
|
||
reviewer_id BIGINT NOT NULL DEFAULT 0,
|
||
review_at TIMESTAMP(3),
|
||
review_note VARCHAR(500) NOT NULL DEFAULT '',
|
||
reject_code VARCHAR(32) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_submission_creator ON content_submission (creator_id, status, created_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_submission_status ON content_submission (status, submit_at);
|
||
COMMENT ON TABLE content_submission IS '创作者内容提交与审核流转';
|
||
|
||
CREATE TABLE IF NOT EXISTS creator_stat_daily (
|
||
id BIGINT PRIMARY KEY,
|
||
creator_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL DEFAULT 0,
|
||
stat_date DATE NOT NULL,
|
||
play_count BIGINT NOT NULL DEFAULT 0,
|
||
finish_count BIGINT NOT NULL DEFAULT 0,
|
||
favorite_count BIGINT NOT NULL DEFAULT 0,
|
||
comment_count BIGINT NOT NULL DEFAULT 0,
|
||
unlock_count BIGINT NOT NULL DEFAULT 0,
|
||
coin_income BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_stat ON creator_stat_daily (creator_id, drama_id, stat_date);
|
||
COMMENT ON TABLE creator_stat_daily IS '创作者数据看板日汇总';
|
||
|
||
CREATE TABLE IF NOT EXISTS creator_settlement (
|
||
id BIGINT PRIMARY KEY,
|
||
creator_id BIGINT NOT NULL,
|
||
period VARCHAR(16) NOT NULL, -- 2026-10
|
||
revenue BIGINT NOT NULL DEFAULT 0,
|
||
share_rate NUMERIC(5,4) NOT NULL DEFAULT 0,
|
||
amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING', -- 预留:第二阶段的创作者结算
|
||
paid_at TIMESTAMP(3),
|
||
remark VARCHAR(255) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_settlement ON creator_settlement (creator_id, period);
|
||
COMMENT ON TABLE creator_settlement IS '预留:创作者结算(第二阶段)';
|
||
|
||
-- ============================================================
|
||
-- 七、媒资与转码域(PRD 07 转码流水线 / 04 多码率播放)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS transcode_task (
|
||
id BIGINT PRIMARY KEY,
|
||
media_asset_id BIGINT NOT NULL DEFAULT 0,
|
||
drama_id BIGINT NOT NULL DEFAULT 0,
|
||
episode_id BIGINT NOT NULL DEFAULT 0,
|
||
source_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
profile VARCHAR(32) NOT NULL DEFAULT 'default', -- 转码模板(码率阶梯组合)
|
||
status VARCHAR(16) NOT NULL DEFAULT 'QUEUED', -- QUEUED/RUNNING/SUCCESS/FAILED/CANCELED
|
||
progress INT NOT NULL DEFAULT 0,
|
||
retry_count INT NOT NULL DEFAULT 0,
|
||
error_msg TEXT NOT NULL DEFAULT '',
|
||
started_at TIMESTAMP(3),
|
||
finished_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_transcode_status ON transcode_task (status, created_at);
|
||
CREATE INDEX IF NOT EXISTS idx_transcode_episode ON transcode_task (episode_id);
|
||
COMMENT ON TABLE transcode_task IS '转码任务队列(本地 worker / 云转码)';
|
||
|
||
CREATE TABLE IF NOT EXISTS media_rendition (
|
||
id BIGINT PRIMARY KEY,
|
||
media_asset_id BIGINT NOT NULL DEFAULT 0,
|
||
drama_id BIGINT NOT NULL DEFAULT 0,
|
||
episode_id BIGINT NOT NULL DEFAULT 0,
|
||
quality VARCHAR(16) NOT NULL, -- 360P/480P/720P/1080P
|
||
format VARCHAR(16) NOT NULL DEFAULT 'HLS',
|
||
play_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
object_key VARCHAR(512) NOT NULL DEFAULT '',
|
||
width INT NOT NULL DEFAULT 0,
|
||
height INT NOT NULL DEFAULT 0,
|
||
video_bitrate INT NOT NULL DEFAULT 0, -- kbps
|
||
audio_bitrate INT NOT NULL DEFAULT 0,
|
||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||
duration_sec INT NOT NULL DEFAULT 0,
|
||
segment_count INT NOT NULL DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'ready',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_rendition ON media_rendition (media_asset_id, quality, format);
|
||
CREATE INDEX IF NOT EXISTS idx_rendition_episode ON media_rendition (episode_id);
|
||
COMMENT ON TABLE media_rendition IS '多码率产物;播放票据按此生成 master playlist';
|
||
|
||
CREATE TABLE IF NOT EXISTS media_thumbnail (
|
||
id BIGINT PRIMARY KEY,
|
||
episode_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL DEFAULT 0,
|
||
vtt_object_key VARCHAR(512) NOT NULL DEFAULT '',
|
||
sprite_prefix VARCHAR(512) NOT NULL DEFAULT '',
|
||
interval_sec INT NOT NULL DEFAULT 1,
|
||
columns INT NOT NULL DEFAULT 10,
|
||
rows INT NOT NULL DEFAULT 1,
|
||
thumb_width INT NOT NULL DEFAULT 160,
|
||
thumb_height INT NOT NULL DEFAULT 0,
|
||
frame_count INT NOT NULL DEFAULT 0,
|
||
duration_sec INT NOT NULL DEFAULT 0,
|
||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'FAILED',
|
||
error_msg VARCHAR(500) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_media_thumbnail_episode
|
||
ON media_thumbnail (episode_id);
|
||
CREATE INDEX IF NOT EXISTS idx_media_thumbnail_drama
|
||
ON media_thumbnail (drama_id);
|
||
COMMENT ON TABLE media_thumbnail IS '单集缩略图雪碧图与 VTT 时间轴';
|
||
|
||
-- ============================================================
|
||
-- 八、运营补充(PRD 08 平台运营后台 / 10 上线)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS ops_notice (
|
||
id BIGINT PRIMARY KEY,
|
||
title VARCHAR(128) NOT NULL,
|
||
content VARCHAR(2000) NOT NULL DEFAULT '',
|
||
target VARCHAR(16) NOT NULL DEFAULT 'ALL', -- ALL/USER/CREATOR
|
||
type VARCHAR(16) NOT NULL DEFAULT 'SYSTEM', -- SYSTEM/AUDIT/PAYMENT/ACTIVITY
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
publish_at TIMESTAMP(3),
|
||
expire_at TIMESTAMP(3),
|
||
created_by BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_notice_target ON ops_notice (target, status, publish_at DESC);
|
||
COMMENT ON TABLE ops_notice IS '系统公告(创作者审核结果、活动、支付通知)';
|
||
|
||
CREATE TABLE IF NOT EXISTS ops_daily_stat (
|
||
id BIGINT PRIMARY KEY,
|
||
stat_date DATE NOT NULL,
|
||
new_user_count BIGINT NOT NULL DEFAULT 0,
|
||
active_user_count BIGINT NOT NULL DEFAULT 0,
|
||
play_count BIGINT NOT NULL DEFAULT 0,
|
||
finish_count BIGINT NOT NULL DEFAULT 0,
|
||
paid_user_count BIGINT NOT NULL DEFAULT 0,
|
||
paid_order_count BIGINT NOT NULL DEFAULT 0,
|
||
revenue_amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
refund_amount NUMERIC(14,2) NOT NULL DEFAULT 0,
|
||
currency VARCHAR(8) NOT NULL DEFAULT 'IDR',
|
||
cdn_traffic_bytes BIGINT NOT NULL DEFAULT 0, -- 成本看板:单位播放成本
|
||
sms_count BIGINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_ops_daily_stat ON ops_daily_stat (stat_date);
|
||
COMMENT ON TABLE ops_daily_stat IS '经营大盘日汇总(含成本口径)';
|
||
|
||
CREATE TABLE IF NOT EXISTS audit_log (
|
||
id BIGINT PRIMARY KEY,
|
||
biz_type VARCHAR(32) NOT NULL, -- ORDER/ENTITLEMENT/USER/CREATOR/PRICE
|
||
biz_id VARCHAR(64) NOT NULL DEFAULT '',
|
||
action VARCHAR(32) NOT NULL,
|
||
before_value TEXT,
|
||
after_value TEXT,
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
operator_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
remark VARCHAR(255) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_audit_log_biz ON audit_log (biz_type, biz_id, created_at DESC);
|
||
COMMENT ON TABLE audit_log IS '业务对象变更审计(支付、权益、定价等敏感操作)';
|
||
|
||
-- ============================================================
|
||
-- 八之二、运营分类(与短剧类型 drama_category 相互独立)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_classification (
|
||
id BIGINT PRIMARY KEY,
|
||
name VARCHAR(64) NOT NULL,
|
||
sort INTEGER NOT NULL DEFAULT 0,
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_drama_classification_name ON drama_classification (name);
|
||
COMMENT ON TABLE drama_classification IS '运营分类;与短剧类型(drama_category)相互独立';
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_classification_drama (
|
||
id BIGINT PRIMARY KEY,
|
||
classification_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_classification_drama
|
||
ON drama_classification_drama (classification_id, drama_id);
|
||
CREATE INDEX IF NOT EXISTS idx_classification_drama_drama
|
||
ON drama_classification_drama (drama_id);
|
||
COMMENT ON TABLE drama_classification_drama IS '分类与短剧的关联关系';
|
||
|
||
-- ============================================================
|
||
-- 八之三、剧单(剧单有自己的一套分类,与运营分类无关)
|
||
-- ============================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_playlist_category (
|
||
id BIGINT PRIMARY KEY,
|
||
name VARCHAR(64) NOT NULL,
|
||
description VARCHAR(255) NOT NULL DEFAULT '',
|
||
sort INTEGER NOT NULL DEFAULT 0,
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_drama_playlist_category_name
|
||
ON drama_playlist_category (name);
|
||
COMMENT ON TABLE drama_playlist_category IS '剧单专属分类;与运营分类(drama_classification)相互独立';
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_playlist_category_drama (
|
||
id BIGINT PRIMARY KEY,
|
||
category_id BIGINT NOT NULL,
|
||
drama_id BIGINT NOT NULL,
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_playlist_category_drama
|
||
ON drama_playlist_category_drama (category_id, drama_id);
|
||
CREATE INDEX IF NOT EXISTS idx_playlist_category_drama_drama
|
||
ON drama_playlist_category_drama (drama_id);
|
||
COMMENT ON TABLE drama_playlist_category_drama IS '剧单分类与短剧的关联关系';
|
||
|
||
-- ============================================================
|
||
-- 九、updated_at 触发器(与已有表保持一致)
|
||
-- ============================================================
|
||
DO $$
|
||
DECLARE
|
||
t TEXT;
|
||
BEGIN
|
||
FOREACH t IN ARRAY ARRAY[
|
||
'app_user', 'app_user_credential', 'app_user_session', 'app_user_otp',
|
||
'app_user_device', 'app_user_ban_record', 'app_account_deletion',
|
||
'watch_progress', 'user_favorite', 'playback_ticket', 'download_license',
|
||
'content_comment', 'content_report', 'sensitive_word', 'content_audit_record',
|
||
'wallet', 'product', 'orders', 'store_receipt', 'entitlement', 'refund_record',
|
||
'reconcile_task', 'creator', 'creator_document', 'content_submission',
|
||
'creator_stat_daily', 'creator_settlement',
|
||
'transcode_task', 'media_rendition', 'media_thumbnail',
|
||
'drama_classification',
|
||
'drama_playlist_category',
|
||
'ops_notice', 'ops_daily_stat'
|
||
]
|
||
LOOP
|
||
EXECUTE format('DROP TRIGGER IF EXISTS trg_%s_updated_at ON %I', t, t);
|
||
EXECUTE format(
|
||
'CREATE TRIGGER trg_%s_updated_at BEFORE UPDATE ON %I FOR EACH ROW EXECUTE FUNCTION set_updated_at()',
|
||
t, t);
|
||
END LOOP;
|
||
END
|
||
$$;
|
||
|
||
-- ============================================================
|
||
-- 十、离线下载(PRD 05)
|
||
-- ============================================================
|
||
|
||
-- 用户侧下载偏好:仅 Wi-Fi 下载、最高清晰度
|
||
ALTER TABLE app_user ADD COLUMN IF NOT EXISTS download_wifi_only SMALLINT NOT NULL DEFAULT 1;
|
||
ALTER TABLE app_user ADD COLUMN IF NOT EXISTS download_max_quality VARCHAR(16) NOT NULL DEFAULT 'AUTO';
|
||
|
||
-- 下载任务与断点续传进度。分片由客户端直接向 CDN 拉取,服务端只记账:
|
||
-- 用它实现"下载中/已暂停/已完成"的跨端展示、断点续传的进度恢复,以及配额统计。
|
||
CREATE TABLE IF NOT EXISTS download_task (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
device_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
drama_id BIGINT NOT NULL,
|
||
episode_id BIGINT NOT NULL,
|
||
license_id BIGINT NOT NULL DEFAULT 0,
|
||
quality VARCHAR(16) NOT NULL DEFAULT 'AUTO',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'QUEUED', -- QUEUED/DOWNLOADING/PAUSED/COMPLETED/FAILED/CANCELED
|
||
progress_percent INT NOT NULL DEFAULT 0,
|
||
downloaded_bytes BIGINT NOT NULL DEFAULT 0,
|
||
total_bytes BIGINT NOT NULL DEFAULT 0,
|
||
error_msg VARCHAR(500) NOT NULL DEFAULT '',
|
||
started_at TIMESTAMP(3),
|
||
finished_at TIMESTAMP(3),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
deleted SMALLINT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_download_task ON download_task (user_id, episode_id, device_id);
|
||
CREATE INDEX IF NOT EXISTS idx_download_task_user ON download_task (user_id, status, updated_at DESC);
|
||
COMMENT ON TABLE download_task IS '离线下载任务与断点续传进度(分片由客户端下载,服务端记账)';
|
||
|
||
DROP TRIGGER IF EXISTS trg_download_task_updated_at ON download_task;
|
||
CREATE TRIGGER trg_download_task_updated_at BEFORE UPDATE ON download_task
|
||
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
||
|
||
-- ============================================================
|
||
-- 九、07 创作者中心(Web 端):后台账号 ↔ 创作者主体
|
||
-- ============================================================
|
||
-- 第一阶段创作者直接登后台,身份复用 sys_admin_user / sys_role / sys_menu,
|
||
-- 所以 creator 需要一列账号映射;用「部分唯一索引」:
|
||
-- admin_user_id = 0 表示尚未绑定账号,多条 0 不冲突;绑定之后一账号只能对应一个创作者主体。
|
||
ALTER TABLE creator ADD COLUMN IF NOT EXISTS admin_user_id BIGINT NOT NULL DEFAULT 0;
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_admin_user ON creator (admin_user_id) WHERE admin_user_id > 0;
|
||
COMMENT ON COLUMN creator.admin_user_id IS '绑定的后台账号 sys_admin_user.id,0 表示未绑定(创作者中心据此判定归属)';
|
||
|
||
-- 手工录入的创作者还没有 C 端账号(user_id=0),而原来 uk_creator_user 是整列唯一,
|
||
-- 第二条 user_id=0 就会撞唯一键。改成部分唯一索引:只约束真的绑定了 C 端账号的行。
|
||
DROP INDEX IF EXISTS uk_creator_user;
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_creator_user ON creator (user_id) WHERE user_id > 0;
|
||
COMMENT ON INDEX uk_creator_user IS 'C 端账号唯一(user_id=0 表示还没绑,允许多条)';
|
||
|
||
-- 首登强制改密:后台给创作者开的账号,初始密码是运营口头/截图转达的,
|
||
-- 需要一个"必须先改密"的标记。放在 sys_admin_user 上(而不是新建表/复用 two_factor),
|
||
-- 语义直白且对其它账号默认 0,不影响既有登录流程。
|
||
ALTER TABLE sys_admin_user ADD COLUMN IF NOT EXISTS must_change_password SMALLINT NOT NULL DEFAULT 0;
|
||
COMMENT ON COLUMN sys_admin_user.must_change_password IS '1 = 首登强制改密(后台开号 / 重置密码后置位,改完密码清零)';
|
||
|
||
-- 超管的菜单是"全量兜底"(不走 sys_role_menu),但创作者中心这类"某类角色专属"的入口
|
||
-- 需要能对超管关掉,否则收窄 sys_role_menu 是无效的。加一个显式开关:
|
||
-- 默认 1(超管照旧看到),置 0 表示"不给超管显示"。
|
||
ALTER TABLE sys_menu ADD COLUMN IF NOT EXISTS super_admin_visible SMALLINT NOT NULL DEFAULT 1;
|
||
COMMENT ON COLUMN sys_menu.super_admin_visible IS '0 = 不给超管显示(角色专属菜单用;超管的菜单是全量兜底,不走 sys_role_menu)';
|
||
|
||
-- ============================================================
|
||
-- 十、C3 埋点事件落库(/app/events 匿名上报,供分析漏斗用)
|
||
-- ============================================================
|
||
CREATE TABLE IF NOT EXISTS app_event_log (
|
||
id BIGINT PRIMARY KEY,
|
||
event_name VARCHAR(64) NOT NULL,
|
||
event_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
user_id BIGINT,
|
||
device_id VARCHAR(64),
|
||
session_id VARCHAR(64),
|
||
page VARCHAR(128),
|
||
properties TEXT,
|
||
client_ip VARCHAR(64),
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_app_event_log_time ON app_event_log (event_time DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_app_event_log_user ON app_event_log (user_id);
|
||
CREATE INDEX IF NOT EXISTS idx_app_event_log_name_time ON app_event_log (event_name, event_time DESC);
|
||
COMMENT ON TABLE app_event_log IS '客户端埋点事件原始落库(匿名上报,供后续分析与漏斗)';
|