fix: 种子改用 UPSERT 建用户且不覆盖密码,解除投顾 FK 导致的跑不通;记录 RBAC 对齐最终状态

This commit is contained in:
2026-09-12 12:22:32 +08:00
parent 39f7b81200
commit 4cc0ecdea9
2 changed files with 61 additions and 45 deletions
+27 -10
View File
@@ -3,13 +3,18 @@
数据使用 9001 起的号段(角色 9001-9003、权限 9001-9046),便于清理,不影响业务数据。
权限码是几条线叠加出来的:9001-9019 基础能力、9020-9034 投顾线、9041-9046 投顾治理类与客服二期。
⚠️ 本脚本是 **DELETE 重建**:`DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099`
会删掉该号段内**所有**权限(包括别的线用 `grant_*.py` 建的),再按 `PERMISSIONS` 重建,
并且**只清 role_id 9001-9003 的角色绑定**。两个已知后果:
⚠️ 本脚本对**权限、角色、角色绑定**是 **DELETE 重建**:`DELETE FROM sys_permission WHERE
id BETWEEN 9001 AND 9099` 会删掉该号段内**所有**权限(包括别的线用 `grant_*.py` 建的),
再按 `PERMISSIONS` 重建,并且**只清 role_id 9001-9003 的角色绑定**。三个已知后果:
1. 新增权限码必须并进本文件的 `PERMISSIONS`,否则重建一次就没了(表现为"接口突然 403");
2. `advisor`(9004)这类不在 `ROLES` 里的角色,其绑定**不会被清** —— 若它们引用的
permission_id 在重建时换了语义,绑定就会指向错误的权限码。改 id→code 映射前先确认。
permission_id 在重建时换了语义,绑定就会指向错误的权限码。改 id→code 映射前先确认;
3. **`sys_user` 不再是 DELETE 重建**(2026-09-12 改):投顾域的 `advisor_profile_tag` 等表用
FK 引用 `sys_user`,一旦库里有数据引用 9001-9003,`DELETE FROM sys_user` 会被外键拒绝
(MySQL 1451),**种子整个跑不完**;而 `commit()` 在最后,所以外部表现是"什么都没发生"。
现已改为「存在则 UPDATE 非密码字段、不存在才 INSERT」,且**不覆盖 `password_hash`**,
因此重跑种子不会再弄丢演示密码,也不需要事后补 `set_user_password.py`。
为什么需要这份种子:权限码必须与**代码实际声明**一致——工具声明写在
`app/service/agent/bootstrap.py`(`check_suitability` 需 `suitability:read`、
@@ -138,18 +143,30 @@ async def seed() -> None:
await session.execute(text("DELETE FROM sys_user_role WHERE user_id IN (9001,9002,9003)"))
await session.execute(text("DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099"))
await session.execute(text("DELETE FROM sys_role WHERE id IN (9001,9002,9003)"))
await session.execute(text("DELETE FROM sys_user WHERE id IN (9001,9002,9003)"))
# 不再 DELETE sys_user:投顾域的 advisor_profile_tag 等表用 FK 引用它,库里有数据引用
# 9001-9003 时删除会被外键拒绝(1451)导致整个种子跑不完,而且删了用户等于把演示密码
# 一起弄丢。改为下面循环里的「更新或插入」,且不覆盖 password_hash。
for user_id, user_no, user_name, user_type in USERS:
await session.execute(
updated = await session.execute(
text(
"INSERT INTO sys_user (id, user_no, username, password_hash, user_type,"
" professional_investor_status, fund_account_status, status,"
" created_at, updated_at)"
" VALUES (:id,:no,:name,'x',:type,'none','closed','正常',:now,:now)"
"UPDATE sys_user SET user_no=:no, username=:name, user_type=:type,"
" professional_investor_status='none', fund_account_status='closed',"
" status='正常', updated_at=:now WHERE id=:id"
),
{"id": user_id, "no": user_no, "name": user_name, "type": user_type, "now": now},
)
if updated.rowcount == 0:
await session.execute(
text(
"INSERT INTO sys_user (id, user_no, username, password_hash, user_type,"
" professional_investor_status, fund_account_status, status,"
" created_at, updated_at)"
" VALUES (:id,:no,:name,'x',:type,'none','closed','正常',:now,:now)"
),
{"id": user_id, "no": user_no, "name": user_name,
"type": user_type, "now": now},
)
for role_id, role_code, role_name in ROLES:
await session.execute(
text(