lzf_0626
5da2fb5790
投顾代客三项的权限码缺失:整片 403(与 9057-9059 同一个坑的第二批)
## 现象
投顾工作台(`advisor_t`,9020)一选客户就整片失败,页面只显示
「请求失败,请检查权限或稍后重试」。API 访问日志给出真相:
```
POST /api/v1/advisor/recommendations 403
POST /api/v1/advisor/asset-allocation 403
POST /api/v1/advisor/portfolio-analysis 403
GET /api/v1/advisor/recommendations/published 200 ← 只有它不需要代客权限
```
## 根因:三个 `*:customer` 权限码在库里根本不存在
三个服务在"代客"(`customer_id != context.user_id`)时要求的是**动态拼出来的
`:customer` 变体**:
- `product_recommendation_service.py:71` → `product-recommendation:generate:customer`
- `asset_allocation_service.py:57` → `asset-allocation:generate:customer`
- `portfolio_analysis_service.py:46` → `portfolio-analysis:read:customer`
而 `sys_permission` 里 `:customer` 后缀**只有 4 个**(`memory:read:customer`、
`investment-goal:{read,write,confirm}:customer`)—— 这三个从来没登记过。
`AuthorizationService.require()` 第一步就查不到该码 ⇒ 直接 403。
这与种子里 **9057-9059 的注释是同一个坑**("动态拼出来的权限码,对账工具抓不到字面量,
于是投顾查/建/确认客户目标全部 403"),前人修了 investment-goal 那三个,这三个漏了。
## 修法(按仓库纪律:种子定义权限码 → grant 脚本绑定角色)
1. `tools/seed_test_rbac.py`:新增 **9066-9068** 三个码,`data_scope=own_customers`
(`require_customer_scope` 只放行 `all`,或 `own_customers` 且客户确在
`context.customer_ids` = 归属客户内 —— 这正是投顾该有的最小权限),
并写明三处调用点,避免后人再漏;
2. `tools/grant_advisor_role.py`:把三个码加入 `ADVISOR_GRANTED_CODES`
(admin 因 `ADMIN_PERMISSIONS = 全部种子权限` 自动获得)。
已执行:`seed_test_rbac.py` → `grant_advisor_role.py`
(advisor 新增 3 项,共 31 项;admin 62 项)。
## 验证(真实 HTTP,9020 身份)
| 客户 | 推荐方案 | 资产配置 | 组合分析 |
|---|---|---|---|
| 9101(演示客户) | 200 `profile_required` | 200 `profile_required` | 200 `no_positions` |
| **9001(真实客户)** | **200 已生成方案**(content_id=5, pending_review) | **200 ready**(含配置比例) | **200 ready**(7 个持仓,市值 10698.60) |
**403 全部消失**;对真实客户三项均返回真实结果。
## 同时补的归属数据
`sys_customer_assignment` 原有 9020→9001 一行;工作台把演示客户的 id
(9101-9104)当真实 `customer_id` 发给后端,而 `own_customers` 要求客户在投顾名下,
因此用 `tools/assign_customer_scope.py` 补了 4 行(9020 → 9101/9102/9103/9104),
回验 `customer_ids = ('9001','9101','9102','9103','9104')`。
## 仍未解决(属**数据**缺口,不是权限)
1. **9101-9104 在库里没有任何数据**:`advisor-config.js:126` 注释指向的
`_seed_demo_customers.py` 在仓库、git 历史与桌面上**都不存在**(从未提交),
所以这四位没有账号 / 风险测评 / 投资目标 / 持仓 —— 只能返回 `profile_required`。
2. **推荐候选恒为 0**:`AdvisorProductRepository.authoritative_tradable_products`
要求**已验证**的适当性证据与合同快照(`review_status='verified'` 且
`source_url` / `document_sha256` 非空,fail closed),而
`advisor_product_suitability_reference` / `advisor_product_contract_snapshot`
均为 0 行。这需要产品治理线提供可核查证据,**不应伪造**。
## 门禁
`tools/check_rbac_seed_consistency.py` 通过(种子内 id 唯一、各 grant 脚本与种子逐条一致);
`pytest tests/unit tests/contract` 全绿。
2026-09-14 22:56:42 +08:00
..
2026-09-12 12:10:10 +08:00
2026-09-12 14:57:17 +08:00
2026-09-10 20:33:13 +08:00
2026-09-14 21:46:29 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 17:26:18 +08:00
2026-09-13 19:47:10 +08:00
2026-09-12 14:40:53 +08:00
2026-09-10 22:29:23 +08:00
2026-09-10 22:42:17 +08:00
2026-09-12 12:20:01 +08:00
2026-09-14 20:29:54 +08:00
2026-09-12 15:21:34 +08:00
2026-09-14 22:17:39 +08:00
2026-09-13 22:57:59 +08:00
2026-09-10 20:15:09 +08:00
2026-09-12 15:21:34 +08:00
2026-09-10 20:29:12 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 10:13:48 +08:00
2026-09-13 21:44:08 +08:00
2026-09-13 19:39:16 +08:00
2026-09-10 17:11:52 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 16:57:47 +08:00
2026-09-10 18:14:03 +08:00
2026-09-14 22:56:42 +08:00
2026-09-12 12:09:55 +08:00
2026-09-14 22:16:29 +08:00
2026-09-14 01:07:43 +08:00
2026-09-12 14:43:05 +08:00
2026-09-13 22:28:42 +08:00
2026-09-11 14:46:40 +08:00
2026-09-11 21:03:58 +08:00
2026-09-11 21:03:58 +08:00
2026-09-09 21:55:37 +08:00
2026-09-12 12:20:01 +08:00
2026-09-10 20:22:53 +08:00
2026-09-10 22:34:20 +08:00
2026-09-12 10:34:28 +08:00
2026-09-14 01:59:46 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 16:57:47 +08:00
2026-09-12 10:45:40 +08:00
2026-09-09 21:55:37 +08:00
2026-09-09 21:55:37 +08:00
2026-09-14 01:59:46 +08:00
2026-09-13 16:20:39 +08:00
2026-09-14 20:35:46 +08:00
2026-09-11 20:30:27 +08:00
2026-09-11 14:24:45 +08:00
2026-09-11 16:51:52 +08:00
2026-09-14 20:35:46 +08:00
2026-09-14 20:35:46 +08:00
2026-09-11 16:51:52 +08:00
2026-09-11 14:19:05 +08:00
2026-09-11 16:28:19 +08:00
2026-09-12 14:40:53 +08:00
2026-09-13 19:17:56 +08:00
2026-09-13 18:54:09 +08:00
2026-09-11 10:13:48 +08:00
2026-09-14 18:13:10 +08:00
2026-09-11 19:51:44 +08:00
2026-09-13 22:08:04 +08:00
2026-09-11 14:46:40 +08:00
2026-09-10 21:36:23 +08:00
2026-09-10 21:55:03 +08:00
2026-09-11 15:25:45 +08:00
2026-09-11 15:25:45 +08:00
2026-09-11 17:26:18 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 21:46:29 +08:00
2026-09-11 14:46:40 +08:00
2026-09-14 10:03:07 +08:00
2026-09-14 20:50:55 +08:00
2026-09-13 21:56:27 +08:00
2026-09-11 14:46:40 +08:00
2026-09-12 14:43:05 +08:00
2026-09-13 21:44:08 +08:00
2026-09-14 20:48:18 +08:00
2026-09-13 22:28:42 +08:00
2026-09-14 22:56:42 +08:00
2026-09-12 14:43:05 +08:00
2026-09-11 14:46:40 +08:00
2026-09-12 10:45:40 +08:00
2026-09-11 16:57:47 +08:00
2026-09-12 12:10:10 +08:00
2026-09-11 19:51:51 +08:00
2026-09-13 21:14:13 +08:00
2026-09-11 21:03:58 +08:00
2026-09-13 23:38:13 +08:00
2026-09-11 13:05:33 +08:00
2026-09-11 10:27:29 +08:00
2026-09-11 16:57:47 +08:00