Files
group_fqcd_jr/开发文档/D4.4-投顾模块清除范围与影响面清单-2026-09-17.md
张胜宇 bc61d5c579 docs: 入库权威文档目录(客服agent/ 24 份 + 开发文档/ 50 份,替换旧命名的过期副本)
## 为什么做这一步

权威文档 74 份此前**只在本机**,评审者 clone 分支后看不到任何设计文档;而仓库里那两份同名目录
是 **2026-09-16 之前的过期副本,连文件名都是旧的**(无体系编号)。本次按「**权威覆盖过期**」入库。

## 入库内容

| 目录 | 文件数 | 体积 | 说明 |
|---|---|---|---|
| `客服agent/` | 24 | 0.77 MB | `D2.1`~`D2.6` 对外交付四件套 + 演示脚本/答辩报告 + `_build` 构建工具 |
| `开发文档/` | 50 | 2.16 MB | `D1.x` 索引与决策、`D3.x` 方案、`D4.x` 清除与重构留痕、`D5.x` 业务流程、`D6.x` 业务事实基座、`D7.x` 交付物、`D8.x` 规范 |

**旧的过期副本整体移除**(`客服Agent执行Todolist.md` → `D2.1-客服Agent执行Todolist.md` 之类
的改名 + 新增 `D2.5`/`D2.6`),入库后目录内容与权威副本**逐文件一致(零差异,已复核)**。

## 入库前的安全扫描(必须留痕)

- 扫描规则:`sk-` 类密钥 / `Bearer` 长串 / `password=`、`api_key=` 赋值 / 会话中出现过的两把明文 key 片段。
- 结论:**真实密钥只出现在 `.env`**(已被 `.gitignore` 命中,未入库);`.env.example` 与
  `config/risk.env.example` 只有**空占位**。
- 文档内唯一命中是 `D3.1` 里一处**截断的示例 JWT**(`Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`),
  末尾带省略号,是接口文档的示意值,**不是可用凭据**。
2026-09-20 15:03:15 +08:00

