Files
FruitServer/sql/data-phase1.sql
T
Vicky 84aa0e17dd fix: S0 后续——种子脚本补显式主键(去掉 identity 后新库种不进数据)
为什么:S0 把主键默认值去掉了,但 sql/data.sql 和 sql/data-phase1.sql 里有一批 INSERT
没给 id(原来靠 IDENTITY 生成),在新库上直接报
`null value in column "id" ... violates not-null constraint`,30 条菜单只种进去 14 条。
线上库因为数据早已存在所以看不出来,是"新装"这条路断了。

- 关联表(sys_role_permission / sys_role_menu / sys_admin_user_role)改用
  「左键 * 1000000 + 右键」的确定性 ID,重复执行结果一致,也不会撞雪花 ID
- data-phase1 新增权限点用「1000 + sort」(1101-1156 保留区间)显式给 ID,避开早期 IDENTITY 用过的 1-251
- 商品占位行补 901-903;sys_config 补 1-2
- 删掉 data.sql 结尾"对齐 IDENTITY 序列"的收尾块(序列已不存在),改为写明种子主键约定
- 实测:新库上 schema+data 五个脚本连跑三轮零 ERROR 且行数稳定(30/48/75/51/6/3/2/5/4);
  线上库跑种子前后行数不变(30/48/75/51/3/2)
2026-10-16 09:18:09 +08:00

