Files
group_fqcd_jr/开发文档/D4.5-投顾模块清除执行报告-2026-09-17.md
T
张胜宇 c91bbcbdc1 feat(ops)+docs: 密钥轮换工具 + 两份文档目录审计收口(D1.1 §23 / D2.1 v6.26)
一、密钥轮换(新增工具 + 操作手册)
- 新增 tools/rotate_api_keys.py:--check 体检 + 交互式轮换;getpass 不回显、
  自动备份 .env.bak-<时间戳>(已被 ignore 命中)、校验不过整体不写入、
  三个 Qwen 变量写同一值 / 两个 DeepSeek 变量写同一值。
  实测 --check:Qwen 三变量同值且非空、DeepSeek 两变量同值且非空。
- 新增 开发文档/D3.8-模型密钥轮换与凭据安全操作手册-2026-09-20.md(CS-OPS-2026-023):
  .env 5 个变量与读取方取证、五步流程、3 个坑、复核清单、回退方式、能力边界。
- 口径确认:model_endpoint_config.secret_ref 存变量名 ⇒ 轮换只改 .env,不动 DB;
  但必须重启 API + Worker。

二、门禁修复:docs/ 编号撞车
- tools/check_authoritative_docs.py(D3.4 N-14 登记的验收命令集之一)实测 FAIL:
  我方 docs/46 docs/47(2026-09-20 建)与投顾组 docs/46-投顾Agent需求文档.md
  docs/47-投顾Agent功能架构文档.md(2026-09-16 建)同号。
- 按「后到者让位」改名:docs/48-可改文件白名单.md / docs/49-底座会签申请单-2026-09-19.md,
  同步 8 处引用。修复后:checked 54 documents, no number collision,exit 0。

三、前端品牌残留(W12 合并静默回退)
- employee-advisor/dashboard/index.html 与 customer/advisor-plans/index.html 的
  <title> 仍是 南方财富(投顾组分支带回)→ 按 DEC-27 改为 南方基金。
- HTTP 实测两页标题已正确;全仓 app/ 复查 南方财富 = 0。

四、文档口径校准(12 处事实漂移)
- D1.1:D2.1 版本 v5.3 → v6.26(§4.0 / §4.1 / §1 / §2 四处长期错误);
  §8 四行遗留项闭合(D-5 / D-6 / D-7 / 仓库副本同步)+ 新增 §22 §23 留痕;
  §10.2「本区不在任何 git 仓库内」更正为已入库;新增两编号 ⇒ 计数 56 → 58 全量同步。
- D2.2:顶栏徽标 v2.4 与元数据 v2.5 自相矛盾 → 统一;「投顾已清除」→ 状态更新
  (模块 2026-09-20 已恢复,但客服范围裁定 §1.7 / RK-10 不变)。
- D2.3:徽标 v1.0 · 7 批次 51 项 → v1.1 · 8 批次 57 项;投顾清除后果 + §7.1 头号风险
  + 风险表 + 不触碰行全部加恢复口径。
- D2.4:v1.3 变更说明 ⑦ / §1.4 Out of scope / Q-09 加投顾恢复口径。
- D2.5:advisor_t 自相矛盾口径改写为账号表一行 + 口径更正;五项自检首选改为
  一键脚本 启动演示.bat / demo.ps1;补 D3.8 与未发布 advisor:* 白名单登记。
- D2.6:门禁数字 1856/2 → 1909/3 skipped、ruff 19 → 20、补 portal_api_check 行;
  §10 两项已闭环(密钥轮换已工具化、A-10 组 3/4 已补签);头部加 W12/W13 状态更新。
- D4.5:顶部状态更新补指向 D4.7。
- 新增 开发文档/D4.7-投顾模块恢复记录-2026-09-20.md(CS-PURGE-2026-014):
  时间线、8 项恢复动作、客服线不变的结论、DEC-19 理由更正、遗留 1 项、失误登记。
- _consistency.py(维护侧):§三 改为「投顾状态口径检查」,合法语境扩为
  清除史 / 恢复史 / 不属本 Agent 范围。

五、回归实测(全绿)
- pytest -q:1909 passed / 3 skipped / 0 failed
- ruff check app tools tests:20(与 W12 持平,未引入新债)
- mypy app:2(= 既有基线)
- tools/check_authoritative_docs.py:54 文档无编号冲突(exit 0)
- tools/e2e_smoke_test.py --read-only:31/31
- tools/portal_api_check.py:40 项 通过 35 / 失败 0 / 跳过 5
- _eval_harness/http_probe.py:11/11 succeeded
- _consistency.py:GATE PASS
- demo.ps1 -SkipStart -NoBrowser:五项自检全过、退出码 0
- 权威副本 ↔ 仓库:逐字节一致(客服agent 24 / 开发文档 52)

