551 lines
25 KiB
Markdown
551 lines
25 KiB
Markdown
# 外部系统风险评估报告接口
|
||
|
||
## 1. 文档说明
|
||
|
||
本文档定义外部业务系统调用蝶雅风险预警平台生成新品风险评估 Markdown 报告的接口协议。
|
||
|
||
> 外部接口代码已实现。启用前需执行数据库迁移、配置系统调用密钥并开通本地钉钉用户;测试和正式地址由部署方提供。本接口与内部 `POST /api/v1/risk-predictions/preview` 分离,不依赖内部用户 JWT 或聊天会话。
|
||
|
||
### 1.1 接口用途
|
||
|
||
外部系统提交产品、包装、供应商和开发阶段等风险评估参数,蝶雅平台完成以下处理:
|
||
|
||
1. 校验调用方系统身份和请求签名。
|
||
2. 根据钉钉用户标识,或管理员预先批准的外部账号映射,匹配蝶雅平台用户。
|
||
3. 使用蝶雅平台风险数据库、标准库和评分模型完成风险评估。
|
||
4. 生成 Markdown 格式的风险评估报告并直接返回。
|
||
|
||
### 1.2 当前数据边界
|
||
|
||
外部系统与蝶雅平台使用不同数据库时,蝶雅平台默认只使用自己的历史异常案例、采购订单、供应商主数据和标准规则库。外部系统数据库中的历史数据不会自动参与评分;如需使用,双方另行约定数据同步或 `externalEvidence` 扩展协议。
|
||
|
||
## 2. 接口信息
|
||
|
||
| 项目 | 内容 |
|
||
|---|---|
|
||
| 请求方式 | `POST` |
|
||
| 请求路径 | `/api/v1/integrations/risk-assessments/report` |
|
||
| 请求类型 | `application/json` |
|
||
| 成功响应类型 | `text/markdown; charset=utf-8` |
|
||
| 字符编码 | UTF-8 |
|
||
| 接口超时时间建议 | 120 秒 |
|
||
| 幂等依据 | `sourceSystem + requestId`;`Idempotency-Key` 必须与 `requestId` 一致 |
|
||
|
||
生产环境完整地址由蝶雅平台提供,例如:
|
||
|
||
```text
|
||
https://risk.example.com/api/v1/integrations/risk-assessments/report
|
||
```
|
||
|
||
## 3. 调用鉴权
|
||
|
||
接口使用“调用方系统身份 + HMAC-SHA256 签名”鉴权。外部系统不使用蝶雅平台用户 JWT,也不直接传入蝶雅平台本地 `userId`。
|
||
|
||
### 3.1 请求头
|
||
|
||
| 请求头 | 必填 | 说明 |
|
||
|---|---:|---|
|
||
| `Content-Type` | 是 | 固定为 `application/json` |
|
||
| `X-Client-Id` | 是 | 蝶雅平台分配的调用方系统编号,例如 `dieya-plm` |
|
||
| `X-Timestamp` | 是 | Unix 时间戳,单位为秒 |
|
||
| `X-Nonce` | 是 | 8~64 位随机字符串,仅允许字母、数字、下划线、短横线,单次请求唯一 |
|
||
| `X-Signature` | 是 | HMAC-SHA256 签名,固定使用标准 Base64 编码(带填充 `=`) |
|
||
| `Idempotency-Key` | 是 | 与请求体中的 `requestId` 保持一致 |
|
||
|
||
### 3.2 签名计算
|
||
|
||
蝶雅平台为每个 `X-Client-Id` 分配独立密钥 `clientSecret`。POST 调用使用以下原文计算签名:
|
||
|
||
```text
|
||
X-Timestamp + "\n" + X-Nonce + "\n" + 原始请求Body
|
||
```
|
||
|
||
伪代码:
|
||
|
||
```python
|
||
message = f"{timestamp}\n{nonce}\n{raw_body}"
|
||
signature = base64.b64encode(
|
||
hmac.new(client_secret.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).digest()
|
||
).decode("ascii")
|
||
```
|
||
|
||
注意事项:
|
||
|
||
- 签名必须基于发送时的原始 JSON 字节内容计算,不能先格式化后再计算。
|
||
- 服务端默认只接受时间误差不超过 5 分钟的请求。
|
||
- 已认证请求的 `X-Nonce` 在数据库中保留 10 分钟,跨进程和重启后仍拒绝重复使用;正常 JSON 的字段校验失败也会消耗 Nonce。
|
||
- `clientSecret` 只通过双方约定的安全渠道保存,不放在前端代码、日志或请求体中。
|
||
- 生产环境建议叠加 IP 白名单;高安全场景可使用 mTLS。
|
||
|
||
### 3.3 用户身份映射
|
||
|
||
请求中的用户身份使用钉钉标识:
|
||
|
||
- `dingtalkUnionId`:首选跨系统关联标识。
|
||
- `dingtalkUserId`:钉钉用户 ID,作为辅助校验和追踪字段。
|
||
- `employeeNo`、`name`:展示和审计信息,不能单独作为身份依据。
|
||
|
||
蝶雅平台按以下顺序处理:
|
||
|
||
1. 按 `dingtalkUnionId` 查找本地用户。
|
||
2. 必要时结合 `dingtalkUserId` 校验用户身份。
|
||
3. 用户不存在、停用或 UnionId 匹配到多个本地用户时返回 `403`,不自动创建账号。
|
||
4. 评分、审计和报告记录使用蝶雅平台本地用户 ID;该 ID 不返回给外部系统作为跨库主键。
|
||
|
||
用户需要拥有本地 `risk.assess` 操作权限(管理员直接允许)。如传入 `dingtalkUserId`,必须与本地用户一致。报告中的姓名使用本地用户资料,不使用调用方提交的姓名作为认证或显示依据。查询报告时再次校验原操作人的账号状态及权限。
|
||
|
||
### 3.4 非钉钉操作人
|
||
|
||
没有钉钉身份的用户可使用 `operator.externalUserId`,前提是风险平台管理员已为当前调用系统批准映射。两种身份模式必须且只能选一种;外部账号模式不传 `dingtalkUnionId`、`dingtalkUserId`,可以省略或传 `null`。不允许按姓名、员工编号自动匹配用户。
|
||
|
||
例如 PLM 账号 `superadmin`(周子墨)的操作人参数为:
|
||
|
||
```json
|
||
{
|
||
"externalUserId": "superadmin",
|
||
"name": "周子墨"
|
||
}
|
||
```
|
||
|
||
这个对象替换请求中的整个 `operator`,其他字段及系统签名要求不变。PLM 后端必须从当前已登录用户取出该账号标识,不能让前端任意指定别人的 `externalUserId`。平台通过 `X-Client-Id + externalUserId` 查映射并检查本地账号及权限,未批准映射返回 `403`。外部系统的管理员角色不授予风险平台管理员权限。
|
||
|
||
GET 查询时也会重新校验记录中的外部账号映射;撤销映射或停用本地账号后禁止查询。
|
||
|
||
如果两个钉钉应用返回的 UnionId 不一致,双方需要先确定统一的钉钉身份源或增加统一身份映射表。
|
||
|
||
## 4. 请求参数
|
||
|
||
### 4.1 请求体
|
||
|
||
```json
|
||
{
|
||
"requestId": "PLM-D-FT-003-0001",
|
||
"sourceSystem": "dieya-plm",
|
||
"operator": {
|
||
"dingtalkUnionId": "ding_union_xxx",
|
||
"dingtalkUserId": "ding_user_xxx",
|
||
"employeeNo": "A10001",
|
||
"name": "张三"
|
||
},
|
||
"assessment": {
|
||
"subjectId": "SKU-20261007-001",
|
||
"productName": "新品洗发水",
|
||
"productCategory": "洗发水/洗护类",
|
||
"specification": "500ml",
|
||
"materialType": "PET瓶/泵头",
|
||
"supplier": "某某供应商",
|
||
"factory": "某某加工厂",
|
||
"stageNew": "产品开发",
|
||
"defectLevel": null,
|
||
"description": "新品上市前风险评估",
|
||
"asOfDate": "2026-10-07"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 顶层字段
|
||
|
||
| 字段 | 类型 | 必填 | 说明 | 校验建议 |
|
||
|---|---|---:|---|---|
|
||
| `requestId` | string | 是 | 调用方请求唯一编号,用于幂等 | 1~128 个字符,仅允许字母、数字、`_`、`.`、`:`、`-` |
|
||
| `sourceSystem` | string | 是 | 来源系统编号 | 必须是已登记的 `X-Client-Id` |
|
||
| `operator` | object | 是 | 发起评估的钉钉用户 | 见下表 |
|
||
| `assessment` | object | 是 | 风险评估数据 | 见下表 |
|
||
|
||
### 4.3 `operator` 字段
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---:|---|
|
||
| `dingtalkUnionId` | string/null | 条件必填 | 钉钉用户模式必填,与 `externalUserId` 二选一 |
|
||
| `externalUserId` | string/null | 条件必填 | 非钉钉用户的调用方稳定账号标识,1~128 字符,必须在当前调用系统下预先批准映射 |
|
||
| `dingtalkUserId` | string | 建议 | 钉钉用户 ID |
|
||
| `employeeNo` | string | 否 | 员工编号,用于审计展示 |
|
||
| `name` | string | 否 | 用户姓名,用于报告展示 |
|
||
|
||
### 4.4 `assessment` 字段
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---:|---|
|
||
| `subjectId` | string | 是 | 产品、SKU 或评估对象编号 |
|
||
| `productName` | string | 否 | 产品名称 |
|
||
| `productCategory` | string | 是 | 按当前风险评分的八类传值,完整清单及兼容规则见 4.6 |
|
||
| `specification` | string | 否 | 规格或容量,例如 `500ml`、`100g` |
|
||
| `materialType` | string | 是 | 包装材质或物料组合,例如 `PET瓶/泵头` |
|
||
| `supplier` | string | 否 | 供应商名称 |
|
||
| `factory` | string | 否 | 加工厂名称 |
|
||
| `stageNew` | string | 否 | 开发阶段 |
|
||
| `defectLevel` | string/null | 否 | 缺陷等级,可取 `A`、`B`、`C` |
|
||
| `description` | string | 否 | 评估背景或补充说明 |
|
||
| `asOfDate` | string/null | 否 | 评估数据截止日期,格式 `YYYY-MM-DD`,不早于 1900-01-01、不允许未来日期;省略时使用首次受理的上海日期 |
|
||
|
||
`stageNew` 建议使用以下枚举值:
|
||
|
||
```text
|
||
需求发现
|
||
产品立项
|
||
产品开发
|
||
量产上市
|
||
产品生命管理
|
||
```
|
||
|
||
### 4.5 字段约束
|
||
|
||
- `productCategory`、`materialType`、`subjectId` 缺失时,接口返回 `422`。
|
||
- `description` 仅作为评估背景,不直接作为已确认的检测结果或历史事实。
|
||
- 调用方不要提交最终风险分数、风险等级或模型版本;这些字段由蝶雅平台统一计算。
|
||
- 当前版本不接收外部数据库连接信息、SQL、文件路径或可执行脚本。
|
||
- 可选字段允许省略或传 `null`。`stageNew`、`defectLevel` 非空时必须符合枚举;未知字段(包括调用方提交的分数、角色)返回 `422`。
|
||
- 参数长度:`subjectId` 128、`productName` 200、`productCategory` 100、`specification` 100、`materialType` 200、`supplier/factory` 300、`description` 2000;`dingtalkUnionId` 64、`dingtalkUserId` 50、`employeeNo` 64、`name` 50。请求体最大 64KB。
|
||
|
||
### 4.6 产品品类字典(当前评分口径)
|
||
|
||
本次 PLM 对接采用现有七维风险评分服务 `normalize_category()` 的八类口径。以下为当前评分分类清单,并非数据库字典接口的返回结果。
|
||
|
||
PLM 优先将产品品类映射成下表的标准值后提交 `assessment.productCategory`,并保存映射关系。无法确定具体类别时,不应仅凭 PLM 的宽泛业务大类强行映射。
|
||
|
||
| 标准值(建议提交值) | 当前兼容识别关键词 |
|
||
|---|---|
|
||
| `洗发水/洗护类` | 洗发、洗护、洗发水、洗发露、洗发乳 |
|
||
| `沐浴露类` | 沐浴、沐浴露 |
|
||
| `面霜/护肤类` | 面霜、乳液、精华、面膜、护肤 |
|
||
| `牙膏/口腔护理类` | 牙膏、口腔 |
|
||
| `清洁剂类` | 清洁剂、清洁、洗洁 |
|
||
| `脱毛类` | 脱毛 |
|
||
| `彩妆/唇部类` | 彩妆、口红、粉底、眼影 |
|
||
| `香氛/止汗类` | 香氛、香水、止汗 |
|
||
|
||
匹配规则及评分边界:
|
||
|
||
- 当前按上表顺序进行关键词子串匹配,多个类别同时命中时取第一类。
|
||
- 空值仍按本接口必填规则返回 `422`。非空值无法识别时,按现有评分逻辑将“产品品类”维度设为默认 50 分,并在报告中注明无法归类;这不代表产品风险已经由历史数据验证。
|
||
- 已识别品类的历史异常案例少于 10 条时,该维度按 45 分处理;其余情况按现有异常数量归一化规则计算。
|
||
- 当前 `防晒`、`洁面`、`护发素` 等名称本身没有专门的评分匹配规则;包含其他兼容关键词的完整名称仍按现有规则归类,例如“防晒乳液”命中“乳液”。
|
||
- `个护清洁` 会命中“清洁”并归为“清洁剂类”,不能将该 PLM 大类整体作为防晒等产品的评分分类。
|
||
- 报告应同时展示调用方提交的品类和实际评分归类;无法识别时注明默认分及原因。
|
||
|
||
历史案例的品类维度统计优先归一化案例的 `product_category`,无法识别时再尝试案例的 `product_name`。本对接口径采用评分规则;看板中的“身体护理类”“洁面/卸妆类”“头发护理类”“包材/物料专项”“待补标品类”不属于当前八类评分字典。
|
||
|
||
## 5. 成功响应
|
||
|
||
### 5.1 响应头
|
||
|
||
```http
|
||
HTTP/1.1 200 OK
|
||
Content-Type: text/markdown; charset=utf-8
|
||
X-Request-Id: PLM-D-FT-003-0001
|
||
X-Assessment-Id: 10086
|
||
X-Model-Version: database-seven-dimension-v1
|
||
X-Risk-Score: 72.50
|
||
X-Risk-Level: moderately_low
|
||
```
|
||
|
||
### 5.2 响应体
|
||
|
||
响应体直接为 Markdown 文本,不再包裹 `data`、`code` 等 JSON 字段:
|
||
|
||
```markdown
|
||
# 新品风险评估报告
|
||
|
||
## 一、评估信息
|
||
|
||
| 项目 | 内容 |
|
||
|---|---|
|
||
| 评估对象 | 新品洗发水 |
|
||
| 对象编号 | SKU-20261007-001 |
|
||
| 产品品类 | 洗发水/洗护类 |
|
||
| 规格 | 500ml |
|
||
| 包装材质 | PET瓶/泵头 |
|
||
| 供应商 | 某某供应商 |
|
||
| 加工厂 | 某某加工厂 |
|
||
| 开发阶段 | 产品开发 |
|
||
| 评估人 | 张三 |
|
||
| 评估时间 | 2026-10-07 |
|
||
|
||
## 二、综合风险结论
|
||
|
||
- 综合得分:72.50
|
||
- 风险等级:较低风险
|
||
- 模型版本:database-seven-dimension-v1
|
||
|
||
## 三、七维风险评分
|
||
|
||
| 风险维度 | 得分 | 权重 | 数据状态 | 说明 |
|
||
|---|---:|---:|---|---|
|
||
| 供应商表现 | 80.00 | 25% | database | 近三年采购订单和异常案例统计 |
|
||
| 物料品类 | 75.00 | 20% | database | 包装材质历史异常统计 |
|
||
| 品类历史 | 70.00 | 15% | database | 产品品类历史案例统计 |
|
||
|
||
## 四、相似异常案例
|
||
|
||
暂无直接匹配案例,建议结合相似品类进行验证。
|
||
|
||
## 五、标准规则
|
||
|
||
| 风险点 | 判定规则 | 来源 | 状态 |
|
||
|---|---|---|---|
|
||
| 密封性 | 按标准要求完成密封性验证 | 标准文件.xlsx | 待检测 |
|
||
|
||
## 六、风险预防建议
|
||
|
||
1. 打样阶段完成内容物与包装材料相容性测试。
|
||
2. 增加密封性和加速老化测试。
|
||
3. 首单加强来料检验和过程巡检。
|
||
|
||
## 七、数据口径说明
|
||
|
||
本报告基于蝶雅风险平台本地数据库中的历史案例、采购订单和标准库生成。
|
||
部分维度无实际数据时,按系统配置的中性值计算。
|
||
```
|
||
|
||
报告中的分数是安全得分,分数越高代表风险越低。风险等级阈值以蝶雅平台当前模型配置为准,调用方不要自行重新计算等级。
|
||
|
||
`X-Risk-Score` 为 0~100 的安全分,保留两位小数。`X-Risk-Level` 为 `low`(低风险)、`moderately_low`(较低风险)、`medium`(中等风险)、`high`(高风险)、`very_high`(极高风险)。`X-Assessment-Id` 按字符串存储。GET 和幂等重试返回原报告及相同结论响应头。
|
||
|
||
## 6. 幂等规则
|
||
|
||
`requestId` 是调用方生成的业务请求唯一编号,同一个 `sourceSystem + requestId` 只能对应一次评估请求。
|
||
|
||
### 首次请求
|
||
|
||
返回 `200` 和 Markdown 报告。
|
||
|
||
### 相同请求重复提交
|
||
|
||
如果请求体和原请求一致,服务端返回同一份已生成报告,仍返回 `200`,并在响应头中带回原始 `X-Assessment-Id`。
|
||
|
||
### 同一编号提交不同内容
|
||
|
||
如果 `requestId` 已经使用,但请求内容摘要不同,返回 `409`,避免覆盖历史报告。
|
||
|
||
### 处理中、失败及重试
|
||
|
||
- 幂等比较基于校验后的 JSON 对象摘要,忽略 JSON 键顺序和排版,省略的可选字段与 `null` 等价;变更任意有效字段视为不同请求。
|
||
- 重试保留原请求体(包括操作人和 `asOfDate`),重新生成时间戳、Nonce 和签名。主动重新评估必须使用新 `requestId`。
|
||
- 同一请求仍在生成时,POST 或 GET 返回 `202` JSON 和 `Retry-After: 5`,不会重复计算。
|
||
- 每次生成最多 110 秒(可配置为 10~110 秒),超时或失败记录为 failed,返回 `503`;相同请求内容可以重新 POST 重试。生成权租约为 150 秒,进程异常退出后租约到期可重新 POST 接管。
|
||
- 调用方网络超时并不代表服务端已经停止。应先 GET 查询,再决定是否使用原请求重试。
|
||
- 省略 `asOfDate` 时首次受理日期单独持久化,跨天重试仍沿用首次日期。
|
||
|
||
处理中响应示例:
|
||
|
||
```json
|
||
{"code":202,"message":"报告正在生成,请稍后查询","data":{"requestId":"PLM-D-FT-003-0001"}}
|
||
```
|
||
|
||
### 报告查询
|
||
|
||
```http
|
||
GET /api/v1/integrations/risk-assessments/report?requestId=PLM-D-FT-003-0001
|
||
```
|
||
|
||
查询参数必须且只能包含一个 `requestId`;GET 不携带 Body,也不要求 `Idempotency-Key`。仅能查询与 `X-Client-Id` 相同来源的记录。成功返回 `200` Markdown,处理中返回 `202`,失败返回 `503`,不存在返回 `404`。
|
||
|
||
GET 签名原文为:
|
||
|
||
```text
|
||
"GET" + "\n" + 请求路径 + "\n" + 原始查询字符串 + "\n" + X-Timestamp + "\n" + X-Nonce + "\n"
|
||
```
|
||
|
||
请求路径包含 `/api/v1`,不含域名、查询字符串;原始查询字符串不含 `?`,包括实际发送的 URL 编码,签名后不得重新编码。网关不能重写对外接口路径。以 UTF-8 字节计算 HMAC-SHA256,再以标准 Base64 编码。报告持久化保存,当前未启用自动清理策略。
|
||
|
||
## 7. 错误响应
|
||
|
||
错误响应统一使用 JSON:
|
||
|
||
```json
|
||
{
|
||
"code": 422,
|
||
"message": "产品品类不能为空",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
| HTTP 状态码 | `code` | 场景 | 调用方处理建议 |
|
||
|---:|---:|---|---|
|
||
| `400` | `400` | 请求格式错误或签名原文不合法 | 修正请求格式后重试 |
|
||
| `401` | `401` | 缺少签名、签名错误、时间戳过期 | 检查系统密钥和签名算法 |
|
||
| `403` | `403` | 调用方未开通接口或用户无风险评估权限 | 联系平台管理员开通权限 |
|
||
| `404` | `404` | 当前调用系统未找到该请求 | 核对 requestId 和调用系统 |
|
||
| `409` | `409` | `requestId` 重复但内容不一致 | 更换请求编号或查询原请求 |
|
||
| `422` | `422` | 参数缺失或字段值不符合约束 | 修正参数后重试 |
|
||
| `429` | `429` | 超过调用频率限制 | 按 `Retry-After` 延迟重试 |
|
||
| `500` | `500` | 平台内部处理异常 | 使用同一 `requestId` 重试并联系平台方 |
|
||
| `503` | `503` | 风险数据库、模型或依赖服务暂不可用 | 稍后重试,不要生成新的业务编号 |
|
||
|
||
## 8. 调用示例
|
||
|
||
### 8.1 cURL
|
||
|
||
```bash
|
||
curl -X POST 'https://risk.example.com/api/v1/integrations/risk-assessments/report' \
|
||
-H 'Content-Type: application/json' \
|
||
-H 'X-Client-Id: dieya-plm' \
|
||
-H 'X-Timestamp: 1791331200' \
|
||
-H 'X-Nonce: 8f3d4c1a9e' \
|
||
-H 'X-Signature: Base64HmacSha256Value' \
|
||
-H 'Idempotency-Key: PLM-D-FT-003-0001' \
|
||
--data-raw '{
|
||
"requestId": "PLM-D-FT-003-0001",
|
||
"sourceSystem": "dieya-plm",
|
||
"operator": {
|
||
"dingtalkUnionId": "ding_union_xxx",
|
||
"dingtalkUserId": "ding_user_xxx",
|
||
"employeeNo": "A10001",
|
||
"name": "张三"
|
||
},
|
||
"assessment": {
|
||
"subjectId": "SKU-20261007-001",
|
||
"productName": "新品洗发水",
|
||
"productCategory": "洗发水/洗护类",
|
||
"specification": "500ml",
|
||
"materialType": "PET瓶/泵头",
|
||
"supplier": "某某供应商",
|
||
"factory": "某某加工厂",
|
||
"stageNew": "产品开发",
|
||
"description": "新品上市前风险评估",
|
||
"asOfDate": "2026-10-07"
|
||
}
|
||
}' \
|
||
--output risk-report.md
|
||
```
|
||
|
||
### 8.2 Python 签名示例
|
||
|
||
```python
|
||
import base64
|
||
import hashlib
|
||
import hmac
|
||
import json
|
||
import secrets
|
||
import time
|
||
|
||
body = {
|
||
"requestId": "PLM-D-FT-003-0001",
|
||
"sourceSystem": "dieya-plm",
|
||
"operator": {"dingtalkUnionId": "ding_union_xxx"},
|
||
"assessment": {
|
||
"subjectId": "SKU-20261007-001",
|
||
"productCategory": "洗发水/洗护类",
|
||
"materialType": "PET瓶/泵头",
|
||
},
|
||
}
|
||
raw_body = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
||
timestamp = str(int(time.time()))
|
||
nonce = secrets.token_urlsafe(16)
|
||
message = timestamp.encode() + b"\n" + nonce.encode() + b"\n" + raw_body
|
||
signature = base64.b64encode(
|
||
hmac.new(client_secret.encode(), message, hashlib.sha256).digest()
|
||
).decode()
|
||
```
|
||
|
||
调用方必须使用计算签名时的同一份 `raw_body` 发送请求。
|
||
|
||
## 9. 评分和报告处理说明
|
||
|
||
平台内部处理逻辑建议统一复用现有七维风险评分服务:
|
||
|
||
- 供应商表现
|
||
- 物料品类
|
||
- 品类历史
|
||
- 加工厂质量
|
||
- 标准符合性
|
||
- 节点阶段
|
||
- 缺陷严重度
|
||
|
||
平台会在报告中标注数据状态。没有真实数据库数据的维度不得伪造历史结论,按模型配置的中性值处理并在报告中说明。
|
||
|
||
外部接口使用结构化参数直接评分,不再通过聊天语义提取参数。沿用现有评分区间起点(截止年份往前 3 年的 1 月 1 日),区间终点为 `asOfDate`,不包含截止日期之后的案例及采购订单。相似案例使用现有检索服务的文本和业务字段排序,输入同一统计区间内的案例;本接口不调用向量嵌入服务。
|
||
|
||
包装组合沿用现有单标签评分口径,例如 `PET 瓶身 + PP 泵头` 命中“泵头”,不代表分别评估了所有组件。报告展示原文及评分标签。缺陷维度来自历史 A/B/C 记录,不预测新品实际缺陷等级。
|
||
|
||
AI 只用于生成预防建议,等待最多 20 秒。AI 不可用时仍返回数据库评分及固定验证建议,并在报告中标注;数据库评分失败则返回 `503`,不生成模拟分数。
|
||
|
||
报告建议包含:
|
||
|
||
1. 评估信息。
|
||
2. 综合风险得分和风险等级。
|
||
3. 七维评分明细。
|
||
4. 品类、供应商和物料历史统计。
|
||
5. 相似异常案例。
|
||
6. 标准规则匹配结果。
|
||
7. 风险预防建议。
|
||
8. 数据口径和模型版本。
|
||
|
||
## 10. 联调前需要双方确认的事项
|
||
|
||
1. 正式接口域名、环境地址和接口版本。
|
||
2. `sourceSystem`、`X-Client-Id` 和密钥分配方式。
|
||
3. 钉钉 `unionId` 是否可以作为双方统一用户标识;如果不能,需要提供统一身份映射方案。
|
||
4. 操作人提前开通、UnionId 唯一映射及 `risk.assess` 权限;本接口已采用不存在返回 `403` 的策略。
|
||
5. 是否启用 IP 白名单、mTLS 和调用频率限制。
|
||
6. `stageNew`、`defectLevel` 枚举,以及 PLM 到第 4.6 节八类评分品类的映射关系;确认无法归类时的默认分提示。
|
||
7. 外部系统历史数据是否需要同步到蝶雅平台。
|
||
8. Markdown 报告的保存、下载和展示方式。
|
||
9. 超时、重试和 `requestId` 查询机制。
|
||
|
||
## 11. 服务端启用配置
|
||
|
||
先执行迁移:
|
||
|
||
```powershell
|
||
python -m alembic upgrade head
|
||
```
|
||
|
||
新增表为 `integration_nonces` 和 `integration_assessments`,分别保存已认证随机串及请求快照、处理状态、评分结果和 Markdown 报告;成功评估同时写入操作审计。现有用户表不自动添加外部用户。报告记录通过本地用户编号逻辑关联,不对已有用户表创建外键,迁移账号不需要该表的 `REFERENCES` 权限;用户状态和权限仍由接口校验。
|
||
|
||
在服务端环境配置中设置:
|
||
|
||
```dotenv
|
||
RISK_INTEGRATION_CLIENTS={"dieya-plm":"replace-with-at-least-32-random-characters"}
|
||
RISK_INTEGRATION_REQUESTS_PER_MINUTE=60
|
||
RISK_INTEGRATION_REPORT_TIMEOUT_SECONDS=110
|
||
```
|
||
|
||
上面的密钥仅为占位,须替换为至少 32 个字符的随机密钥;测试、正式各使用一套。调用系统编号仅允许字母、数字、下划线和短横线,最长 64 位。默认 `{}` 不允许任何系统调用。配置后重启服务。
|
||
|
||
也可以在后端项目根目录使用启用工具生成密钥,工具会保留 `.env` 中的其他设置及已有系统密钥,不打印密钥、不自动轮换已有密钥:
|
||
|
||
```powershell
|
||
.\.venv\Scripts\python.exe scripts/setup_external_risk.py configure --client-id dieya-plm
|
||
```
|
||
|
||
让操作人在钉钉工作台中登录风险平台,正常免登会同步用户资料和 UnionId。然后使用 PLM 保存的真实 UnionId 检查映射与权限:
|
||
|
||
```powershell
|
||
.\.venv\Scripts\python.exe scripts/setup_external_risk.py check-user --union-id "PLM操作人的真实UnionId" --dingtalk-user-id "同一人的真实UserId"
|
||
```
|
||
|
||
工具只检查、不修改账号或权限。不存在时先通过钉钉免登开通;已停用或无权限时由账号管理员确认处理;映射不唯一或身份不一致时核对两边钉钉身份源,不能按姓名猜测绑定。当前启用的 admin、product_manager、reviewer、user 角色均有风险评估操作权限,以本地 `role_permissions` 表实际记录为准。
|
||
|
||
限流按调用系统统计已通过签名的请求,POST、GET、幂等重试共用滚动 60 秒窗口;达到阈值返回 `429` 和 `Retry-After: 60`。IP 白名单和 mTLS 可在网关配置,当前应用层不提供这两项配置。
|
||
|
||
## 12. 联调调用工具
|
||
|
||
非钉钉用户需由风险平台管理员在本地显式开通,例如:
|
||
|
||
```powershell
|
||
.\.venv\Scripts\python.exe scripts/setup_external_risk.py allow-user --client-id dieya-plm --external-user-id superadmin --employee-no plm_superadmin --name "周子墨"
|
||
```
|
||
|
||
工具创建无密码、无钉钉绑定的普通本地账号,并写入 `.env`:
|
||
|
||
```dotenv
|
||
RISK_INTEGRATION_OPERATOR_MAPPINGS='{"dieya-plm":{"superadmin":"plm_superadmin"}}'
|
||
```
|
||
|
||
映射目标是本地 `emp_no`。默认 `{}` 禁止所有非钉钉账号;已有映射不会自动指向其他账号,已有账号不会自动更改角色、姓名或启用状态。配置后重启后端,无需数据库迁移。
|
||
|
||
项目提供 `scripts/call_external_risk_report.py`,自动生成 POST/GET 签名并保存 Markdown。调用方将本系统分配的密钥写入环境变量 `RISK_INTEGRATION_CLIENT_SECRET`(客户端单个密钥,与服务端 JSON 密钥表是不同配置)。`assessment.json` 使用本文第 4.1 节请求结构,填入已开通的真实 UnionId。
|
||
|
||
```powershell
|
||
python scripts/call_external_risk_report.py --url https://risk.example.com/api/v1/integrations/risk-assessments/report --client-id dieya-plm --body-file assessment.json --output risk-report.md
|
||
```
|
||
|
||
查询原请求:
|
||
|
||
```powershell
|
||
python scripts/call_external_risk_report.py --url https://risk.example.com/api/v1/integrations/risk-assessments/report --client-id dieya-plm --request-id PLM-D-FT-003-0001 --output risk-report.md
|
||
```
|
||
|
||
只有 `200` 会保存报告;`202`/错误响应会展示 JSON 和重试等待时间,不会覆盖已有 Markdown。
|