GaoYiYuan_0626
|
d0097d6004
|
基金转换 T-9:API 模型 + 网关分派(HTTP 层 convert 走通)+ 展示位数口径修复
一、T-9 本体:HTTP 层 convert 端到端走通
- api/simulate.py:TradeRequest 三型字段分池(subscribe/redeem → product_id+amount;
convert → from/to_product_id + qty + 可选 client_request_id)+ model_validator 分支校验;
未知类型放行给网关抛 400(保住既有 purchase → 400 断言);model_dump(exclude_none=True);
PROCESSING → 202;异常捕获由 except LookupError 收窄为 except NotFoundError
(原写法把 KeyError 这类编程错误静默转成 404,实测掩盖 convert 分支真实诊断)。
- gateway/trade_gateway.py:移除 convert 显式拒绝,新增 _submit_convert 分派
(只做参数映射 + 仓储装配);convert 不写 trade_request 审计(审计归 convert_service)。
- utils/response.py:错误体合入 exc.extra(TOO_MANY_LOTS 的 batch_count/max_lots);
既有 ApiError 无 extra 属性 → 老错误体逐字节不变。
- utils/trace.py + main.py:正则收敛单点定义。执行期发现 trace.py 与 main.py 各有一份
内容完全相同的白名单副本 —— S4 要防的「漂移」其实已经发生,现将常量上移 trace.py
成公开 HEADER_ID_PATTERN(同时解决 main→simulate 反向导入成环)。
二、展示位数口径修复(执行期发现 → 联网核验 → 修复 → 文档订正)
发现:同一逻辑响应两种写法 —— 首次 "53456.95" vs 幂等重放 "53456.9500",数值相等、字符串不等。
根因不是 T-7 写错,是契约缺位:§2.5 只规定「金额/份额 2 位」,净值、费率、申请份额的
回显位数根本没定义 → 实现只能 str(Decimal) 原样出网 → 位数随数据来源漂移。
修复:convert_service 新增 _q(value, unit) + _D2/_D4 规格常量作对外唯一出口 ——
金额/份额 2 位、净值/费率/份额尾差 4 位;响应 + 审计 summary + 异常日志共用该出口;
原 _s() 全部替换。首次路径幂等(除 requested_qty/actual_qty/lot[].qty 由 4 位补齐 2 位外不变)。
依据(2026-09-10 联网核验 7 家管理人公告):金额/份额「四舍五入保留至小数点后两位」;
「申请转换份额精确到小数点后两位」;净值保留 4 位第 5 位四舍五入(中欧/国泰公告由 3 位提高至 4 位);
费率以百分比 2 位表示。已知不统一:易方达 ETF 场外份额取整数位、南方基金取截断 → 取主流口径
并记入 PRD 已知差异(未来接真实 TA 需按基金合同配置化)。
三、文档订正
- PRD → v0.9.2:§2.5 拆 2.5.1 计算精度 / 2.5.2 展示位数(新增按字段分类的规格表 + 外部依据);
§5.3 示例 requested_qty/actual_qty/lot_breakdown[].qty 4 位 → 2 位(原示例与 §2.5
「计算与对外展示按 2 位」自相矛盾,属漏改);字段类型约定补「位数不自由 + 两条路径须逐字节一致」。
- 架构 → v1.0.1:§1 原则 11 补「str() 前必须按 §2.5.2 量化」,无结构变更。
四、验证
- 新增 tests/test_convert_integration.py(8 条真 MySQL 端到端,CNV-TEST-/TRD-TEST- 前缀隔离):
折算与 PRD §5.3 逐项吻合、两条流水同组、持仓与批次如实变动、明细 completed + 审计、
幂等重试不产生第二组、跨主体 400、未知类型 400,以及
「首次与重放逐字段逐字节相等」+「展示位数规格」两条新闸门。
- test_trade_gateway.py +17(11 条错误码映射全表参数化 · 202 · 200 透传 · 不写 trade_request 审计)。
- test_integration_risk.py:R15 处置 —— 端到端已迁入新文件,原槽位改造为
test_invalid_type_400_and_no_new_trade_audit(改用 purchase 触发),保住「校验失败不落审计」不变量。
- pytest -q → 697 passed / 3 skipped(基线 672 +25,零回归)。
- 真库复跑:T-6 24/24 · T-7 35/35 · T-8 31/31;calc_convert_demo.py 15/15。
- 突变验证 4 组:关掉 convert 分派 → 21 条红;关掉错误体 extra 展开 → 精准 1 条;
关掉 client_request_id 正则 → 精准 1 条;关掉 _q() 展示量化 → 2 条红
(assert '50000.0000' == '50000' 直接复现原缺陷)。均已恢复。
|
2026-09-10 18:00:20 +08:00 |
|
GaoYiYuan_0626
|
66ec7eba08
|
feat: C6 FR-10 代理人行为链 RISK-008
代理人异常行为链识别(RISK-008)落地:
- 新增 agent_behavior_service:三条件证据聚合(A 诱导调仓/B AUTH_403_SCOPE 越权试探/C AUTH_403_NOT_OWNER|NOT_ASSIGNED 越权查询),按代理人维度独立出 pattern 单,payload.actor_id 指向代理人,审计仅 INSERT event_type=agent_behavior_detected。
- risk_repository 新增 list_audit_events / find_agent_behavior_alert / merge_agent_behavior_payload(同日同代理人一张单,证据并集)。
- trade_gateway.submit_trade 补 actor_id 透传(代理人发起交易归属发起人,缺省 SYSTEM);simulate 路由传入 auth.actor_id。
- chat_tools 新增 query_agent_behavior 只读 Tool(agent_id 过滤 + 客户脱敏),tool_service 补意图词与摘要。
- scripts/cron/agent_behavior_scan.py 定时扫描脚本。
- 修复 append_alert_event 序列化缺 default=str(C6 evidence 含 datetime 字段)。
- 单测 12 例(_count_induce 边界 / 三条件 / 出单去重 / payload 归属 / Tool 过滤脱敏)。
全量 pytest 482 passed 0 failed(470 基线 + 12 C6)。
|
2026-09-07 19:53:39 +08:00 |
|
GaoYiYuan_0626
|
6195349dd6
|
feat: main 集成(路由挂载/trace 中间件/lifespan) + B7 挂账收口(引擎工厂/统一错误体/锁公共化/L3 缓存 DEL/死代码清理/处置原子事务/启动期 debug 校验, B7)
|
2026-09-06 20:48:50 +08:00 |
|
GaoYiYuan_0626
|
a78e9d4d31
|
fix: B6 复审收口——测试 Redis 隔离/网关审计口径 platform/死常量清理/回归断言补强
- P2: test_risk_api env 注入 FakePublisher,aml 推送不触真 Redis
- P3: deny/_authz_audit 增 agent_type 参数,simulate 越权审计传 platform
(与网关放行审计同口径);删 HANDLE_RESULTS 死常量(Literal 单处定义)
- P3: 补 start_date/end_date 过滤+分页 422 边界+suitability api 层审计断言;
补 app/service/risk/__init__.py
- 挂账: B7 行新增④项(启动期拒绝/引擎工厂覆盖三实例化点/handle 原子性/
input_guard_log);B9b 行核查单(disclaimer/scan 幂等/Swagger 手测/analyst);
TODO.md T-30/T-31/T-32 进度同步
|
2026-09-06 19:21:15 +08:00 |
|
GaoYiYuan_0626
|
3e40a2bb49
|
fix: B6 评审修复——鉴权审计统一 deny/401 留痕、start_date/end_date 对齐 PRD、simulate 回挂鉴权、异常与审计注入收敛
- P1-1: 403/401 全部经 deps.deny/_authz_audit 留痕(event_type='authz')
- P2-1: GET /alerts 参数名 start_date/end_date 对齐 PRD;page/page_size 用 fastapi.Query
- P2-2: /api/simulate/trade 回挂 get_auth_context(risk_demo 或本人),越权 403+审计
- P2-3: suitability/aml 直调补审计+request_ref 透传
- P3-1: 多角色 fail-closed 口径固化进测试;P3-3/P3-4 core_ro/risk_repo 必传;
P3-5 LookupError→NotFoundError 统一;P3-9 alert_service import 上提
- 测试: test_risk_api 权限矩阵/审计断言/多角色组合/非 dev 拒绝;
test_trade_gateway 客户本人 vs 越权 403(P3-4 模拟 _repo 注入 sqlite)
- 挂账: P3-2/P3-7(响应外壳+disclaimer)→B7;P3-6(scan 幂等)→B9b 前
|
2026-09-06 18:58:48 +08:00 |
|
GaoYiYuan_0626
|
46a1f24870
|
fix: 引擎异常审计降级/审计全量输入输出/TRD-TEST 注入点(B5 评审 P1-1、P2-1~3、P3 批量)
|
2026-09-06 18:17:44 +08:00 |
|
GaoYiYuan_0626
|
2a95b65ec8
|
feat: 模拟交易网关(suitability 阻断→落库→引擎, convert 400, B5)
|
2026-09-06 18:05:44 +08:00 |
|