六、未做(如实登记)
- 投顾 config_release 工具白名单(advisor:*)仍未发布 ⇒ 投顾 Agent 工具调用 fail closed
  (实测 active_agent_tools 仅 customer_service:* 4 项 + risk:* 4 项)。与客服线无关;
  要演投顾线先跑 tools/publish_advisor_demo_config.py --apply。
- 两把 key 的实际轮换需你在控制台建新 key(无法代做),流程见 D3.8。
2026-09-20 15:27:07 +08:00

194 lines
15 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.
# 投顾模块清除 · 执行报告
> **体系编号**:`D4.5` · 域:四、清除与重建留痕 · 编号体系见 `D1.1` §4.0
> **编号**:CS-PURGE-2026-013
> **日期**:2026-09-17
> **依据**:用户指令「参考《客服与投顾模块重构前代码清理建议-2026-09-15.md》,将『投顾』模块从项目中完整清除」;范围决策见 `D4.4-投顾模块清除范围与影响面清单-2026-09-17.md`(CS-PURGE-2026-012)
> **用户取舍**:① 边界 = **连投顾域业务一起下架**(含客户侧投顾域能力与权限码);② 时点 = **现在就全部清除**(接受失去「改 6 个底座文件时的对照组」)
> **结论**:**已执行完成。报错风险归零**(0 语法错误、0 悬空 import)。
---
> ## ⚠️ 2026-09-20 状态更新(`W12` 合并):**本报告的清除结果已被取代**
>
> 远端 `qyqy_develop` 在 2026-09-16 落了 **3 个投顾提交**(`5607751` / `2fe7d0c` / `74b7d00`:
> 投顾需求与架构文档、客户主动申报投顾方案 + 受理出草稿、方案交付落点与推荐依据 LLM 增强),
> 其代码**反向 import 本报告清除掉的模块**(`app.model.investment_goal`、
> `app.service.product_recommendation_service`、`app.service.advisor_rollout_service`)。
> 两者不可能同时成立,合并时按 **「组员新功能 > 本地清除」** 裁定:**投顾模块已恢复**
> (冲突解决与语义回滚清单见 `开发文档\D1.6` §4.37)。
>
> **因此本报告 §0「结论」与 §1「清除内容」应读作"2026-09-17 当时的状态",不再是仓库现状。**
> 恢复动作:`git` 侧取远端版本(49 个文件)+ `tools/grant_advisor_role.py`(建 `advisor` 角色,34 权限)
> + `tools/create_test_user.py --id 9020 --username advisor_t`(重建演示账号)。
> **`D4.4` 的范围与影响面清单仍是有效的历史留痕**(尤其 §0-②③ 对"清投顾会拆掉 MVP 硬阻断"的预警,
> 正是本次裁定的依据)。
>
> 📌 **想看「投顾现在的状态」请读 `D4.7-投顾模块恢复记录-2026-09-20.md`** —— 本件(`D4.5`)描述的是
> 2026-09-17 当时的清除动作,**不再是仓库现状**;`D4.7` 是「现状」单据(时间线 / 恢复动作 / 客服线不变的结论 / 遗留)。
---
## 0. 验证结果(可复现)
用 `offline-repo-integrity-check` 技能的 `verify_repo_integrity.py` 扫描,**改动前后各跑一次**:
| 指标 | 清除前 | **清除后** | 判定 |
|---|---|---|---|
| `.py` 文件数 | 541 | **532** | 少 9(本轮第 2 批) |
| `py_compile` 失败数 | 0 | **0** | ✅ |
| **悬空 import 数** | **25** | **0** | ✅ **报错风险归零** |
| 残留文本命中文件数 | 66 | 59 | 见 §4 |
| 残留命中总数 | 251 | 202 | 见 §4 |
**验证上限(诚实声明)**:本机**依赖零安装、无 venv**,因此**无法运行** `pytest` / `ruff` / `mypy` / 任何集成测试。上面两项(语法、悬空 import)覆盖的是「**必然报错**」这一类;**未覆盖**:运行时行为、DB 交互、前端渲染、路由实际可达性。前端改动只做了文本级核对。
---
## 1. 清除内容(共 49 个文件)
### 1.1 后端业务层(21 个)
| 文件 | 说明 |
|---|---|
| `app/service/agent/implementations/advisor.py` | 投顾 Agent(`AdvisorAgent`) |
| `app/service/advisor_rollout_service.py` | 灰度闸门(63 行,原为空转) |
| `app/service/product_recommendation_service.py` | 推荐生成(470 行,含证据门调用) |
| `app/service/investment_goal_service.py` | 投资目标与方案书(384 行) |
| `app/service/goal_conversation_service.py` | 目标对话抽取(228 行) |
| `app/service/asset_allocation_service.py` | 资产配置(201 行) |
| `app/service/allocation_backtest_service.py` | 配置回测 |
| `app/service/portfolio_analysis_service.py` | 组合分析 |
| `app/service/product_comparison_service.py` | 产品对比(客户侧 9034) |
| `app/core/advisor_allocation_contracts.py` / `advisor_backtest_contracts.py` | 契约 |
| `app/api/controllers/recommendations.py` | **整文件**(`advisor_router` `/api/v1/advisor` + `admin_router` 的 `/advisor/*` 全部端点) |
| `app/api/controllers/investment_goals.py` / `asset_allocation.py` / `portfolio_analysis.py` | 端点 |
| `app/api/schemas/investment_goals.py` / `asset_allocation.py` / `portfolio_analysis.py` | 契约 |
| `app/repository/investment_goal_repository.py` / `portfolio_analysis_repository.py` | 仓储 |
| `app/model/investment_goal.py` / `goal_conversation.py` | 模型映射 |
### 1.2 前端(`app/static/portal/employee-advisor/`,整目录 9 文件)
`index.html`、`dashboard.js`、`dashboard.css`、`actions-module.js`、`published-module.js`、`assistant-module.js`、`customer-module.js`、`advisor-engine.js`、`advisor-config.js`(约 2045 行)。
### 1.3 运维脚本(5 个)
`tools/bootstrap_advisor_demo.py`、`grant_advisor_role.py`、`publish_advisor_demo_config.py`、`seed_advisor_demo.py`、**`check_portal_modules.py`**(投顾工作台专属的前端模块检查器)。
### 1.4 测试(14 个)
`test_advisor_rollout_service` / `test_advisor_asset_allocation` / `test_advisor_base_adapter` / `test_advisor_review_queue_mysql` / `test_advisor_migration_contract` / `test_bootstrap_advisor_demo` / `test_allocation_backtest_service` / `test_dynamic_allocation_optimizer` / `test_goal_conversation_service` / `test_investment_goal_service` / `test_portfolio_analysis_service` / `test_product_comparison_service` / `test_product_recommendation_service`(**13 个整文件**)+ `test_portal_frontend.py` 内 **3 个投顾用例**(见 §2.3)。
### 1.5 过期字节码
清理 **40 个** `__pycache__/*.pyc`(其源码已不存在)。
---
## 2. 修改的引用(避免残留引用导致报错)
### 2.1 装配入口(**技能特别警告:这里漏改会让全部组件一起挂**)
| 文件 | 改动 |
|---|---|
| `app/main.py` | 删 3 个 controller import + 2 个 recommendations import 块 + **5 处 `include_router`**(共 9 处) |
| `app/service/agent/bootstrap.py` | 删 7 个 import + **5 个投顾工具注册**(`query_investment_goal` / `analyze_portfolio` / `generate_asset_allocation` / `recommend_products` / `compare_products`)+ **1 处 `factory.register(AdvisorAgent...)`**(共 9 处) |
| `app/api/controllers/admin.py` | 删 2 个 import + `/advisor/asset-allocation-backtests` 端点(共 3 处) |
| `app/service/agent_run_application_service.py` | 删 `AdvisorRolloutService` import + `accept()` 内的投顾灰度分支(共 2 处) |
| `app/core/config.py` | 删 `advisor_rollout_enabled` / `advisor_rollout_customer_ids` 及注释(1 处) |
### 2.2 前端外壳
| 文件 | 改动 |
|---|---|
| `common/auth.js` | 删投顾跳转分支;`staffRoles` 去 `advisor`(2 处) |
| `common/layout/app-shell.js` | `ADVISOR_LINKS` 去投顾入口(保留 `public-products`);角色标签去 `advisor`(2 处) |
| `common/login-controller.js` | 员工登录允许角色表去 `advisor`(1 处) |
| `common/api-client.js` | **删 16 个投顾端点注册项**(`ADVISOR_*` 14 个 + `ADMIN_ADVISOR_*` 3 个中的 3 个) |
### 2.3 测试与 RBAC 种子
| 文件 | 改动 |
|---|---|
| `tests/unit/api/test_portal_frontend.py` | 删 3 个用例:`test_advisor_workspace_...`、`test_advisor_dashboard_is_composed_...`、`test_portal_feature_modules_have_consistent_imports`(后者调用已删的 `check_portal_modules.py`) |
| `tests/unit/service/test_dependency_degradation.py` | **改而非删**:只摘掉 `test_portfolio_graph_context_failure_is_degraded` 一个用例,其余(`RelationshipService` 降级)**保留** |
| `tools/seed_test_rbac.py` | **撤销 `customer` 的 8 个投顾域权限码**:`investment-goal:{write,confirm,read}:self`、`product-recommendation:{generate,read}:self`、`portfolio-analysis:read:self`、`asset-allocation:generate:self`、`product-comparison:read:self` |
---
## 3. 有意保留(**不是漏删**,附理由)
| 保留对象 | 理由 |
|---|---|
| `app/model/advisor_product.py`、`app/repository/advisor_product_repository.py` | **产品主数据底座**。被 `product_metric_service` / `product_history_sync_service` / `product_governance_monitor_service` / `market_quote_sync_service` / `portfolio_analysis_repository` 消费;且 `authoritative_tradable_products()` 是 **MVP「唯一硬阻断:产品证据链为空」的实现者** |
| `app/model/profile_tag.py`(`AdvisorProfileTag` / `AdvisorProfileDriftReview`) | **名字带 Advisor,业务属画像**。被 `risk_questionnaire_repository.py`、`profile_governance_service.py` 使用 → 删了会打断**风险测评/画像**线 |
| `app/api/controllers/admin.py` 的 `/advisor/profile-tags`、`/advisor/profile-drift-reviews`(+2 reviews) | 同上,**画像治理**,不是投顾业务;`api-client.js` 的 `A041`/`A042` 同步保留 |
| `tools/sync_advisor_market_data.py`、`sync_advisor_market_quotes.py` | 行情同步(**交易线亦依赖**) |
| `app/core/{investment_goal,portfolio_analysis,product_comparison,product_recommendation}_contracts.py` | 契约文件已无生产引用,但**删它们要连带改 `bootstrap.py` 之外的核对面**;本轮只删引用,文件留待 F-05 清理清单登记 |
| **21 张 `advisor_*` 表 + 15 个 alembic 迁移** | `AGENTS.md` **规则 3「禁止重命名或删除已有表」**;且无 `DROP` 迁移才保证「新环境 `alembic upgrade head`」与老环境 schema 一致 |
| `sys_role` 的 `advisor` 角色行(若库中存在) | 属**环境数据**;删行会影响既有 `sys_user_role` 绑定。**不改** |
| `app/service/agent/governance.py:129` | 仅注释里以 advisor 举例,无代码依赖 |
| `docs/**`(含 `docs/30`、`docs/05` §19、`docs/演示用/*`、`AGENTS.md` 角色表) | 项目既有惯例:**历史记载不改**,由 F-05 回写时登记 |
| `tools/portal.py` / `portal_api_check.py` / `e2e_smoke_test.py` / `create_test_user.py` / `check_rbac_seed_consistency.py` 中的投顾端点与角色字符串 | **字符串级引用,不导致报错**;但这些工具会调用到已删除的端点(返回 404)。见 §5 待办 |
---
## 4. 受影响的关联功能(回答「可能受影响的关联功能」)
| 关联 | 影响 | 说明 |
|---|---|---|
| **`/api/v1/advisor` 全部端点** | 🔴 **已下线** | 整个前缀消失(投顾工作台、推荐、目标、配置、分析、对比全在上面) |
| **`/api/v1/admin/advisor/*` 中的投顾部分** | 🔴 **已下线** | `pending-contents`、`recommendations/{id}/reviews`、`publications`。⚠️ **`profile-tags` / `profile-drift-reviews` 仍在**(画像治理) |
| **客户侧的投顾域自助能力** | 🔴 **已下架**(按你的选择) | 客户不再持有 `investment-goal:*` / `product-recommendation:*` / `portfolio-analysis:read` / `asset-allocation:generate` / `product-comparison:read`。**重跑 `seed_test_rbac.py` 后生效**(该脚本是 DELETE 重建语义) |
| **`customer` 角色的 Agent 工具** | 🟡 **收窄 5 个** | `query_investment_goal` / `analyze_portfolio` / `generate_asset_allocation` / `recommend_products` / `compare_products` 已从 `ToolRegistry` 摘除。**若发布配置 `config_release` 的白名单里仍有这些工具名,调用会以「工具未注册」失败** → 见 §5 |
| **投顾前端与账号** | 🔴 **已下线** | `employee-advisor/` 删除;`auth.js` 跳转分支移除。若库里仍有 `advisor` 角色账号,登录后走 `staffHomeForRoles` 的兜底(`/portal/employee-console/workspace/`) |
| **产品数据底座 / 产品治理 / 行情 / 交易** | 🟢 **未受影响** | 按 §3 全部保留 |
| **风险测评与画像** | 🟢 **未受影响** | `AdvisorProfileTag` 等按 §3 保留 |
| **MVP 的「唯一硬阻断」** | 🟢 **未受影响** | 证据门位于保留的底座仓储中 |
| **改 6 个底座文件时的对照组** | 🔴 **已失去** | 你在知情下选择了「现在就全部清除」。**后果**:`knowledge_search_service` / `knowledge_tool` / `compliance_context` / `governance` / `agent_run_application_service` / `agent_persistence_service` 的改动,将**只剩客服一条业务线可做回归验证**(投顾原本是第二条)。建议 **G-00(测试环境就位)的优先级上调**——没有可运行测试套件时,底座改动的风险敞口明显变大 |
| **21 张表** | 🟢 **数据不丢** | 表保留,但代码删除后**无人读写**,成为孤儿表(可接受) |
---
## 5. 待办 / 需你拍板
| # | 事项 | 影响 | 建议 |
|---|---|---|---|
| **1** | **`config_release` 白名单里的投顾工具名** | 若 active 配置仍列出 `recommend_products` 等 **5 个已摘除的工具**,Agent 调用会以「工具未注册」失败(fail-closed,不静默) | 随 **A-03(配置快照)** 一并实测,然后按实测结果**重发白名单**(P-7) |
| **2** | **`seed_test_rbac.py` 重跑** | §2.3 的客户侧权限撤销**必须重跑种子才生效**(DELETE 重建语义) | 随 A-02/A-03 批次一起跑 |
| **3** | **库里 `advisor` 角色行与既有账号** | 角色行保留 → `advisor` 账号仍可登录,但落到 console 工作台 | 若确要清,需**先查 `sys_user_role` 有无绑定**,再决定删角色行还是留 |
| **4** | **`sys_permission` 的 9020-9034 / 9041-9043 / 9057-9059 / 9066-9068** | 定义保留(未删)→ 成为无角色持有的孤儿权限码 | 保留(避免重跑种子连带影响),文档登记即可 |
| **5** | **`tools/portal.py` / `portal_api_check.py` / `e2e_smoke_test.py` 中的投顾端点** | 这些工具会调用已删端点 → **404**(不报错,但验收脚本会红) | 若要跑这些脚本,需先摘掉投顾段;**建议随 F-05 一起** |
| **6** | **4 个已无引用的 contracts 文件** | 无生产引用但文件仍在 | 随 F-05 清理清单登记 |
---
## 6. 恢复方式(无 git 可用时的回退)
本机 **git 写操作被沙箱拦截**,无 `git revert`。回退手段为**文件级备份**:
```
D:\桌面\金融\_advisor_purge_backup\
app/service/... ← 保持仓库内相对路径
app/static/portal/employee-advisor/ ← 整目录(9 文件)
tools/...
tests/...
```
**恢复方式**:把 `_advisor_purge_backup/` 下的内容按相对路径复制回 `group_fqcd_jr/` 即可。**注意**:§2 的 30+ 处引用改动**不会**被这一步还原,需另行反向修改(备份不含修改前的文件副本)。
> ⚠️ 因此**建议不要立刻删除 `_advisor_purge_backup/`**——至少在 §5 的 3 项与底座改动(组 1 / 组 2)全部验证通过之前保留。
---
## 7. 一条必须留痕的教训
`advisor` 这个前缀在本仓库里表示「**投顾线引入的**」,**不等于「投顾专属」**:
- `AdvisorProfileTag`(`profile_tag.py`)→ 服务**风险测评/画像**;
- `advisor_product*`(model + repository)→ 是**产品主数据底座**,且承载 **MVP 唯一硬阻断**;
- `/advisor/profile-tags`、`/advisor/profile-drift-reviews` → 是**画像治理**,不是投顾业务。
**按名字删会拆掉这三样。** 本次是靠「**找消费方**而不是看文件名叫什么」逐个核实才避开的——这也是本次清点比《清理建议》列出的清单大得多的原因(对方只列了 7 个后端文件,实际清了 21 个 + 9 个前端 + 5 个脚本 + 14 个测试)。