Files
group_fqcd_jr/开发文档/D4.4-投顾模块清除范围与影响面清单-2026-09-17.md
T

276 lines
18 KiB
Markdown
Raw Normal View History

# 投顾模块清除:范围与影响面清单
> **体系编号**:`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 强制) |