一、密钥轮换(新增工具 + 操作手册) - 新增 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。
Portal 路由与角色映射
正式门户使用原生 JavaScript、原生 CSS 与同源 fetch,静态资源统一从 /static/portal/ 加载。
| 路由 | 角色 | 数据源 |
|---|---|---|
/portal/guest/home/ |
访客 / 全部 | P001(推荐位取前 3 只) |
/portal/guest/products/ |
访客 / 全部 | P001 |
/portal/guest/product-detail/?code= |
访客 / 全部 | P001(不含历史净值曲线) |
/portal/customer/login/ |
未登录客户 | A034 |
/portal/customer/dashboard/ |
customer / admin | T001、T002 |
/portal/customer/holdings/ |
customer / admin | T006 |
/portal/customer/profit-loss/ |
customer / admin | T001 |
/portal/customer/orders/ |
customer / admin | T003 |
/portal/customer/transactions/ |
customer / admin | T007 |
/portal/customer/cash-ledger/ |
customer / admin | T009 |
/portal/customer/risk-questionnaire/ |
customer | ONB001、ONB002 |
/portal/employee-console/login/ |
未登录员工 / 管理员 | A034 |
/portal/employee-console/workspace/ |
admin / super_admin | A002-A006、A012、A033、A035-A040、客服转人工管理接口 |
/portal/employee-risk/dashboard/ |
risk_operator / admin / super_admin | /api/v1/risk/**、R001-R003 |
/portal/employee-advisor/dashboard/ |
advisor / admin / super_admin | /api/v1/advisor/recommendations/published(本人 + 名下归属客户的已发布交付物) |
运营账号默认进入 /portal/employee-operations/offsite/ |
operator / admin / super_admin | /api/v1/offsite-fund/mails、/api/v1/offsite-fund/mailbox-status |
投顾页的数据口径:接口按「本人 +
sys_customer_assignment里名下归属客户」过滤,且同时覆盖investment_goal_book(方案书,发布后review_status='published')与advisor_recommendation_plan(推荐方案,审核后review_status='approved')两类内容。 方案书的审核与发布都要求管理员(investment-goal:review/publish都带admin=True), 投顾自己发不出来 —— 这是有意设计的复核环节,不是缺陷。
客服浮窗(common/customer-service-widget/)由公开首页、基金产品页和客户工作台统一挂载,
挂载点是 common/layout/app-shell.js 的 mountShell() —— 按页面形态白名单
(public / customer)挂,员工四类工作台刻意不挂;样式也是同一个地方注入,
避免九个页面各写一份 ?v= 版本号。
访客使用 /api/v1/visitor-tokens
获取短期令牌(角色 visitor,权限只有 agent:run + knowledge:query),因此必须走
query_knowledge 这个工具名;登录客户走 search_knowledge。两者都要出现在发布配置的
agent_tools/customer_service:<intent> 白名单里,缺哪一条,对应人群就一问即失败。
公开产品数据来自 GET /api/v1/products(编号 P001,见 docs/05 §19),
产品与净值取自 fin_product(status='上市')、行情取自 fin_market_price 的最新一行。
访客页面通过 common/visitor-token.js 取短期访客令牌后调用;该文件是访客令牌的唯一实现
(客服浮窗也用它),不要在页面里另写一份,否则存储 key 与过期判断迟早不一致。
公开页一律用访客令牌,即使浏览器里还留着登录令牌:api-client.js 对调用方
显式传入的 Authorization 不再覆盖(此前会覆盖,于是同一个公开页对访客和已登录
用户显示不同内容)。客户工作台则相反,走登录令牌并建会话(C001),
这样多轮澄清与「转人工」才能挂到同一个 session_id 上。
公开产品数据不得与登录后的真实账户数据混用。
三点口径(改前端前先读):
change_pct可能是null(行情只同步过一个交易日时算不出涨跌)。 调用方必须显示"暂无",不得当成0——formatPercent收到null会渲染成+0.00%, 那等于告诉客户"今天平盘"。- 历史净值曲线没有数据源:
fin_nav_history目前 0 行,详情页因此不画走势图, 并显式说明"尚未接入"。此前那条曲线是 mock 里 12 个编造点位 —— 走势图最容易被当成真数据。 - 产品级披露在
common/product-notes.js(如 510300 的"同指数参考产品,非本公司发行")。fin_product没有这个字段,所以它留在前端;新增需要披露的产品时改那一份。 凡渲染公开产品的页面都要挂data-source-notice说明数据来源。
客户页面均由 common/auth.js 执行入口守卫,接口路径只在 common/api-client.js 的端点表登记。