zhangshy
|
2f3138113f
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-13 23:52:17 +08:00 |
|
zhangshy
|
2a3146e050
|
增加风控列表总数并优化站内提醒
|
2026-09-13 23:51:45 +08:00 |
|
lzf_0626
|
3ada1f6c87
|
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
|
2026-09-13 23:38:57 +08:00 |
|
lzf_0626
|
7208713c17
|
feat(portal): 接入历史净值,产品详情页恢复真实走势图
`fin_nav_history` 一直是**空表**,所以产品详情页画不出走势图 —— 此前那条曲线是
前端 `mock-data.js` 里编的 12 个点位,接入公开产品接口时把它去掉了
(走势图最容易被当成真实业绩),页面改为显示"尚未接入"。本次补上完整链路。
## 1. 取数(app/infrastructure/fund_market_adapter.py)
新增 `fetch_nav_history()`:东方财富历史净值接口(`api.fund.eastmoney.com/f10/lsjz`)
分页取序列。
- **为什么不复用 `fetch_kline`**:它走 `push2his.eastmoney.com`,
该域名在本环境实测连接被拒(`RemoteProtocolError`);
- **为什么不复用 `hq.get_southern_fund_nav_history`**:那个函数校验**南方基金白名单**,
而 `fin_product` 里有非南方基金的产品(510300 是华泰柏瑞的),调它会直接 `ValueError`。
⚠️ 实现里踩到一个坑:接口**会忽略请求中的 `pageSize`**(实测固定每次返回 20 条)。
最初按硬编码的 30 判断"是否最后一页",于是 `len(items) < 30` 永远成立、只取到第一页 ——
走势图看上去"有数据",其实只有最近 20 天,而且毫无报错。
改为按首屏**实际条数** + `TotalCount` 推算页数后,同样区间取到 124 条。
## 2. 落库(tools/sync_nav_history.py,新增)
写入 `fin_nav_history`,按 `(product_id, nav_date)` 幂等 upsert。
实测:20 只产品 / **2513 行** / 2026-03-17 ~ 09-13;重跑**新写入 0 行**。
## 3. 接口(P002)
`GET /api/v1/products/{product_code}/nav-history`,编号 **P002**,已登记 `docs/05` §19。
鉴权口径与 P001 相同(要求有效令牌、不校验权限码,访客令牌可用);`days` 有界 1–365。
**表为空时返回 `count=0` 与空数组,而不是报错** —— 调用方据此显示"尚未接入",
**不得回退到编造曲线**。产品不存在或未上市 → `404`(否则前端分不清"没有数据"
和"没有这只产品")。
## 4. 前端
详情页按序列画 SVG 折线,期数标题改为动态("近 N 个交易日")。
表为空时仍显示"尚未接入"占位,并补上此前缺失的 `.detail-chart__empty` 样式。
`product-detail.js` / `.css` / `index.html` 的缓存版本参数一并 bump 到 `-7`。
## 5. 演示数据
`tools/seed_demo_data.py` 增加第 5 步「历史净值」(现 **11 步**),
否则换台机器演示时走势图又会是空的。
验证:P002 实测 515450 / 510300 各 120 个净值点;ruff 通过;mypy 251 文件 0 错;
unit+contract 1391 passed;integration 108 passed;e2e 冒烟 40/40。
|
2026-09-13 23:38:13 +08:00 |
|
zhangshy
|
38fe6f3689
|
合并主项目最新改动并解决风控前端冲突
|
2026-09-13 23:22:12 +08:00 |
|
zhangshy
|
4f75d32ea1
|
优化风控详情展示与前端交互
|
2026-09-13 23:19:52 +08:00 |
|
lzf_0626
|
9cb0f6e474
|
feat(portal): 公开产品接口(P001)落地,访客三页改读真实数据
访客首页推荐、产品列表、产品详情此前读的是前端手写的
`app/static/portal/common/mock-data.js`:只有 8 只,且**其中 6 只根本不在
`fin_product` 里**(159915 / 512100 / 513100 / 511360 / 159645 / 159925),
还把海富通的 `511360` 标成"南方短融ETF"、把 `510500` 净值写成 6.742(真实 7.6027)。
`README.md` 把它记为"公开产品 HTTP 接口尚未实现"的临时方案。
## 接口
新增 `GET /api/v1/products`(编号 **P001**,已在 `docs/05` §19 总目录与 §19 说明中登记):
- **要求有效令牌但不校验权限码**:访客令牌的角色是 `visitor`、不带任何权限,
这与 `/api/v1/agent-runs`、`/api/v1/conversations` 面向访客的口径一致;
产品信息本身是公开信息。数据面只暴露 `fin_product`(`status='上市'`)与
`fin_market_price` 的最新一行,**不含任何账户/客户字段**(由 integration 测试守着)。
- 字段与 `mock-data.js` 对齐,因此前端渲染与筛选逻辑**一行未改**。
- `change_pct` **可能是 `null`**:当日涨跌需要两个交易日的收盘价,行情只同步过一天时
算不出来。前端对 `null` 显示"暂无" —— `formatPercent(null)` 会渲染成 `+0.00%`,
那等于对客户说"今天平盘",是编出来的结论。
## 前端
- 新增 `common/visitor-token.js`:访客令牌的**唯一实现**(这段逻辑原先只写在客服浮窗里,
现在四处要用;复制四份的话存储 key 与过期判断迟早不一致);`widget.js` 改为复用它。
- 新增 `common/product-notes.js`:产品级披露文案。510300"非本公司发行"那条是**合规披露**,
不能随 mock 一起删掉。
- 三个访客页改读接口,并显式区分 loading / error 状态。
- **删除 `common/mock-data.js`**。
- 产品详情页**不再画走势图**:`fin_nav_history` 目前 0 行,此前那条曲线是 mock 里
12 个编造点位 —— 走势图最容易被当成真数据,宁可不画,并显式说明"尚未接入"。
## 顺带修正
`min_amount` 在库里全是 0.00(那本是场外"最小认购金额"的概念,对场内按手交易的 ETF
不适用),页面不再显示"¥0.00"(会被读成零元起购),改为"1 手(100 份)起"。
验证:接口实测 `count=20`;ruff 通过;mypy 251 文件 0 错;unit+contract 1387 passed;
integration 106 passed;e2e 冒烟 40/40。
|
2026-09-13 22:57:48 +08:00 |
|
lzf_0626
|
f6bfcc26b3
|
fix(demo): 把未上市的 160129 换成真实挂牌的 515450,并修掉种子假行情盖住真行情
## 1. 160129 本就不该在场内清单里
`160129`(南方金利定开债券C)是 `160128`(金利定开债券 **A** 类)的 C 类份额,
而 C 类份额只在场外销售、**不在交易所挂牌** —— 行情源对它永远返回空,下单只能走
净值降级(`source=eastmoney_nav_fallback`),既掩盖了"该产品并不交易"这一事实,
又让成交价带上折溢价偏差。
选型依据:把南方基金全部 **882 个代码**逐个问过腾讯行情源(该源只对交易所上市证券
返回数据),确认真实上市交易的只有 **109 个**;其中 R1/R2 的场内产品
(159700、160128、511070、511810)**原先已在清单内**,所以替换品只能来自 R3 及以上。
选定 **`515450` 红利低波50ETF南方**:仍是南方基金旗下、成交额约 1.3 亿元流动性充足、
红利低波定位偏稳健,与它替换掉的债券 LOF 定位最接近;客户风险等级已覆盖 R3
(原有 `510300` 即 R3)。
改动:`hq.py` 的 `FUND_TYPE_GROUPS`、`tools/import_hq_test_products.py` 的场内映射,
两处都留了注释防止被加回来;`docs/42` / `docs/43` 的产品表与费率表同步
(净值 1.4027、管理费 0.50%、托管费 0.10%);`docs/42` 另加了一条决策记录。
库内用**复用 `fin_product.id = 9100005`** 的方式替换,使 `fin_holding` /
`fin_sim_order` / `fin_transaction` / `fin_market_price` 的既有引用自动跟随,
不产生孤儿数据。那笔历史成交保留 `quote_source='eastmoney_nav_fallback'` ——
它确实是当时的事实,不该被粉饰。同时清掉了 `fin_market_price` 里那条净值降级行,
全库净值降级行情行数归零。
## 2. 种子假行情会盖住真实行情(更严重,且会反复出现)
`seed_sim_account_demo.py` 原来按"今天"upsert 一条假行情(510300=4.50、
510500=6.20,`source='eastmoney_demo_seed'`),而真实行情来自行情源、日期是
**最近交易日**。下单按 `trade_date DESC` 取行情,这条种子行**永远排在真实行情前面**;
它的 `source_updated_at` 只是 seed 运行时刻,过了 `MAX_QUOTE_AGE`(15 分钟)就让整个
产品变成 `503 行情已过期` —— 而库里明明躺着一条刚同步好的真实行情。周末尤其明显:
`trade_date` 落在**非交易日**,价格还是编的。
实测表现:`tools/seed_demo_data.py` 跑完第 4 步刚同步过真实行情,冒烟脚本的下单仍报
`503`,且本该 4.579 的成交价被查到 4.50。
改为:**已有任何行情行就绝不插手**(真实行情优先,种子不参与竞争);
一条都没有时才补,且 `trade_date` 退到最近交易日。
验证:`tools/e2e_smoke_test.py` → **40/40**(下单成交价 4.579 = 真实行情);
ruff 通过;mypy 250 文件 0 错;unit+contract 1381 passed / 0 failed。
|
2026-09-13 22:28:42 +08:00 |
|
lzf_0626
|
4629e7d5dd
|
docs(44): 补一步风控助手演示(意图分派已修复,实测置信度 1.0)
|
2026-09-13 22:08:39 +08:00 |
|
lzf_0626
|
bfd3964ef8
|
feat(demo): 演示数据一键准备、启动脚本与演示流程文档
交付"别人能自己把系统跑起来做演示"所需的四件东西:
- tools/seed_demo_data.py 演示数据一键准备:10 步按依赖排序
(此前散在 10 个脚本里,没人知道该跑哪些、按什么顺序跑,且知识库那步
根本没有脚本、靠手工调接口)
- tools/seed_knowledge_demo.py 知识库演示素材灌入,幂等(先删同 source_file 再灌)
- start.ps1 启动 API + Worker,含依赖检查与**自动刷新行情**
- docs/44-演示流程.md 8 个主线场景的照读流程 + 排障表 + 账号/命令速查
两条硬约束同时写进了脚本和文档:
1. **Worker 必须常驻**:没有它客服对话一直停在 queued(前端只显示"超时")、
新知识不进 Milvus 且**没有任何报错**。
2. **行情有效期仅 15 分钟**(`trade_service.MAX_QUOTE_AGE`):超时后所有委托
直接 503「行情已过期」,而系统**没有自动刷新机制**。故 start.ps1 启动时刷一次,
文档另给"演示中途 503 时补刷、无需重启服务"的处置方法(已实测)。
实测证据:
- start.ps1 在 8099 完整启动,`/docs` 与 `/portal/` 均 HTTP 200(测试进程已清理)
- `tools/e2e_smoke_test.py` → 40/40 通过
- `tools/seed_demo_data.py --dry-run` → 10 步全部正常列出
- 文档中的下单命令实跑:510300 成交价 4.579、金额 457.90、手续费 0.05
|
2026-09-13 21:56:27 +08:00 |
|
lzf_0626
|
2fcc4939e7
|
feat(tools): 新增全流程端到端冒烟脚本 e2e_smoke_test.py
把验收清单里"能用接口判定"的部分自动化:6 条线 40 项,打真实 HTTP、逐条打印 PASS/FAIL。
不是 pytest 的替代 —— 单测看代码正确性,它看"部署后底座是不是活的"。
覆盖:
A 访客 令牌 → 提问 → 收到回答 → 未转人工
B 客户 登录 → 看板/持仓/流水 → 真实下单成交 → 委托详情 → 会话与转人工
C 风控 概览/队列/详情/八类证据/通知/扫描 → 处置闭环(确认→调查→结案)
D 投顾 已发布方案 E 运营 场外邮件与邮箱状态
F 管理员 角色/权限/身份/配置发布/模型端点/审计/转人工工单/画像候选
两个实现细节值得留意:
- **各端点 data 形状不统一**(角色列表裸数组、持仓在 holdings、成交明细在 transactions、
资金流水在 entries、知识列表不套 data),所以这里用 listed(payload, *keys) 依次尝试,
并在注释里写明"别猜字段名"—— 我自己写联调脚本时连续猜错三次。
- 库里没有"待处理"预警时**自动造一条**再验证处置闭环,否则该环节只能跳过
(实测库里的预警常常都已被处置过,第一次跑就跳过了)。
新增 --read-only:跳过所有写操作,用于生产或不想动数据时。
实测:40/40 通过(含真实下单 201 已成交、风控处置闭环三段全 PASS)。
docs/40 补上"一键冒烟"一节。
|
2026-09-13 21:39:37 +08:00 |
|
lzf_0626
|
b6281ba6bc
|
docs(40): 把行情同步列为下单前必跑的前置
全流程验收时踩到:不跑 tools/sync_market_prices.py 就下单,只有 2 只产品有行情、
其余直接 503,而且错误信息只说"行情已过期",看不出根因。把它写进第 0 节,
并说明数据源(腾讯行情 / 东财只有行情类域名不可达)与总份额口径。
|
2026-09-13 21:15:43 +08:00 |
|
zhangshy
|
522c9b19fe
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-13 21:09:09 +08:00 |
|
zhangshy
|
09c80575b7
|
延长风控手动扫描前端超时
|
2026-09-13 21:08:57 +08:00 |
|
lzf_0626
|
de208a039d
|
docs(40): 补全流程验收实测到的接口形状差异
跑全流程 E2E 时连续踩到(都是调用方视角的真实摩擦点):
- 各端点的 data 形状不统一:账户看板 data.account + data.summary、持仓 data.holdings、
成交明细 data.transactions、资金流水 **data.entries**、角色列表 data(裸数组)、
知识列表 items(不套 data)。我写脚本时连续猜错三个字段名,一度误判"资金流水没记录"。
- 创建类接口返回 201 而非 200:下单 T002、创建会话 C001、访客令牌 V001 都是 201。
两条都写进"通用预期",并说明是实测结论。
|
2026-09-13 20:40:16 +08:00 |
|
lzf_0626
|
ff37b0b476
|
docs(40): 记录知识治理端点的两处接口差异
联调时踩到并查明(更正前一版判断:不是接口故障,是调用方解包方式不对):
1) GET /api/v1/knowledge/list **不套 data 信封**,直接返回 {"items": [...], "count": N},
而多数端点返回 {"code":…, "data": {…}}。按 data.items 解包会拿到 0 条,
看着像"库里没数据"——我据此误判过一次,也因此在重灌时没能用列表接口批量删除,
改为按 id 逐个 DELETE。
2) 上传是 POST /api/v1/knowledge/upload(/documents 会 405);
DELETE /api/v1/knowledge/{id} 的语义是标记 expired + 投向量删除事件,不是物理删除。
两条都写进验收清单的"通用预期",避免下一个人重复踩。
|
2026-09-13 20:26:56 +08:00 |
|
lzf_0626
|
7fa4b4c4a5
|
feat(knowledge): 手册补「购买基金需要什么条件」一节并重灌
排查「购买基金需要什么条件」仍转人工的根因:**不是检索缺陷,是知识覆盖不足**。
实测该问法下手册 1.1 节其实排第 1(score=0.5416),但低于 MID_SCORE(0.55),
且与次优间隙只有 0.0195 < MIN_GAP(0.07),所以按置信度规则转人工 —— 规则本身工作正常。
对照:手册里有对应条目的问法分数是 1.0 / 0.7946 / 0.7932,链路健康。
补 1.6 节「购买基金需要什么条件」,只写有依据的事实(开户状态、风险测评与等级匹配、
按手交易的整数倍委托),不编造资金门槛或资质要求。
重灌过程顺带发现:GET /api/v1/knowledge/list 返回 0 块,而 fin_knowledge_meta 当时
有 22 行 active —— 管理端列表与实际入库不一致,所以删除只能按 id 逐个走
DELETE /api/v1/knowledge/{id}。这条接口的问题单独记,不在本次改动范围。
重灌结果:旧 22 块标记过期并投删除事件,新 23 块写入;问「购买基金需要什么条件」
命中 1.6 节 score=0.7793(≥ HIGH_SCORE 0.75),客服从转人工变为直接作答(4.08s)。
|
2026-09-13 20:25:57 +08:00 |
|
lzf_0626
|
fef4c826ff
|
fix(worker): 知识写入 Milvus 必须探测字段名并补齐必填字段
背景:走 `POST /api/v1/knowledge/upload` 灌了 22 块场内基金知识,MySQL 全部写入成功、
向量事件也全部投递,却全部同步失败(重试 3 次进死信),客服检索不到新知识。
逐层定位出三个真问题,都在写入侧:
1) **字段名硬编码**。检索侧早已按 AGENTS.md 改用运行时探测
(`app/core/knowledge_schema.py`:`doc_id`↔`knowledge_id`、`content`↔`snippet`),
写侧却一直硬编码 `knowledge_id` / `snippet`。本机集合实际是
`doc_id`/`content`/`chapter`/`visibility`/…(架构师那套 schema),于是
`Attempt to insert an unexpected field knowledge_id` 整条失败。
现在两侧共用 `resolve_schema` 的同一份映射表,调用方只用**逻辑**字段名;
集合没有的字段(如另一套环境无 `intent`)跳过而不是报错。
2) **非 nullable 必填字段没给**。改完名字后报
`Insert missed an field chapter to collection without set nullable==true`:
集合里存在、调用方没提供的 VARCHAR 字段必须补值。VARCHAR 的判据用
`params.max_length`,不必 import pymilvus 的枚举。
3) **补值不能一律空串**:`visibility` 留空会被检索侧
`visibility == "public"` 的过滤整行排除。这条最隐蔽 —— `query` 查得到、`search`
查不到,表现为「入库成功但客服永远答不出新知识」,比写入直接失败更难定位。
现在按 `FIELD_DEFAULTS` 给有语义的字段默认值。
Worker 侧把 `fields` 的键改成逻辑名(`snippet` → `content`),上限表随之改名。
**测试**:原来的桩没有 `describe_collection`,所以这条路径从未覆盖到真机 schema
(这正是缺陷长期存在的原因)。现在桩提供两套真实 schema 并参数化,另加
「跳过集合没有的字段」「探测不出必要字段必须失败关闭」两个用例。
**验证**:22 块重新同步后 `fin_product_collection` 236 行、`visibility` 全为 `public`;
问「南方沪深300ETF 的起投金额是多少」命中新块 `score=0.7932`(≥ `HIGH_SCORE` 0.75),
客服从「转人工」变为直接作答,端到端 3.34 秒。
**另**:新增 `docs/43-场内基金产品手册(知识库入库版).md` —— 从 `docs/42` 抽出
**客户可见**的纯净内容(去掉全部内部决策备注)供入库;`docs/42` 保留为草稿与决策记录。
|
2026-09-13 20:08:31 +08:00 |
|
lzf_0626
|
6f397ab513
|
feat(data): 510300 回填真实值并在产品页披露;511810 口径查清
按项目方决定处理三件事。
## 510300(沪深300ETF)
决定:保留 fund_manager = "南方基金" 作演示口径,**不**改成"华泰柏瑞"
(改了它会退出 MarketQuoteSyncService 的同步范围,投顾与知识条目也不该再算自家产品),
但净值与费率改成真实值,并在产品页显式披露它不是本公司产品。
- 回填(tools/backfill_product_snapshot.py --codes 510300 --apply --overwrite):
current_nav 4.500000 → 4.5794、current_nav_at 同步为 2026-09-11 15:00:00、
management_fee_rate 0.5000 → 0.15、custodian_fee_rate 0.1000 → 0.05
- 产品页披露:mock-data.js 给这只加 product_note,详情页新增提示块渲染它
("本产品为同指数参考产品,非本公司发行的基金,仅用于功能演示")。
同时把 mock 里这只的净值/费率也对齐真实值,避免出现"页面显示 mock 值、
库里是真实值"两套数字。
- 产品详情页的 css/js 版本参数提到 20260913-5,避免旧缓存。
## 511810(货币ETF南方)
之前说它"三个数量级不一致"是**我比错了接口**:QUOTE_API 返的是**场内交易价格**
(货币 ETF 约 100 元/份),而 current_nav 的口径是 DWJZ 单位净值。
查证结果:库里 0.2661 正是 09-11 的真实 DWJZ,快照时间也对得上 —— 不是脏数据,是旧值。
口径由其余 19 只的一致性确定(current_nav == DWJZ)。
要不要刷新到最新(0.2332)属运维节奏问题,**本次未执行**。
## 最小交易金额
按项目方口径:一律**以产品字段为准**(100.00 元),不用「1 手 × 净值」推算金额 ——
后者会与字段值不一致,客户会看到两套数字。docs/42 的 1.1 节据此重写。
## 工具改名
tools/backfill_product_fees.py → tools/backfill_product_snapshot.py:扩展为
净值 + 费率统一入口,新增 --codes 限定范围(避免顺手把全部快照刷成最新,
全量刷新是 ProductHistorySyncService / MarketQuoteSyncService 的职责)。
安全设计不变:默认 dry-run、默认只补 NULL(净值永不为空,所以刷净值需显式
--overwrite)、写入走单事务。
门禁:ruff 通过 / mypy 249 文件 0 错 / 单元+契约 1377 passed 2 skipped。
|
2026-09-13 19:47:10 +08:00 |
|
lzf_0626
|
02ba1befc0
|
feat(data): 取真实费率并回填 fin_product;发现 510300 的身份问题
## 取费率
项目原有的行情适配器只有 names/quotes/history,没有费率。东财也没有可用的 JSON 接口
(`FundArchivesDatas.aspx?type=jjfl` 实测正文只有一句 `var apidata=`),
所以给 EastmoneyFundAdapter 加了 fetch_fees:抓 f10 基金概况页
(fundf10.eastmoney.com/jbgk_{code}.html)去标签后匹配字段名,取管理费/托管费/
销售服务费/申赎费,并顺带带回基金全称。取不到的基金不进返回(而不是给全 None),
免得调用方把"没抓到"当成"该基金确实没有费率"。
实测 20/20 全部取到。
## 回填
新增 tools/backfill_product_fees.py,默认 dry-run、默认只补 NULL:
- 只补空值:写入 38 个字段(19 只 × 管理费/托管费),已执行
- 已有值一律不动 —— `510300` 库里的 0.5000/0.1000 保持原样,要改需显式 --overwrite
- 写入走单事务
回填前 grep 确认:`fin_product.management_fee_rate` / `custodian_fee_rate`
**目前没有任何业务代码读取**(advisor 域那两个 fee 字段属于另一张表
advisor_product_contract),所以这次回填不改变任何现有行为。
要让客户真的看到费率,还需要接口与前端读它 —— 那不在本次范围。
## 两个数据问题(都指向 510300)
1) **它不是南方基金的产品**。数据源返回的基金全称是
「华泰柏瑞沪深300交易型开放式指数证券投资基金」,而库里 fund_manager 写「南方基金」。
其余 19 只全称都是"南方…"开头,只有它例外。
影响面不只是知识库:MarketQuoteSyncService 按 fund_manager='南方基金' 筛选同步对象,
所以它会被当成自家产品一起同步、一起展示。
2) **它的费率是照抄的**。库里 0.5000/0.1000,真实 0.15/0.05;而 0.50/0.10 恰好是
159329/159382/159511/588890 这四只的真实费率。结合它的净值快照时间是当天
(其余 19 只停在 09-11)、净值取整数 4.500000,判断是演示用途的人为设定。
这两条都由用户决定怎么处理,本次只记录、不擅自改(510300 的 fund_manager、净值、费率
三处原值都没动)。
## 其他
- tools/fetch_live_quotes.py 增加费率核对段落与 --no-fees(费率要串行抓 45KB/只,较慢)
- docs/42 用真实费率重写 1.2 节与新增 4.3 真实费率清单,3.1 节标出 510300 的身份问题
- docs/42 的数据缺口表更新:费率不再是缺口
门禁:ruff 通过 / mypy 249 文件 0 错 / 单元+契约 1377 passed 2 skipped。
|
2026-09-13 19:39:16 +08:00 |
|
lzf_0626
|
131effcfae
|
feat(tools): 新增 fetch_live_quotes 拉真实行情并核对库里快照
项目本来就对接了东方财富真实行情(app/infrastructure/fund_market_adapter.py:
push2.eastmoney.com 的行情接口 + api.fund.eastmoney.com 的历史净值接口),
下单链路取 quote_price/quote_source 走的也是它。之前做知识草稿时用的是
fin_product 表里的快照,没有去核对真实数据源,这次补上。
新增 tools/fetch_live_quotes.py:
- 用项目自带的 EastmoneyFundAdapter 取数,不自己拼 HTTP、不引第三方行情库
- 产品范围与 MarketQuoteSyncService.sync 一致(南方基金 / SSE,SZSE / 上市)
- 逐只对比真实净值与 fin_product.current_nav,只报告差异、**不写库**
(刷新快照是 ProductHistorySyncService / MarketQuoteSyncService 的职责)
实测结果(2026-09-13,20 只全部取到真实数据):
- 18 只完全一致(库里 6 位小数、数据源 4 位,是同一笔数)
- 2 只不一致:
· 510300 沪深300ETF:库里 4.500000,真实 4.5794。库里快照时间是今天 08:19:37,
其余 19 只都停在 09-11 15:00:00 —— 说明这是被人为改过的演示值(4.5 凑"1 手 450 元")。
需要定:知识用真实值还是演示值;若交易按库里的 4.5 成交而知识说 4.5794,两处口径会打架。
· 511810 货币ETF南方:库里 0.266100,NAV_API 给 0.2332,而 QUOTE_API(场内实时价)
给 100.012 —— 差三个数量级,说明两个接口对这只的含义不同,需人工核实口径。
docs/42 随之更新:
- 产品清单改用真实净值,并加上当日涨跌与净值日期,注明取数来源与复现命令
- 3.1 节的沪深300ETF 条目改用真实值 4.5794,并列出两处待确认口径
- 新增 4.2 节记录本次核对结果与两只不一致项的判断依据
|
2026-09-13 19:27:58 +08:00 |
|
lzf_0626
|
f72a545c39
|
refactor: 品牌统一为「南方财富」(项目方定)
背景:品牌名此前三处不一致 —— 后端 Agent 自称「奶龙基金」(customer_service_rules
的防诈骗/转人工话术、risk_agent 与 risk_analysis_service 的系统提示词)、前端全站
「南方财富」、闲聊提示词与知识素材「南方科技」。docs/36 已把它登记为"上报项目方后
待定",现按项目方决定统一为「南方财富」。
改动:
- app/core/customer_service_rules.py:P0 防诈骗话术与 P2 转人工话术里的品牌名
- app/service/agent/implementations/customer_service.py:COMPANY 常量,
以及那处引用实测样本的注释(改为不绑定具体品牌名,免得下次改名又过时)
- app/service/agent/implementations/risk_agent.py:docstring、自我介绍、system prompt
- app/service/risk_analysis_service.py:SYSTEM_PROMPT
- app/worker/risk_scan_scheduler.py:--help 描述
- app/static/index.html(旧联调页 4 处)、portal/employee-risk/dashboard/index.html
- tests/unit/api/test_customer_service_test_page.py:同步断言(它断言的正是页面里的品牌名)
- tools/publish_chitchat_prompt.py:SYSTEM_PROMPT 改品牌;并修掉"存在即跳过"的检查
—— 原来只判当前版本有没有这一行,于是改了文案也发不出去(脚本打印"无需发布"直接
退出),没有任何提示。改为比对 system_prompt/user_prompt_template 内容。
闲聊提示词已重发为 release 308 / v5,生效内容为"你是南方财富的智能客服助手…"。
刻意未动:
- fin_product.fund_manager = "南方基金" —— 它被 market_quote_sync_service 与
product_history_sync_service 当过滤条件使用,改名会让同步链路查不到产品
- knowledge_search_service.py 注释里引用的知识库实际标题「南方科技有限公司…」
- docs/客服docs 下的历史素材与 docs/ 下的过程记录(属历史留痕)
⚠️ Milvus 里的知识条目仍写「奶龙基金」(RAG-*/NF-*)与「南方科技」(PROD-*),
属知识数据,需重灌才能统一;本轮不动。
同时:
- docs/40 把品牌条目标为已处理,并补上知识库缺口的现状
- 新增 docs/42-场内基金知识条目草稿.md:按 fin_product 的 20 只产品生成,
含通用交易规则与产品清单;费率等缺失字段一律标"以交易页面为准",未编造数字。
**该文件是草稿,未入库**,待审核后走 POST /api/v1/knowledge/documents 灌库。
|
2026-09-13 19:17:56 +08:00 |
|
lzf_0626
|
52ae38efd7
|
docs(40): 补齐前端验收清单(投顾/运营页、访客浮窗、Worker 前置)
- 关键修正:第 0 节原先写「停常驻 Worker」,方向是反的 —— 客服对话**必须**有
Worker,否则 agent_run 停在 queued,前端只显示超时。补上排查第一步(查最新
那行的 status)与本机实测延迟(端到端 4.1-4.8 秒,受理只占 0.05 秒)。
- 新增第 6 节投顾工作台:数据口径(本人 + 名下归属客户,不用 data_scope)、
方案书审核与发布必须由管理员做(admin=True)、归属数据不随环境走。
- 新增第 7 节运营工作台,含客户/风控访问被 403 拦的实测。
- 新增 2.5 节访客客服浮窗:访客令牌的角色与权限、访客与客户走不同检索工具名。
- 新增第 9 节「看起来像 bug、其实是设计」:置信度阈值 0.75/0.55/0.07 与三条实测
分数逐条对应(0.8203 直接答;0.6818 但间隙 0.0034 转人工;0.5221 转人工),
说明「总转人工」的根因在知识库内容而不是代码;另两条是投顾接口非越权、
方案书复核环节。
- 第 1 节更新为 18 个页面路径 + 12 个静态资源,全部 200(实测值)。
- 第 10 节补入本轮修的 4 个问题(角色落点、访客白名单、mock 声明、发布脚本丢提示词)。
- 新增第 11 节已知未修:知识库无场内 ETF 内容、fin_knowledge_meta 与 Milvus 不一致
(列表接口实测 count=0)、品牌名三处不一致、ADVISOR_GOAL/ANALYSIS 声明未用等。
- 附录补演示账号,并注明 9006/9020 的口令不在 set_user_password.py 的演示规则里。
|
2026-09-13 19:10:21 +08:00 |
|
张胜宇
|
b9aafce120
|
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
|
2026-09-13 18:33:34 +08:00 |
|
张胜宇
|
e740a58e9d
|
前端2
|
2026-09-13 18:31:38 +08:00 |
|
zhangshy
|
c93c490814
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-13 16:22:53 +08:00 |
|
lzf_0626
|
4fdf1762e6
|
docs: 验收清单改为面向正式前端 /portal/
|
2026-09-13 16:22:25 +08:00 |
|
zhangshy
|
8914b6ed99
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-13 16:21:35 +08:00 |
|
zhangshy
|
7ab49c3f7f
|
新增风控模块需求说明书和接口文档
|
2026-09-13 16:20:52 +08:00 |
|
lzf_0626
|
f7f43742fd
|
chore: 移除误提交的 .agents/skills 与 skills-lock.json(AI 助手配置,与项目无关)
- 这 15 个文件随 e38ece3 前端提交一起进了仓库,内容与本项目无关
- 用 git rm --cached 从索引移除(本地文件保留),并加入 .gitignore
|
2026-09-13 16:17:00 +08:00 |
|
张胜宇
|
0ee3e894e7
|
merge: integrate latest qyqy_develop + push docs/41
|
2026-09-12 17:14:36 +08:00 |
|
ZSY_docs
|
554d125190
|
docs(41): 客服 Agent 前端开发约束 v1.0(团队强制规范)
新增 docs/41-客服Agent前端开发约束_v1.md(720 行 / 68 个标题 / 13 节 + 3 附录):
- §2 技术栈:原生 JS + CSS + ECharts;禁 React/Vue/TS/Tailwind(与底座现有 app/static/index.html 风格一致)
- §3 目录结构:portal/<role>/<page>/ 三层命名空间
- §7 接口调用:api-client.js 封装;trace_id + JWT 自动注入;4xx 不重试
- §8 权限 RBAC:9060-9065(§T 段)映射可见性;前端不发明角色
- §11 看板专属:模块归属 portal/customer/dashboard/;4 模块按 T001 字段拆;60s 缓存 + 仅手动刷新;3 状态必齐
- §13 验收清单:通用 10 项 + 看板 9 项 + 自动化 3 项
本文件与 docs/40-前端验收清单.md 互补(约束 ≠ 验收),编号 41 不撞号
|
2026-09-12 17:04:37 +08:00 |
|
zhangshy
|
4f495c6048
|
修正风控前端文档重命名后的引用
|
2026-09-12 16:58:31 +08:00 |
|
zhangshy
|
6039ee3d89
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-12 16:56:46 +08:00 |
|
zhangshy
|
d919b2ae5c
|
重命名风控前端合并文档并更新引用
|
2026-09-12 16:56:27 +08:00 |
|
lzf_0626
|
280c8e5026
|
docs: 清单风控章节按 17- 前端合并约束重写(15 项,含实测证据与三个坑)
|
2026-09-12 16:31:55 +08:00 |
|
zhangshy
|
a03d5e33c3
|
更新风控邮件通知与定时扫描文档
|
2026-09-12 16:30:01 +08:00 |
|
lzf_0626
|
6e916aab03
|
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
|
2026-09-12 16:08:39 +08:00 |
|
lzf_0626
|
76923d7e8b
|
fix(portal): 补齐写操作的必填 body(风控升级/解决、场外六个写接口)
- 风控:escalations 要 reason、resolutions 要 resolution(字段名不同),
且真实顺序是 先确认接收 才能升级/解决(否则 409 请先确认接收预警)
- 场外:六个写接口都必填 operator_id(防伪校验,须等于当前登录用户),
confirmations 还要 decision(中文枚举)、notifications 还要 notification_type、
recalculate 要 fund_code+application_date;门户自动带当前 user_id
- 实测:recalculate 200 code=0;不传 operator_id 必得 422(证明该字段必须)
- 清单 §3/§4 更新为实测结果并列出各写接口的必填字段表
|
2026-09-12 16:08:38 +08:00 |
|
ZSY_merge
|
e25296f30a
|
merge: integrate origin/qyqy_develop (552055ee)
|
2026-09-12 16:05:24 +08:00 |
|
lzf_0626
|
552055ee2d
|
fix(portal): 修风控预警列表 422(limit 上限是 5,改为不传);运营邮件改用 page/page_size;审计 limit 钳制到 1-100
- risk/alerts 传 limit=20 会 422 query.limit<=5,导致整张表格渲染不出来、
行内确认/升级/解决按钮随之消失 —— 这正是验收清单 3-3/3-6~3-9 看不到功能的原因
- 实测列表返回 2 条预警;处置接口有状态前置条件(409 = 当前状态不允许)
- 清单 3-3/3-6 更新为实测结果并写明 limit 上限与状态机约束
|
2026-09-12 16:03:22 +08:00 |
|
ZSY_merge_bot
|
4c651ccce1
|
merge: integrate latest origin/qyqy_develop (frontend acceptance checklist + portal flow wizard)
|
2026-09-12 15:58:08 +08:00 |
|
张胜宇
|
ebc3fe4cbe
|
feat(§T): 账户看板 + 场内模拟交易 9 端点(用户自助首版)
新增 §T 用户自助段(docs/05 §19 新号段 7 个 = A×40/C×7/K×4/M×4/O×3/R×4/T×9):
- T001 GET /api/v1/users/me/account/dashboard — 账户/资金/持仓/盈亏汇总
- T002 POST /api/v1/users/me/orders — 委托提交(首版 market 立即全额成交)
- T003 / T004 / T005 委托列表/详情/撤单
- T006 GET /api/v1/users/me/holdings — 持仓列表(含市值/盈亏/当日盈亏)
- T007 / T008 成交记录列表/详情
- T009 GET /api/v1/users/me/cash-ledger — 资金账本
要点(与 docs/00 §6.6 一致):
- 首版市价委托立即全额成交,不实现撮合队列/部分成交;T005 撤单首版对任何在场委托返回 ORDER_NOT_CANCELLABLE (409)
- 价格来源复用 base FundQuoteService;service 层不二次封装(满足 AGENTS 第 2 条)
- 首版风控 3 条硬性:产品可交易、客户适当性、持仓比例上限(fin_market_price 缺失或过期 → 拒绝买入)
- 数据库零修改:10 张 fin_* 表全部 docs/00 既定,本批 PR 改列类型与可空性均 0;底座实际偏差(id 无 AUTO_INCREMENT、所谓'生成列'是普通 NOT NULL)由 service _next_id / 业务派生值补偿
注册 API:9 端点均注册进 app.main;user=9001(cust)'s id 写账
权限码(tools/seed_test_rbac.py 同步登记 + CUSTOMER 全量):
9047 account:read:self
9048 trade:order:create
9049 trade:order:read
9050 trade:order:cancel
9051 holding:read:self
9052 trade:txn:read
错误码(app/core/errors.py + docs/05 §3.6 + tests/unit/core/test_errors.py DOCUMENTED 三方同步):
404 ACCOUNT_NOT_FOUND / ORDER_NOT_FOUND
409 ORDER_NOT_CANCELLABLE
422 INSUFFICIENT_FUNDS / INSUFFICIENT_HOLDING / HOLDING_RATIO_EXCEEDED / SUITABILITY_MISMATCH / PRODUCT_NOT_TRADABLE
503 FUND_QUOTE_UNAVAILABLE(可重试)
新增:app/api/controllers/trading.py / app/api/schemas/trading.py / app/service/trade_service.py / tools/seed_sim_account_demo.py / tests/unit/service/test_trade_service.py(unit×8) / tests/contract/test_trading_endpoint_contract.py(contract×11)
修改:app/main.py(挂载 controller) / app/core/errors.py(10 新异常类) / tools/seed_test_rbac.py / docs/05-接口文档.md(§19 T001-T009 + §3.6 9 新码) / tests/unit/core/test_errors.py(DOCUMENTED 同步)
门禁:pytest tests/unit tests/contract 1313 passed (+19 新增) / ruff all clean / 三道守卫全过
|
2026-09-12 15:50:37 +08:00 |
|
lzf_0626
|
49ebda4cb7
|
docs: 新增前端验收清单(64 项,逐项给出预期结果,区分实测与按契约)
|
2026-09-12 15:48:31 +08:00 |
|
lzf_0626
|
4c00db5636
|
docs: docs/32 工具索引补上功能测试台与两个新守卫,修正已过期的权限号段口径
|
2026-09-12 14:57:56 +08:00 |
|
zhangshy
|
5634fdc023
|
完善风控登录权限和模型能力配置
|
2026-09-12 14:43:05 +08:00 |
|
Windows
|
026fec7514
|
Merge remote-tracking branch 'origin/qyqy_develop' into lzl_qyqy_integration
|
2026-09-12 14:41:13 +08:00 |
|
Windows
|
9c6f839a6e
|
feat: add advisor demo environment bootstrap
|
2026-09-12 14:40:53 +08:00 |
|
lzf_0626
|
c200a61cd5
|
docs: docs/32 更新到 2026-09-12 状态,补功能性测试的触发条件与外部依赖前置
|
2026-09-12 14:22:34 +08:00 |
|
lzf_0626
|
c8cdc06e80
|
Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
|
2026-09-12 14:18:06 +08:00 |
|