## 为什么做这一步 权威文档 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...`), 末尾带省略号,是接口文档的示意值,**不是可用凭据**。
18 KiB
投顾模块清除:范围与影响面清单
体系编号:
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. 我实际是怎么清点的(方法)
不是只看《清理建议》,而是自己做了全仓扫描:
Glob **/*advisor*→ 拿到全部同名文件(含__pycache__与迁移);Grep -i "advisor" --glob *.py逐文件计数 →app/下 48 个文件命中;- 对关键符号(
AdvisorProductRepository/app.model.advisor_product)做导入方追踪,区分「投顾业务」与「产品数据底座」; - 检查路由注册(
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 的唯一硬阻断
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 清除的既有做法)
- 先备份:文件级复制到
_advisor_purge_backup/(本机 git 写被拦截,无git revert); - 不删表、不写 DROP 迁移(
AGENTS.md规则 3); - 不按 grep 批量删——§5.1 里标「疑似误命中」的 8 个文件必须逐处人工核对;
- 验证手段(本机依赖零安装,只能做语法层):
- 全部
.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 强制) |