Commit Graph
4 Commits
Author SHA1 Message Date
zhangshy 5634fdc023 完善风控登录权限和模型能力配置 2026-09-12 14:43:05 +08:00
lzf_0626 5018f11843 增加投顾(advisor)角色;修正登录测试的错误假设;修投顾带入的 2 处文档重号
## 投顾角色

投顾线合并后,bootstrap.py 有 10 处 allowed_roles 引用 advisor,
financial_nl2sql_service.py:272 还硬编码检查 {"advisor","operator","admin","super_admin"},
promotion_material_service.py:164 按 "advisor" in context.roles 走业务分支 ——
但 sys_role 里没有这个角色、sys_permission 里也没有投顾那 16 个权限码(种子只建到 9019)。
表现是所有投顾接口 403,而报错看起来像"权限配错了",实际是角色根本不存在。

- tools/grant_advisor_role.py:建 advisor 角色(id=9004,避开种子的 9001-9003 重建范围)
  + 16 个投顾权限(id 9020-9035)+ 授权(advisor 拿 10 项工作流、admin 补齐 16 项)。
  只增不删、可重复执行、带 --dry-run。
- tools/create_test_user.py:ROLE_IDS 加 advisor。
- 先跑 alembic upgrade head:补 21 张 advisor_* 表,业务表 68 → 89,审计通过。

权限划分:投顾工作流 10 项(read:self / generate:self / review / publish)给 advisor;
治理类 6 项(product-governance:*、profile-governance:*、asset-allocation:backtest)只给 admin。
review/publish 也给 advisor,与既有决策一致(此前已裁定不做双人复核)。

验证:advisor_t 登录 200,roles=['advisor'] data_scope=all 权限 10 项;
用它查 RBAC 清单得 403(没有 audit:read),边界正确。

⚠️ 与种子的冲突:seed_test_rbac.py 是 DELETE 重建语义,其
DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099 会清掉本脚本建的权限。
要把投顾权限固化,应并进 seed_test_rbac.py 的 PERMISSIONS 常量。

## 修正登录测试的一个错误假设

test_issued_token_actually_works_on_a_real_endpoint 原本用客户的
/users/me/memory-profile 验证令牌可用,投顾合并后它返回 403。追下去发现**与令牌无关**:
那个接口对客户有业务前置"请先完成开户风险测评问卷",而演示客户 9001 没有测评记录。
是我的测试选错了验证端点,把"业务前置未满足"误判成"令牌坏了"。

- 改用管理员令牌调 /api/v1/admin/roles(需要 audit:read,走完整鉴权链路),
  并补一条反向对照:不带令牌必须 401,否则那个 200 说明不了令牌有效。
- 把那个业务前置单独写成一个用例,让后来者一眼看到条件,而不是反复怀疑令牌。

过程里我先按控制台乱码猜了两次失败原因,都不对;最后把响应抓成 UTF-8 文件才看到真实
消息。教训记下:不要读乱码猜消息。

## 修投顾带入的 2 处文档重号

21-投顾Agent迁移TODO.md → 30-…、22-投顾Agent灰度与回滚操作手册.md → 31-…
(沿用 NL 那次让号的先例:既有文档更早、引用更多;且这两份新文档没有被任何地方引用。)
文档守卫:40 份无编号冲突。

验证:ruff 干净 / mypy 228 文件 0 错 / 文档守卫 40 份无冲突 /
unit+contract 1207 passed(0 failed)/ integration 99 passed / 业务表 89 张。
2026-09-11 21:49:46 +08:00
lzf_0626 edd8c53c3a 修正 create_test_user 里写反的理由:sys_user_role 有唯一键,先清后插是为了支持改角色 2026-09-11 21:17:49 +08:00
lzf_0626 b1429aa364 登录接口配套:加人工具 + 给组员的转交文档(docs/29)
- tools/create_test_user.py:一条命令建"可登录的测试账号"(用户 + 角色 + bcrypt 密码),
  并用 IdentityService.resolve 打印**真实解析结果**。sys_user / sys_user_role 没有 ORM
  模型、全靠裸 SQL,手写容易漏必填字段;更要紧的是 assigned_at 那个静默陷阱(见下)。
  重复执行同一 --id 是覆盖语义,改角色也用它。
- tools/set_user_password.py:hash_password 改从 auth_service 取,消除第二份实现。
- app/service/auth_service.py:新增 hash_password,与 verify_password 放在一起,
  让"写密码"和"校验密码"永远同一套算法。
- docs/29-Agent组员登录接口使用说明.md:给组员的转交文档(接口契约、加人步骤、
  前端接入示例、常见问题、当前边界)。

文档里专门写清三条最容易踩的:

1. 登录用 username 而不是用户 id —— 演示账号是 cust_t / risk_t / admin_t,
   不是 9001/9002/9003。这条不写明,联调时一定有人按 id 试。
2. sys_user_role.assigned_at 的 DATETIME(0) 毫秒舍入陷阱:落在未来会让账号
   "登录成功但 roles=()",**不报错**。create_test_user 统一往前留 5 秒。
3. roles / data_scope 只用于前端分流界面,不是权限凭证 —— 鉴权每次请求查库解析,
   所以权限变更立即生效,前端也不该拿它们做安全判断。

另:create_test_user 的 ON DUPLICATE KEY UPDATE 用 MySQL 8.0.19+ 的 `AS new` 别名语法,
避开已弃用的 VALUES()(实测本机 8.0.27 会打弃用警告)。

验证:文档守卫 38 份无编号冲突 / ruff 干净 / mypy 183 文件 0 错 /
登录集成测试 10 passed。
2026-09-11 20:50:41 +08:00