lzf_0626
|
661b17c244
|
风控全量查询封顶并在响应里标注截断(docs/25 P3 #20)
预警详情里的资金流水/持仓/登录记录、以及日报的四组预警都是"一次性取全量"。某个客户
的历史数据一旦异常膨胀,单次响应就能把内存和连接拖垮;登录记录此前更是连时间窗都
没有。
处理原则是"封顶但**不静默**":详情证据每类最多 200 条、日报每组最多 5000 条,用
`limit + 1` 多取一行判断是否真被截断(只看"取满没取满"会把恰好等于上限的正常数据
误报成截断),被截断时:
- 详情新增 `evidence_truncated: string[]`,列出被截断的证据类型;
- 日报新增 `data_truncated: bool` —— 日报的 daily_alert_count 等数字直接来自行数,
静默截断等于给出一份看起来正常、实际少统计的日报,那比报错更难发现。
`scalars(...)` 统一用 `list(...)` 而不是 `.all()`,实现不再依赖 ScalarResult 的专有方法。
新增 tests/unit/repository/test_risk_repository_limits.py(3 例,用 literal_binds
编译 SQL,确保断言看到的是 201 而不只是"有 LIMIT"),并补日报与详情的标记用例;
风控文档 06 同步登记两个新字段与写接口的幂等要求。
|
2026-09-11 14:16:07 +08:00 |
|
lzf_0626
|
790518114b
|
风控 SSE 内容协商与鉴权时序;docs/05 补齐 413 与风控入口(docs/25 P3 #23 #24 #25)
#23:413 是上传超限的标准语义,前端文档(风控业务演示文档 17)也已按 413 做提示
映射,所以不把代码降成 422,而是在 docs/05 §3.5 状态码表补登 413 —— 契约以"补齐"
而不是"改动"的方式对齐。
#24:/api/v1/risk/daily-report/stream 此前既不校验 Accept,又把鉴权留在 async
generator 内部。后者更隐蔽:StreamingResponse 已经返回、响应头已经发出,403 只能
变成"200 + 半截流"。现在 controller 先 await service.authorize(context) 再判定
Accept,顺序与 §6.4 一致(鉴权先行,不用状态码差异做探测)。SSE 协商逻辑抽到
app/api/dependencies/negotiation.py,与 /agent-runs/{run_id}/events 共用同一口径,
避免同一种客户端在一个端点上 200、另一个端点上 406。
#25:复核后确认前半段不成立 —— §19 末尾写明业务域接口由各自业务文档登记,风控 15 条
端点已在 06-模块接口与字段映射.md 逐条登记。真问题是 §12 表里写的
/api/v1/risk-scans/**、/api/v1/risk-alerts/** 与实际实现 /api/v1/risk/** 不符,
按实际实现更新 §12 并加说明;顺带把风控文档里 /daily-report/mail 的权限从
"按主项目邮件策略执行"改为实际的 risk:report:mail。
新增 tests/unit/api/test_risk_stream_negotiation.py(7 例)。
|
2026-09-11 14:08:55 +08:00 |
|
lzf_0626
|
8ac0b794ff
|
fix(risk): 收敛剩余的时区口径(REST 时间参数、年龄、日报日期字段)
接 b3da1b6。上一条只改了凌晨规则与日报日界,剩下几处一并收掉:
1. risk_query_service.py:77-78:REST 的 start_time/end_time 是**裸 datetime**,
原先原样透传去比库内 UTC 列,而 Agent 路径本来就带时区
(risk_natural_language.py:117)——同一条筛选条件在界面与对话里会查出不同结果。
timeutil 新增 from_local:裸值按**北京时间**解释(面向中国客户的业务系统,
填表人的预期就是本地时间),带时区的按其自身时区处理。它与 to_utc_naive 的区别
正在裸值上:取库里的值用后者,接客户端输入用这个。
2. risk_scan_service.py 与 risk_judgement_service.py 的 _age:一处用 UTC 日期、
一处用服务器 date.today(),生日边界上同一客户会差一岁、65 岁阈值可能翻面。
统一走 local_date(北京时间)。
3. risk_daily_report_service.py:122 的 report_date 与 :244 的 created_today:
原先取 UTC 日期,北京 08:00 之前会把"今天新增的预警"算成昨天。
ruff / mypy(135 文件) / 603 unit+contract 全绿。
|
2026-09-11 12:55:00 +08:00 |
|
lzf_0626
|
b3da1b65ed
|
fix(risk): 修正风控的时区缺陷——凌晨规则与日报日界
docs/25 P1 #6。根因是"库内存 UTC naive"这个约定在**业务判断层**没被遵守,
而展示层其实已经是对的(risk_daily_report_service.py:418 按配置时区转换)。
1. 新增 app/core/timeutil.py 作为统一换算入口:约定库内 UTC naive,提供
local_hour / local_date / local_day_bounds(后两者用于查库时必须返回 UTC naive,
否则区间与库内值整体错开 8 小时),带时区的入参按其自身时区解释。
2. risk_scan_service.py:248:0 <= confirmed_at.hour < 6 → local_hour(...)。
原先 [0,6) UTC 被当成"凌晨",实际是北京时间 08:00-14:00,整条
「凌晨时段小额操作」规则判的是上午。
3. risk_judgement_service.py:238/243:判断**与展示**都换算。展示不改的话,
风控专员看到的时刻与直觉差 8 小时,无法与客户核对。
4. risk_daily_report_service.py:89:日界改用 local_day_bounds(按北京时间自然日,
再折回 UTC naive)。原先按 UTC 日期切日,北京 08:00 前生成的日报统计窗口跨零点。
测试:
- 新增 tests/unit/core/test_timeutil.py(8 条),含"UTC 凌晨 0-6 点不是北京凌晨"
这一缺陷复现,以及"日界必须返回 UTC naive"。
- 改写 test_risk_scan_service.py::test_night_small_trade_boundaries:它原本就拿
UTC 小时构造数据(写 0 点/6 点),等于在测北京 08:00/14:00;语义一并修正为
北京 00:00(含)与 06:00(不含)两个边界,三个边界场景保持不变。
ruff / mypy(135 文件) / 603 unit+contract 全绿。
|
2026-09-11 12:50:52 +08:00 |
|
zhangshy
|
c7c6736c54
|
fix: 修复日报模型装配并统一本地时间格式
|
2026-09-11 11:10:33 +08:00 |
|
zhangshy
|
a94d5c754d
|
feat: 迁移奶龙风控业务模块与演示文档
|
2026-09-10 21:03:44 +08:00 |
|