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)
This commit is contained in:
Vicky committed 2026-10-16 09:18:09 +08:00
1 parent 37c9a141d3
commit 84aa0e17dd
2 files changed
+94 -102

No files matched your search

+67 -64
View File
@@ -27,50 +27,53 @@ BEGIN
END LOOP;
END $$;
INSERT INTO sys_permission (code, name, group_name, type, sort) VALUES
('app-user:list', '查看 App 用户', '用户管理', 'BUTTON', 101),
('app-user:update', '处置 App 用户', '用户管理', 'BUTTON', 102)
-- 主键说明: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 (code, name, group_name, type, sort) VALUES
('download:list', '查看下载授权', '用户管理', 'BUTTON', 103),
('download:revoke', '吊销下载授权', '用户管理', 'BUTTON', 104)
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 (code, name, group_name, type, sort) VALUES
('comment:list', '查看评论', '内容治理', 'BUTTON', 111),
('comment:audit', '审核评论', '内容治理', 'BUTTON', 112),
('report:list', '查看举报', '内容治理', 'BUTTON', 113),
('report:handle', '处理举报', '内容治理', 'BUTTON', 114),
('sensitive-word:list', '查看敏感词', '内容治理', 'BUTTON', 115),
('sensitive-word:update','维护敏感词', '内容治理', 'BUTTON', 116)
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 (code, name, group_name, type, sort) VALUES
('order:list', '查看订单', '订单与权益', 'BUTTON', 121),
('order:update', '补发/退款', '订单与权益', 'BUTTON', 122),
('entitlement:list', '查看权益', '订单与权益', 'BUTTON', 123),
('entitlement:update', '补发/回收权益', '订单与权益', 'BUTTON', 124),
('wallet:update', '人工补币', '订单与权益', 'BUTTON', 125),
('reconcile:run', '手动对账', '订单与权益', 'BUTTON', 126)
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 (code, name, group_name, type, sort) VALUES
('creator:list', '查看创作者', '创作者管理', 'BUTTON', 131),
('creator:audit', '创作者入驻审核','创作者管理', 'BUTTON', 132),
('submission:list', '查看内容提交', '创作者管理', 'BUTTON', 133),
('submission:audit', '内容提交审核', '创作者管理', 'BUTTON', 134),
('price:update', '配置分集价格', '内容管理', 'BUTTON', 135)
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 (code, name, group_name, type, sort) VALUES
('dashboard:view', '查看运营大盘', '数据看板', 'BUTTON', 141)
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 (role_id, permission_id)
SELECT r.id, p.id
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'
@@ -95,8 +98,8 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
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 (role_id, menu_id)
SELECT r.id, m.id
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'
@@ -104,19 +107,19 @@ WHERE r.code = 'super_admin'
ON CONFLICT DO NOTHING;
-- 第一阶段新增的白名单数据(当前阶段都是免费内容,先把演示商品占位好)
INSERT INTO product (code, name, type, platform, store_product_id, coin_amount, bonus_coin,
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 'coin_100', '100 金币', 'COIN_PACK', 'ALL', '', 100, 0, 0.99, 'USD', 'ID', 1, 1
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 (code, name, type, platform, store_product_id, coin_amount, bonus_coin,
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 'coin_550', '550 金币', 'COIN_PACK', 'ALL', '', 500, 50, 4.99, 'USD', 'ID', 2, 1
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 (code, name, type, platform, store_product_id, coin_amount, bonus_coin,
INSERT INTO product (id, code, name, type, platform, store_product_id, coin_amount, bonus_coin,
price_amount, currency, region_codes, sort, status)
SELECT 'coin_1200', '1200 金币', 'COIN_PACK', 'ALL', '', 1000, 200, 9.99, 'USD', 'ID', 3, 1
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');
-- ---------- 菜单:用户管理 ----------
@@ -131,8 +134,8 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
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 (role_id, menu_id)
SELECT r.id, m.id
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'
@@ -156,8 +159,8 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
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 (role_id, menu_id)
SELECT r.id, m.id
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'
@@ -177,34 +180,34 @@ 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 (code, name, group_name, type, sort) VALUES
('creator-center:view', '访问创作者中心', '创作者中心', 'BUTTON', 150),
('creator-center:drama:edit', '创作者中心-编辑作品', '创作者中心', 'BUTTON', 151),
('creator-center:upload', '创作者中心-上传与挂载', '创作者中心', 'BUTTON', 152),
('creator-center:price:update', '创作者中心-配置分集价格', '创作者中心', 'BUTTON', 153),
('creator-center:stat:view', '创作者中心-查看看板', '创作者中心', 'BUTTON', 154)
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 (code, name, group_name, type, sort) VALUES
('creator-center:impersonate', '代看创作者(只读)', '创作者中心', 'BUTTON', 155)
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 (code, name, group_name, type, sort) VALUES
('creator:account', '开通/重置创作者账号', '创作者管理', 'BUTTON', 136)
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 (role_id, permission_id)
SELECT r.id, p.id
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 (role_id, permission_id)
SELECT r.id, p.id
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'
@@ -212,8 +215,8 @@ WHERE r.code = 'super_admin'
ON CONFLICT DO NOTHING;
-- 创作者角色只拿创作者中心这几个点,不给任何后台运营权限
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT r.id, p.id
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'
@@ -222,8 +225,8 @@ WHERE r.code = 'creator'
ON CONFLICT DO NOTHING;
-- 超管也留一份,方便排查创作者视角的问题(超管没有绑定创作者主体,进 creator-center 仍会被挡)
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT r.id, p.id
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'
@@ -233,8 +236,8 @@ ON CONFLICT DO NOTHING;
-- 媒资分片上传是复用后台已有的 /admin/media/upload/*,所以创作者角色要用现成的上传权限点,
-- 不再造一套 creator-center 的上传接口(上传会话归属与产物归属另在服务层校验)。
INSERT INTO sys_role_permission (role_id, permission_id)
SELECT r.id, p.id
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'
@@ -260,8 +263,8 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
sort = EXCLUDED.sort, visible = EXCLUDED.visible;
-- 创作者角色:只挂创作者中心这五条(含隐藏的编辑页),**不给任何运营后台菜单**
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT r.id, m.id
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'
+27 -38
View File
@@ -76,37 +76,37 @@ ON CONFLICT (id) DO UPDATE SET parent_id = EXCLUDED.parent_id, title = EXCLUDED.
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'
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'
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
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 = '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
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 = '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
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 = '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'
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'
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
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 IN ('content_operator','content_reviewer','data_analyst') AND m.id IN (1,2,3,4,5,12,13)
ON CONFLICT DO NOTHING;
@@ -122,14 +122,15 @@ INSERT INTO sys_admin_user (id, username, password, real_name, mobile, email, st
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)
INSERT INTO sys_admin_user_role (id, user_id, role_id) VALUES
(1001000001, 1001, 1), (1002000001, 1002, 1), (1003000002, 1003, 2),
(1004000002, 1004, 2), (1005000003, 1005, 3), (1006000004, 1006, 4)
ON CONFLICT DO NOTHING;
-- ---------- 系统配置 ----------
INSERT INTO sys_config (config_key, config_value, remark) VALUES
('workspace.name', 'FruitShorts Studio', '工作空间名称'),
('content.default-status', 'draft', '内容默认状态')
INSERT INTO sys_config (id, config_key, config_value, remark) VALUES
(1, 'workspace.name', 'FruitShorts Studio', '工作空间名称'),
(2, 'content.default-status', 'draft', '内容默认状态')
ON CONFLICT (config_key) DO UPDATE SET config_value = EXCLUDED.config_value, remark = EXCLUDED.remark;
-- ---------- 短剧类型 ----------
@@ -168,24 +169,12 @@ ON CONFLICT (id) DO UPDATE SET drama_id = EXCLUDED.drama_id, ep_no = EXCLUDED.ep
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 $$;
-- ---------- 关于主键 ----------
-- S0 之后主键由应用侧雪花算法生成(MyBatis-Plus IdType.ASSIGN_ID),数据库里已经没有自增序列,
-- 所以本文件不再需要"把 IDENTITY 序列对齐到最大 ID"那段收尾逻辑(原来那段是防止显式 ID 之后
-- 自增从 1 重新开始而撞主键;序列已经不存在,这个风险也随之消失)。
--
-- 种子行的主键约定(三端一致,改动时请沿用):
-- 1) 普通种子行沿用既有小整数区间(如角色 1-5、菜单 1-30、管理员 1001+);
-- 2) 关联表用「左键 * 1000000 + 右键」拼出的确定性 ID,重复执行结果完全一致;
-- 3) 应用运行时产生的数据才用雪花 ID(19 位),两者不会相撞。