276 lines
18 KiB
Markdown
Raw Permalink 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.4` · 域:四、清除与重建留痕 · 编号体系见 `D1.1` §4.0
> **编号**:CS-PURGE-2026-012
> **日期**:2026-09-17
> **触发**:用户要求「参考《客服与投顾模块重构前代码清理建议-2026-09-15.md》,将『投顾』模块从项目中完整清除」
> **性质**:**只读清点 + 范围界定**。**尚未执行任何删除**——因为清点过程中发现按名字删会拆掉 MVP 的硬阻断,须先与你确认边界
> **被参考的文档**:`开发文档/客服与投顾模块重构前代码清理建议-2026-09-15.md`(下称《清理建议》)
---
## 0. 结论前置(请先看这三条)
### ① 你给的这份文档,不支持「整模块删除」
《清理建议》的**性质是「死代码清理」**,不是「模块拆除方案」。它的 §二「建议移除清单」里真正**属于投顾**的只有 C1(灰度闸门 63 行);而它对投顾另外两项的结论是:
| 《清理建议》条目 | 对投顾的结论 |
|---|---|
| **C1** 投顾灰度闸门(`advisor_rollout_service.py`) | 可删,但**风险中**,须先确认「灰度是已结束还是尚未开始」 |
| **C2** 投顾本地演示引擎(`advisor-engine.js` + `?demo=1`) | **明确「不建议直接删」**——「**仍在活跃使用**,不是死代码」;且它兼任「未登录/无 dbId 时的回退」 |
| **C3** 前端 `?v=` 版本号 | **「保留」**——是 ESM 缓存破坏机制,不是冗余 |
| **§四.7** | 「投顾前端(2045 行)建议**单独立项**」 |
**它真正对本任务有用的部分**,是 §一 的**模块清单**(后端 7 文件 / 前端 9 文件)——本文档就是用这份清单做的起点。
### ② 🔴 头号风险:「投顾」这个名字在代码里对应**两类完全不同的东西**
按名字删(删掉一切带 `advisor` 的文件)会**同时删掉产品数据底座**,其中包括 **MVP 的硬阻断实现**。详见 §3。
### ③ ⚠️ 与 10 分钟前刚拍板的一项决策冲突
`D3.4-客服Agent重构Todolist.md` v5.1 §9-3(今日 10:00 前已拍板、且已写进三份文档)明确:
> **「先重建客服、投顾留到最后」——投顾是唯一完好业务线,是改 6 个底座文件时的对照组。**
现在删掉投顾,等于**在动 6 个底座文件之前先拆掉唯一的对照组**。详见 §8。
**⇒ 我的建议**:**先确认边界(§4)+ 决定时点(§8),再动手**。删除不可逆(本机 git 写被沙箱拦截,无 `git revert` 可用)。
---
## 1. 投顾模块的真实规模(清点结果,比你给的清单更大)
| 类别 | 数量 | 说明 |
|---|---|---|
| 后端业务文件 | **约 20 个** | 《清理建议》列了 7 个;实际清单见 §4 |
| 前端文件 | **9 个** | `app/static/portal/employee-advisor/dashboard/**`(整目录) |
| **alembic 迁移** | **15 个** | `20260910_advisor_*.py` × 12、`20260911_advisor_*.py` × 2、`merge_advisor_risk_heads.py` |
| **数据库表** | **21 张** `advisor_*` | 见 AGENTS.md §E(投顾 21 张,**不进 `docs/00` 基线**) |
| **tools 脚本** | **7 个** | `bootstrap_advisor_demo` / `grant_advisor_role` / `publish_advisor_demo_config` / `seed_advisor_demo` / `sync_advisor_market_data` / `sync_advisor_market_quotes` / (+ `check_portal_modules.py` 内引用) |
| **测试文件** | **7 个** | 见 §5.4 |
| **RBAC 权限码** | **4 段** | 9020-9034、9041-9043、9057-9059、**9066-9068** |
| **HTTP 端点前缀** | **1 个** | `/api/v1/advisor`(另有 `/api/v1/admin` 与投顾共处一个文件) |
| **前端跳转/导航** | **2 处** | `auth.js` 角色跳转表、`app-shell.js` 导航表 |
| **配置项** | **2 个** | `advisor_rollout_enabled`、`advisor_rollout_customer_ids` |
---
## 2. 我实际是怎么清点的(方法)
不是只看《清理建议》,而是**自己做了全仓扫描**:
1. `Glob **/*advisor*` → 拿到全部同名文件(含 `__pycache__` 与迁移);
2. `Grep -i "advisor" --glob *.py` 逐文件计数 → **`app/` 下 48 个文件命中**;
3. 对关键符号(`AdvisorProductRepository` / `app.model.advisor_product`)做**导入方追踪**,区分「投顾业务」与「产品数据底座」;
4. 检查路由注册(`app/main.py`)、前端挂载(`auth.js` / `app-shell.js`)、工具与测试引用。
**关键判据**:一个文件是否属于投顾,**不取决于它名字里有没有 `advisor`,取决于「谁在消费它」**。
---
## 3. 🔴 名字骗人:`advisor_*` 的两类含义
### 3.1 第一类:投顾业务层(**可删**)
真正只服务投顾业务、删掉不影响别人的文件(见 §4.1)。
### 3.2 第二类:**产品数据底座**(**必须保留**)
以下文件名字里都有 `advisor`,但它们是**产品主数据底座**,被**非投顾**模块消费:
| 文件 | 谁在消费(非投顾) |
|---|---|
| `app/model/advisor_product.py` | 产品指标 / 行情 / 对比 / 治理 / 组合分析 |
| `app/repository/advisor_product_repository.py` | **同上,且含 MVP 硬阻断** |
| `app/service/product_metric_service.py` | 产品指标 |
| `app/service/product_history_sync_service.py` | 产品历史同步 |
| `app/service/product_governance_monitor_service.py` | **产品治理**(运营线) |
| `app/service/market_quote_sync_service.py` | 行情同步(**交易线也依赖**) |
| `app/service/product_comparison_service.py` | **客户侧产品对比**(见 §3.4) |
| `app/service/portfolio_analysis_service.py` / `portfolio_analysis_repository.py` | 组合分析 |
#### 🔴 `advisor_product_repository.py` 里有 MVP 的**唯一硬阻断**
```python
candidates = await AdvisorProductRepository(session).authoritative_tradable_products(...)
candidates, _excluded = AdvisorProductRepository.hard_suitability_filter(...)
```
`authoritative_tradable_products()` 就是**产品证据链门**——而 MVP 业务基线的**唯一硬阻断**正是「**产品证据链为空**」。
**删掉它 = MVP 的硬阻断没有实现者。**
### 3.3 混合文件:一个文件里既有投顾又有别人
| 文件 | 混合内容 |
|---|---|
| `app/api/controllers/recommendations.py` | 同时导出 `advisor_router`(`/api/v1/advisor`)**与** `admin_router`(`/api/v1/admin`)→ **不能整文件删**,只删前者 |
| `app/api/controllers/admin.py` | 含投顾治理端点,与其它管理端点混装 |
| `app/service/agent/bootstrap.py` | 注册投顾 Agent 与投顾工具,与其它 7 个 Agent 的注册混在一个函数里 |
### 3.4 ⚠️ 部分「投顾域」功能其实是**客户角色**在用
`tools/seed_test_rbac.py:187-193` 显示 **`customer` 角色**持有这些权限:
```
9020 investment-goal:write:self 9021 investment-goal:confirm:self
9022 investment-goal:read:self 9023 product-recommendation:generate:self
9024 product-recommendation:read:self 9025 portfolio-analysis:read:self
9026 asset-allocation:generate:self 9034 product-comparison:read:self
```
**⇒ 投资目标、组合分析、产品对比、资产配置这些「投顾域」能力,客户侧也在用。** 删除前必须区分「投顾**工作台**(员工用它给客户出方案)」与「**客户自助**的投顾域功能」——**这两者不是一回事**,混删会打断客户线。
---
## 4. 清除范围(三层边界,逐项)
### 4.1 ✅ 可删:投顾业务层(建议确认后执行)
| # | 文件 | 类型 |
|---|---|---|
| 1 | `app/service/agent/implementations/advisor.py` | 投顾 Agent(`AdvisorAgent`,189 行) |
| 2 | `app/service/advisor_rollout_service.py` | 灰度闸门(63 行,当前空转,**【待确认灰度状态】**) |
| 3 | `app/service/product_recommendation_service.py` | 推荐生成(470 行,含证据门调用) |
| 4 | `app/service/investment_goal_service.py` | 投资目标与方案书(384 行) |
| 5 | `app/service/goal_conversation_service.py` | 目标对话抽取(228 行) |
| 6 | `app/service/asset_allocation_service.py` | 资产配置(201 行) |
| 7 | `app/service/allocation_backtest_service.py` | 配置回测 |
| 8 | `app/core/advisor_allocation_contracts.py` | 配置契约 |
| 9 | `app/core/advisor_backtest_contracts.py` | 回测契约 |
| 10 | `app/api/controllers/investment_goals.py` | 投资目标端点 |
| 11 | `app/api/controllers/asset_allocation.py` | 资产配置端点 |
| 12 | `app/api/controllers/portfolio_analysis.py` | 组合分析端点 |
| 13 | `app/api/schemas/asset_allocation.py` | 契约 |
| 14 | `app/static/portal/employee-advisor/**` | **前端整目录(9 文件 / 约 2045 行)** |
| 15 | `tools/{bootstrap_advisor_demo, grant_advisor_role, publish_advisor_demo_config, seed_advisor_demo}.py` | 投顾专属脚本(4 个) |
### 4.2 ❌ 不可删:产品数据底座(7 个文件)
`app/model/advisor_product.py`、`app/repository/advisor_product_repository.py`、
`product_metric_service.py`、`product_history_sync_service.py`、`product_governance_monitor_service.py`、
`market_quote_sync_service.py`、`product_comparison_service.py`(+ `tools/sync_advisor_market_{data,quotes}.py`)
**理由**:被产品治理 / 行情 / 客户侧对比消费;且 `advisor_product_repository` 承载 **MVP 唯一硬阻断**。
### 4.3 ❓ 需你决策:边界上的三项
| # | 事项 | 两种处理 | 我的建议 |
|---|---|---|---|
| **D-1** | **21 张 `advisor_*` 表** | ① 保留表、只删代码;② 连表一起删 | **① 保留表**。`AGENTS.md` **规则 3 明令「禁止重命名或删除已有表」**,规则 8 也把场外/投顾那 38 张表列为「不进基线但存在」。删表须改 `.env` 与基线文档,且**违反项目级强制约束** |
| **D-2** | **15 个 alembic 迁移** | ① 保留(历史记录);② 写新迁移 DROP | **① 保留**。迁移是**历史**,删它会让「新环境 `alembic upgrade head`」与老环境 schema 不一致。**不新增 DROP 迁移**(与 D-1 同源) |
| **D-3** | **投顾域但客户在用的能力**(投资目标 / 组合分析 / 资产配置 / 产品对比的**客户侧**端点) | ① 全删(含客户侧);② 只删投顾工作台侧 | **需你明确**。若目标是「去掉投顾这个角色」,则应保留客户自助能力;若目标包含「连投顾域业务一起下架」,则客户侧权限码(9020-9026/9034)也要从 `seed_test_rbac.py` 撤掉——**那会改变客户线功能** |
### 4.4 ❓ 需你决策:RBAC 与环境数据
| 事项 | 影响 | 建议 |
|---|---|---|
| `sys_role` 的 `advisor` 角色(9004,由 `grant_advisor_role.py` 建) | 登录跳转、`sys_user_role` 绑定 | **保留角色行**(环境数据;删了会影响既有账号),仅删代码侧引用 |
| 权限码 9020-9034 / 9041-9043 / 9057-9059 / 9066-9068 | **`seed_test_rbac.py` 是 DELETE 重建语义**——号码段内不并进去的权限码「重建一次就没了」 | **保留定义**(避免重跑种子时连带删除客户侧权限),由文档登记「投顾专属权限码暂留」 |
| `config_release` 的投顾白名单 | 环境数据,本机 active 里有没有投顾 key 尚**未实测**(A-03 待做) | 与 A-03 合并实测后再定 |
---
## 5. 全部引用与依赖清单(删除时的改动清单)
### 5.1 后端引用(`app/`,必须逐处摘除)
| 文件 | 要改什么 |
|---|---|
| `app/main.py:29,137` | 摘 `recommendation_advisor_router` 的 import 与 `include_router`(**保留 `recommendation_admin_router`**) |
| `app/api/controllers/recommendations.py` | 删 `advisor_router`(`/api/v1/advisor`),**保留 `admin_router`** |
| `app/service/agent/bootstrap.py` | 摘 `AdvisorAgent` 注册 + 投顾工具注册(`query_investment_goal` / `analyze_portfolio` / 等),**其余 Agent 注册不动** |
| `app/service/agent/governance.py:1 处` | 摘投顾相关分支 |
| `app/service/agent_run_application_service.py:3 处` | 摘 `AdvisorRolloutService` 调用(`accept()` 内 `if request.agent_type == "advisor"` 分支) |
| `app/core/config.py:2 处` | 摘 `advisor_rollout_enabled` / `advisor_rollout_customer_ids` |
| `app/api/controllers/admin.py:5 处` | 摘投顾治理端点 |
| `app/api/schemas/promotion_material.py`、`app/core/nl2sql_catalog.py`、`app/service/financial_nl2sql_service.py` | 各 1 处投顾引用 |
| `app/model/{investment_goal, goal_conversation, profile_tag}.py`、`app/repository/{investment_goal,portfolio_analysis}_repository.py` | 视 D-3 决策:若客户侧也要下架则随之处理;否则**保留** |
| `app/service/{risk_questionnaire_service, suitability_service, risk_query_service}.py`、`app/model/{risk,fund,promotion_material}.py` | **疑似同名字符串误命中**,须逐处人工核对后再动(**不要按 grep 结果批量删**) |
### 5.2 前端引用(3 处)
| 位置 | 要改什么 |
|---|---|
| `app/static/portal/common/auth.js:261` | `roles.includes('advisor')` 的跳转分支(删该分支 → 投顾账号登录后无处可去,须给兜底) |
| `app/static/portal/common/layout/app-shell.js:29` | 导航表条目 `['advisor-dashboard', '投顾工作台', ...]` |
| `app/static/portal/README.md:21` | 文档中的角色目录表 |
### 5.3 tools 引用(2 处)
`tools/check_portal_modules.py:38`(`DASHBOARD_DIR` 指向 employee-advisor)、
`tools/publish_advisor_demo_config.py`、`tools/grant_advisor_role.py`、`tools/seed_advisor_demo.py`、`tools/bootstrap_advisor_demo.py`(随 4.1 一起删)。
### 5.4 测试引用(3 个文件,必改否则红)
| 测试 | 影响 |
|---|---|
| `tests/unit/api/test_portal_frontend.py:171,184,189` | 读 `employee-advisor/dashboard/{index.html, dashboard.js, advisor-config.js}` → **文件删了必红**。(含 AGENTS.md 记录的既有失败用例 `test_advisor_workspace_registers_documented_operation_endpoints`) |
| `tests/unit/service/test_advisor_rollout_service.py` | 随灰度闸门一起删 |
| `tests/{unit/service/test_advisor_asset_allocation, test_advisor_base_adapter}.py`、`tests/unit/repository/test_advisor_product_repository.py`、`tests/integration/test_advisor_review_queue_mysql.py`、`tests/unit/test_advisor_migration_contract.py`、`tests/unit/tools/test_bootstrap_advisor_demo.py` | 分别随**对应实现**处置;⚠️ `test_advisor_product_repository` 测的是**底座**,**必须保留** |
### 5.5 文档引用
`AGENTS.md:37,40`(角色目录表)、`docs/40-前端验收清单.md`、`docs/44-演示流程.md:275`、`docs/05-接口文档.md` §19、
`docs/演示用/*`(4 份)、`group_fqcd_jr/docs/验收与审计/advisor-e2e-acceptance-20260912.md`。
**处理口径**:`docs/**` 属**历史记载**,按项目既有惯例(见 Todolist §9 校正 12)**不改**,只在 F-05 回写时登记。
---
## 6. 受影响的关键关联
| 关联 | 影响 | 严重度 |
|---|---|---|
| **MVP 演示 9 步** | 投顾是 MVP **三条业务线之一**;删了演示脚本要相应下架投顾场景 | 🔴 高 |
| **MVP 唯一硬阻断**(产品证据链) | 阻断实现位于**底座**,按 §4.2 保留 → **不受影响** | 🟡 中(若误删则变 🔴) |
| **改 6 个底座文件时的对照组**(Todolist §9-3) | **对照组消失** → 底座改动的回归验证少了一条独立业务线 | 🔴 高 |
| **客户侧投顾域功能** | 投资目标 / 组合分析 / 产品对比 / 资产配置的客户自助能力(权限码 9020-9026/9034)**可能一并受影响** | 🔴 高(取决于 D-3) |
| **运营线 / 产品治理** | 依赖 `advisor_product` 底座 → 按 §4.2 保留则**不受影响** | 🟡 中 |
| **数据侧** | 21 张表保留 → **数据不丢**;但代码删除后这些表**无人写入/读取**(成为孤儿表) | 🟢 低(可接受) |
| **前端账号** | `advisor_t`(9020)登录后跳转目标消失 | 🟡 中 |
---
## 7. 与既有决策的冲突(必须先解决)
| 决策 | 出处 | 与本次的关系 |
|---|---|---|
| **「先重建客服、投顾留到最后」——投顾是对照组** | Todolist **v5.1 §9-3**(2026-09-17 已拍板、已写入三份文档) | **直接冲突**。今天刚定,现在要拆对照组 |
| 「投顾模块用户将自行删除」 | 项目长期记忆(2026-09-16) | 与本次方向一致,但**时点**是「最后」 |
| 「投顾前端建议单独立项」 | 《清理建议》§四.7 | 支持「分阶段、不并进本次」 |
---
## 8. 建议的执行方案(确认边界后即可执行)
### 8.1 建议分两批,不并进客服重构
| 批 | 内容 | 时点 |
|---|---|---|
| **第 1 批·低风险** | 删 `tools/` 4 个投顾脚本 + `employee-advisor/` 前端整目录 + 2 处前端引用 + `test_portal_frontend.py` 相应用例 | 可**现在**做(与底座零交集) |
| **第 2 批·需决策** | 后端 13 个业务文件 + 路由/工厂/config 摘除 + 测试调整 | **待 D-1~D-3 定后**,且**建议排在客服重建之后**(保住对照组) |
### 8.2 执行纪律(沿用形态A 清除的既有做法)
1. **先备份**:文件级复制到 `_advisor_purge_backup/`(**本机 git 写被拦截,无 `git revert`**);
2. **不删表、不写 DROP 迁移**(`AGENTS.md` 规则 3);
3. **不按 grep 批量删**——§5.1 里标「疑似误命中」的 8 个文件必须逐处人工核对;
4. **验证手段**(本机依赖零安装,只能做语法层):
- 全部 `.py` 跑 `py_compile`,要求 0 错误;
- AST 扫悬空 import,要求 0 条(沿用 `_purge_verify.py` 的做法);
- 清过期 `__pycache__`(本次涉及 13 个投顾相关 `.pyc`);
- 复核 `main.py` 能 import(`python -c "import app.main"` 需依赖,做不到则退化为 AST 检查)。
---
## 9. 待你拍板(4 项)
| # | 事项 | 我的建议 |
|---|---|---|
| **1** | **边界确认**:§4.1 的 15 项为「清除范围」,§4.2 的 7 个底座文件**保留** | **按此边界**(否则会拆掉 MVP 硬阻断) |
| **2** | **D-3**:客户侧的投顾域能力(投资目标/组合分析/资产配置/产品对比)是否一并下架? | **保留下架范围 = 仅投顾工作台侧**;客户侧能力暂留(避免改变客户线功能) |
| **3** | **时点**:现在做,还是等客服重建完(保住对照组)? | **第 1 批现在做;第 2 批排客服重建之后** |
| **4** | **表与迁移**:21 张表 + 15 个迁移保留(不写 DROP) | **保留**(`AGENTS.md` 规则 3 强制) |