Files
group_fqcd_jr/docs/32-平台侧交接与联调准备.md
T

241 lines
14 KiB
Markdown
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.
# 平台侧交接与联调准备
> **读者**:接手平台侧的人,以及联调前要确认状态的人
> **时点**:2026-09-11 建立,2026-09-12 更新
> **一句话**:四条线(袁聪的场外/推广、NL 的客服画像与知识、投顾、ZSY 的客服接入)已并入
> `qyqy_develop`,库已跟上(89 张业务表),登录与 RBAC 只读接口已补齐;
> **但截至 2026-09-12 14:20 仍有线在推**,功能性测试/联调按 §5 的触发条件启动。
---
## 1. 当前状态(2026-09-12 实测,**非终态**)
> ⚠️ **这不是最终快照**:`lzl_qyqy_integration` 那条线仍在持续推送 —— 本轮我推第一次时
> 就被它抢先(远端在两次 `fetch` 之间前进了 4 个提交)。**做功能性测试前请先按 §5.0
> 确认各线都已停止推送**,然后重跑 §5.3 的命令并把数字更新到新的交接文档里。
| 项 | 值 |
|---|---|
| 分支 | `qyqy_develop` = `c8cdc06`(与 `origin` 同步,待推送 0,工作区干净) |
| 数据库 | **89 张业务表**,`alembic current` = head = `20260911_merge_adv_risk_heads` |
| `ruff check app tests tools` | 干净 |
| `mypy app` | **245 个文件 0 错** |
| `pytest tests/unit tests/contract` | **1317 passed, 2 skipped, 0 failed** |
| `pytest tests/integration` | **104 passed** |
| 文档守卫 / 端点编号守卫 | 53 份文档无编号冲突;§19 **62 个端点 / 6 个号段**无重复 |
| RBAC 号段自检 | 一致(种子 40 条权限,各 `grant_*.py` 与种子逐条一致) |
| 后台进程 | 无 |
> `mypy` 与测试数在本项目**必须带环境**读:架构师环境用
> `D:\conda\envs\jr_py313\python.exe`,NL 那边用本机 `.venv`。此前出现过
> "他报 184 个错、这边 0 个错"的对不上,根因是**对方的虚拟环境没满足
> `pyproject.toml` 的 `sqlalchemy>=2.0,<3` / `mypy>=1.14,<2`**,不是代码质量问题。
> 讨论这个数字前先对版本。
---
## 2. 这一轮做了什么
### 2.1 合进来的三条线
| 来源 | 内容 |
|---|---|
| **袁聪** | 场外基金运营(`offsite_*` 10 张)、推广(`promotion_*` 7 张)、NL2SQL、行情 |
| **NL** | 客服画像出口、知识库管理三端点、合规语境豁免、知识向量链路、检索字段运行时探测 |
| **投顾** | 21 张 `advisor_*` 表、投顾 Agent、组合分析、产品对比、投资目标、方案生成/审核/发布 |
### 2.2 平台侧补齐的
- **登录**:`POST /api/v1/auth/tokens`(bcrypt 校验 + 服务端签发 + 限流 + 成功/失败都审计)。
令牌里**只放 `sub`**,角色/权限/数据范围由 `IdentityService` 每次请求查库解析 ——
所以权限变更立即生效,现有鉴权链路一行未改。
- **RBAC 只读**:`GET /admin/roles`、`/admin/roles/{code}`、`/admin/roles/{code}/permissions`、
`/admin/users/{user_id}/roles`(后一个直接复用 `IdentityService.resolve`,回答"这个人为什么 403")。
- **投顾角色**:`advisor`(id=9004)+ 16 个投顾权限(id 9020–9035)。
投顾拿 10 项工作流权限;治理类 6 项只给 `admin`。
- **迁移**:跑了两次 `alembic upgrade head`,业务表 **51 → 89**。
### 2.3 修掉的真缺陷
| 问题 | 性质 |
|---|---|
| `aiosqlite` 未声明 | 袁聪 6 个测试必挂,报错指向 SQLAlchemy 内部而非"缺依赖" |
| `python-docx` 未声明 | 知识入库解析 `.docx` 时 `ModuleNotFoundError` |
| 3 个 mypy 错 | 全在 `offsite_fund_service.py` / `offsite_document_recognition_adapter.py`,各一行 |
| 7 项 ruff | 含 NL 的 conftest 漏 import(`F821`)、发布脚本死变量(`F841`) |
| 2 处文档重号 | `21-`、`22-` 被投顾线占用 → 让号到 `30-`/`31-` |
| `.gitignore` 编码混合 | 之前用 `Add-Content -Encoding utf8` 追加造成的,read 工具报 invalid UTF-8 |
---
## 3. 演示账号与环境口径
### 3.1 账号(`sys_user.username`,**不是**用户 id)
| user id | username | 密码 | 角色 | 能进什么界面 |
|---|---|---|---|---|
| 9001 | **`cust_t`** | `123456` | `customer` | 客户:会话、适当性、知识问答、自己的画像 |
| 9002 | **`risk_t`** | `666666` | `risk_operator` | 风控:预警队列、证据、日报 |
| 9003 | **`admin_t`** | `88888888` | `admin` | 管理:配置发布、模型端点、审计、RBAC 清单 |
| 9020 | **`advisor_t`** | `abc12345` | `advisor` | 投顾:组合分析、产品对比、投资目标、方案发布 |
> 密码由 `tools/set_user_password.py` 设置,**仅限演示环境**。
### 3.2 环境数据**不随代码合并**(三方都踩过这条)
| 数据 | 说明 |
|---|---|
| `config_release` | 各环境自己发。**整版本替换语义**:新版本没带上的配置项等于被删除;同 key 的继承项必须被本次定义覆盖,否则旧值会被子集校验 422 拦下整次发布。 |
| `sys_role` / `sys_permission` / `sys_user` | 各环境自己建。所以"我这儿能登录的账号,别人那台不一定有"。 |
| Milvus 集合 schema | 因环境而异(一边是 `doc_id`/`content`/`visibility`,另一边是 `knowledge_id`/`snippet`/无 `visibility`)。**检索层已改运行时探测,禁止硬编码字段名。** |
| `advisor_product_*` | 投顾的证据数据,靠外部披露文件导入,仓库里没有。 |
### 3.3 命令口径
```powershell
# 架构师环境
D:\conda\envs\jr_py313\python.exe -m pytest -q
# 组员环境(.venv 被 .gitignore 忽略、不进仓库)
.\.venv\Scripts\python.exe -m pytest -q
```
跑真机验证前先确认 **Docker Desktop 在运行**(Milvus 依赖它,它不常驻)。
---
## 4. 平台侧新增的工具(按用途索引)
| 工具 | 用途 |
|---|---|
| `tools/set_user_password.py` | 设置/查看登录密码(`--list` 只读) |
| `tools/create_test_user.py` | 建可登录的测试账号(用户+角色+密码),并**验证权限能解析出来** |
| `tools/grant_advisor_role.py` | 建 `advisor` 角色 + 补种子里缺的 3 个治理类权限并绑定(只增不删,带 `--dry-run`) |
| `tools/grant_customer_service_phase2_permissions.py` | 补客服二期的 3 个权限并授权(幂等;这 3 个已并进种子,脚本只作局部补齐) |
| `tools/publish_profile_tool_whitelist.py` | 补发客服白名单(画像工具),继承全部受管表 |
| `tools/login_console.py` | 浏览器登录测试台(`http://127.0.0.1:8099`) |
| **`tools/api_console.py`** | **平台功能测试台**(`http://127.0.0.1:8100`):自动覆盖 `docs/05` §19 全部 62 个端点、用平台 OpenAPI 补齐参数与请求体模板、四个演示账号一键切换身份,并提供**一键只读冒烟**(30 个 GET,**只有 5xx 算缺陷**)。还会列出「OpenAPI 有、§19 未登记」的接口 —— 首次运行查出 **67 个未登记** |
| `tools/seed_advisor_demo.py` | 投顾演示数据(产品目录+测评+归属),并报告方案那步卡在哪 |
| `tools/probe_release_state.py` | 只读:生效配置版本与 `agent_tools` 明细 |
| `tools/probe_auth_state.py` | 只读:`sys_user` 的密码状态(只出前缀与长度,不出完整哈希) |
| `tools/probe_knowledge_collections.py` | 只读:Milvus 集合 schema 与行数 |
| `tools/check_rbac_seed_consistency.py` | 只读:权限号段一致性(种子 vs 各 `grant_*.py`),**已纳入门禁** |
| `tools/check_docs_endpoint_ids.py` | 只读:`docs/05` §19 端点编号唯一性 + 号段覆盖自检,**已纳入门禁** |
> ⚠️ **RBAC 权限码的定义源是 `tools/seed_test_rbac.py` 的 `PERMISSIONS`**(9001-9046 号段)。
> 那个脚本对权限/角色/绑定是 **DELETE 重建**(`DELETE FROM sys_permission WHERE id BETWEEN
> 9001 AND 9099`,且只清 `role_id 9001-9003` 的角色绑定),**没并进它的权限码重建一次就没了**,
> 表现是"接口突然 403"而没有任何报错线索。投顾的 13 个业务权限、治理类 3 个、客服二期 3 个
> **都已并进种子**;`grant_*.py` 只补种子里缺的,且 id 必须与种子逐条一致 —— 一致性由
> `python tools/check_rbac_seed_consistency.py` 及其单测守着(2026-09-12 出过一次
> "两套 id→code 映射并存、`advisor` 静默拿到语义错误权限"的事故)。
> 另:种子对 `sys_user` 已是「存在则更新、不存在才插入」且**不覆盖密码**,所以重跑种子不会再弄丢演示口令。
---
## 5. 等组员推完之后的联调清单
### 5.0 触发条件:先确认"没人还在推"
**判据**:下面这段输出**为空**,才算各线都合完了。都搞完之前不要开始功能性测试 ——
否则测的是一个还会变的树,结论没有意义。
```powershell
git fetch --all --prune
foreach ($b in (git branch -r --format='%(refname:short)' | Where-Object { $_ -notmatch 'HEAD' })) {
$n = git rev-list --count "origin/qyqy_develop..$b" 2>$null
if ($n -gt 0) { "$b 独有 $n 个提交未合" }
}
```
> 2026-09-12 的实测:`NL_develop` 曾独有 16 个(已合)、`lzl_qyqy_integration` 正在持续推送。
> 注意**别把"落后很多的老分支"当成待合分支**(如 `lzl_develop` 落后 206 个提交,
> 它的产出走的是新建的 `lzl_qyqy_integration`),但也**别反过来把活跃分支当废弃分支**——
> 先看它的最新提交时间。
### 5.1 外部依赖前置(不满足会产生**假失败**,别当代码缺陷)
| 依赖 | 检查方式 | 不满足的后果 |
|---|---|---|
| **MySQL + RBAC 种子** | `python tools/seed_test_rbac.py` → `python tools/set_user_password.py` | **不跑这两步,`tests/integration` 会有 13 个登录/RBAC 用例因 401 而红**。口令脚本**非幂等**(重复执行等于重设密码) |
| Docker Desktop(Milvus) | `docker ps` 能连上 | 检索、知识链路不可用;`tools/setup_milvus_profile_collection.py` 建不了集合 |
| Redis | 健康检查 | 登录限流、客服短期会话记忆链路不可用 |
| Neo4j | `127.0.0.1:7687` 可连 | 关系/图投影链路不可用 |
| SMTP / IMAP | `.env` 里的开关 | 场外通知与邮件识别只能走离线数据集 |
### 5.2 已知的"看起来像缺陷但不是"
- `tests/unit/service/test_offsite_document_recognition_adapter.py` 有 2 个用例被记为**环境相关失败**
(断言请求体里是中文原文,而 httpx 会把中文序列化成 `\uXXXX`,字节序列自然不匹配)。
**主干的架构师环境复现不了**(2026-09-12 全量为 0 failed)。不要为了"让它绿"去动实现;
若要修,正确做法是断言 `json.loads(body)` 后的字段值 —— 字节级断言不该用来测 JSON。
- `mypy app` 的数字**先对版本再对代码**:SQLAlchemy 补丁版不同会差出上百个错,
复现矩阵见 `AGENTS.md` 的"环境与命令口径"一节。
### 5.3 合并与验证命令
```powershell
git fetch origin --prune
# 1) 先试合,看冲突。加 --no-commit 是为了留出验证机会,别直接让它落提交
git merge --no-commit --no-ff origin/<他们的分支>
# 2) 合并后必跑(表与文档都是环境/仓库数据,容易漏)
python -m alembic upgrade head
python tools/audit_schema.py # 期望 no missing or unexpected
python tools/check_authoritative_docs.py # 期望 no number collision
# 3) 门禁
ruff check app tests tools
mypy app
pytest tests/unit tests/contract -q
pytest tests/integration -q
# 4) 真机联调
python tools/seed_advisor_demo.py # 投顾数据(方案那步见 §6)
python tools/login_console.py --port 8099 # 浏览器登录测试台
```
**为什么迁移与文档守卫要单独列**:三家合并里,有两次带进了文档重号、两次库与代码
版本不一致(一次"版本号跑了、表没建",一次"表没建、版本号也没跑")。两者都靠
`alembic upgrade head` 收敛,而重号只有守卫能发现。
### 跑验收前**必须先停常驻 Worker**
`python -m app.worker` 与验收脚本共享 `agent_run` 队列,它会抢走 run,导致假失败。
(见 `docs/20` §5、`docs/19` §7。)
---
## 6. 悬置的决策项(需要业务/架构拍板)
1. **画像与测评的等级口径不一致**:`fin_customer_profile.investor_type = C2`,
而 `tools/seed_advisor_demo.py` 补的测评是 `C5`。前者决定画像展示、后者决定适当性裁决,
**两者会同时出现在界面上**。演示前需统一(把测评改成 C2,或把画像也改成 C5——后者要改既有行)。
2. **投顾方案暂时配不出来**:`authoritative_tradable_products` 是 fail closed 的,
没有带 `source_url` / `document_sha256` 的 verified 适当性参考就排除全部产品。
那些行必须来自**真实的销售适当性披露 / 基金合同文件**(`import_product_governance_reference.py`
的两个 CSV,不在仓库里)。**不伪造这两个字段**是证据链红线。
拿到文件后按 `seed_advisor_demo.py` 打印的三条命令补。
3. **`operator` 角色只有 1 项权限**(`offsite:write`),而 `bootstrap.py` 有 8 处引用它。
与 `advisor` 之前同类:角色在、权限不全。好在那些入口通常也允许 `admin`,故不是全挂。
4. **`memory_sync_outbox` 没有消费端**:`ProjectionReconciliationService` 有定义、
无实例化点;`GraphProjectionWorker` 同样没有实例化点。组件写好了、线没接。
5. **权限的可写接口(B2)未做**,且**不应随手做**:必须带三条红线 ——
审计留痕、禁止自我提权、保护内置角色(否则可能把自己锁在门外)。
当前只做了只读(§2.2)。
---
## 7. 平台侧的边界(别重复实现)
`docs/05` §11 把"JWT 签发、刷新、注销"划给统一身份认证模块。平台现在**只做登录**
(§2.2 已注明这是该句的唯一例外):**刷新与注销仍留给那个模块**,
`app/core/security.py` 已经留好 `RevocationStore` 协议,接上 Redis 即可。
认证是**平台级能力**,不是某个业务域的事。业务分支**不要自己加登录或鉴权路由**,
否则会绕开公共鉴权(`AGENTS.md` 规则 7)。
---
*本文所有数字均为 2026-09-11 实测;若与代码不一致,以代码与 `docs/05-接口文档.md` 为准。*