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

13 KiB
Raw Blame History

平台侧交接与联调准备

读者:接手平台侧的人,以及联调前要确认状态的人 时点: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 命令口径

# 架构师环境
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 触发条件:先确认"没人还在推"

判据:下面这段输出为空,才算各线都合完了。都搞完之前不要开始功能性测试 —— 否则测的是一个还会变的树,结论没有意义。

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 合并与验证命令

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 为准。