diff --git a/docs/40-前端验收清单.md b/docs/40-前端验收清单.md index 3aad0c0..b69b408 100644 --- a/docs/40-前端验收清单.md +++ b/docs/40-前端验收清单.md @@ -72,25 +72,38 @@ D:\conda\envs\jr_py313\python.exe tools\portal.py --base-url http://127.0.0.1:80 ## 3. 员工 · 风控工作台(`risk_t`) +> 本节按 `docs/风控业务演示文档/17-前端合并提示词与验收约束.md` 重写过,功能清单与交互约束都对齐了那份文档。 + | # | 操作 | 预期结果 | |---|---|---| -| 3-1 | 进入即自动加载"总览" | 数字卡片;数据范围 `all`(能看全部客户的预警) ✅实测 | -| 3-2 | 点「刷新总览」 | 同上,HTTP 200 ✅实测 | -| 3-3 | 进入即自动加载"预警列表" | **表格出现**:预警号 / 客户 / 等级 / 规则 / 状态 / 操作。本机实测 2 条:`ALDEMO0002`(高,RW-015/RW-003)、`ALDEMO0001`(中,RW-007/RW-002/RW-012),均"待处理" ✅实测
⚠️ **门户刻意不传 `limit`**:该接口 `limit` 上限是 **5**,传 20 会得到 `422 query.limit: Input should be less than or equal to 5`,**整张表格渲染不出来**(行内按钮也随之消失) | -| 3-4 | 点「触发一次扫描」 | **HTTP 200,`body.code=0`**(这条以前会因缺幂等头报 422,已修) ✅实测 | -| 3-5 | 点「生成日报」 | HTTP 200,返回日报内容 ⚠️按契约 | -| 3-6 | 对某条预警点「确认」 | 二次确认后返回业务结果。✅实测:`POST .../acknowledgements`(**无必填 body**)→ **409「只有待处理的预警才能确认解决」** —— 该动作**有状态前置条件**,不是任意状态都能点 | -| 3-7 | 点「升级」/「解决」 | ✅实测:**必须先「确认」接收预警**,否则两者都返回 **409「请先确认接收预警」**。
升级要填 reason、解决要填 resolution(**字段名不同**,各 1-500 字),门户已做成弹窗必填;不填会是 422 | -| 3-8 | 点一个**不存在**的预警号 | 404 资源不存在 —— 正常 fail closed ⚠️按契约 | -| 3-9 | 用**客户**账号访问风控接口 | **403 缺少操作权限**(`risk_t` 能看,`cust_t` 不能) ✅实测(客户访问 `/admin/roles` 为 403) | +| 3-1 | 进入即自动加载"风险概览" | 五张指标卡:**预警总量 / 待处理 / 已超时 / 高风险 / 重点预警** ✅实测(total=2 pending=1 overdue=2 高风险=2 重点=2) | +| 3-2 | 看"预警队列" | **每页 5 条**、按风险等级优先、行高固定且超长省略 ✅实测
本机 2 条:`ALDEMO0002`(高,RW-015/RW-003)、`ALDEMO0001`(高,RW-007/RW-002/RW-012)
⚠️ 该接口 `limit` 上限就是 **5**,传 20 会 422 且**整张表渲染不出来** | +| 3-3 | 用筛选栏逐项筛 | 8 个条件:关键词、客户号、风险等级、规则码、产品代码、产品名、起止时间 ✅实测
`rule_code=RW-015 → ALDEMO0002`、`customer_no=T-CUST → 2 条`、`keyword=ALDEMO0002 → 1 条`
⚠️ **风险等级必须填「高/中/低」**:预警对象用「高」,而概览 `levels` 用「高风险」,填「高风险」会 422 | +| 3-4 | 点「下一页 / 上一页」 | 走游标分页(`meta.next_cursor` + `has_more`),页脚显示"第 N 页 · 每页 5 条" ✅实测(本机 2 条 → 已到末页) | +| 3-5 | 点某行「详情」 | **弹窗**显示:预警编号、类型、等级与优先级分、处置状态、回执状态、证据归档、规则、到期时间、结案原因、证据摘要、证据快照、客户(行为分/投资者类型/风险标签)✅实测(alert 23 字段、customer 13 字段) | +| 3-6 | 弹窗里点「确认接收」 | 二次确认后提交。✅实测:`POST .../acknowledgements`(无 body)→ **409「只有待处理的预警才能确认解决」**,说明该动作有状态前置 | +| 3-7 | 弹窗里点「进入调查」 | 二次确认后 `POST .../investigations`(无 body)⚠️按契约 | +| 3-8 | 弹窗里点「关闭误报」 | **必须填写理由**(1-500 字),空值会被前端与后端双重拒绝 → `POST .../exclusions {reason}` ✅实测(body 字段已核对) | +| 3-9 | 弹窗里点「升级」 | **必须先「确认接收」**,否则 409「请先确认接收预警」;填 `reason` → `POST .../escalations` ✅实测 | +| 3-10 | 弹窗里点「完成结案」 | 填 `resolution`(**字段名不是 reason**)→ `POST .../resolutions` ✅实测 | +| 3-11 | 点「手动扫描」 | 二次确认后 `POST /alerts/scan` → **HTTP 200, code=0** ✅实测 | +| 3-12 | 点「生成日报」 | 弹窗内**流式生成**(SSE:`start` / `progress` / `replace`),生成后可**编辑内容**再填收件人发送 ✅实测(事件类型已核对) | +| 3-13 | 点「八类证据」 | 八个页签:**客户 / 产品 / 交易 / 资金 / 持仓 / 登录 / 预警 / 通知**,各带关键词、行为分等级、发送状态、起止时间筛选 ✅实测(8/8 全部 HTTP 200)
⚠️ 正确路径是 **`holdings`**,写成 `positions` 会被 422 拒绝 | +| 3-14 | 点「通知记录」 | 表格:预警编号 / 类型 / 发送状态 / 时间 ✅实测(HTTP 200) | +| 3-15 | 用**客户**账号访问风控接口 | **403 缺少操作权限** ✅实测 | > ⚠️ **注意**:`risk_t` 有 `audit:read`,所以它访问 `/api/v1/admin/roles` 是 **200 而不是 403** —— > 这是种子设计如此,不是越权漏洞。 > -> ⚠️ 行内三条按钮对应 `acknowledgements` / `escalations` / `resolutions` 三个端点,都是**写操作**: -> 会在库里留数据与审计,且**受状态机约束**(**确认 → 升级/解决**)。三个动作的请求体各不相同: -> 确认无 body、升级要 `reason`、解决要 `resolution`。列表拿不到数据时这三个按钮不会出现 —— -> 先确认 3-3 是否正常。 +> ⚠️ 五个处置动作都是**写操作**,会在库里留数据与审计,且**受状态机约束**(**先确认 → 再调查/误报/升级/结案**)。 +> 各自动作的请求体不同:确认与进入调查无 body、误报与升级要 `reason`、结案要 `resolution`。 +> 列表拿不到数据时行内按钮不会出现 —— 先确认 3-2 是否正常。 +> +> ⚠️ **排序局限**:接口没有排序参数,门户只在**当前页内**按风险等级+优先级分排序;跨页整体排序需要服务端支持。 +> +> ⚠️ 本轮顺带修掉一个平台 bug:`rule_code` 筛选此前**恒返回 0 条**(`risk_repository.py` 用 +> `.contains([code])`,SQLAlchemy 把它编译成 `LIKE`,而列是 JSON 数组,等于匹配字符串 +> `'["RW-015"]'`)。已改为 `func.json_contains(col, json.dumps(code))`,扫描去重处的同一写法也一并修了。 ---