Merge remote-tracking branch 'origin/qyqy_develop' into qyqy_develop
This commit is contained in:
+119
-205
@@ -1,248 +1,162 @@
|
||||
# 前端验收清单(统一登录门户)
|
||||
# 前端验收清单(正式门户)
|
||||
|
||||
> **目的**:把门户的**每一个功能**都走一遍,逐项对照"预期结果"判断是否符合预期。
|
||||
> **被测对象**:`tools/portal.py`(统一登录门户),默认 <http://127.0.0.1:8101>
|
||||
> **时点**:2026-09-12
|
||||
> **证据口径**:标 ✅实测 的项是本轮真实跑过并确认的;标 ⚠️按契约 的项是照接口定义推断的
|
||||
> (沙箱里不便反复写业务数据,留给你点的时候确认)。
|
||||
> **被测对象**:**正式前端** `app/static/portal/`,由 `app/main.py` 挂载在 **`/portal/`**
|
||||
> **启动**:`python -m uvicorn app.main:app --host 127.0.0.1 --port 8000`
|
||||
> (模块级变量是 **`app`**,不是 `application`)
|
||||
> **入口**:<http://127.0.0.1:8000/portal/>(`/` 会 307 跳到 `/portal/guest/home/`)
|
||||
> **时点**:2026-09-13
|
||||
>
|
||||
> **证据口径**:标 ✅实测 的项目**由我用真实请求打过**;标 ⚠️待点验 的是**页面交互层面**,
|
||||
> 我只能从代码与接口推断,需要你在浏览器里点一遍。
|
||||
> 交付方自述的路由与数据源见 `app/static/portal/README.md`。
|
||||
|
||||
---
|
||||
|
||||
## 0. 启动与前置
|
||||
|
||||
### 0.1 前置(不满足会直接显示原因,不会静默)
|
||||
|
||||
| # | 项 | 命令 / 检查 | 预期 |
|
||||
|---|---|---|---|
|
||||
| 0-1 | 依赖服务 | MySQL、Redis 可用(Docker Desktop 要在跑,Milvus 才可用) | 门户顶部不出现"平台初始化失败"红条 |
|
||||
| 0-2 | 演示账号 | `python tools/seed_test_rbac.py` 然后 `python tools/set_user_password.py` | 五个账号都能登录;**口令脚本非幂等**,重复执行等于重设密码 |
|
||||
| 0-3 | 停常驻 Worker | 确认没有 `python -m app.worker` 在跑 | 否则客服对话会被抢队列,页面一直转圈 |
|
||||
|
||||
### 0.2 启动
|
||||
| 0-1 | 依赖服务 | MySQL、Redis 可用 | 平台能起,无 500 |
|
||||
| 0-2 | RBAC 种子 | `python tools/seed_test_rbac.py` | 五个演示账号可登录 ✅实测 |
|
||||
| 0-3 | 演示口令 | `python tools/set_user_password.py` | 口令生效(**非幂等**,重跑等于改密)✅实测 |
|
||||
| 0-4 | **虚拟资金账户** | `python -m tools.seed_sim_account_demo` | 客户 9001 开 10 万初始资金 + 2 只持仓。<br>**不跑这一步,`/customer/dashboard/` 与 `/customer/cash-ledger/` 必然打不开**(404「客户未开户」)✅实测 |
|
||||
| 0-5 | 风控 Agent 配置 | `python tools/publish_risk_agent_config.py` | `risk_overview` / `risk_search` / `risk_evidence` 三个工具生效 ✅实测 |
|
||||
| 0-6 | 停常驻 Worker | 确认没有 `python -m app.worker` | 否则客服对话 / Agent Run 会被抢队列 |
|
||||
|
||||
```powershell
|
||||
D:\conda\envs\jr_py313\python.exe tools\portal.py # 进程内直挂平台,走真实鉴权栈
|
||||
D:\conda\envs\jr_py313\python.exe tools\portal.py --base-url http://127.0.0.1:8000
|
||||
# 一次性把前置跑齐
|
||||
python tools/seed_test_rbac.py
|
||||
python tools/set_user_password.py
|
||||
python -m tools.seed_sim_account_demo
|
||||
python tools/publish_risk_agent_config.py
|
||||
python -m uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||||
```
|
||||
|
||||
- [ ] 打开 <http://127.0.0.1:8101> → **预期**:出现登录卡片,标题"基金智能服务平台"
|
||||
- [ ] 页面顶部右侧显示连接环境 → **预期**:`进程内 · 127.0.0.1:3306/jr`(口令已脱敏成 `***`)
|
||||
- [ ] 用**错的密码**登录 → **预期**:红条提示 `登录失败(HTTP 200):...`,停在本页 ✅实测
|
||||
- [ ] 同一个浏览器开**两个标签页**,分别登录客户与管理员 → **预期**:互不干扰(会话号存 `sessionStorage`)
|
||||
---
|
||||
|
||||
## 1. 路由与静态资源 ✅实测
|
||||
|
||||
| # | 路径 | 预期 | 实测 |
|
||||
|---|---|---|---|
|
||||
| 1-1 | `/` | 307 → `/portal/guest/home/` | ✅ |
|
||||
| 1-2 | `/portal/` | 307 → 同上 | ✅ |
|
||||
| 1-3 | `/portal/guest/home/` | 200 | ✅ 5740 B |
|
||||
| 1-4 | `/portal/guest/products/` | 200 | ✅ |
|
||||
| 1-5 | `/portal/guest/product-detail/` | 200 | ✅ |
|
||||
| 1-6 | `/portal/customer/login/` | 200 | ✅ |
|
||||
| 1-7 | `/portal/employee-console/login/` | 200 | ✅ |
|
||||
| 1-8 | `common/api-client.js`、`auth.js`、`permission-guard.js`、`app-shell.js`、`base.css`、`tokens.css`、`mock-data.js` | 全部 200 且 content-type 正确 | ✅ 9/9 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 登录与角色分流
|
||||
## 2. 访客(未登录)
|
||||
|
||||
五个演示账号(点"演示账号"按钮可自动填入):
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 2-1 | 打开 `/portal/guest/home/` | 首屏含品牌与产品摘要 | ⚠️待点验 |
|
||||
| 2-2 | **看页面的数据来源提示** | 显示"公开产品接口尚未提供,本页使用与 `fin_product`、`fin_nav_history` 模型同字段的演示数据" | ⚠️待点验<br>`MOCK_SOURCE_NOTICE` 常量已确认存在 |
|
||||
| 2-3 | 进产品列表 / 详情 | 能筛选、能按排名排序、详情有净值走势 | ⚠️待点验(数据来自 `mock-data.js`,**非真实接口**) |
|
||||
| 2-4 | 点登录入口 | 进 `/portal/customer/login/` | ⚠️待点验 |
|
||||
|
||||
| 账号 | 密码 | 角色 | 预期进入 | 预期权限数 |
|
||||
|---|---|---|---|---|
|
||||
| `cust_t` | `123456` | customer | 客服 | 20 |
|
||||
| `risk_t` | `666666` | risk_operator | 风控工作台 | 10 |
|
||||
| `offsite_t` | `offsite123` | operator | 运营工作台 | 2 |
|
||||
| `admin_t` | `88888888` | admin | 权限管理 | 50 |
|
||||
| `advisor_t` | `abc12345` | advisor | 投顾工作台 | 28 |
|
||||
|
||||
- [ ] 逐个登录 → **预期**:顶栏显示"用户名(user_id)"与角色徽章,选项卡标题与上表一致 ✅实测
|
||||
- [ ] 多角色账号 → **预期**:选项卡按最高权限界面进入,其余已具备的界面也可切换(便于一次演示)
|
||||
- [ ] 点"退出登录" → **预期**:回到登录卡片
|
||||
|
||||
> 角色是**每次请求现查库**的(令牌里只有 `sub`)。改了库里角色,**重新登录**即生效,不用重启门户。
|
||||
> **关于 mock**:这三页用的是 `common/mock-data.js`,因为**公开产品 HTTP 接口尚未实现**。
|
||||
> 页面显著标注了来源,README 也写明"不得与登录后的真实账户数据混用"。
|
||||
> **这不是 17- 文档禁止的"为业务演示造数据"**,而是接口缺位时的显式降级;
|
||||
> 真正的解法是补公开产品接口。
|
||||
|
||||
---
|
||||
|
||||
## 2. 客户 · 客服视图(`cust_t`)
|
||||
## 3. 客户(`cust_t` / `123456`)
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 2-1 | 输入"赎回基金多久到账?"发送 | 气泡里出现回答(如"货币基金T+0或T+1到账…QDII T+7、T+10"),下方标签显示**意图**(如 `faq`) ✅实测 |
|
||||
| 2-2 | 看回答末尾 | 附**合规提示**:"本内容仅为投资风险参考,不构成任何直接投资建议…" ✅实测 |
|
||||
| 2-3 | 问一个答不了的(如"帮我下单") | Agent 引导拨打客服热线;若判定需人工,标签显示**已转人工** ✅实测(问"赎回费怎么算"得到引导话术) |
|
||||
| 2-4 | 点「我的画像」 | 返回当前客户的记忆画像(HTTP 200) ✅实测 |
|
||||
| 2-5 | 点「我的画像候选」 | 返回候选列表;**没有候选时**显示"暂无候选(HTTP 200)"而不是报错 ⚠️按契约 |
|
||||
| 2-6 | 候选里点「确认」/「拒绝」 | HTTP 200,状态变更;再刷新列表状态已更新 ⚠️按契约(本机暂无候选数据) |
|
||||
| 2-7 | 点「转人工」 | 弹出说明输入框 → 确认后 **HTTP 202**,返回 `handover_id` 与 `status=pending` ✅实测 |
|
||||
| 2-8 | 转人工后,用管理员看"客服转人工工单" | 新工单出现在队列里 ✅实测(`ticket-d8c526a9…`) |
|
||||
### 3.1 登录与账户域
|
||||
|
||||
> **2-7 的实现细节**:门户会先调 `POST /api/v1/conversations` 真实建会话(**201**)再转人工。
|
||||
> 早期版本用自己编的 session id,会得到 404「会话不存在」。
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 3-1 | 用 `cust_t` / `123456` 登录(A034) | 200,拿到令牌,跳 dashboard | ✅ A034 200,`user_id=9001`,`expires_in=1800` |
|
||||
| 3-2 | dashboard(T001 `GET /users/me/account/dashboard`) | 总资产 / 可用资金 / 持仓市值等 | ✅ 200(**修前置前是 404「客户未开户」**) |
|
||||
| 3-3 | 持仓(T006 `GET /users/me/holdings`) | 持仓表 | ✅ 200 |
|
||||
| 3-4 | 交易流水(T007) | 流水表 | ✅ 200 |
|
||||
| 3-5 | 资金流水(T009 `GET /users/me/cash-ledger`) | 资金明细 | ✅ 200(同 3-2,修前置前 404) |
|
||||
| 3-6 | 订单列表(T003 `GET /users/me/orders`) | 空列表不报错 | ✅ 200(0 条) |
|
||||
| 3-7 | 下单(T002 `POST /users/me/orders`) | 报文 `{product_code, order_side, quantity}`,`order_side ∈ {buy, sell}` | ⚠️待点验<br>需用真实 product_code(`7002` 是 product_id 不是 code) |
|
||||
| 3-8 | 盈亏(T001) | 收益曲线 | ⚠️待点验 |
|
||||
| 3-9 | 风险测评(ONB001) | 返回问卷定义 | ✅ 200 |
|
||||
| 3-10 | 提交问卷(ONB002) | 提交后状态变更;**未提交测评时部分接口会 403/409** | ⚠️待点验(写操作) |
|
||||
|
||||
### 3.2 客服
|
||||
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 3-11 | 与客服 Agent 对话(R001 + R002/R003) | 受理 202 → SSE 出结果 | ⚠️待点验(需停常驻 Worker) |
|
||||
| 3-12 | 转人工 | 工单进管理员队列 | ✅ 我之前实测过 `POST /conversations/{sid}/handover-requests` → 202,前提是**先调 C001 真实创建会话** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 员工 · 风控工作台(`risk_t`)
|
||||
## 4. 员工 / 管理员(`admin_t` / `88888888`)
|
||||
|
||||
> 本节按 `docs/风控业务演示文档/17-风控模块-前端合并提示词与验收约束.md` 重写过,功能清单与交互约束都对齐了那份文档。
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 4-1 | `/portal/employee-console/login/` 登录 | 进 workspace | ✅ A034 200 |
|
||||
| 4-2 | 配置发布列表(A002) | 版本列表 | ✅ 200 |
|
||||
| 4-3 | 模型端点(A012) | 端点列表 | ✅ 200 |
|
||||
| 4-4 | 审计查询(A033) | 时间/动作/操作者/结果 | ✅ 200 |
|
||||
| 4-5 | 角色列表 / 权限 / 用户角色(A035–A038) | 角色与权限明细 | ✅ 200(`customer` 角色 26 项权限) |
|
||||
| 4-6 | 画像候选审核(A039) | 候选列表 | ✅ 200 |
|
||||
| 4-7 | 客服转人工工单(ADMIN_HANDOVERS) | 工单队列,**不返回客户标识与原始正文** | ✅ 200 |
|
||||
| 4-8 | 配置发布四态(A004/A005/A006) | 校验→审核→激活,**必须带 `Idempotency-Key`** | ⚠️待点验(写操作) |
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 3-1 | 进入即自动加载"风险概览" | 五张指标卡:**预警总量 / 待处理 / 已超时 / 高风险 / 重点预警** ✅实测(total=2 pending=1 overdue=2 高风险=2 重点=2) |
|
||||
| 3-2 | 看"预警队列" | **每页 5 条**、按风险等级优先、行高固定且超长省略 ✅实测<br>本机 2 条:`ALDEMO0002`(高,RW-015/RW-003)、`ALDEMO0001`(高,RW-007/RW-002/RW-012)<br>⚠️ 该接口 `limit` 上限就是 **5**,传 20 会 422 且**整张表渲染不出来** |
|
||||
| 3-3 | 用筛选栏逐项筛 | 8 个条件:关键词、客户号、风险等级、规则码、产品代码、产品名、起止时间 ✅实测<br>`rule_code=RW-015 → ALDEMO0002`、`customer_no=T-CUST → 2 条`、`keyword=ALDEMO0002 → 1 条`<br>⚠️ **风险等级必须填「高/中/低」**:预警对象用「高」,而概览 `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)<br>⚠️ 正确路径是 **`holdings`**,写成 `positions` 会被 422 拒绝 |
|
||||
| 3-14 | 点「通知记录」 | 表格:预警编号 / 类型 / 发送状态 / 时间 ✅实测(HTTP 200) |
|
||||
| 3-15 | 用**客户**账号访问风控接口 | **403 缺少操作权限** ✅实测 |
|
||||
|
||||
> ⚠️ **注意**:`risk_t` 有 `audit:read`,所以它访问 `/api/v1/admin/roles` 是 **200 而不是 403** ——
|
||||
> 这是种子设计如此,不是越权漏洞。
|
||||
>
|
||||
> ⚠️ 五个处置动作都是**写操作**,会在库里留数据与审计,且**受状态机约束**(**先确认 → 再调查/误报/升级/结案**)。
|
||||
> 各自动作的请求体不同:确认与进入调查无 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))`,扫描去重处的同一写法也一并修了。
|
||||
> **平台只提供 RBAC 只读查询**:没有改权限的写接口,权限变更走 `config_release` 发布。
|
||||
> 这是设计,不是功能缺失。
|
||||
|
||||
---
|
||||
|
||||
## 4. 员工 · 运营工作台(`offsite_t`)
|
||||
## 5. 风控(`risk_t` / `666666`)—— 按 `docs/风控业务演示文档/17-*.md` 对齐
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 4-1 | 进入即加载"邮箱状态" | 数字卡片,HTTP 200 ✅实测 |
|
||||
| 4-2 | 点「拉取邮件列表」 | 表格(邮件 ID / 主题 / 状态 / 操作)。**邮箱未配置时条数为 0**、不白屏 ✅实测(HTTP 200,0 条) |
|
||||
| 4-3 | 点某封邮件的「识别字段」 | 返回识别结果 JSON ⚠️按契约(需库里有邮件数据) |
|
||||
| 4-4 | 点「删除」邮件 | 二次确认后写操作。门户自动带 `operator_id`(= 当前登录 user_id)✅实测(字段齐了才会进业务层) |
|
||||
| 4-5 | 点「触发邮箱恢复」 | ✅实测:`HTTP 200 + body.code=404「邮箱尚未初始化」` —— 请求体已合法,是本机没配邮箱,属正常 |
|
||||
| 4-6 | 在"单据处理"填一个**真实存在**的 task_id,点「识别字段」「规则结果」 | 返回该单据的字段与规则判定 ⚠️按契约 |
|
||||
| 4-7 | 填一个**不存在**的 task_id | 422/404,页面上原样显示 —— 正常 ⚠️实测(假 task_id 得到 422) |
|
||||
| 4-8 | 点「确认单据」/「重试识别」/「创建通知」 | 见下方"三个写操作的必填字段",各自会弹窗要必填值 ⚠️按契约 |
|
||||
| 4-9 | 点「重算结算统计」 | ✅实测:要填 `fund_code` + `application_date`(门户已弹窗),**HTTP 200 `code=0` ok** |
|
||||
|
||||
> **场外写操作的三条硬约束**(实测得出,门户已按此实现):
|
||||
>
|
||||
> | 接口 | 必填字段 |
|
||||
> |---|---|
|
||||
> | `mailbox-status/recoveries`、`mails/{id}/deletions`、`documents/{id}/recognition-retries` | `operator_id` |
|
||||
> | `documents/{id}/confirmations` | `decision`(**中文枚举**:确认无误 / 确认异常 / 未处理)+ `operator_id` |
|
||||
> | `documents/{id}/notifications` | `notification_type`(risk / settlement / mail_return / normal_return / exception_return…)+ `operator_id` |
|
||||
> | `settlement-statistics/recalculate` | `fund_code` + `application_date`(**没有** operator_id) |
|
||||
>
|
||||
> `operator_id` 是**防伪校验**:平台会核对它是否等于当前登录用户(传别人的会被拒)。
|
||||
> 实测不传它必得 **422**,所以门户一律自动带本次登录的 user_id,不让你手填。
|
||||
|
||||
> **运营为什么只有 2 项权限却能用**:场外线的服务层用的是**角色门槛**
|
||||
> `{"operator","risk_operator","admin","super_admin"}`(`offsite_fund_service.py:2600`),
|
||||
> 不是权限码。那 2 项是 `offsite:write` 和 `financial:nl2sql:read`。
|
||||
> 换句话说:**"看不到运营界面"以前是前端没做,不是权限问题**。
|
||||
| # | 操作 | 预期 | 证据 |
|
||||
|---|---|---|---|
|
||||
| 5-1 | 风险概览(RK001) | 总量 / 等级 / 待处理 / 超时 / 重点预警 | ✅ 200(total=2 pending=1 overdue=2 levels{高风险:2}) |
|
||||
| 5-2 | 预警队列(RK002) | **每页 5 条**、风险等级优先、筛选、分页 | ✅ 200(2 条:`ALDEMO0002` 高、`ALDEMO0001` 高)<br>⚠️ `limit` **上限就是 5**,传 20 会 422 且表格渲染不出来 |
|
||||
| 5-3 | 队列筛选 | 关键词 / 客户号 / 风险等级 / 规则码 / 产品 / 时间 | ✅ `rule_code=RW-015 → ALDEMO0002`、`customer_no=T-CUST → 2 条`<br>⚠️ **风险等级要填「高/中/低」**:预警对象用「高」,概览 `levels` 用「高风险」 |
|
||||
| 5-4 | 预警详情(RK003) | 编号 / 状态 / 规则 / 证据 / 回执 / 客户 | ✅ 200(alert 23 字段 + customer 13 字段) |
|
||||
| 5-5 | 八类证据(RK004) | `customers`/`products`/`transactions`/`capital_flows`/`holdings`/`login_records`/`alerts`/`notifications` | ✅ 8/8 全 200<br>⚠️ 路径是 **`holdings`**,写 `positions` 会被 422 |
|
||||
| 5-6 | 通知记录(RK005) | 预警编号 / 类型 / 发送状态 | ✅ 200 |
|
||||
| 5-7 | 手动扫描(RK006) | 200 `code=0`,**要带 `Idempotency-Key`** | ✅ 200 |
|
||||
| 5-8 | 确认接收(RK007) | **二次确认**;状态不符返回 409 | ✅ 409「只有待处理的预警才能确认解决」 |
|
||||
| 5-9 | 进入调查(RK008)/ 关闭误报(RK009) | 误报**必须填理由** | ⚠️待点验(写操作) |
|
||||
| 5-10 | 结案(RK010)/ 升级(RK011) | 结案填 `resolution`、升级填 `reason`;**需先确认接收** | ⚠️待点验(顺序:确认 → 调查/误报/升级/结案) |
|
||||
| 5-11 | 证据上传(RK012) | **`multipart/form-data`** | ⚠️待点验 |
|
||||
| 5-12 | 日报(RK013 / RK014 / RK015) | 生成 / **SSE 流式** / 多邮箱发送 | ✅ RK013 200;SSE 事件类型实测为 `start`/`progress`/`replace` |
|
||||
|
||||
---
|
||||
|
||||
## 5. 管理员 · 权限管理(`admin_t`)
|
||||
## 6. 通用预期(跨页面)
|
||||
|
||||
### 5.1 角色与权限
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
| # | 情形 | 预期 |
|
||||
|---|---|---|
|
||||
| 5-1 | 进入即加载角色卡片 | 每个角色显示 **权限数 / 角色名 / user_count** ✅实测(`GET /admin/roles` 200) |
|
||||
| 5-2 | 点任一角色卡片 | 列出该角色的**权限码**(药丸标签)+ 角色详情 ✅实测(customer 20 项) |
|
||||
| 5-3 | 对照第 1 节的权限数 | 与登录时顶栏显示的权限数一致 |
|
||||
| 5-4 | 输入 `9001` 点「查询该用户的角色」 | 返回 `cust_t → customer` 的解析结果 ✅实测 |
|
||||
|
||||
> **平台只提供只读查询**:改权限要发布新的 `config_release`,**没有直接写接口** ——
|
||||
> 这是设计(配置受版本控制),不是功能没做完。页面上也这么写了。
|
||||
|
||||
### 5.2 审计与工单
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 5-5 | 点「刷新」审计流水 | 时间 / 动作 / 操作者 / 结果;做过写操作后能看到刚才那条 ✅实测(200) |
|
||||
| 5-6 | 点「刷新工单」 | 客户在第 2-7 步建的工单出现在这里(工单号 / 来源 / 优先级 / 原因 / 状态)✅实测 |
|
||||
| 5-7 | 点「列出知识文档」 | 文档清单(ID / 标题 / 状态)⚠️按契约(需 KB 有数据) |
|
||||
|
||||
### 5.3 推广材料审核
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 5-8 | 填投顾给你的任务单号,点「查任务」 | 返回任务详情,**若已有 `approved/sent` 版本会自动填进版本号框** ✅实测 |
|
||||
| 5-9 | 填 `material_version_id`,点「通过」 | 二次确认后 HTTP 200 `code=0`;此后投顾才能投递 ✅实测(版本 19 通过) |
|
||||
| 5-10 | 点「退回修改」/「拒绝」 | 同样 200,状态流转 ⚠️按契约 |
|
||||
| 6-1 | **业务失败也返回 HTTP 200** | 本平台把业务错误放在 `body.code`(如生成失败 `HTTP 200 + code=422`),前端按 `error-codes.js` 解析 ✅实测 |
|
||||
| 6-2 | 403 | "没有当前操作权限";按钮可见性应与服务端权限一致 ✅实测(客户访问 `/admin/roles` → 403) |
|
||||
| 6-3 | 404 | 注意 `SESSION_NOT_FOUND` 被**三个异常类共用**,可能是"会话不存在"、"客户不可访问"或"知识文档不存在",只能看 message ✅实测 |
|
||||
| 6-4 | 幂等 | 写请求必须带唯一 `Idempotency-Key`(`api-client.js` 已用 `idempotent: true` 标记)✅实测 |
|
||||
| 6-5 | 多标签 | 各页面独立,会话互不干扰 ⚠️待点验 |
|
||||
| 6-6 | 排版 | 文字不重叠、内容不溢出、表格行高稳定 ⚠️待点验 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 投顾 · 投顾工作台(`advisor_t`)
|
||||
## 7. 我做了什么验证(可复现)
|
||||
|
||||
### 6.1 投资目标与方案
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
| 项 | 方式 | 结果 |
|
||||
|---|---|---|
|
||||
| 6-1 | 客户 ID 填 `9001`,点「查投资目标」 | **200**,返回目标(目标区间、基准)—— 前提是 9001 在你名下且已建过目标 ✅实测(`4.5000 - 8.0000`、沪深300) |
|
||||
| 6-2 | 改填一个**不在你名下**的客户 ID | **404「客户不可访问」** —— 最小权限,`data_scope=own_customers` 生效 ✅实测设计如此 |
|
||||
| 6-3 | 点「已发布方案」 | 200,返回已发布方案列表 ✅实测 |
|
||||
| 6-4 | 点「跑组合分析」 | 二次确认后 200(请求体是**空对象** `{}`,多传字段会 422)✅实测 |
|
||||
| 6-5 | 点「生成资产配置」 | 同上 ✅实测 |
|
||||
| 16 个页面路由 + 9 个静态资源 | 逐个 GET | 全部 200 / 307 符合预期 |
|
||||
| 49 个端点里的 26 个(客户域 / 风控域 / 管理面 / Agent) | 用五个角色真实登录后逐个请求 | 除 3 个(见下)全通 |
|
||||
| 登录链路 | `POST /api/v1/auth/tokens` × 5 角色 | 全部 200 |
|
||||
| 平台门禁 | ruff / mypy / pytest / 表审计 | ruff 干净 / **mypy 249 文件 0 错** / 单元+契约 **1376 passed** / 集成 **104 passed** / **89 张业务表** |
|
||||
|
||||
### 6.2 推广材料(**六步流程,顺序不能跳**)
|
||||
**联调时发现并已修的问题**:
|
||||
|
||||
| # | 操作 | 预期结果 |
|
||||
|---|---|---|
|
||||
| 6-6 | ① 点「创建任务」 | 200,返回 `task_no`(如 `PM-20260912-0007`)并**自动填进单号框** ✅实测 |
|
||||
| 6-7 | ② 改一下结构化输入(**六个块必填**),点「保存输入」 | 200 `code=0` ✅实测 |
|
||||
| 6-8 | ③ 点「生成 PPTX」 | 200 `code=0`,返回 **`material_version_id`** 与 `status=pending_review`,**版本号自动填进投递框** ✅实测(真实产出 `v1.pptx`) |
|
||||
| 6-9 | 若**没填费率**就点生成 | `body.code=422`「材料内容未通过合规校验」,`findings` 里是 `fee_structure.incomplete`(severity=**block**),任务被置为 `compliance_failed` ✅实测 |
|
||||
| 6-10 | ④ 让管理员在 5.3 审核通过 | 见 5-9 |
|
||||
| 6-11 | ⑤ 点「投递」(投顾 id 填 `9020`) | 200 `code=0` ✅实测 |
|
||||
| 6-12 | ⑥ 点「查询任务」 | **200**,`status="sent"`,含 `material_version`(版本号、pptx 路径)✅实测 |
|
||||
| 6-13 | **没投递就查询** | **404「该材料尚未发送给当前投顾」** —— 这是**合规设计**,不是故障;页面会追加提示告诉你怎么走完 ✅实测 |
|
||||
| 6-14 | 点「合规检查结果」 | 列出 findings(通过的会显示 `overall.pass`)✅实测 |
|
||||
|
||||
> ⚠️ **两条必须知道的约定**:
|
||||
> 1. **费率七项必须非空**(`subscription_fee`/`purchase_fee`/`redemption_fee`/`sales_service_fee`/
|
||||
> `management_fee`/`custody_fee`/`client_maintenance_fee`),否则合规规则**阻断**生成。
|
||||
> 骨架里填的是「待填写」占位,**真实材料必须换成真实费率**。
|
||||
> 2. 骨架**刻意不预填任何业绩数字**:`performance_info` 的业绩字段全部可选且带
|
||||
> `show_product_performance` 开关,关掉即可 —— 编造业绩是红线。
|
||||
|
||||
---
|
||||
|
||||
## 7. 通用行为预期(跨视图)
|
||||
|
||||
| # | 情形 | 预期表现 |
|
||||
|---|---|---|
|
||||
| 7-1 | **业务失败也返回 HTTP 200** | 本平台把业务错误放在 `body.code`(如生成失败 `HTTP 200 + code=422`)。门户按 **body.code** 判成败并标红 ✅实测 |
|
||||
| 7-2 | 403 | "当前角色权限不足(平台按设计 fail closed)",原样显示不隐藏 ✅实测 |
|
||||
| 7-3 | 404 | 显示 message。注意 `SESSION_NOT_FOUND` 被**三个异常类共用**,可能是"会话不存在"、"客户不可访问"或"知识文档不存在",只能看 message 区分 ✅实测 |
|
||||
| 7-4 | 422 | 报文格式问题,`error.field_errors` 会指出具体字段 ✅实测 |
|
||||
| 7-5 | 写操作 | 一律二次确认(`confirm`),避免误点改数据 ✅实测 |
|
||||
| 7-6 | 令牌 | 只存在服务端,浏览器拿不到;前端只有一个随机会话号 ✅设计 |
|
||||
| 7-7 | 空数据 | 显示"暂无…(HTTP xxx)"或原始返回,不白屏 ✅实测 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 已知限制(先说明,免得当成 bug)
|
||||
|
||||
1. **权限界面只读** —— 平台没有写接口,改权限走 `config_release` 发布;
|
||||
2. **运营工作台的"单据处理"需要一个真实的 task_id** —— 本机没有场外单据数据时,识别/确认/通知只能看到 404,属正常;
|
||||
3. **组合分析与资产配置只对空请求体有效**(`{}`,`additionalProperties:false`);
|
||||
4. **风控日报/处置类写操作**会在库里留数据与审计,验收时建议用专用库或事后清理;
|
||||
5. **门户是单进程工具**:会话存内存,重启门户需要重新登录。
|
||||
|
||||
---
|
||||
|
||||
## 9. 验收记录表
|
||||
|
||||
| 章节 | 项数 | 通过 | 不符合预期 | 备注 |
|
||||
|---|---:|---:|---:|---|
|
||||
| 0 启动与前置 | 4 | | | |
|
||||
| 1 登录与分流 | 3 | | | |
|
||||
| 2 客户 · 客服 | 8 | | | |
|
||||
| 3 员工 · 风控 | 9 | | | |
|
||||
| 4 员工 · 运营 | 9 | | | |
|
||||
| 5 管理员 · 权限 | 10 | | | |
|
||||
| 6 投顾 · 投顾台 | 14 | | | |
|
||||
| 7 通用行为 | 7 | | | |
|
||||
| **合计** | **64** | | | |
|
||||
|
||||
> 发现不符合预期的项,记下**章节号 + 当时的 `trace_id`**(响应里带),可以直接定位到那一次请求。
|
||||
1. **`T001` / `T009` 恒 404「客户未开户」** —— 根因是 `tools/seed_test_rbac.py` 把
|
||||
`fund_account_status` **硬编码成 `'closed'`**,与 `tools/create_test_user.py` 的
|
||||
`{customer: 已开户, employee: closed}` 口径不一致。已改为按 `user_type` 取状态;
|
||||
再配合 `python -m tools.seed_sim_account_demo` 开虚拟资金账户后,T001/T006/T007/T009 全部 200。
|
||||
2. **`rule_code` 筛选恒为空** —— `risk_repository.py` 用 `.contains([code])`,SQLAlchemy 会把它
|
||||
编译成 `LIKE`,而 `trigger_rule_codes` 是 JSON 数组,等于匹配字符串 `'["RW-015"]'`。
|
||||
已改为 `func.json_contains(...)`;扫描去重处的同一写法也一并修了。
|
||||
3. **误提交的 `.agents/skills`(15 个文件)** —— AI 助手配置,与本项目无关,已从仓库移除并加入 `.gitignore`。
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
# 风控模块需求说明书
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| 文档版本 | v1.0 |
|
||||
| 编制日期 | 2026-09-13 |
|
||||
| 适用对象 | 主项目架构、前端、后端、测试、运维和风控业务人员 |
|
||||
| 实现基线 | 当前 `RM2_develop` 风控模块代码 |
|
||||
| 文档定位 | 主项目合并和联调时的风控模块统一需求口径 |
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
本模块面向公募基金模拟交易场景,基于客户、产品、交易、资金、持仓、登录、工单等数据执行确定性风险扫描,生成风险预警,并提供证据查询、人工处置、通知、日报和只读 Agent 研判能力。
|
||||
|
||||
模块目标:
|
||||
|
||||
- 对异常交易和适当性风险形成可解释、可追溯的结构化预警。
|
||||
- 支持风控人员确认、调查、关闭误报、完成结案和升级处理。
|
||||
- 提供客户、产品、交易、资金、持仓、登录、预警和通知八类证据。
|
||||
- 对高风险预警提供站内通知和邮件通知。
|
||||
- 提供九段式风险日报和日报邮件。
|
||||
- 提供“奶龙风控智能助手”只读查询和研判草案能力。
|
||||
- 所有关键动作写入审计,支持按预警和客户追溯。
|
||||
|
||||
## 2. 范围
|
||||
|
||||
### 2.1 本期范围
|
||||
|
||||
- 风险概览。
|
||||
- 预警队列、筛选、排序、分页和详情。
|
||||
- 手动规则扫描。
|
||||
- 定时规则扫描。
|
||||
- 适当性错配、异常交易和频率筛查规则。
|
||||
- 预警确认、调查、误报关闭、完成结案、升级和证据归档。
|
||||
- 客户行为分更新。
|
||||
- 八类证据只读查询。
|
||||
- 通知记录查询。
|
||||
- 高风险预警邮件通知。
|
||||
- 九段式日报、流式生成和日报邮件发送。
|
||||
- 奶龙风控智能助手只读工具调用。
|
||||
- 风控操作审计。
|
||||
|
||||
### 2.2 不在本期范围
|
||||
|
||||
- 用户登录、JWT 签发和通用 RBAC 底座。
|
||||
- 客户账户开户、充值、提现和真实资金操作。
|
||||
- 修改交易、资金、持仓和产品事实。
|
||||
- 自动确认、自动关闭或自动升级预警。
|
||||
- 由风控模块提供演示数据初始化。
|
||||
- 正式前端页面实现。
|
||||
- 高风险预警邮件多收件人扩展。
|
||||
- 将定时扫描自动注册到主项目统一 Worker。
|
||||
|
||||
## 3. 用户、角色与权限
|
||||
|
||||
### 3.1 角色
|
||||
|
||||
| 角色 | 使用范围 |
|
||||
|---|---|
|
||||
| 风控专员 `risk_operator` | 查询风控数据、执行规则扫描、人工处置和日报操作 |
|
||||
| 管理员 `admin` | 具备风控业务权限,并负责配置和运维 |
|
||||
|
||||
### 3.2 权限
|
||||
|
||||
| 权限码 | 用途 |
|
||||
|---|---|
|
||||
| `risk:alert:read` | 风险概览、预警、证据、通知、日报和 Agent 只读查询 |
|
||||
| `risk:alert:write` | 确认、调查、误报关闭、完成结案、升级和证据归档 |
|
||||
| `risk:alert:scan` | 手动规则扫描 |
|
||||
| `risk:report:mail` | 发送日报邮件 |
|
||||
| `agent:run` | 运行奶龙风控智能助手 |
|
||||
|
||||
### 3.3 数据范围
|
||||
|
||||
- 风控查询必须执行服务端客户数据范围过滤。
|
||||
- `data_scope=all` 可访问全部客户;受控范围只能访问归属客户。
|
||||
- 没有有效客户归属时必须失败关闭,不能默认放开。
|
||||
- 列表、详情、证据、通知、日报和 Agent 工具使用同一数据范围。
|
||||
|
||||
## 4. 预警状态与人工处置
|
||||
|
||||
### 4.1 状态
|
||||
|
||||
| 状态 | 含义 | 是否未闭环 |
|
||||
|---|---|---|
|
||||
| 待处理 | 预警已生成,尚未确认接收 | 是 |
|
||||
| 调查中 | 已确认接收并进入人工调查 | 是 |
|
||||
| 已排除 | 人工认定为误报并关闭 | 否 |
|
||||
| 已结案 | 人工调查完成并形成处置结论 | 否 |
|
||||
|
||||
“升级处理”通过 `is_escalated`、`escalated_at` 和 `escalation_reason` 标记,不改变上述状态。
|
||||
|
||||
### 4.2 状态流转
|
||||
|
||||
```text
|
||||
待处理
|
||||
-> 确认接收
|
||||
-> 进入调查
|
||||
-> 关闭误报
|
||||
|
||||
待处理
|
||||
-> 确认接收
|
||||
-> 进入调查
|
||||
-> 完成结案
|
||||
|
||||
待处理或调查中
|
||||
-> 升级处理
|
||||
-> 保持原未闭环状态
|
||||
```
|
||||
|
||||
### 4.3 处置规则
|
||||
|
||||
| 操作 | 前置条件 | 结果 |
|
||||
|---|---|---|
|
||||
| 确认接收 | 状态为待处理且尚未确认 | 写确认状态、确认时间和处理人 |
|
||||
| 进入调查 | 已确认且状态为待处理 | 状态改为调查中 |
|
||||
| 关闭误报 | 已确认且未闭环 | 状态改为已排除,记录误报理由和关闭时间 |
|
||||
| 完成结案 | 已确认且状态为调查中 | 状态改为已结案,写处置结论并更新行为分 |
|
||||
| 升级处理 | 已确认、未闭环且尚未升级 | 写明升级理由,不改变闭环状态 |
|
||||
| 上传证据 | 已确认且状态为调查中 | 每笔预警最多归档一个文件,归档后不可覆盖 |
|
||||
|
||||
### 4.4 行为分
|
||||
|
||||
- 初始行为分为 20 分。
|
||||
- 完成结案时按预警等级扣分:
|
||||
- 低风险扣 3 分。
|
||||
- 中风险扣 5 分。
|
||||
- 高风险扣 20 分。
|
||||
- 最低扣至 0 分。
|
||||
- 关闭误报和未闭环预警不扣分。
|
||||
- 行为分变化必须写入审计。
|
||||
|
||||
行为分关注等级:
|
||||
|
||||
| 等级 | 分数范围 | 筛选值 |
|
||||
|---|---|---|
|
||||
| 正常 | 16-20 | `normal` |
|
||||
| 轻微关注 | 11-15 | `slight` |
|
||||
| 需要关注 | 6-10 | `attention` |
|
||||
| 高度关注 | 1-5 | `high` |
|
||||
| 立即关注 | 0 | `immediate` |
|
||||
|
||||
## 5. 风控规则
|
||||
|
||||
### 5.1 通用扫描规则
|
||||
|
||||
- 扫描只读取交易、资金、持仓、客户、产品、工单和登录数据。
|
||||
- 同一交易和同一规则组合不能重复生成预警。
|
||||
- 同一交易命中多个规则时合并为一条预警,保留最高风险等级并汇总规则和证据。
|
||||
- 扫描结果写入 `fin_risk_alert` 和审计。
|
||||
- 手动扫描和定时扫描共用同一扫描服务。
|
||||
|
||||
### 5.2 RW-003 大额快进快出
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 触发对象 | 赎回交易 |
|
||||
| 条件 | 3 天内存在成功入金,且赎回比例不低于 80% |
|
||||
| 金额要求 | 赎回金额不低于 500000 元 |
|
||||
| 风险等级 | 高 |
|
||||
| 核心证据 | 入金流水、赎回交易、金额、时间、赎回比例 |
|
||||
|
||||
### 5.3 RW-007 适当性错配
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 触发对象 | 申购交易 |
|
||||
| 条件 | 产品风险等级高于客户风险承受等级 |
|
||||
| 风险留痕 | 缺少风险揭示、二次确认或双录任一项即认为留痕不完整 |
|
||||
| 豁免额度 | C3 买 R4 的单只持仓占比超过 20%,或 C4 买 R5 超过 10% |
|
||||
| 风险等级 | 等级差不少于 2 时为高;等级差为 1 时为中 |
|
||||
| 核心证据 | 客户等级、产品等级、等级差、留痕、豁免比例和工单 |
|
||||
|
||||
### 5.4 RW-012 老年客户异常赎回
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 触发对象 | 赎回交易 |
|
||||
| 年龄要求 | 不低于 65 岁 |
|
||||
| 金额要求 | 赎回金额不低于 300000 元 |
|
||||
| 均值要求 | 赎回金额不低于近一年历史平均金额的 3 倍 |
|
||||
| 设备要求 | 交易前最近一次成功登录使用非常用设备 |
|
||||
| 风险等级 | 高 |
|
||||
| 核心证据 | 年龄、金额、历史均值、倍数、登录时间和设备 |
|
||||
|
||||
### 5.5 RW-015 夜间小额交易
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 触发时间 | 北京时间 00:00-05:59 |
|
||||
| 金额要求 | 不超过 10000 元 |
|
||||
| 风险等级 | 低 |
|
||||
| 定位 | 夜间小额行为筛查,不直接认定异常 |
|
||||
|
||||
### 5.6 RW-018 自动定投频繁交易筛查
|
||||
|
||||
| 项目 | 要求 |
|
||||
|---|---|
|
||||
| 触发对象 | 存在工单关联的交易 |
|
||||
| 条件 | 工单渠道为“定投”或“自动定投” |
|
||||
| 风险等级 | 低 |
|
||||
| 定位 | 识别可能由有效定投产生的误报候选 |
|
||||
|
||||
## 6. 功能需求
|
||||
|
||||
### FR-01 风险概览
|
||||
|
||||
- 展示当前未闭环预警总数,不是当天新增数量。
|
||||
- 展示高风险、中风险和低风险数量。
|
||||
- 展示待处理数量和已超时数量。
|
||||
- 返回最多 3 条重点高风险预警。
|
||||
|
||||
### FR-02 预警队列
|
||||
|
||||
- 默认只查询未闭环预警。
|
||||
- 支持按关键词、客户编号、产品、风险等级、规则和创建时间筛选。
|
||||
- 按高风险、中风险、低风险排序,同等级按创建时间倒序。
|
||||
- 预警队列每页固定最多 5 条。
|
||||
|
||||
### FR-03 预警详情
|
||||
|
||||
- 返回预警编号、客户编号、脱敏姓名、产品、规则、等级、状态和证据摘要。
|
||||
- 返回客户、交易、产品、工单、资金流水、持仓、登录记录和证据快照。
|
||||
- 资金流水、持仓和登录记录各有详情上限,超过时通过 `evidence_truncated` 标识。
|
||||
|
||||
### FR-04 手动规则扫描
|
||||
|
||||
- 需要 `risk:alert:scan` 权限。
|
||||
- 需要 `Idempotency-Key`。
|
||||
- 与定时扫描互斥,不能同时执行。
|
||||
- 返回新增预警数、高风险数、通知记录数和通知创建失败原因。
|
||||
|
||||
### FR-05 定时规则扫描
|
||||
|
||||
- 默认关闭。
|
||||
- 开启后按配置间隔扫描。
|
||||
- 多个进程同时运行时通过分布式锁避免并发重复扫描。
|
||||
- 当前使用独立进程 `python -m app.worker.risk_scan_scheduler`。
|
||||
- 正式接入主项目时,应注册到统一 Worker 或由部署编排统一启动。
|
||||
|
||||
### FR-06 人工处置
|
||||
|
||||
- 支持确认接收、进入调查、关闭误报、完成结案和升级处理。
|
||||
- 所有写操作需要 `risk:alert:write`。
|
||||
- 写操作需要幂等键,重复请求回放首次响应。
|
||||
- 状态不允许跳步,重复操作返回冲突错误。
|
||||
|
||||
### FR-07 行为分
|
||||
|
||||
- 结案时更新客户行为分。
|
||||
- 行为分与预警状态更新在同一事务内提交。
|
||||
- 审计记录应包含变更前分数、扣分和变更后分数。
|
||||
|
||||
### FR-08 证据归档
|
||||
|
||||
- 支持 JPG、JPEG、PNG、WEBP、PDF、DOC、DOCX、XLS、XLSX、PPT、PPTX 和 TXT。
|
||||
- 单个文件默认最大 10 MB。
|
||||
- 文件保存到 `RISK_EVIDENCE_DIR`。
|
||||
- 文件重命名为预警编号并增加原扩展名。
|
||||
- 一笔预警只能归档一个文件,成功或重复归档均需有明确结果。
|
||||
- 归档状态写入预警证据快照。
|
||||
|
||||
### FR-09 通知与预警邮件
|
||||
|
||||
- 高风险预警同时创建站内通知和邮件通知。
|
||||
- 手动扫描和定时扫描均自动触发高风险预警邮件。
|
||||
- 邮件发送成功写 `已发送`,失败写 `发送失败` 和失败原因。
|
||||
- 邮件发送失败不回滚预警和站内通知。
|
||||
- 当前高风险预警邮件只支持一个收件人。
|
||||
- 日报邮件支持最多 10 个收件人,与预警邮件配置独立。
|
||||
|
||||
### FR-10 风险日报
|
||||
|
||||
日报按北京时间和固定九段式模板生成:
|
||||
|
||||
1. 当日预警数量。
|
||||
2. 等级分布。
|
||||
3. 重点风险事件。
|
||||
4. 未闭环事项,包含全部历史未闭环预警。
|
||||
5. 误报统计。
|
||||
6. 类型分布。
|
||||
7. 处置结果。
|
||||
8. 规则效果。
|
||||
9. 建议优化方向。
|
||||
|
||||
- 日报统计数据来自数据库,模型只生成建议。
|
||||
- 模型不可用、返回空内容或输出不合规时使用规则化建议。
|
||||
- 日报生成写入审计。
|
||||
|
||||
### FR-11 日报流式生成与邮件
|
||||
|
||||
- 流式接口使用 SSE。
|
||||
- 事件类型包括 `start`、`progress`、`replace` 和 `done`。
|
||||
- 日报邮件需要 `risk:report:mail` 权限。
|
||||
- 日报邮件支持多个收件人和 dry-run。
|
||||
|
||||
### FR-12 奶龙风控智能助手
|
||||
|
||||
- 使用主项目 `agent_type=risk`。
|
||||
- 只允许查询风险概览、预警列表和指定预警证据。
|
||||
- 支持生成研判草案、沟通话术和工单摘要。
|
||||
- Agent 不能确认、调查、关闭、升级预警,不能修改客户和交易事实。
|
||||
- 客户姓名和敏感信息必须脱敏。
|
||||
|
||||
### FR-13 审计
|
||||
|
||||
以下动作必须写审计:
|
||||
|
||||
- 规则扫描生成预警。
|
||||
- 定时扫描成功或失败。
|
||||
- 确认、调查、误报关闭、完成结案和升级。
|
||||
- 证据归档。
|
||||
- 日报生成。
|
||||
- 权限拒绝和安全操作。
|
||||
|
||||
## 7. 数据读写边界
|
||||
|
||||
### 7.1 主要写入表
|
||||
|
||||
| 表 | 用途 |
|
||||
|---|---|
|
||||
| `fin_risk_alert` | 预警、状态、人工处置和证据快照 |
|
||||
| `fin_risk_notification` | 站内通知和邮件通知状态 |
|
||||
| `biz_work_order` | 风险处置和关联工单兼容 |
|
||||
| `interaction_audit` | 风控操作审计 |
|
||||
|
||||
### 7.2 只读数据源
|
||||
|
||||
| 数据 | 表 |
|
||||
|---|---|
|
||||
| 客户 | `sys_user`、`fin_customer_profile`、`fin_risk_assessment` |
|
||||
| 产品 | `fin_product` |
|
||||
| 交易 | `fin_transaction` |
|
||||
| 资金 | `fin_capital_flow` |
|
||||
| 持仓 | `fin_holding` |
|
||||
| 登录 | `sys_login_record` |
|
||||
| 工单 | `biz_work_order` |
|
||||
|
||||
风控模块不得修改交易、资金、持仓和产品事实。
|
||||
|
||||
## 8. 非功能要求
|
||||
|
||||
### 8.1 安全
|
||||
|
||||
- 所有接口必须经过主项目鉴权。
|
||||
- 所有业务查询必须执行数据范围校验。
|
||||
- Agent 工具必须执行工具白名单和权限校验。
|
||||
- 日志、错误和响应不得泄露密码、密钥和完整敏感信息。
|
||||
|
||||
### 8.2 幂等
|
||||
|
||||
- 扫描、确认、调查、关闭、结案、升级等写接口必须携带 `Idempotency-Key`。
|
||||
- 幂等键长度为 16-128 位 ASCII。
|
||||
- 同一键重复提交返回首次结果。
|
||||
- 同一键对应不同请求返回 `409 IDEMPOTENCY_CONFLICT`。
|
||||
|
||||
### 8.3 分页
|
||||
|
||||
- 预警列表每页最多 5 条。
|
||||
- 其他列表每页最多 10 条。
|
||||
- 使用游标分页,游标绑定用户、筛选条件和数据范围。
|
||||
- 不允许只把页码或 offset 暴露给客户端。
|
||||
|
||||
### 8.4 时区
|
||||
|
||||
- 数据库保存 UTC naive 时间。
|
||||
- 对外时间统一转换为 ISO 8601。
|
||||
- 日内判断、日报日期和夜间规则统一按 `Asia/Shanghai`。
|
||||
|
||||
### 8.5 降级
|
||||
|
||||
- 模型不可用时使用规则化建议。
|
||||
- 邮件失败只影响通知状态。
|
||||
- Redis 等外部依赖失败不得破坏核心预警查询和处置链路。
|
||||
|
||||
## 9. 配置要求
|
||||
|
||||
### 9.1 扫描
|
||||
|
||||
- `RISK_SCAN_SCHEDULE_ENABLED`
|
||||
- `RISK_SCAN_INTERVAL_MINUTES`
|
||||
- `RISK_SCAN_RUN_IMMEDIATELY`
|
||||
- `RISK_SCAN_RETRY_LIMIT`
|
||||
- `RISK_SCAN_POLL_SECONDS`
|
||||
|
||||
`RISK_SCAN_RUN_IMMEDIATELY` 当前仅保留兼容,不控制首次执行。
|
||||
|
||||
### 9.2 高风险预警邮件
|
||||
|
||||
- `RISK_ALERT_MAIL_ENABLED`
|
||||
- `RISK_ALERT_MAIL_DRY_RUN`
|
||||
- `RISK_ALERT_MAIL_RECIPIENTS`
|
||||
|
||||
当前只支持一个收件人。
|
||||
|
||||
### 9.3 SMTP
|
||||
|
||||
- `RISK_SMTP_HOST`
|
||||
- `RISK_SMTP_PORT`
|
||||
- `RISK_SMTP_USERNAME`
|
||||
- `RISK_SMTP_PASSWORD`
|
||||
- `RISK_SMTP_SENDER`
|
||||
- `RISK_SMTP_USE_SSL`
|
||||
- `RISK_SMTP_TIMEOUT_SECONDS`
|
||||
|
||||
### 9.4 证据
|
||||
|
||||
- `RISK_EVIDENCE_DIR`
|
||||
- `RISK_EVIDENCE_MAX_FILE_SIZE_MB`
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
- 未闭环预警、等级分布、待处理和超时统计正确。
|
||||
- 预警队列按风险等级排序,每页最多 5 条。
|
||||
- 五类规则按条件正确触发,不重复生成同一交易同规则预警。
|
||||
- 多规则命中同一交易时正确合并。
|
||||
- 人工处置状态机不允许非法跳转。
|
||||
- 结案后行为分正确扣减。
|
||||
- 八类证据可按数据范围查询。
|
||||
- 证据归档格式、大小和重复归档规则正确。
|
||||
- 高风险预警邮件状态能体现真实发送结果。
|
||||
- 日报九段式内容完整,历史未闭环预警不被分页截断。
|
||||
- Agent 只读边界和脱敏规则有效。
|
||||
- 关键操作均可在审计中追溯。
|
||||
|
||||
## 11. 当前限制与后续事项
|
||||
|
||||
- 高风险预警邮件当前只支持一个收件人。
|
||||
- 定时扫描当前仍需独立进程,主项目需提供统一 Worker 注册或部署编排。
|
||||
- 本期不支持对话历史长期归档和 Redis-only 会话方案。
|
||||
- 正式前端需要遵循 `17-风控模块-前端合并提示词与验收约束.md`。
|
||||
- 主项目合并前需要按 `21-主项目合并后后端必改清单.md` 完成接入。
|
||||
@@ -0,0 +1,851 @@
|
||||
# 风控模块接口文档
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| 文档版本 | v1.0 |
|
||||
| 编制日期 | 2026-09-13 |
|
||||
| 接口前缀 | `/api/v1/risk` |
|
||||
| Agent 接口 | `/api/v1/agent-runs` |
|
||||
| 适用对象 | 主项目后端、前端、联调和测试人员 |
|
||||
| 实现基线 | 当前 `RM2_develop` 风控模块代码 |
|
||||
|
||||
## 1. 通用约定
|
||||
|
||||
### 1.1 编码与鉴权
|
||||
|
||||
- 请求和响应使用 UTF-8。
|
||||
- JSON 请求使用 `Content-Type: application/json`。
|
||||
- 鉴权使用 `Authorization: Bearer <JWT>`。
|
||||
- 用户、角色、权限和客户数据范围由服务端实时解析,客户端不能提交这些字段。
|
||||
|
||||
### 1.2 成功响应信封
|
||||
|
||||
资源类响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
列表类响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [],
|
||||
"meta": {
|
||||
"trace_id": "trace-id",
|
||||
"next_cursor": "opaque-cursor-or-null",
|
||||
"has_more": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "AGENT_PERMISSION_DENIED",
|
||||
"message": "缺少操作权限",
|
||||
"trace_id": "trace-id",
|
||||
"retryable": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
常用错误:
|
||||
|
||||
| HTTP | 错误码 | 含义 |
|
||||
|---:|---|---|
|
||||
| 400 | `INVALID_CURSOR` | 游标无效或与当前查询不匹配 |
|
||||
| 401 | `AUTHENTICATION_REQUIRED` | 未登录或令牌无效 |
|
||||
| 403 | `AGENT_PERMISSION_DENIED` | 缺少权限或数据范围不允许 |
|
||||
| 404 | `SESSION_NOT_FOUND` | 资源不存在或不可见 |
|
||||
| 406 | `SSE_NOT_ACCEPTABLE` | SSE 接口未接受 `text/event-stream` |
|
||||
| 409 | `IDEMPOTENCY_CONFLICT` | 幂等键对应了不同请求 |
|
||||
| 409 | `RUN_NOT_CANCELLABLE` | 当前状态不允许执行操作 |
|
||||
| 413 | `AGENT_INPUT_INVALID` | 上传文件超过大小限制 |
|
||||
| 422 | `AGENT_INPUT_INVALID` | 请求字段或业务输入不合法 |
|
||||
| 429 | `RATE_LIMITED` | 请求超过限流 |
|
||||
| 503 | `DEPENDENCY_UNAVAILABLE` | 依赖服务不可用 |
|
||||
| 504 | `UPSTREAM_TIMEOUT` | 上游服务超时 |
|
||||
|
||||
### 1.4 幂等
|
||||
|
||||
除证据上传外,风控写接口必须携带:
|
||||
|
||||
```text
|
||||
Idempotency-Key: <16-128 位 ASCII 字符串>
|
||||
```
|
||||
|
||||
幂等范围为:
|
||||
|
||||
```text
|
||||
user_id + method + normalized_path + idempotency_key
|
||||
```
|
||||
|
||||
重复请求返回首次响应;同一键对应不同正文返回 `409 IDEMPOTENCY_CONFLICT`。
|
||||
|
||||
### 1.5 分页
|
||||
|
||||
- 预警队列:每页最多 5 条。
|
||||
- 其他列表:每页最多 10 条。
|
||||
- 游标绑定用户、筛选条件和数据范围。
|
||||
- 客户端应原样回传 `meta.next_cursor`。
|
||||
|
||||
### 1.6 时间与脱敏
|
||||
|
||||
- 请求时间按北京时间解释。
|
||||
- 响应时间为 ISO 8601 UTC 格式。
|
||||
- 客户姓名只返回脱敏值。
|
||||
- 手机号使用脱敏字段。
|
||||
- 不返回密码、密钥和完整敏感信息。
|
||||
|
||||
## 2. 权限
|
||||
|
||||
| 权限码 | 覆盖接口 |
|
||||
|---|---|
|
||||
| `risk:alert:read` | 概览、预警、详情、证据、通知、日报、Agent 只读工具 |
|
||||
| `risk:alert:write` | 确认、调查、误报关闭、完成结案、升级、证据归档 |
|
||||
| `risk:alert:scan` | 手动规则扫描 |
|
||||
| `risk:report:mail` | 日报邮件发送 |
|
||||
| `agent:run` | 创建奶龙风控智能助手运行 |
|
||||
|
||||
## 3. 接口总览
|
||||
|
||||
| 方法 | 路径 | 说明 | 权限 |
|
||||
|---|---|---|---|
|
||||
| GET | `/api/v1/risk/overview` | 风险概览 | `risk:alert:read` |
|
||||
| GET | `/api/v1/risk/alerts` | 预警队列 | `risk:alert:read` |
|
||||
| GET | `/api/v1/risk/alerts/{alert_no}` | 预警详情 | `risk:alert:read` |
|
||||
| POST | `/api/v1/risk/alerts/scan` | 手动规则扫描 | `risk:alert:scan` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/acknowledgements` | 确认接收 | `risk:alert:write` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/investigations` | 进入调查 | `risk:alert:write` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/exclusions` | 关闭误报 | `risk:alert:write` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/resolutions` | 完成结案 | `risk:alert:write` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/escalations` | 升级处理 | `risk:alert:write` |
|
||||
| POST | `/api/v1/risk/alerts/{alert_no}/evidence` | 上传并归档证据 | `risk:alert:write` |
|
||||
| GET | `/api/v1/risk/evidence/{source}` | 查询八类证据 | `risk:alert:read` |
|
||||
| GET | `/api/v1/risk/notifications` | 查询通知记录 | `risk:alert:read` |
|
||||
| POST | `/api/v1/risk/daily-report` | 生成结构化日报 | `risk:alert:read` |
|
||||
| POST | `/api/v1/risk/daily-report/stream` | 流式生成日报 | `risk:alert:read` |
|
||||
| POST | `/api/v1/risk/daily-report/mail` | 发送日报邮件 | `risk:report:mail` |
|
||||
| POST | `/api/v1/agent-runs` | 创建风控 Agent 运行 | `agent:run` + `risk:alert:read` |
|
||||
| GET | `/api/v1/agent-runs/{run_id}` | 查询 Agent 运行结果 | 运行所有者或数据范围 |
|
||||
| GET | `/api/v1/agent-runs/{run_id}/events` | 订阅 Agent SSE | 运行所有者或数据范围 |
|
||||
|
||||
## 4. 风险概览
|
||||
|
||||
### GET `/api/v1/risk/overview`
|
||||
|
||||
请求参数:无。
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"total": 12,
|
||||
"levels": {
|
||||
"高风险": 3,
|
||||
"中风险": 4,
|
||||
"低风险": 5
|
||||
},
|
||||
"pending": 7,
|
||||
"overdue": 2,
|
||||
"high_priority": []
|
||||
},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `total` | 当前未闭环预警总数,不是今日新增数 |
|
||||
| `levels` | 未闭环预警等级分布 |
|
||||
| `pending` | 状态为待处理的未闭环预警数 |
|
||||
| `overdue` | 已超过 `due_at` 的未闭环预警数 |
|
||||
| `high_priority` | 最多 3 条重点高风险预警 |
|
||||
|
||||
## 5. 预警队列
|
||||
|
||||
### GET `/api/v1/risk/alerts`
|
||||
|
||||
查询参数:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `keyword` | string | 否 | 预警编号、类型、摘要、客户或姓名关键词 |
|
||||
| `customer_no` | string | 否 | 业务客户编号 |
|
||||
| `product_code` | string | 否 | 产品代码 |
|
||||
| `product_name` | string | 否 | 产品名称模糊匹配 |
|
||||
| `risk_level` | string | 否 | 高、中、低 |
|
||||
| `rule_code` | string | 否 | `RW-003` 等规则代码 |
|
||||
| `start_time` | datetime | 否 | 创建时间起 |
|
||||
| `end_time` | datetime | 否 | 创建时间止 |
|
||||
| `cursor` | string | 否 | 分页游标 |
|
||||
| `limit` | integer | 否 | 1-5,默认 5 |
|
||||
|
||||
默认只返回未闭环预警,排序为高风险、中风险、低风险,同等级按创建时间倒序。
|
||||
|
||||
`data` 元素字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `alert_no` | 预警编号 |
|
||||
| `customer_id` | 客户内部 ID,字符串 |
|
||||
| `customer_no` | 业务客户编号 |
|
||||
| `customer_name` | 脱敏客户姓名 |
|
||||
| `product_code` | 产品代码 |
|
||||
| `product_name` | 产品名称 |
|
||||
| `alert_type` | 预警类型 |
|
||||
| `risk_level` | 风险等级 |
|
||||
| `rule_codes` | 命中规则数组 |
|
||||
| `evidence_summary` | 证据摘要 |
|
||||
| `evidence_snapshot` | 结构化证据快照 |
|
||||
| `priority_score` | 优先级分数 |
|
||||
| `event_status` | 事件状态 |
|
||||
| `status` | 处置状态 |
|
||||
| `ack_status` | 确认状态 |
|
||||
| `ack_at` | 确认时间 |
|
||||
| `due_at` | 处理时限 |
|
||||
| `is_escalated` | 是否升级 |
|
||||
| `escalated_at` | 升级时间 |
|
||||
| `evidence_archived` | 是否已归档证据 |
|
||||
| `close_reason` | 误报关闭原因 |
|
||||
| `created_at` | 创建时间 |
|
||||
| `updated_at` | 更新时间 |
|
||||
|
||||
## 6. 预警详情
|
||||
|
||||
### GET `/api/v1/risk/alerts/{alert_no}`
|
||||
|
||||
路径参数:
|
||||
|
||||
- `alert_no`:1-64 位字母、数字、下划线或连字符。
|
||||
|
||||
响应 `data` 顶层字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `alert` | 预警主体,字段与预警队列元素一致 |
|
||||
| `customer` | 客户脱敏信息 |
|
||||
| `transaction` | 关联交易原始只读字段 |
|
||||
| `product` | 关联产品原始只读字段 |
|
||||
| `work_order` | 关联工单原始只读字段 |
|
||||
| `capital_flows` | 资金流水列表 |
|
||||
| `holdings` | 持仓列表 |
|
||||
| `login_records` | 登录记录列表 |
|
||||
| `evidence_snapshot` | 扫规则形成的证据快照和归档信息 |
|
||||
| `evidence_truncated` | 被截断的证据类型数组 |
|
||||
|
||||
`customer` 字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `customer_id` | 业务客户编号 |
|
||||
| `customer_no` | 业务客户编号 |
|
||||
| `name` | 脱敏姓名 |
|
||||
| `birth_date` | 出生日期 |
|
||||
| `occupation` | 职业 |
|
||||
| `mobile_masked` | 脱敏手机号 |
|
||||
| `investor_type` | 风险承受等级 |
|
||||
| `investment_horizon` | 投资期限 |
|
||||
| `trading_frequency` | 交易频率 |
|
||||
| `total_asset` | 总资产 |
|
||||
| `behavior_score` | 行为分 |
|
||||
| `risk_tags` | 风险标签 |
|
||||
| `updated_at` | 更新时间 |
|
||||
|
||||
## 7. 手动规则扫描
|
||||
|
||||
### POST `/api/v1/risk/alerts/scan`
|
||||
|
||||
请求头:
|
||||
|
||||
```text
|
||||
Idempotency-Key: <必填>
|
||||
```
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"message": "规则扫描完成",
|
||||
"created_count": 5,
|
||||
"high_risk_count": 3,
|
||||
"notification_count": 6,
|
||||
"notification_failure": "可选,通知记录创建失败原因"
|
||||
},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 与定时扫描互斥。
|
||||
- 高风险预警会创建站内通知和邮件通知。
|
||||
- 邮件发送失败不回滚预警。
|
||||
- `notification_failure` 只代表通知记录创建失败。
|
||||
- 邮件真实状态通过通知记录查询。
|
||||
|
||||
## 8. 人工处置接口
|
||||
|
||||
以下接口均需要 `risk:alert:write` 和 `Idempotency-Key`。
|
||||
|
||||
### 8.1 确认接收
|
||||
|
||||
`POST /api/v1/risk/alerts/{alert_no}/acknowledgements`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
前置条件:状态为待处理,且尚未确认。
|
||||
|
||||
### 8.2 进入调查
|
||||
|
||||
`POST /api/v1/risk/alerts/{alert_no}/investigations`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
前置条件:已确认,且状态为待处理。
|
||||
|
||||
### 8.3 关闭误报
|
||||
|
||||
`POST /api/v1/risk/alerts/{alert_no}/exclusions`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"reason": "误报理由,1-500 字"
|
||||
}
|
||||
```
|
||||
|
||||
前置条件:已确认,且当前未闭环。
|
||||
|
||||
### 8.4 完成结案
|
||||
|
||||
`POST /api/v1/risk/alerts/{alert_no}/resolutions`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"resolution": "处置结论,1-500 字"
|
||||
}
|
||||
```
|
||||
|
||||
前置条件:已确认,且状态为调查中。
|
||||
|
||||
结案后按风险等级更新行为分。
|
||||
|
||||
### 8.5 升级处理
|
||||
|
||||
`POST /api/v1/risk/alerts/{alert_no}/escalations`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"reason": "升级理由,1-500 字"
|
||||
}
|
||||
```
|
||||
|
||||
前置条件:已确认、未闭环且尚未升级。
|
||||
|
||||
### 8.6 人工处置通用响应
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"alert_no": "ALERT-001",
|
||||
"status": "调查中",
|
||||
"ack_status": "已确认",
|
||||
"alert_level": "高",
|
||||
"handle_result": null,
|
||||
"closed_at": null
|
||||
},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
完成结案额外返回:
|
||||
|
||||
- `behavior_score_before`
|
||||
- `behavior_score_deduction`
|
||||
- `behavior_score_after`
|
||||
|
||||
升级处理额外返回:
|
||||
|
||||
- `is_escalated`
|
||||
- `escalated_at`
|
||||
- `escalation_reason`
|
||||
|
||||
## 9. 证据归档
|
||||
|
||||
### POST `/api/v1/risk/alerts/{alert_no}/evidence`
|
||||
|
||||
请求类型:`multipart/form-data`
|
||||
|
||||
表单字段:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `evidence_file` | file | 是 | 待归档文件 |
|
||||
|
||||
支持扩展名:
|
||||
|
||||
```text
|
||||
.jpg .jpeg .png .webp .pdf .doc .docx
|
||||
.xls .xlsx .ppt .pptx .txt
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- 单文件默认不超过 10 MB。
|
||||
- 只有已确认且状态为调查中的预警可以归档。
|
||||
- 一笔预警只能归档一次,不能覆盖。
|
||||
- 该接口不要求 `Idempotency-Key`,通过“一预警一文件”保证唯一性。
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"alert_no": "ALERT-001",
|
||||
"evidence_archived": true,
|
||||
"stored_name": "ALERT-001.pdf",
|
||||
"file_size": 102400
|
||||
},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 八类证据查询
|
||||
|
||||
### GET `/api/v1/risk/evidence/{source}`
|
||||
|
||||
`source` 可选值:
|
||||
|
||||
| source | 数据 |
|
||||
|---|---|
|
||||
| `customers` | 客户及行为分 |
|
||||
| `products` | 产品 |
|
||||
| `transactions` | 交易 |
|
||||
| `capital_flows` | 资金流水 |
|
||||
| `holdings` | 持仓 |
|
||||
| `login_records` | 登录记录 |
|
||||
| `alerts` | 全部预警,包括已闭环 |
|
||||
| `notifications` | 通知记录 |
|
||||
|
||||
通用查询参数:
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `keyword` | string | 关键词 |
|
||||
| `behavior_level` | string | 仅客户证据使用 |
|
||||
| `send_status` | string | 仅通知证据使用 |
|
||||
| `start_time` | datetime | 开始时间 |
|
||||
| `end_time` | datetime | 结束时间 |
|
||||
| `cursor` | string | 分页游标 |
|
||||
| `limit` | integer | 1-10,默认 10 |
|
||||
|
||||
### 10.1 customers
|
||||
|
||||
返回字段:
|
||||
|
||||
- `customer_id`
|
||||
- `customer_no`
|
||||
- `username`
|
||||
- `name`
|
||||
- `age`
|
||||
- `occupation`
|
||||
- `mobile_masked`
|
||||
- `total_asset`
|
||||
- `customer_tier`
|
||||
- `risk_level`
|
||||
- `risk_score`
|
||||
- `assessment_date`
|
||||
- `assessment_valid_until`
|
||||
- `assessment_expired`
|
||||
- `behavior_score`
|
||||
- `risk_tags`
|
||||
- `opened_at`
|
||||
- `status`
|
||||
|
||||
### 10.2 products
|
||||
|
||||
返回 `fin_product` 表字段,包含:
|
||||
|
||||
- `id`
|
||||
- `product_code`
|
||||
- `product_name`
|
||||
- `exchange_code`
|
||||
- `product_category`
|
||||
- `risk_level`
|
||||
- `currency`
|
||||
- `min_amount`
|
||||
- `status`
|
||||
|
||||
### 10.3 transactions
|
||||
|
||||
返回字段:
|
||||
|
||||
- `transaction_no`
|
||||
- `customer_no`
|
||||
- `product_code`
|
||||
- `product_name`
|
||||
- `transaction_type`
|
||||
- `amount`
|
||||
- `channel`
|
||||
- `trade_status`
|
||||
- `risk_disclosure_signed`
|
||||
- `second_confirmation`
|
||||
- `recording_id`
|
||||
- `work_order_no`
|
||||
- `confirmed_at`
|
||||
- `executed_at`
|
||||
|
||||
### 10.4 capital_flows
|
||||
|
||||
返回字段:
|
||||
|
||||
- `flow_no`
|
||||
- `customer_no`
|
||||
- `flow_type`
|
||||
- `amount`
|
||||
- `status`
|
||||
- `settled_at`
|
||||
- `occurred_at`
|
||||
- `source_type`
|
||||
- `match_status`
|
||||
|
||||
### 10.5 holdings
|
||||
|
||||
返回字段:
|
||||
|
||||
- `customer_no`
|
||||
- `product_code`
|
||||
- `product_name`
|
||||
- `shares`
|
||||
- `cost_amount`
|
||||
- `current_value`
|
||||
- `profit_loss`
|
||||
- `holding_days`
|
||||
- `holding_ratio`
|
||||
|
||||
### 10.6 login_records
|
||||
|
||||
返回字段:
|
||||
|
||||
- `id`
|
||||
- `customer_no`
|
||||
- `login_at`
|
||||
- `login_result`
|
||||
- `ip_region`
|
||||
- `device_id`
|
||||
- `is_common_device`
|
||||
- `failure_reason`
|
||||
|
||||
### 10.7 alerts
|
||||
|
||||
返回全部预警,包括未闭环和已闭环,字段与预警队列一致。
|
||||
|
||||
### 10.8 notifications
|
||||
|
||||
返回字段:
|
||||
|
||||
- `notification_id`
|
||||
- `notification_no`
|
||||
- `alert_no`
|
||||
- `customer_no`
|
||||
- `channel`
|
||||
- `title`
|
||||
- `send_status`
|
||||
- `receiver_email`
|
||||
- `send_time`
|
||||
|
||||
## 11. 通知记录
|
||||
|
||||
### GET `/api/v1/risk/notifications`
|
||||
|
||||
查询参数:
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `keyword` | string | 通知编号、预警编号、标题、状态或邮箱 |
|
||||
| `send_status` | string | 发送状态 |
|
||||
| `start_time` | datetime | 创建时间起 |
|
||||
| `end_time` | datetime | 创建时间止 |
|
||||
| `cursor` | string | 分页游标 |
|
||||
| `limit` | integer | 1-10,默认 10 |
|
||||
|
||||
发送状态:
|
||||
|
||||
| 状态 | 说明 |
|
||||
|---|---|
|
||||
| `待发送` | 已创建邮件记录,尚未完成发送 |
|
||||
| `已发送` | SMTP 发送成功 |
|
||||
| `发送失败` | SMTP 发送失败 |
|
||||
| `未启用` | 邮件功能未启用 |
|
||||
|
||||
## 12. 风险日报
|
||||
|
||||
### 12.1 生成结构化日报
|
||||
|
||||
`POST /api/v1/risk/daily-report`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"report_date": "2026-09-13"
|
||||
}
|
||||
```
|
||||
|
||||
`report_date` 可省略,省略时使用当前北京时间日期。
|
||||
|
||||
主要响应字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `type` | 日报类型 |
|
||||
| `report_date` | 报表日期 |
|
||||
| `generated_at` | 生成时间,北京时间文本 |
|
||||
| `data_truncated` | 是否因查询上限被截断 |
|
||||
| `daily_alert_count` | 当日预警数量 |
|
||||
| `level_distribution` | 等级分布 |
|
||||
| `key_risk_events` | 重点风险事件 |
|
||||
| `unresolved_items` | 所有历史未闭环事项 |
|
||||
| `false_positive_statistics` | 误报统计 |
|
||||
| `type_distribution` | 类型分布 |
|
||||
| `disposition_results` | 处置结果 |
|
||||
| `rule_effectiveness` | 规则效果 |
|
||||
| `optimization_suggestions` | 优化建议 |
|
||||
| `source` | `模型` 或 `规则模板` |
|
||||
| `prompt_version` | 提示词版本 |
|
||||
| `content` | 渲染后的九段式日报文本 |
|
||||
|
||||
### 12.2 流式生成日报
|
||||
|
||||
`POST /api/v1/risk/daily-report/stream`
|
||||
|
||||
请求头:
|
||||
|
||||
```text
|
||||
Accept: text/event-stream
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
请求体与结构化日报相同。
|
||||
|
||||
SSE 事件:
|
||||
|
||||
| 事件 | 说明 |
|
||||
|---|---|
|
||||
| `start` | 开始生成 |
|
||||
| `progress` | 阶段进度 |
|
||||
| `replace` | 替换当前日报正文 |
|
||||
| `done` | 最终结果,包含完整 `report` |
|
||||
|
||||
### 12.3 发送日报邮件
|
||||
|
||||
`POST /api/v1/risk/daily-report/mail`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"recipients": ["risk@example.com"],
|
||||
"subject": "风控日报",
|
||||
"content": "九段式日报正文"
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- 收件人 1-10 个。
|
||||
- 收件人自动去重并校验格式。
|
||||
- 主题最长 128 字。
|
||||
- 正文最长 20000 字。
|
||||
- 需要 `risk:report:mail`。
|
||||
|
||||
响应状态:
|
||||
|
||||
| 状态 | 说明 |
|
||||
|---|---|
|
||||
| `sent` | 已发送 |
|
||||
| `disabled` | 日报邮件未启用 |
|
||||
| `dry_run` | dry-run,未连接 SMTP |
|
||||
| `configuration_error` | SMTP 配置不完整 |
|
||||
|
||||
## 13. 奶龙风控智能助手
|
||||
|
||||
风控 Agent 不单独定义业务 Controller,统一使用主项目 Agent Run 接口。
|
||||
|
||||
### 13.1 创建运行
|
||||
|
||||
`POST /api/v1/agent-runs`
|
||||
|
||||
请求体:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_type": "risk",
|
||||
"message": "查询当前高风险预警",
|
||||
"session_id": "session-uuid",
|
||||
"idempotency_key": "16-128位ASCII字符串"
|
||||
}
|
||||
```
|
||||
|
||||
成功返回 `202`:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"run_id": "run-uuid",
|
||||
"trace_id": "trace-id",
|
||||
"status": "queued",
|
||||
"status_url": "/api/v1/agent-runs/run-uuid",
|
||||
"events_url": "/api/v1/agent-runs/run-uuid/events"
|
||||
},
|
||||
"meta": {
|
||||
"trace_id": "trace-id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 13.2 查询运行
|
||||
|
||||
`GET /api/v1/agent-runs/{run_id}`
|
||||
|
||||
主要字段:
|
||||
|
||||
- `run_id`
|
||||
- `trace_id`
|
||||
- `status`
|
||||
- `agent_type`
|
||||
- `session_id`
|
||||
- `result`
|
||||
- `error_code`
|
||||
- `created_at`
|
||||
- `completed_at`
|
||||
|
||||
### 13.3 订阅 SSE
|
||||
|
||||
`GET /api/v1/agent-runs/{run_id}/events`
|
||||
|
||||
请求头:
|
||||
|
||||
```text
|
||||
Accept: text/event-stream
|
||||
```
|
||||
|
||||
事件类型:
|
||||
|
||||
- `start`
|
||||
- `tools`
|
||||
- `delta`
|
||||
- `replace`
|
||||
- `done`
|
||||
- `error`
|
||||
|
||||
### 13.4 Agent 工具边界
|
||||
|
||||
允许工具:
|
||||
|
||||
| 工具 | 用途 |
|
||||
|---|---|
|
||||
| `get_risk_overview` | 查询风险概览 |
|
||||
| `search_risk_alerts` | 查询预警列表 |
|
||||
| `get_alert_evidence` | 查询指定预警证据 |
|
||||
|
||||
禁止能力:
|
||||
|
||||
- 确认、调查、关闭、解决或升级预警。
|
||||
- 修改客户、交易、资金、持仓和产品事实。
|
||||
- 绕过权限和数据范围。
|
||||
- 在未取得完整数据时声称已经覆盖全部数据。
|
||||
|
||||
## 14. 状态和枚举
|
||||
|
||||
### 14.1 预警等级
|
||||
|
||||
| 值 | 展示 |
|
||||
|---|---|
|
||||
| 高 | 高风险 |
|
||||
| 中 | 中风险 |
|
||||
| 低 | 低风险 |
|
||||
|
||||
### 14.2 预警状态
|
||||
|
||||
| 值 | 是否未闭环 |
|
||||
|---|---|
|
||||
| 待处理 | 是 |
|
||||
| 调查中 | 是 |
|
||||
| 已排除 | 否 |
|
||||
| 已结案 | 否 |
|
||||
|
||||
### 14.3 确认状态
|
||||
|
||||
- `未确认`
|
||||
- `已确认`
|
||||
|
||||
### 14.4 通知渠道
|
||||
|
||||
- `站内提醒`
|
||||
- `邮件`
|
||||
|
||||
### 14.5 行为分筛选
|
||||
|
||||
| 值 | 范围 |
|
||||
|---|---|
|
||||
| `normal` | 16-20 |
|
||||
| `slight` | 11-15 |
|
||||
| `attention` | 6-10 |
|
||||
| `high` | 1-5 |
|
||||
| `immediate` | 0 |
|
||||
|
||||
## 15. 主项目接入注意事项
|
||||
|
||||
- 路由需要通过主项目统一注册 `/api/v1/risk`。
|
||||
- 鉴权、数据范围、限流、幂等、错误信封和 SSE 协商必须复用主项目公共能力。
|
||||
- 风控查询必须携带客户端 `data_scope` 和客户归属,不能只依赖前端过滤。
|
||||
- 手动扫描需要 `Idempotency-Key`。
|
||||
- 证据上传使用 `multipart/form-data`,不要求幂等键。
|
||||
- 高风险预警邮件由后端配置控制,当前只支持一个收件人。
|
||||
- 高风险预警邮件状态通过通知接口查询,不新增独立邮件状态接口。
|
||||
- 定时扫描不是 HTTP 接口,正式接入需要统一 Worker 或部署编排。
|
||||
- 日报流式接口必须使用 SSE,并使用 `Accept: text/event-stream`。
|
||||
- Agent 必须使用主项目 `/api/v1/agent-runs`,不能新增风控专用 Agent Controller。
|
||||
@@ -37,11 +37,13 @@
|
||||
| 14 | 审计与追溯映射 | 说明业务动作、工具调用和运行审计 |
|
||||
| 15 | 模块验收与演示清单 | 说明演示前置条件和验收步骤 |
|
||||
| 16 | 已知限制与待办 | 说明当前限制、暂缓项和外部依赖 |
|
||||
| 17 | 前端合并提示词与验收约束 | 指导后续模型识别风控功能并合并前端 |
|
||||
| 17 | 风控模块-前端合并提示词与验收约束 | 指导后续模型识别风控功能并合并前端 |
|
||||
| 18 | 当前项目完成进度 | 汇总当前完成度、验证结果和剩余任务 |
|
||||
| 19 | 风控模块配置项清单 | 单独说明风控专用和公共依赖配置 |
|
||||
| 20 | Agent工具白名单与意图配置 | 交付主项目方的工具和意图配置 |
|
||||
| 21 | 主项目合并后后端必改清单 | 列出合并后必须处理的后端事项 |
|
||||
| 22 | 风控模块需求说明书 | 统一业务需求、规则、边界和验收口径 |
|
||||
| 23 | 风控模块接口文档 | 提供 REST、SSE、字段和联调约定 |
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
@@ -55,3 +57,4 @@
|
||||
8. 19:合并部署和联调时核对环境变量。
|
||||
9. 20:主项目方导入 Agent 工具白名单和意图配置。
|
||||
10. 21:主项目代码合并后的后端整改与验收。
|
||||
11. 22、23:分别作为需求交付和接口联调的统一入口。
|
||||
|
||||
Reference in New Issue
Block a user