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