285 lines
18 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- FruitShorts 短剧服务 · 第一阶段增量数据(PostgreSQL 18)
--
-- 只放「代码已经实现、需要权限点才能调通」的数据。
-- 菜单(sys_menu)暂时不加:前端页面还没做,加了会出现指向不存在组件的动态路由,
-- 等 fruitshorts-web 的 App 用户管理页落地后再补菜单与角色菜单绑定。
--
-- 幂等:可重复执行。
-- 先对齐 IDENTITY 序列:历史种子数据用了显式 ID,序列没有前移,
-- 不对齐的话下面的插入会从 1 开始而撞主键。
DO $$
DECLARE
t text;
seq text;
maxid bigint;
BEGIN
FOREACH t IN ARRAY ARRAY['sys_permission','sys_menu','sys_role_permission','sys_role_menu',
'product','app_user','app_user_credential','orders','wallet'] LOOP
seq := pg_get_serial_sequence(t, 'id');
IF seq IS NULL THEN
CONTINUE;
END IF;
EXECUTE format('SELECT COALESCE(MAX(id), 0) FROM %I', t) INTO maxid;
IF maxid > 0 THEN
PERFORM setval(seq, maxid);
END IF;
END LOOP;
END $$;
-- 主键说明:S0 之后数据库不再自增,种子行必须显式给主键。
-- 本文件新增的权限点统一用「1000 + sort」当 ID(1101-1156 这段保留区间),
-- 避开早期 IDENTITY 已经用掉的 1-251;应用运行时产生的行才用雪花 ID。
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1101, 'app-user:list', '查看 App 用户', '用户管理', 'BUTTON', 101),
(1102, 'app-user:update', '处置 App 用户', '用户管理', 'BUTTON', 102)
ON CONFLICT (code) DO NOTHING;
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1103, 'download:list', '查看下载授权', '用户管理', 'BUTTON', 103),
(1104, 'download:revoke', '吊销下载授权', '用户管理', 'BUTTON', 104)
ON CONFLICT (code) DO NOTHING;
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1111, 'comment:list', '查看评论', '内容治理', 'BUTTON', 111),
(1112, 'comment:audit', '审核评论', '内容治理', 'BUTTON', 112),
(1113, 'report:list', '查看举报', '内容治理', 'BUTTON', 113),
(1114, 'report:handle', '处理举报', '内容治理', 'BUTTON', 114),
(1115, 'sensitive-word:list', '查看敏感词', '内容治理', 'BUTTON', 115),
(1116, 'sensitive-word:update','维护敏感词', '内容治理', 'BUTTON', 116)
ON CONFLICT (code) DO NOTHING;
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1121, 'order:list', '查看订单', '订单与权益', 'BUTTON', 121),
(1122, 'order:update', '补发/退款', '订单与权益', 'BUTTON', 122),
(1123, 'entitlement:list', '查看权益', '订单与权益', 'BUTTON', 123),
(1124, 'entitlement:update', '补发/回收权益', '订单与权益', 'BUTTON', 124),
(1125, 'wallet:update', '人工补币', '订单与权益', 'BUTTON', 125),
(1126, 'reconcile:run', '手动对账', '订单与权益', 'BUTTON', 126)
ON CONFLICT (code) DO NOTHING;
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1131, 'creator:list', '查看创作者', '创作者管理', 'BUTTON', 131),
(1132, 'creator:audit', '创作者入驻审核','创作者管理', 'BUTTON', 132),
(1133, 'submission:list', '查看内容提交', '创作者管理', 'BUTTON', 133),
(1134, 'submission:audit', '内容提交审核', '创作者管理', 'BUTTON', 134),
(1135, 'price:update', '配置分集价格', '内容管理', 'BUTTON', 135)
ON CONFLICT (code) DO NOTHING;
-- 看板权限:菜单在 3-4 补页面时一起加,避免菜单指向不存在的组件
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1141, 'dashboard:view', '查看运营大盘', '数据看板', 'BUTTON', 141)
ON CONFLICT (code) DO NOTHING;
-- 超管默认拥有全部权限点
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'super_admin'
AND p.code IN ('app-user:list', 'app-user:update', 'download:list', 'download:revoke',
'comment:list', 'comment:audit', 'report:list', 'report:handle',
'sensitive-word:list', 'sensitive-word:update',
'order:list', 'order:update', 'entitlement:list', 'entitlement:update',
'wallet:update', 'reconcile:run',
'creator:list', 'creator:audit', 'submission:list', 'submission:audit',
'price:update', 'dashboard:view')
ON CONFLICT DO NOTHING;
-- ---------- 菜单:创作者管理 ----------
-- component 指向 src/views/creator/ 下的页面,前端按这个字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(17, 0, '创作者管理', '/creator', 'CreatorCenter', '#', '/creator/creators', 'ep:user-filled', 'creator:list', 'CATALOG', 1, 0, 5),
(18, 17, '创作者入驻', 'creators', 'creators', 'views/creator/CreatorAuditView', '', '', 'creator:list', 'MENU', 0, 0, 1),
(19, 17, '内容审核', 'submissions', 'submissions', 'views/creator/SubmissionAuditView', '', '', 'submission:list', 'MENU', 0, 0, 2)
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_menu (id, role_id, menu_id)
SELECT r.id * 1000000 + m.id, r.id, m.id
FROM sys_role r
CROSS JOIN sys_menu m
WHERE r.code = 'super_admin'
AND m.id IN (17, 18, 19)
ON CONFLICT DO NOTHING;
-- 第一阶段新增的白名单数据(当前阶段都是免费内容,先把演示商品占位好)
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 901, 'coin_100', '100 金币', 'COIN_PACK', 'ALL', '', 100, 0, 0.99, 'USD', 'ID', 1, 1
WHERE NOT EXISTS (SELECT 1 FROM product WHERE code = 'coin_100');
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 902, 'coin_550', '550 金币', 'COIN_PACK', 'ALL', '', 500, 50, 4.99, 'USD', 'ID', 2, 1
WHERE NOT EXISTS (SELECT 1 FROM product WHERE code = 'coin_550');
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 903, 'coin_1200', '1200 金币', 'COIN_PACK', 'ALL', '', 1000, 200, 9.99, 'USD', 'ID', 3, 1
WHERE NOT EXISTS (SELECT 1 FROM product WHERE code = 'coin_1200');
-- ---------- 菜单:用户管理 ----------
-- C 端用户与后台管理员是两套账号体系,单独挂一个一级目录,避免和管理员列表混淆。
-- component 指向 src/views/userManagement/AppUsersView.vue,前端按这个字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(15, 0, '用户管理', '/user', 'UserCenter', '#', '/user/app-users', 'ep:user', 'app-user:list', 'CATALOG', 1, 0, 4),
(16, 15, 'App 用户', 'app-users', 'app-users', 'views/userManagement/AppUsersView', '', '', 'app-user:list', 'MENU', 0, 0, 1)
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_menu (id, role_id, menu_id)
SELECT r.id * 1000000 + m.id, r.id, m.id
FROM sys_role r
CROSS JOIN sys_menu m
WHERE r.code = 'super_admin'
AND m.id IN (15, 16)
ON CONFLICT DO NOTHING;
-- ---------- 菜单:内容治理 / 订单与权益 / 数据看板 ----------
-- 这三条菜单拖到 3-4 页面做出来才加:菜单先落库而组件不存在的话,
-- 前端动态路由会指向空组件、侧边栏直接报错(3-3 时就是这么刻意留空的)。
-- component 指向 src/views/governance|commerce|dashboard/ 下的页面,前端按字符串动态加载。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort) VALUES
(20, 0, '内容治理', '/governance', 'Governance', '#', '/governance/content', 'ep:chat-dot-round', 'comment:list', 'CATALOG', 1, 0, 6),
(21, 20, '评论与举报', 'content', 'governance-content', 'views/governance/ContentGovernanceView', '', '', 'comment:list', 'MENU', 0, 0, 1),
(22, 0, '订单与权益', '/commerce', 'Commerce', '#', '/commerce/orders', 'ep:coin', 'order:list', 'CATALOG', 1, 0, 7),
(23, 22, '订单管理', 'orders', 'commerce-orders', 'views/commerce/OrderManagementView', '', '', 'order:list', 'MENU', 0, 0, 1),
(24, 0, '数据看板', '/board', 'BoardCenter', '#', '/board/overview', 'ep:data-line', 'dashboard:view', 'CATALOG', 1, 0, 8),
(25, 24, '运营大盘', 'overview', 'board-overview', 'views/dashboard/BusinessDashboardView', '', '', 'dashboard:view', 'MENU', 0, 0, 1)
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_menu (id, role_id, menu_id)
SELECT r.id * 1000000 + m.id, r.id, m.id
FROM sys_role r
CROSS JOIN sys_menu m
WHERE r.code = 'super_admin'
AND m.id IN (20, 21, 22, 23, 24, 25)
ON CONFLICT DO NOTHING;
-- ---------- 07 创作者中心:角色、权限点、授权 ----------
-- 创作者第一阶段直接登后台(复用 sys_admin_user + sys_role + sys_menu),
-- 这里只落"身份与权限";菜单等页面做出来再挂 —— 菜单先入库会让前端动态路由指向不存在的组件。
INSERT INTO sys_role_category (id, name, description, sort) VALUES
(4, '创作者', '外部内容创作者,只能管理自己的作品与数据', 4)
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
(5, '创作者', 'creator', 4, 5, '只能查看与编辑自己的作品与数据', 1, 0)
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, code = EXCLUDED.code,
description = EXCLUDED.description, category_id = EXCLUDED.category_id, level = EXCLUDED.level;
-- 权限点按批补:批 1 只有「进创作者中心」这一个,后面各批用到什么再幂等追加
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1150, 'creator-center:view', '访问创作者中心', '创作者中心', 'BUTTON', 150),
(1151, 'creator-center:drama:edit', '创作者中心-编辑作品', '创作者中心', 'BUTTON', 151),
(1152, 'creator-center:upload', '创作者中心-上传与挂载', '创作者中心', 'BUTTON', 152),
(1153, 'creator-center:price:update', '创作者中心-配置分集价格', '创作者中心', 'BUTTON', 153),
(1154, 'creator-center:stat:view', '创作者中心-查看看板', '创作者中心', 'BUTTON', 154)
ON CONFLICT (code) DO NOTHING;
-- 代看创作者(只读):第一阶段只给超管,content_operator 不给
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1155, 'creator-center:impersonate', '代看创作者(只读)', '创作者中心', 'BUTTON', 155)
ON CONFLICT (code) DO NOTHING;
-- 开通 / 重置创作者账号
INSERT INTO sys_permission (id, code, name, group_name, type, sort) VALUES
(1136, 'creator:account', '开通/重置创作者账号', '创作者管理', 'BUTTON', 136)
ON CONFLICT (code) DO NOTHING;
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'super_admin'
AND p.code = 'creator:account'
ON CONFLICT DO NOTHING;
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'super_admin'
AND p.code = 'creator-center:impersonate'
ON CONFLICT DO NOTHING;
-- 创作者角色只拿创作者中心这几个点,不给任何后台运营权限
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'creator'
AND p.code IN ('creator-center:view', 'creator-center:drama:edit',
'creator-center:upload', 'creator-center:price:update', 'creator-center:stat:view')
ON CONFLICT DO NOTHING;
-- 超管也留一份,方便排查创作者视角的问题(超管没有绑定创作者主体,进 creator-center 仍会被挡)
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'super_admin'
AND p.code IN ('creator-center:view', 'creator-center:drama:edit',
'creator-center:upload', 'creator-center:price:update', 'creator-center:stat:view')
ON CONFLICT DO NOTHING;
-- 媒资分片上传是复用后台已有的 /admin/media/upload/*,所以创作者角色要用现成的上传权限点,
-- 不再造一套 creator-center 的上传接口(上传会话归属与产物归属另在服务层校验)。
INSERT INTO sys_role_permission (id, role_id, permission_id)
SELECT r.id * 1000000 + p.id, r.id, p.id
FROM sys_role r
CROSS JOIN sys_permission p
WHERE r.code = 'creator'
AND p.code IN ('cover:upload', 'episode:upload')
ON CONFLICT DO NOTHING;
-- ---------- 菜单:创作者中心(批 5,前端页面落地后才挂) ----------
-- 26 是目录,27/29/30 是三个可见页面,28 是隐藏路由(作品编辑详情页,visible=0 → meta.hidden),
-- 组件路径对应 src/views/creator-center/ 下的四个页面。
INSERT INTO sys_menu (id, parent_id, title, route_path, route_name, component, redirect, icon,
permission_code, type, always_show, affix, sort, visible) VALUES
-- route_name 必须全局唯一:17 号菜单(创作者管理)已经叫 CreatorCenter,
-- 同名会让 Vue Router 后者覆盖前者、整个 /creator-center 子树注册不上(超管点进去就 404)
(26, 0, '创作者中心', '/creator-center', 'CreatorWorkspace', '#', '/creator-center/workspace', 'ep:film', 'creator-center:view', 'CATALOG', 1, 0, 3, 1),
(27, 26, '我的作品', 'workspace', 'creator-workspace', 'views/creator-center/WorkspaceView', '', '', 'creator-center:view', 'MENU', 0, 0, 1, 1),
(28, 26, '作品编辑', 'dramas/:id', 'creator-drama-edit', 'views/creator-center/DramaEditView', '', '', 'creator-center:drama:edit', 'MENU', 0, 0, 2, 0),
(29, 26, '收费配置', 'prices', 'creator-prices', 'views/creator-center/PriceConfigView', '', '', 'creator-center:price:update', 'MENU', 0, 0, 3, 1),
(30, 26, '数据看板', 'dashboard', 'creator-dashboard', 'views/creator-center/DashboardView', '', '', 'creator-center:stat:view', 'MENU', 0, 0, 4, 1)
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, visible = EXCLUDED.visible;
-- 创作者角色:只挂创作者中心这五条(含隐藏的编辑页),**不给任何运营后台菜单**
INSERT INTO sys_role_menu (id, role_id, menu_id)
SELECT r.id * 1000000 + m.id, r.id, m.id
FROM sys_role r
CROSS JOIN sys_menu m
WHERE r.code = 'creator'
AND m.id IN (26, 27, 28, 29, 30)
ON CONFLICT DO NOTHING;
-- 创作者中心只归创作者:超管**不再**挂 26-30(用户口径「只要不是创作者,就不要显示创作者中心了」)。
-- 这里必须显式 delete 而不是只 insert:data.sql 里有"把全部菜单授给超管"的兜底语句,
-- 会给超管补上这几条;本脚本按约定最后执行,delete 放在菜单 insert 之后即可保证结果一致。
-- 注意:如果之后重新执行 sql/data.sql,需要再跑一次本脚本把这几条清掉。
DELETE FROM sys_role_menu
WHERE menu_id IN (26, 27, 28, 29, 30)
AND role_id IN (SELECT id FROM sys_role WHERE code = 'super_admin');
-- 光收 sys_role_menu 不够:超管的菜单是"全量兜底",不看角色-菜单表。
-- 这几条置 0,超管的菜单树里就不会再出现创作者中心(连带隐藏的作品编辑路由)。
UPDATE sys_menu SET super_admin_visible = 0 WHERE id IN (26, 27, 28, 29, 30);