feat: 初始化短剧服务端(管理后台 API + 客户端 API)
Spring Boot 3.2 / MyBatis-Plus / PostgreSQL,7 个 Maven 模块;含认证鉴权、内容与媒资管理、操作日志,以及客户端 /app/** 接口。
This commit is contained in:
commit
a4aa749cc6
152 files changed
+8027
No files matched your search
+191
@@ -0,0 +1,191 @@
|
||||
-- ReelShort 短剧服务 · 初始化数据(PostgreSQL 18,可重复执行)
|
||||
-- 演示账号:admin / admin123(其余种子账号密码相同)
|
||||
--
|
||||
-- MySQL 的 ON DUPLICATE KEY UPDATE 在这里换成 ON CONFLICT (id) DO UPDATE;
|
||||
-- INSERT IGNORE 换成 ON CONFLICT DO NOTHING。
|
||||
-- 末尾会把自增序列对齐到已有最大 ID,避免后续插入主键冲突。
|
||||
|
||||
-- ---------- 角色分类 ----------
|
||||
INSERT INTO sys_role_category (id, name, description, sort) VALUES
|
||||
(1, '系统管理员', '负责账号、权限和菜单配置', 1),
|
||||
(2, '内容管理', '负责短剧与剧集内容维护', 2),
|
||||
(3, '数据分析', '负责查看运营数据', 3)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description, sort = EXCLUDED.sort;
|
||||
|
||||
-- ---------- 角色 ----------
|
||||
INSERT INTO sys_role (id, name, code, category_id, level, description, status, is_builtin) VALUES
|
||||
(1, '超级管理员', 'super_admin', 1, 1, '拥有系统全部管理权限', 1, 1),
|
||||
(2, '内容运营', 'content_operator', 2, 2, '管理短剧、剧集及封面素材', 1, 1),
|
||||
(3, '审核专员', 'content_reviewer', 2, 3, '审核短剧内容与发布状态', 1, 1),
|
||||
(4, '数据分析师', 'data_analyst', 3, 4, '查看内容数据与运营报表', 1, 1)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, code = EXCLUDED.code,
|
||||
description = EXCLUDED.description, category_id = EXCLUDED.category_id, level = EXCLUDED.level;
|
||||
|
||||
-- ---------- 权限点 ----------
|
||||
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
|
||||
(1, 'drama:list', '查看短剧列表', '短剧管理', 'BUTTON', 1),
|
||||
(2, 'drama:create', '创建短剧', '短剧管理', 'BUTTON', 2),
|
||||
(3, 'drama:update', '编辑短剧信息', '短剧管理', 'BUTTON', 3),
|
||||
(4, 'drama:delete', '删除短剧', '短剧管理', 'BUTTON', 4),
|
||||
(5, 'episode:list', '查看剧集列表', '剧集管理', 'BUTTON', 5),
|
||||
(6, 'episode:upload', '上传剧集视频', '剧集管理', 'BUTTON', 6),
|
||||
(7, 'episode:sort', '管理剧集排序', '剧集管理', 'BUTTON', 7),
|
||||
(8, 'cover:upload', '上传封面素材', '封面管理', 'BUTTON', 8),
|
||||
(9, 'drama:review', '审核并发布', '审核与系统', 'BUTTON', 9),
|
||||
(10, 'stat:view', '查看数据报表', '审核与系统', 'BUTTON', 10),
|
||||
(11, 'system:role', '角色与权限配置', '后台管理', 'BUTTON', 11),
|
||||
(12, 'system:admin-user', '管理员用户列表', '后台管理', 'BUTTON', 12),
|
||||
(13, 'system:menu', '菜单管理', '后台管理', 'BUTTON', 13),
|
||||
(14, 'system:role-category', '角色分类管理', '后台管理', 'BUTTON', 14),
|
||||
(15, 'banner:list', '查看轮播图', '运营编排', 'BUTTON', 15),
|
||||
(16, 'banner:create', '新建轮播图', '运营编排', 'BUTTON', 16),
|
||||
(17, 'banner:update', '编辑轮播图', '运营编排', 'BUTTON', 17),
|
||||
(18, 'banner:delete', '删除轮播图', '运营编排', 'BUTTON', 18),
|
||||
(19, 'log:list', '查看操作日志', '后台管理', 'BUTTON', 19)
|
||||
ON CONFLICT (id) DO UPDATE SET code = EXCLUDED.code, name = EXCLUDED.name,
|
||||
group_name = EXCLUDED.group_name, sort = EXCLUDED.sort;
|
||||
|
||||
-- ---------- 菜单 ----------
|
||||
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
|
||||
permission_code, type, always_show, affix, sort) VALUES
|
||||
(1, 0, '工作台', '/dashboard', 'Dashboard', '#', '/dashboard/index', 'ant-design:dashboard-filled', 'drama:list', 'CATALOG', 0, 0, 1),
|
||||
(2, 1, '工作台', 'index', 'dashboard', 'views/common/DashboardView', '', 'ant-design:dashboard-filled', 'drama:list', 'MENU', 0, 1, 1),
|
||||
(3, 0, '内容管理', '/content', 'Content', '#', '/content/dramas', 'ep:video-camera', 'drama:list', 'CATALOG', 1, 0, 2),
|
||||
(4, 3, '短剧管理', 'dramas', 'dramas', 'views/contentManagement/DramaManagementView', '', '', 'drama:list', 'MENU', 0, 0, 1),
|
||||
(5, 3, '剧集管理', 'episodes', 'episodes', 'views/contentManagement/EpisodeManagementView', '', '', 'episode:list', 'MENU', 0, 0, 2),
|
||||
(12, 3, '轮播图管理', 'banners', 'banners', 'views/contentManagement/BannerManagementView', '', '', 'banner:list', 'MENU', 0, 0, 3),
|
||||
(13, 3, '类型管理', 'categories', 'categories', 'views/contentManagement/CategoryManagementView','', '', 'drama:list', 'MENU', 0, 0, 4),
|
||||
(6, 0, '后台管理', '/system', 'System', '#', '/system/access', 'ep:setting', 'system:role', 'CATALOG', 1, 0, 3),
|
||||
(7, 6, '角色权限配置', 'access', 'access', 'views/permission/RoleManagementView', '', '', 'system:role', 'MENU', 0, 0, 1),
|
||||
(8, 6, '管理员用户列表','admin-users','admin-users', 'views/userManagement/AdminUsersView', '', '', 'system:admin-user','MENU', 0, 0, 2),
|
||||
(9, 6, '菜单管理', 'menus', 'menus', 'views/permission/MenuManagementView', '', '', 'system:menu', 'MENU', 0, 0, 3),
|
||||
(10, 6, '角色分类', 'role-categories','role-categories','views/permission/RoleCategoriesView', '', '', 'system:role-category','MENU', 0, 0, 4),
|
||||
(11, 6, '系统设置', 'settings', 'settings', 'views/systemManagement/SettingsView', '', '', 'system:role', 'MENU', 0, 0, 5)
|
||||
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
|
||||
route_path = EXCLUDED.route_path, route_name = EXCLUDED.route_name, component = EXCLUDED.component,
|
||||
redirect = EXCLUDED.redirect, icon = EXCLUDED.icon, permission_code = EXCLUDED.permission_code,
|
||||
type = EXCLUDED.type, always_show = EXCLUDED.always_show, affix = EXCLUDED.affix, sort = EXCLUDED.sort;
|
||||
|
||||
-- 操作日志菜单(挂在后台管理下,仅超级管理员可见)
|
||||
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
|
||||
permission_code, type, always_show, affix, sort) VALUES
|
||||
(14, 6, '操作日志', 'operation-logs', 'operation-logs', 'views/systemManagement/OperationLogView', '', '', 'log:list', 'MENU', 0, 0, 6)
|
||||
ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.title,
|
||||
route_path = EXCLUDED.route_path, route_name = EXCLUDED.route_name, component = EXCLUDED.component,
|
||||
redirect = EXCLUDED.redirect, icon = EXCLUDED.icon, permission_code = EXCLUDED.permission_code,
|
||||
type = EXCLUDED.type, always_show = EXCLUDED.always_show, affix = EXCLUDED.affix, sort = EXCLUDED.sort;
|
||||
|
||||
-- ---------- 角色-权限 ----------
|
||||
INSERT INTO sys_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM sys_role r CROSS JOIN sys_permission p WHERE r.code = 'super_admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO sys_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM sys_role r CROSS JOIN sys_permission p
|
||||
WHERE r.code = 'content_operator'
|
||||
AND p.code IN ('drama:list','drama:create','drama:update','drama:delete',
|
||||
'episode:list','episode:upload','episode:sort','cover:upload',
|
||||
'banner:list','banner:create','banner:update','banner:delete')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO sys_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM sys_role r CROSS JOIN sys_permission p
|
||||
WHERE r.code = 'content_reviewer'
|
||||
AND p.code IN ('drama:list','episode:list','drama:review','stat:view','banner:list')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO sys_role_permission (role_id, permission_id)
|
||||
SELECT r.id, p.id FROM sys_role r CROSS JOIN sys_permission p
|
||||
WHERE r.code = 'data_analyst'
|
||||
AND p.code IN ('drama:list','stat:view','banner:list')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------- 角色-菜单 ----------
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.id, m.id FROM sys_role r CROSS JOIN sys_menu m WHERE r.code = 'super_admin'
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.id, m.id FROM sys_role r CROSS JOIN sys_menu m
|
||||
WHERE r.code IN ('content_operator','content_reviewer','data_analyst') AND m.id IN (1,2,3,4,5,12,13)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------- 管理员(密码均为 admin123) ----------
|
||||
INSERT INTO sys_admin_user (id, username, password, real_name, mobile, email, status, ip_whitelist,
|
||||
two_factor, last_login_at, created_at) VALUES
|
||||
(1001, 'admin', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '系统管理员', '', '', 1, '', 0, NULL, '2026-09-01 09:00:00'),
|
||||
(1002, 'lin.xiaoman', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '林小满', '', '', 1, '', 1, '2026-10-12 09:41:22', '2026-09-18 09:30:00'),
|
||||
(1003, 'content.editor01', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '陈一诺', '', '', 1, '', 0, '2026-10-11 16:20:15', '2026-09-21 10:15:00'),
|
||||
(1004, 'content.editor02', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '周子瑜', '', '', 1, '', 0, '2026-10-11 13:34:10', '2026-09-25 14:20:00'),
|
||||
(1005, 'content.reviewer01', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '林晓彤', '', '', 1, '', 1, '2026-10-10 11:20:08', '2026-10-02 11:05:00'),
|
||||
(1006, 'data.analyst01', '$2a$10$cknSDsmYgEADwwv0HdwzUOPqiz8D6mO/OOWMm3QEx.zmJW4iQqLcq', '程予安', '', '', 1, '', 0, '2026-10-09 14:10:02', '2026-10-05 13:02:00')
|
||||
ON CONFLICT (id) DO UPDATE SET username = EXCLUDED.username, real_name = EXCLUDED.real_name,
|
||||
status = EXCLUDED.status, two_factor = EXCLUDED.two_factor, last_login_at = EXCLUDED.last_login_at;
|
||||
|
||||
INSERT INTO sys_admin_user_role (user_id, role_id) VALUES
|
||||
(1001, 1), (1002, 1), (1003, 2), (1004, 2), (1005, 3), (1006, 4)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------- 系统配置 ----------
|
||||
INSERT INTO sys_config (config_key, config_value, remark) VALUES
|
||||
('workspace.name', 'ReelShort Studio', '工作空间名称'),
|
||||
('content.default-status', 'draft', '内容默认状态')
|
||||
ON CONFLICT (config_key) DO UPDATE SET config_value = EXCLUDED.config_value, remark = EXCLUDED.remark;
|
||||
|
||||
-- ---------- 短剧类型 ----------
|
||||
INSERT INTO drama_category (id, name, sort, status) VALUES
|
||||
(1, '都市情感', 1, 1),
|
||||
(2, '重生逆袭', 2, 1),
|
||||
(3, '甜宠爱情', 3, 1),
|
||||
(4, '悬疑复仇', 4, 1)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, sort = EXCLUDED.sort, status = EXCLUDED.status;
|
||||
|
||||
-- ---------- 短剧 ----------
|
||||
INSERT INTO drama (id, code, title, category_id, genre, synopsis, total_episodes, free_episodes,
|
||||
unlock_coin, is_hot, status, published_at, view_count, sort, created_at) VALUES
|
||||
(1, 'D20250612001', '替嫁新娘:总裁的掌心宠', 1, '都市情感',
|
||||
'一场意外替嫁,让她成为冷面总裁的契约新娘。', 68, 3, 60, 1, 'published', '2025-06-12 10:00:00', 2400000, 1, '2025-06-01 09:00:00'),
|
||||
(2, 'D20250618002', '重生之我在豪门当千金', 2, '重生逆袭',
|
||||
'重活一世,她要拿回属于自己的一切。', 42, 3, 60, 0, 'reviewing', NULL, 0, 2, '2025-06-05 09:00:00'),
|
||||
(3, 'D20250529003', '心动信号:请和我恋爱', 3, '甜宠爱情',
|
||||
'恋爱观察室里,心动正在悄悄发生。', 56, 3, 50, 0, 'published', '2025-05-29 10:00:00', 1800000, 3, '2025-05-20 09:00:00'),
|
||||
(4, 'D20250620004', '她从深渊中走来', 4, '悬疑复仇',
|
||||
'真相只有一个,而她亲手揭开迷雾。', 24, 3, 80, 0, 'draft', NULL, 0, 4, '2025-06-15 09:00:00'),
|
||||
(5, 'D20250521005', '闪婚后,傅先生每天都在撒糖', 1, '都市情感',
|
||||
'一纸婚约,开启了甜蜜又心动的同居生活。', 72, 3, 60, 1, 'published', '2025-05-21 10:00:00', 3100000, 5, '2025-05-10 09:00:00')
|
||||
ON CONFLICT (id) DO UPDATE SET code = EXCLUDED.code, title = EXCLUDED.title, genre = EXCLUDED.genre,
|
||||
synopsis = EXCLUDED.synopsis, category_id = EXCLUDED.category_id, status = EXCLUDED.status,
|
||||
total_episodes = EXCLUDED.total_episodes, free_episodes = EXCLUDED.free_episodes,
|
||||
unlock_coin = EXCLUDED.unlock_coin, is_hot = EXCLUDED.is_hot, view_count = EXCLUDED.view_count;
|
||||
|
||||
-- ---------- 剧集(示例:短剧 1) ----------
|
||||
INSERT INTO drama_episode (id, drama_id, ep_no, title, duration_sec, is_free, unlock_coin, status, play_count) VALUES
|
||||
(1, 1, 1, '命运的相遇', 138, 1, 0, 'published', 48201),
|
||||
(2, 1, 2, '意外的婚约', 116, 1, 0, 'published', 42817),
|
||||
(3, 1, 3, '心动的开始', 124, 1, 0, 'published', 39506),
|
||||
(4, 1, 4, '他的秘密', 151, 0, 60, 'draft', 0)
|
||||
ON CONFLICT (id) DO UPDATE SET drama_id = EXCLUDED.drama_id, ep_no = EXCLUDED.ep_no,
|
||||
title = EXCLUDED.title, duration_sec = EXCLUDED.duration_sec, is_free = EXCLUDED.is_free,
|
||||
unlock_coin = EXCLUDED.unlock_coin, status = EXCLUDED.status, play_count = EXCLUDED.play_count;
|
||||
|
||||
-- ---------- 对齐自增序列 ----------
|
||||
-- 上面用了显式 ID,但 IDENTITY 序列不会因此前移;不对齐的话,后续插入会从 1 开始而撞主键。
|
||||
DO $$
|
||||
DECLARE
|
||||
t text;
|
||||
seq text;
|
||||
maxid bigint;
|
||||
BEGIN
|
||||
FOREACH t IN ARRAY ARRAY[
|
||||
'sys_admin_user','sys_role_category','sys_role','sys_admin_user_role','sys_permission',
|
||||
'sys_menu','sys_role_permission','sys_role_menu','sys_config','sys_operation_log',
|
||||
'drama_category','drama','drama_episode','drama_review_record','content_banner',
|
||||
'media_asset','media_upload_task'
|
||||
] LOOP
|
||||
seq := pg_get_serial_sequence(t, 'id');
|
||||
EXECUTE format('SELECT COALESCE(MAX(id), 0) FROM %I', t) INTO maxid;
|
||||
IF seq IS NOT NULL AND maxid > 0 THEN
|
||||
PERFORM setval(seq, maxid);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$;
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
-- ReelShort 短剧服务 · 建表脚本(PostgreSQL 18)
|
||||
--
|
||||
-- 与 MySQL 版本的对应关系:
|
||||
-- AUTO_INCREMENT -> GENERATED BY DEFAULT AS IDENTITY
|
||||
-- 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 sys_operation_log (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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,
|
||||
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',
|
||||
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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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 '轮播图';
|
||||
|
||||
-- ========== 媒资域 ==========
|
||||
-- 本地存储版本:文件落在 reelshort.media.root-dir 下,通过 /files/** 访问
|
||||
|
||||
CREATE TABLE IF NOT EXISTS media_asset (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY 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 GENERATED BY DEFAULT AS IDENTITY 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;
|
||||
$$;
|
||||
Reference in new issue
Block a user