337 lines
15 KiB
PL/PgSQL
337 lines
15 KiB
PL/PgSQL
-- Author: fruit.com.cn
|
||
|
||
-- FruitShorts 短剧服务 · 建表脚本(PostgreSQL 18)
|
||
--
|
||
-- 与 MySQL 版本的对应关系:
|
||
-- AUTO_INCREMENT -> 无(主键由应用侧雪花算法生成:MyBatis-Plus IdType.ASSIGN_ID)
|
||
-- TINYINT -> SMALLINT
|
||
-- DATETIME(3) -> TIMESTAMP(3)
|
||
-- ON UPDATE CURRENT_TIMESTAMP -> set_updated_at() 触发器
|
||
-- 表内 KEY / UNIQUE KEY -> 单独的 CREATE INDEX / UNIQUE 约束
|
||
-- 列注释 -> 行内注释 + 表注释(COMMENT ON TABLE)
|
||
|
||
-- ========== 系统域 ==========
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_admin_user (
|
||
id BIGINT PRIMARY KEY,
|
||
username VARCHAR(64) NOT NULL, -- 登录名
|
||
password VARCHAR(100) NOT NULL, -- BCrypt 哈希
|
||
real_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
mobile VARCHAR(20) NOT NULL DEFAULT '',
|
||
email VARCHAR(128) NOT NULL DEFAULT '',
|
||
avatar VARCHAR(255) NOT NULL DEFAULT '',
|
||
status SMALLINT NOT NULL DEFAULT 1, -- 1 启用 0 停用
|
||
ip_whitelist VARCHAR(255) NOT NULL DEFAULT '',-- 空 = 不限
|
||
two_factor SMALLINT NOT NULL DEFAULT 0,
|
||
last_login_at TIMESTAMP(3),
|
||
last_login_ip VARCHAR(64) 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_sys_admin_user_username ON sys_admin_user (username);
|
||
COMMENT ON TABLE sys_admin_user IS '管理员';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_role_category (
|
||
id BIGINT PRIMARY KEY,
|
||
name VARCHAR(64) NOT NULL,
|
||
description VARCHAR(255) NOT NULL DEFAULT '',
|
||
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_sys_role_category_name ON sys_role_category (name);
|
||
COMMENT ON TABLE sys_role_category IS '角色分类';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_role (
|
||
id BIGINT PRIMARY KEY,
|
||
name VARCHAR(64) NOT NULL,
|
||
code VARCHAR(64) NOT NULL, -- 如 super_admin
|
||
category_id BIGINT NOT NULL DEFAULT 0,
|
||
level INT NOT NULL DEFAULT 9, -- 越小权限越高
|
||
description VARCHAR(255) NOT NULL DEFAULT '',
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
is_builtin SMALLINT 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_sys_role_code ON sys_role (code);
|
||
COMMENT ON TABLE sys_role IS '角色';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_admin_user_role (
|
||
id BIGINT PRIMARY KEY,
|
||
user_id BIGINT NOT NULL,
|
||
role_id BIGINT NOT NULL
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sys_admin_user_role ON sys_admin_user_role (user_id, role_id);
|
||
CREATE INDEX IF NOT EXISTS idx_sys_admin_user_role_role ON sys_admin_user_role (role_id);
|
||
COMMENT ON TABLE sys_admin_user_role IS '管理员-角色';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_permission (
|
||
id BIGINT PRIMARY KEY,
|
||
code VARCHAR(128) NOT NULL, -- 如 drama:create
|
||
name VARCHAR(64) NOT NULL,
|
||
group_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
type VARCHAR(16) NOT NULL DEFAULT 'BUTTON',
|
||
sort INT NOT NULL DEFAULT 0
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sys_permission_code ON sys_permission (code);
|
||
COMMENT ON TABLE sys_permission IS '权限点';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_menu (
|
||
id BIGINT PRIMARY KEY,
|
||
parent_id BIGINT NOT NULL DEFAULT 0,
|
||
title VARCHAR(64) NOT NULL,
|
||
route_path VARCHAR(128) NOT NULL DEFAULT '',
|
||
route_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
component VARCHAR(255) NOT NULL DEFAULT '',
|
||
redirect VARCHAR(128) NOT NULL DEFAULT '',
|
||
icon VARCHAR(64) NOT NULL DEFAULT '',
|
||
permission_code VARCHAR(128) NOT NULL DEFAULT '',
|
||
type VARCHAR(16) NOT NULL DEFAULT 'MENU', -- CATALOG/MENU/BUTTON
|
||
always_show SMALLINT NOT NULL DEFAULT 0,
|
||
affix SMALLINT NOT NULL DEFAULT 0,
|
||
sort INT NOT NULL DEFAULT 0,
|
||
visible SMALLINT NOT NULL DEFAULT 1,
|
||
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 INDEX IF NOT EXISTS idx_sys_menu_parent ON sys_menu (parent_id);
|
||
COMMENT ON TABLE sys_menu IS '菜单';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_role_permission (
|
||
id BIGINT PRIMARY KEY,
|
||
role_id BIGINT NOT NULL,
|
||
permission_id BIGINT NOT NULL
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sys_role_permission ON sys_role_permission (role_id, permission_id);
|
||
COMMENT ON TABLE sys_role_permission IS '角色-权限';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_role_menu (
|
||
id BIGINT PRIMARY KEY,
|
||
role_id BIGINT NOT NULL,
|
||
menu_id BIGINT NOT NULL
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sys_role_menu ON sys_role_menu (role_id, menu_id);
|
||
COMMENT ON TABLE sys_role_menu IS '角色-菜单';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_config (
|
||
id BIGINT PRIMARY KEY,
|
||
config_key VARCHAR(128) NOT NULL,
|
||
config_value VARCHAR(1024) NOT NULL DEFAULT '',
|
||
remark VARCHAR(255) NOT NULL DEFAULT ''
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_sys_config_key ON sys_config (config_key);
|
||
COMMENT ON TABLE sys_config IS '系统配置';
|
||
|
||
CREATE TABLE IF NOT EXISTS legal_document (
|
||
id BIGINT PRIMARY KEY,
|
||
doc_code VARCHAR(16) NOT NULL, -- terms / privacy
|
||
lang VARCHAR(8) NOT NULL, -- zh / en / id
|
||
title VARCHAR(128) NOT NULL DEFAULT '',
|
||
content TEXT NOT NULL DEFAULT '',
|
||
updated_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_legal_document_code_lang ON legal_document (doc_code, lang);
|
||
COMMENT ON TABLE legal_document IS '服务条款与隐私协议';
|
||
|
||
CREATE TABLE IF NOT EXISTS sys_operation_log (
|
||
id BIGINT PRIMARY KEY,
|
||
admin_user_id BIGINT NOT NULL DEFAULT 0,
|
||
username VARCHAR(64) NOT NULL DEFAULT '',
|
||
module VARCHAR(64) NOT NULL DEFAULT '',
|
||
action VARCHAR(64) NOT NULL DEFAULT '',
|
||
request_method VARCHAR(10) NOT NULL DEFAULT '',
|
||
request_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
request_params TEXT, -- 查询串
|
||
request_body TEXT, -- 请求体(已脱敏、截断)
|
||
response_body TEXT, -- 响应体(截断)
|
||
status VARCHAR(16) NOT NULL DEFAULT 'SUCCESS', -- SUCCESS/FAIL
|
||
error_msg VARCHAR(500) NOT NULL DEFAULT '',
|
||
duration_ms INT NOT NULL DEFAULT 0,
|
||
ip VARCHAR(64) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_sys_operation_log_admin_time ON sys_operation_log (admin_user_id, created_at);
|
||
COMMENT ON TABLE sys_operation_log IS '操作日志';
|
||
|
||
-- ========== 内容域 ==========
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_category (
|
||
id BIGINT PRIMARY KEY,
|
||
name VARCHAR(64) NOT NULL,
|
||
sort INT NOT NULL DEFAULT 0,
|
||
status SMALLINT NOT NULL DEFAULT 1
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uk_drama_category_name ON drama_category (name);
|
||
COMMENT ON TABLE drama_category IS '短剧类型';
|
||
|
||
CREATE TABLE IF NOT EXISTS drama (
|
||
id BIGINT PRIMARY KEY,
|
||
code VARCHAR(64) NOT NULL DEFAULT '',
|
||
title VARCHAR(128) NOT NULL,
|
||
subtitle VARCHAR(128) NOT NULL DEFAULT '',
|
||
category_id BIGINT NOT NULL DEFAULT 0,
|
||
category_ids VARCHAR(255) NOT NULL DEFAULT '',
|
||
genre VARCHAR(64) NOT NULL DEFAULT '',
|
||
synopsis VARCHAR(1000) NOT NULL DEFAULT '',
|
||
cover_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
horizontal_cover_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
language VARCHAR(16) NOT NULL DEFAULT 'zh-CN',
|
||
drama_type VARCHAR(16) NOT NULL DEFAULT 'LIVE',
|
||
drama_types VARCHAR(32) NOT NULL DEFAULT '',
|
||
total_episodes INT NOT NULL DEFAULT 0,
|
||
free_episodes INT NOT NULL DEFAULT 3,
|
||
unlock_coin INT NOT NULL DEFAULT 0,
|
||
is_hot SMALLINT NOT NULL DEFAULT 0, -- 是否热门,供首页热门位使用
|
||
-- draft/reviewing/approved/published/offline
|
||
status VARCHAR(16) NOT NULL DEFAULT 'draft',
|
||
publish_at TIMESTAMP(3),
|
||
published_at TIMESTAMP(3),
|
||
view_count BIGINT NOT NULL DEFAULT 0,
|
||
play_count BIGINT NOT NULL DEFAULT 0,
|
||
favorite_count BIGINT NOT NULL DEFAULT 0,
|
||
like_count BIGINT NOT NULL DEFAULT 0,
|
||
sort INT NOT NULL DEFAULT 0,
|
||
create_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_drama_status_publish ON drama (status, publish_at);
|
||
CREATE INDEX IF NOT EXISTS idx_drama_category ON drama (category_id);
|
||
CREATE INDEX IF NOT EXISTS idx_drama_updated ON drama (updated_at);
|
||
-- C 端目录:按状态取热门 / 按分类取列表
|
||
CREATE INDEX IF NOT EXISTS idx_drama_status_hot ON drama (status, is_hot, sort DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_drama_status_category ON drama (status, category_id, sort DESC);
|
||
COMMENT ON TABLE drama IS '短剧';
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_episode (
|
||
id BIGINT PRIMARY KEY,
|
||
drama_id BIGINT NOT NULL,
|
||
ep_no INT NOT NULL,
|
||
title VARCHAR(128) NOT NULL DEFAULT '',
|
||
duration_sec INT NOT NULL DEFAULT 0,
|
||
cover_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
video_asset_id BIGINT NOT NULL DEFAULT 0,
|
||
video_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
is_free SMALLINT NOT NULL DEFAULT 0,
|
||
unlock_coin INT NOT NULL DEFAULT 0,
|
||
status VARCHAR(16) NOT NULL DEFAULT 'draft',
|
||
release_at TIMESTAMP(3),
|
||
play_count BIGINT NOT NULL DEFAULT 0,
|
||
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_drama_episode_no ON drama_episode (drama_id, ep_no);
|
||
CREATE INDEX IF NOT EXISTS idx_drama_episode_status ON drama_episode (drama_id, status);
|
||
COMMENT ON TABLE drama_episode IS '剧集';
|
||
|
||
CREATE TABLE IF NOT EXISTS drama_review_record (
|
||
id BIGINT PRIMARY KEY,
|
||
biz_type VARCHAR(16) NOT NULL, -- DRAMA/EPISODE
|
||
biz_id BIGINT NOT NULL,
|
||
action VARCHAR(16) NOT NULL, -- SUBMIT/APPROVE/REJECT/PUBLISH/OFFLINE
|
||
reason VARCHAR(500) NOT NULL DEFAULT '',
|
||
operator_id BIGINT NOT NULL DEFAULT 0,
|
||
operator_name VARCHAR(64) NOT NULL DEFAULT '',
|
||
created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_drama_review_biz ON drama_review_record (biz_type, biz_id, created_at);
|
||
COMMENT ON TABLE drama_review_record IS '审核记录';
|
||
|
||
CREATE TABLE IF NOT EXISTS content_banner (
|
||
id BIGINT PRIMARY KEY,
|
||
image_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
slot VARCHAR(32) NOT NULL DEFAULT 'HOME_TOP', -- 展示位置:HOME_TOP/DISCOVER
|
||
link_type VARCHAR(16) NOT NULL DEFAULT 'NONE', -- NONE/DRAMA/URL
|
||
link_value VARCHAR(512) NOT NULL DEFAULT '',
|
||
sort INT NOT NULL DEFAULT 0,
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
create_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_content_banner_slot ON content_banner (slot, status, sort);
|
||
COMMENT ON TABLE content_banner IS '轮播图';
|
||
|
||
-- ========== 媒资域 ==========
|
||
-- 本地存储版本:文件落在 fruitshorts.media.root-dir 下,通过 /files/** 访问
|
||
|
||
CREATE TABLE IF NOT EXISTS media_asset (
|
||
id BIGINT PRIMARY KEY,
|
||
biz_type VARCHAR(32) NOT NULL DEFAULT 'EPISODE',
|
||
file_name VARCHAR(255) NOT NULL DEFAULT '',
|
||
object_key VARCHAR(512) NOT NULL DEFAULT '',
|
||
source_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
size_bytes BIGINT NOT NULL DEFAULT 0,
|
||
md5 VARCHAR(64) NOT NULL DEFAULT '',
|
||
duration_sec INT NOT NULL DEFAULT 0,
|
||
width INT NOT NULL DEFAULT 0,
|
||
height INT NOT NULL DEFAULT 0,
|
||
-- uploading/uploaded/transcoding/ready/failed
|
||
status VARCHAR(16) NOT NULL DEFAULT 'uploading',
|
||
hls_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
mp4_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
poster_url VARCHAR(512) NOT NULL DEFAULT '',
|
||
create_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_media_asset_status ON media_asset (status);
|
||
CREATE INDEX IF NOT EXISTS idx_media_asset_biz_type ON media_asset (biz_type);
|
||
COMMENT ON TABLE media_asset IS '媒资';
|
||
|
||
CREATE TABLE IF NOT EXISTS media_upload_task (
|
||
id BIGINT PRIMARY KEY,
|
||
upload_id VARCHAR(64) NOT NULL,
|
||
biz_type VARCHAR(32) NOT NULL DEFAULT 'EPISODE',
|
||
file_name VARCHAR(255) NOT NULL DEFAULT '',
|
||
file_size BIGINT NOT NULL DEFAULT 0,
|
||
chunk_size INT NOT NULL DEFAULT 0,
|
||
total_chunks INT NOT NULL DEFAULT 0,
|
||
received_chunks INT NOT NULL DEFAULT 0,
|
||
object_key VARCHAR(512) NOT NULL DEFAULT '',
|
||
status VARCHAR(16) NOT NULL DEFAULT 'uploading',
|
||
asset_id BIGINT NOT NULL DEFAULT 0,
|
||
create_by 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_media_upload_task ON media_upload_task (upload_id);
|
||
COMMENT ON TABLE media_upload_task IS '分片上传任务';
|
||
|
||
-- ========== updated_at 维护 ==========
|
||
-- PostgreSQL 没有 ON UPDATE CURRENT_TIMESTAMP,用触发器统一维护
|
||
|
||
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS trigger AS $$
|
||
BEGIN
|
||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||
RETURN NEW;
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
|
||
DO $$
|
||
DECLARE
|
||
t text;
|
||
BEGIN
|
||
FOREACH t IN ARRAY ARRAY[
|
||
'sys_admin_user', 'sys_role_category', 'sys_role', 'sys_menu',
|
||
'drama', 'drama_episode', 'content_banner', 'media_asset', 'media_upload_task'
|
||
] 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;
|
||
$$;
|