# 平台侧交接与联调准备 > **读者**:接手平台侧的人,以及联调前要确认状态的人 > **时点**: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` 角色与 16 个权限(只增不删,带 `--dry-run`) | | `tools/publish_profile_tool_whitelist.py` | 补发客服白名单(画像工具),继承全部受管表 | | `tools/login_console.py` | 浏览器登录测试台(`http://127.0.0.1:8099`) | | `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/seed_test_rbac.py` 是 **DELETE 重建**语义,它的 > `DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099` 会**清掉 > `grant_advisor_role.py` 建的投顾权限**。要在种子里固化投顾权限,请并进它的 > `PERMISSIONS` 常量,而不是两个脚本各建一套。 --- ## 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` 为准。*