huwei 2026-07-14 10:55:53 +08:00
commit 5531797462
11 changed files with 1308 additions and 12 deletions

View File

@ -0,0 +1,536 @@
openapi: 3.0.3
info:
title: 监管端-风险预警中心 API
description: |
重庆市安全评价服务平台 - 监管端「风险预警中心」页面接口。
- 统一前缀:`/safetyEval/regulator/risk-center`
- 当前为 Mock 实现,数据存于 Controller 内存
- 成功响应 `code = "0"`
**Apifox 导入方式**:项目设置 → 导入数据 → 选择本文件OpenAPI/Swagger 格式)
version: 1.0.0
contact:
name: safety-eval-service
servers:
- url: http://localhost:8095
description: 本地开发
- url: http://192.168.20.100:30140
description: 测试网关100
tags:
- name: 监管端-风险预警中心
description: 风险预警中心页面相关接口
paths:
/safetyEval/regulator/risk-center/overview:
get:
tags:
- 监管端-风险预警中心
summary: 页面概览
description: 加载统计卡片与消息处理动作说明
operationId: regulatorRiskOverview
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorRiskOverviewCO'
example:
success: true
code: "0"
data:
stats:
- label: 未处理预警
value: "5"
hint: 需监管人员处置
- label: 紧急消息
value: "4"
hint: 证书过期、社保异常
- label: 重要提醒
value: "2"
hint: 证书临期、变更停滞
- label: 今日已处置
value: "2"
hint: 含发函、确认、转办
processGuide: 查看来源 → 核查材料/字段 → 发函或转办 → 设置处理结论 → 标记闭环。所有按钮均写入处置记录,用于监管留痕。
/safetyEval/regulator/risk-center/high-priority-alerts:
get:
tags:
- 监管端-风险预警中心
summary: 高优先级消息列表
description: 加载页面中部「高优先级消息」区块,默认返回紧急且未处理的前 2 条
operationId: regulatorRiskHighPriorityAlerts
parameters:
- $ref: '#/components/parameters/levelParam'
- name: warningType
in: query
description: 预警类型
schema:
type: string
enum:
- 证书过期
- 社保异常
- 信息变更
- 项目超期
- 账号限制
- name: sourceModule
in: query
description: 来源模块
schema:
type: string
example: 评价师信息管理
- name: keyword
in: query
description: 标题/预警编号/预警内容关键字
schema:
type: string
- name: onlyUntreated
in: query
description: 仅未处理(未处理/待确认/处理中)
schema:
type: boolean
default: true
- name: limit
in: query
description: 返回条数上限
schema:
type: integer
default: 2
minimum: 1
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorRiskPriorityAlertList'
example:
success: true
code: "0"
data:
- title: 评价师证书已过期
description: 来源:评价师信息管理.证书有效期;触发:有效期至 2026-06-01。
messageId: W-2026-008
level: danger
warningType: 证书过期
sourceModule: 评价师信息管理
status: 未处理
- title: 社保多处缴纳
description: 来源:资质保持监控.社保缴纳单位;触发:同一人员两家机构。
messageId: W-2026-007
level: danger
warningType: 社保异常
sourceModule: 资质保持监控
status: 未处理
/safetyEval/regulator/risk-center/page:
get:
tags:
- 监管端-风险预警中心
summary: 分页查询预警消息
description: |
消息列表分页查询,支持搜索栏与 Tab 筛选。
Tab 对应关系:
- 全部消息:`filterKind=all` 或不传
- 紧急:`filterKind=urgent`
- 未读/未处理:`filterKind=unread`
operationId: regulatorRiskPage
parameters:
- name: keyword
in: query
description: 机构/人员/项目/预警编号
schema:
type: string
- name: warningType
in: query
description: 预警类型
schema:
type: string
enum:
- 证书过期
- 社保异常
- 信息变更
- 项目超期
- 账号限制
- $ref: '#/components/parameters/levelParam'
- name: filterKind
in: query
description: Tab 筛选
schema:
type: string
enum:
- all
- urgent
- unread
default: all
- $ref: '#/components/parameters/currentParam'
- $ref: '#/components/parameters/sizeParam'
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/PageResponseRegulatorRiskMessageCO'
example:
success: true
code: "0"
total: 4
data:
- id: W-2026-008
warningType: 证书过期
level: urgent
levelName: 紧急
sourceModule: 评价师信息管理
sourceFields:
- 人员姓名
- 证书类型
- 有效期至
content: 张建国一级安全评价师证书已过期,机构未上传延续材料。
status: 未处理
sourceModuleKey: evaluator
/safetyEval/regulator/risk-center/handle:
post:
tags:
- 监管端-风险预警中心
summary: 处置预警
description: |
处理弹窗提交、列表行内「闭环」「发函」「提醒」等操作。
status 自动推断规则(未传 status 时):
- handleType 含「闭环」→ 已闭环
- handleType 含「发函」→ 已发函
- handleType 含「提醒」→ 已提醒
- handleType 含「转办」「转备案」→ 处理中
- 其他 → 已处置
operationId: regulatorRiskHandle
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorRiskHandleCmd'
example:
id: W-2026-008
handleType: 标记闭环
deadline: "2026-07-18"
opinion: 请核查证书延续材料
status: 已闭环
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorRiskMessageCO'
example:
success: true
code: "0"
data:
id: W-2026-008
warningType: 证书过期
level: urgent
levelName: 紧急
sourceModule: 评价师信息管理
sourceFields:
- 人员姓名
- 证书类型
- 有效期至
content: 张建国一级安全评价师证书已过期,机构未上传延续材料。
status: 已闭环
sourceModuleKey: evaluator
'404':
description: 预警不存在
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseError'
example:
success: false
code: "404"
message: 预警不存在
errMessage: 预警不存在
components:
parameters:
levelParam:
name: level
in: query
description: 等级编码
schema:
type: string
enum:
- urgent
- important
- normal
default: urgent
currentParam:
name: current
in: query
description: 当前页
schema:
type: integer
format: int64
default: 1
minimum: 1
sizeParam:
name: size
in: query
description: 每页条数
schema:
type: integer
format: int64
default: 10
minimum: 1
schemas:
# ---------- 通用响应 ----------
SingleResponseError:
type: object
properties:
success:
type: boolean
example: false
code:
type: string
example: "404"
message:
type: string
errMessage:
type: string
SingleResponseRegulatorRiskOverviewCO:
type: object
properties:
success:
type: boolean
example: true
code:
type: string
example: "0"
message:
type: string
errMessage:
type: string
data:
$ref: '#/components/schemas/RegulatorRiskOverviewCO'
SingleResponseRegulatorRiskPriorityAlertList:
type: object
properties:
success:
type: boolean
example: true
code:
type: string
example: "0"
message:
type: string
errMessage:
type: string
data:
type: array
items:
$ref: '#/components/schemas/RegulatorRiskPriorityAlertCO'
SingleResponseRegulatorRiskMessageCO:
type: object
properties:
success:
type: boolean
example: true
code:
type: string
example: "0"
message:
type: string
errMessage:
type: string
data:
$ref: '#/components/schemas/RegulatorRiskMessageCO'
PageResponseRegulatorRiskMessageCO:
type: object
properties:
success:
type: boolean
example: true
code:
type: string
example: "0"
message:
type: string
errMessage:
type: string
total:
type: integer
format: int64
description: 总条数
example: 8
data:
type: array
items:
$ref: '#/components/schemas/RegulatorRiskMessageCO'
# ---------- 业务模型 ----------
RegulatorRiskOverviewCO:
type: object
description: 页面概览
properties:
stats:
type: array
description: 统计卡片
items:
$ref: '#/components/schemas/StatCardCO'
processGuide:
type: string
description: 消息处理动作说明
StatCardCO:
type: object
properties:
label:
type: string
description: 标签
example: 未处理预警
value:
type: string
description: 数值
example: "5"
hint:
type: string
description: 提示
example: 需监管人员处置
RegulatorRiskPriorityAlertCO:
type: object
description: 高优先级预警消息
properties:
title:
type: string
description: 标题
example: 评价师证书已过期
description:
type: string
description: 描述(含来源与触发规则)
messageId:
type: string
description: 关联预警编号
example: W-2026-008
level:
type: string
description: 展示级别
enum:
- danger
- warning
- info
example: danger
warningType:
type: string
description: 预警类型
example: 证书过期
sourceModule:
type: string
description: 来源模块
example: 评价师信息管理
status:
type: string
description: 当前状态
example: 未处理
RegulatorRiskMessageCO:
type: object
description: 预警消息
properties:
id:
type: string
description: 预警编号
example: W-2026-008
warningType:
type: string
description: 预警类型
enum:
- 证书过期
- 社保异常
- 信息变更
- 项目超期
- 账号限制
level:
type: string
description: 等级编码
enum:
- urgent
- important
- normal
levelName:
type: string
description: 等级名称
enum:
- 紧急
- 重要
- 一般
sourceModule:
type: string
description: 来源模块
sourceFields:
type: array
description: 来源字段
items:
type: string
content:
type: string
description: 预警内容
status:
type: string
description: 状态
enum:
- 未处理
- 处理中
- 待确认
- 已闭环
- 已发函
- 已提醒
- 已处置
sourceModuleKey:
type: string
description: 来源模块跳转标识
enum:
- evaluator
- qual-monitor
- qual-change
- institution-account
RegulatorRiskHandleCmd:
type: object
description: 预警处置请求
required:
- id
- handleType
properties:
id:
type: string
description: 预警编号
example: W-2026-008
handleType:
type: string
description: 处置方式
example: 标记闭环
deadline:
type: string
format: date
description: 办理期限 yyyy-MM-dd
example: "2026-07-18"
opinion:
type: string
description: 处置意见
example: 请核查证书延续材料
status:
type: string
description: 处置后状态(不传则根据 handleType 自动推断)
example: 已闭环

View File

@ -0,0 +1,303 @@
# 监管端 - 风险预警中心接口文档
> 仅覆盖原型页 **风险预警中心**Mock 数据内置于 `RegulatorRiskCenterController`,无数据库依赖。
> 统一前缀:`/safetyEval/regulator/risk-center`
> 接口数量:**4 个**
> **OpenAPI 导入文件**[监管端-风险预警中心.openapi.yaml](./openapi/监管端-风险预警中心.openapi.yaml)(可直接导入 Apifox
---
## 1. 页面与接口映射
| 页面区域 | 接口 | 说明 |
|----------|------|------|
| 顶部 4 个统计卡片 + 处理动作说明 | `GET /overview` | 页面首屏统计区 |
| 高优先级消息区块 | `GET /high-priority-alerts` | 支持等级/类型/来源等筛选 |
| 搜索栏 + Tab全部/紧急/未读)+ 消息列表 | `GET /page` | 分页 + 筛选 |
| 处理 / 发函 / 闭环 / 提醒 / 审核 | `POST /handle` | 更新预警状态 |
---
## 2. 通用约定
### 响应格式
成功时 `code = "0"`
```json
// SingleResponse
{ "success": true, "code": "0", "data": {} }
// PageResponse
{ "success": true, "code": "0", "data": [], "total": 8 }
```
### 分页参数
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
| current | Long | 1 | 当前页 |
| size | Long | 10 | 每页条数 |
---
## 3. 领域模型
### RegulatorRiskOverviewCO — 页面概览
| 字段 | 类型 | 说明 |
|------|------|------|
| stats | List\<StatCardCO\> | 统计卡片 |
| processGuide | String | 消息处理动作说明文案 |
**StatCardCO**`label` / `value` / `hint`
### RegulatorRiskPriorityAlertCO — 高优先级消息
| 字段 | 类型 | 说明 |
|------|------|------|
| title | String | 标题 |
| description | String | 描述(含来源与触发规则) |
| messageId | String | 关联预警编号 |
| level | String | danger / warning / info |
| warningType | String | 预警类型 |
| sourceModule | String | 来源模块 |
| status | String | 当前状态 |
### RegulatorRiskMessageCO — 预警消息
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String | 预警编号,如 W-2026-008 |
| warningType | String | 证书过期 / 社保异常 / 信息变更 / 项目超期 / 账号限制 |
| level | String | urgent / important / normal |
| levelName | String | 紧急 / 重要 / 一般 |
| sourceModule | String | 来源模块名称 |
| sourceFields | List\<String\> | 来源字段标签 |
| content | String | 预警内容 |
| status | String | 未处理 / 处理中 / 待确认 / 已闭环 / 已发函 / 已提醒 / 已处置 |
| sourceModuleKey | String | 来源跳转模块标识evaluator、qual-monitor、qual-change 等) |
---
## 4. 接口详情
### 4.1 GET `/overview` — 页面概览
**用途**:加载统计卡片、处理流程说明。
**响应示例**
```json
{
"success": true,
"code": "0",
"data": {
"stats": [
{ "label": "未处理预警", "value": "5", "hint": "需监管人员处置" },
{ "label": "紧急消息", "value": "4", "hint": "证书过期、社保异常" },
{ "label": "重要提醒", "value": "2", "hint": "证书临期、变更停滞" },
{ "label": "今日已处置", "value": "2", "hint": "含发函、确认、转办" }
],
"processGuide": "查看来源 → 核查材料/字段 → 发函或转办 → 设置处理结论 → 标记闭环。所有按钮均写入处置记录,用于监管留痕。"
}
}
```
> 统计数值由 Mock 消息列表动态计算,处置后再次请求会变化。
---
### 4.2 GET `/high-priority-alerts` — 高优先级消息
**用途**:加载页面中部「高优先级消息」区块,默认展示紧急且未处理的前 2 条。
**查询参数**
| 参数 | 必填 | 默认 | 说明 |
|------|------|------|------|
| level | 否 | urgent | 等级urgent / important / normal |
| warningType | 否 | — | 证书过期、社保异常、信息变更、项目超期、账号限制 |
| sourceModule | 否 | — | 来源模块,如 评价师信息管理 |
| keyword | 否 | — | 标题/预警编号/预警内容关键字 |
| onlyUntreated | 否 | true | 仅未处理(未处理/待确认/处理中) |
| limit | 否 | 2 | 返回条数上限 |
**请求示例**
```
GET /safetyEval/regulator/risk-center/high-priority-alerts
GET /safetyEval/regulator/risk-center/high-priority-alerts?level=urgent&onlyUntreated=true&limit=2
GET /safetyEval/regulator/risk-center/high-priority-alerts?level=important&limit=5
```
**响应示例**
```json
{
"success": true,
"code": "0",
"data": [
{
"title": "评价师证书已过期",
"description": "来源:评价师信息管理.证书有效期;触发:有效期至 2026-06-01。",
"messageId": "W-2026-008",
"level": "danger",
"warningType": "证书过期",
"sourceModule": "评价师信息管理",
"status": "未处理"
},
{
"title": "社保多处缴纳",
"description": "来源:资质保持监控.社保缴纳单位;触发:同一人员两家机构。",
"messageId": "W-2026-007",
"level": "danger",
"warningType": "社保异常",
"sourceModule": "资质保持监控",
"status": "未处理"
}
]
}
```
---
### 4.3 GET `/page` — 分页查询消息列表
**查询参数**
| 参数 | 必填 | 说明 |
|------|------|------|
| keyword | 否 | 机构/人员/项目/预警编号 |
| warningType | 否 | 证书过期、社保异常、信息变更、项目超期、账号限制 |
| level | 否 | urgent / important / normal |
| filterKind | 否 | Tab 筛选:`all`(默认)/ `urgent` / `unread` |
| current | 否 | 当前页 |
| size | 否 | 每页条数 |
**Tab 对应关系**(原型页右上角):
| Tab | filterKind |
|-----|------------|
| 全部消息 | all 或不传 |
| 紧急 | urgent |
| 未读/未处理 | unread |
**请求示例**
```
GET /safetyEval/regulator/risk-center/page?filterKind=urgent&current=1&size=10
GET /safetyEval/regulator/risk-center/page?keyword=张建国&warningType=证书过期
```
**响应示例**
```json
{
"success": true,
"code": "0",
"total": 4,
"data": [
{
"id": "W-2026-008",
"warningType": "证书过期",
"level": "urgent",
"levelName": "紧急",
"sourceModule": "评价师信息管理",
"sourceFields": ["人员姓名", "证书类型", "有效期至"],
"content": "张建国一级安全评价师证书已过期,机构未上传延续材料。",
"status": "未处理",
"sourceModuleKey": "evaluator"
}
]
}
```
---
### 4.4 POST `/handle` — 处置预警
**用途**:处理弹窗提交、列表行内「闭环」「发函」「提醒」等操作。
**请求体**
```json
{
"id": "W-2026-008",
"handleType": "标记闭环",
"deadline": "2026-07-18",
"opinion": "请核查证书延续材料",
"status": "已闭环"
}
```
| 字段 | 必填 | 说明 |
|------|------|------|
| id | 是 | 预警编号 |
| handleType | 是 | 发函核查 / 转备案审核 / 要求整改 / 标记闭环 / 已发函 / 已提醒 等 |
| deadline | 否 | 办理期限 |
| opinion | 否 | 处置意见 |
| status | 否 | 不传时根据 handleType 自动推断 |
**status 自动推断规则**
| handleType 含 | 默认 status |
|---------------|-------------|
| 闭环 | 已闭环 |
| 发函 | 已发函 |
| 提醒 | 已提醒 |
| 转办 / 转备案 | 处理中 |
| 其他 | 已处置 |
**响应**:返回更新后的 `RegulatorRiskMessageCO`
---
## 5. 前端对接建议
```javascript
const BASE = '/safetyEval/regulator/risk-center';
// 1. 页面初始化 - 统计区
const overview = await fetch(`${BASE}/overview`).then(r => r.json());
// 2. 高优先级消息默认紧急未处理前2条
const alerts = await fetch(`${BASE}/high-priority-alerts`).then(r => r.json());
// 3. Tab 切换 / 搜索
const list = await fetch(`${BASE}/page?filterKind=unread&current=1&size=10`).then(r => r.json());
// 4. 处置
await fetch(`${BASE}/handle`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: 'W-2026-008', handleType: '标记闭环', status: '已闭环' })
});
```
**来源跳转**:使用 `sourceModuleKey` 切换监管端左侧菜单模块(与原型 `openRegSource()` 一致)。
---
## 6. 代码位置
| 类型 | 路径 |
|------|------|
| Controller含 Mock | `safety-eval-adapter/.../web/regulator/RegulatorRiskCenterController.java` |
| 响应模型 | `safety-eval-client/.../co/regulator/RegulatorRiskOverviewCO.java` |
| | `safety-eval-client/.../co/regulator/RegulatorRiskPriorityAlertCO.java` |
| | `safety-eval-client/.../co/regulator/RegulatorRiskMessageCO.java` |
| 入参模型 | `safety-eval-client/.../dto/regulator/RegulatorRiskPriorityAlertQuery.java` |
| | `safety-eval-client/.../dto/regulator/RegulatorRiskMessagePageQuery.java` |
| | `safety-eval-client/.../dto/regulator/RegulatorRiskHandleCmd.java` |
---
## 7. 后续真实数据对接
保留上述 4 个接口契约不变,将 Controller 内 Mock 逻辑替换为领域服务调用即可,前端无需改动。
| 日期 | 说明 |
|------|------|
| 2026-07-14 | 初版:风险预警中心 3 接口 + Controller 内 Mock |
| 2026-07-14 | 拆分 highPriorityAlerts 为独立接口 `/high-priority-alerts` |

17
pom.xml
View File

@ -46,8 +46,7 @@
<!-- Lombok --> <!-- Lombok -->
<lombok.version>1.18.30</lombok.version> <lombok.version>1.18.30</lombok.version>
<jjb-saas-system-client.version>1.7.1.1-SNAPSHOT</jjb-saas-system-client.version>
<jjb-saas-system-client.version>1.7.0-SNAPSHOT</jjb-saas-system-client.version>
<!-- X File Storage闂佹寧绋戦悧鍡浰囩缓濂缂傚倷鑳堕崰宥囩博閹绢喖妫橀柛銉檮椤愪粙鎮楀☉娅亪宕戝澶嬫櫖?--> <!-- X File Storage闂佹寧绋戦悧鍡浰囩缓濂缂傚倷鑳堕崰宥囩博閹绢喖妫橀柛銉檮椤愪粙鎮楀☉娅亪宕戝澶嬫櫖?-->
<x-file-storage.version>2.2.0</x-file-storage.version> <x-file-storage.version>2.2.0</x-file-storage.version>
@ -60,9 +59,8 @@
<dependency> <dependency>
<groupId>com.jjb.saas</groupId> <groupId>com.jjb.saas</groupId>
<artifactId>jjb-saas-system-client</artifactId> <artifactId>jjb-saas-system-client</artifactId>
<version>1.7.1.1-SNAPSHOT</version> <version>${jjb-saas-system-client.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.zcloud.gbscommon</groupId> <groupId>com.zcloud.gbscommon</groupId>
<artifactId>zcloud_gbscommon</artifactId> <artifactId>zcloud_gbscommon</artifactId>
@ -232,18 +230,13 @@
<repositories> <repositories>
<repository> <repository>
<id>gbs-snapshots</id> <id>gbs-public</id>
<name>GBS Snapshots Repository</name> <name>GBS Public Repository (group)</name>
<url>http://192.168.20.100:8578/repository/maven-snapshots/</url> <url>http://192.168.20.100:8578/repository/maven-public/</url>
<snapshots> <snapshots>
<enabled>true</enabled> <enabled>true</enabled>
<updatePolicy>always</updatePolicy> <updatePolicy>always</updatePolicy>
</snapshots> </snapshots>
</repository>
<repository>
<id>gbs-releases</id>
<name>GBS Releases Repository</name>
<url>http://192.168.20.100:8578/repository/maven-releases/</url>
<releases> <releases>
<enabled>true</enabled> <enabled>true</enabled>
</releases> </releases>

View File

@ -0,0 +1,274 @@
package org.qinan.safetyeval.adapter.web.regulator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.qinan.safetyeval.client.co.regulator.RegulatorRiskMessageCO;
import org.qinan.safetyeval.client.co.regulator.RegulatorRiskOverviewCO;
import org.qinan.safetyeval.client.co.regulator.RegulatorRiskPriorityAlertCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.RegulatorRiskHandleCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorRiskMessagePageQuery;
import org.qinan.safetyeval.client.dto.regulator.RegulatorRiskPriorityAlertQuery;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.stream.Collectors;
/**
* -Mock Controller
*/
@Api(tags = "监管端-风险预警中心")
@RestController
@RequestMapping("/safetyEval/regulator/risk-center")
public class RegulatorRiskCenterController {
private static final String PROCESS_GUIDE = "查看来源 → 核查材料/字段 → 发函或转办 → 设置处理结论 → 标记闭环。所有按钮均写入处置记录,用于监管留痕。";
private final List<RegulatorRiskMessageCO> messages = new ArrayList<>();
@PostConstruct
public void initMockData() {
messages.clear();
messages.add(message("W-2026-008", "证书过期", "urgent", "评价师信息管理",
Arrays.asList("人员姓名", "证书类型", "有效期至"),
"张建国一级安全评价师证书已过期,机构未上传延续材料。", "未处理", "evaluator"));
messages.add(message("W-2026-007", "社保异常", "urgent", "资质保持监控",
Arrays.asList("人员姓名", "所在机构", "社保缴纳单位"),
"王丽萍在恒安安全评价和渝安风险评估同时缴纳社保。", "未处理", "qual-monitor"));
messages.add(message("W-2026-006", "证书临期", "important", "资质保持监控",
Arrays.asList("证书有效期", "预警阈值"),
"陈华安全评价师二级证书将于 2026-08-01 到期,距到期不足 30 天。", "处理中", "evaluator"));
messages.add(message("W-2026-005", "信息变更", "normal", "备案变更管理",
Arrays.asList("注册地址", "变更材料"),
"恒安安全评价注册地址变更材料超过 5 个工作日未确认。", "待确认", "qual-change"));
messages.add(message("W-2026-004", "项目超期", "important", "项目过程控制",
Arrays.asList("项目名称", "计划完成日期"),
"某化工项目安全评价报告超过计划节点 7 天未完成。", "未处理", "qual-monitor"));
messages.add(message("W-2026-003", "账号限制", "normal", "机构账号管理",
Arrays.asList("机构名称", "账号状态"),
"渝安风险评估中心账号处于禁用状态,仍有新业务备案尝试。", "待确认", "institution-account"));
messages.add(message("W-2026-002", "证书过期", "urgent", "评价师信息管理",
Arrays.asList("人员姓名", "证书类型", "有效期至"),
"李明华注册安全工程师证书已过期。", "已闭环", "evaluator"));
messages.add(message("W-2026-001", "社保异常", "urgent", "资质保持监控",
Arrays.asList("人员姓名", "社保缴纳单位"),
"赵磊社保缴纳单位与备案机构不一致。", "已发函", "qual-monitor"));
}
@ApiOperation("页面概览(统计卡片 + 处理动作说明)")
@GetMapping("/overview")
public SingleResponse<RegulatorRiskOverviewCO> overview() {
RegulatorRiskOverviewCO co = new RegulatorRiskOverviewCO();
co.setStats(buildStats());
co.setProcessGuide(PROCESS_GUIDE);
return SingleResponse.success(co);
}
@ApiOperation("高优先级消息列表")
@GetMapping("/high-priority-alerts")
public SingleResponse<List<RegulatorRiskPriorityAlertCO>> highPriorityAlerts(RegulatorRiskPriorityAlertQuery query) {
if (query == null) {
query = new RegulatorRiskPriorityAlertQuery();
}
return SingleResponse.success(buildHighPriorityAlerts(query));
}
@ApiOperation("分页查询预警消息")
@GetMapping("/page")
public PageResponse<RegulatorRiskMessageCO> page(@Validated RegulatorRiskMessagePageQuery query) {
List<RegulatorRiskMessageCO> filtered = messages.stream()
.filter(item -> matchQuery(query, item))
.collect(Collectors.toList());
long current = query.getCurrent() == null || query.getCurrent() < 1 ? 1L : query.getCurrent();
long size = query.getSize() == null || query.getSize() < 1 ? 10L : query.getSize();
int from = (int) ((current - 1) * size);
List<RegulatorRiskMessageCO> page;
if (from >= filtered.size()) {
page = Collections.emptyList();
} else {
int to = Math.min(from + (int) size, filtered.size());
page = filtered.subList(from, to);
}
return PageResponse.of(page, (long) filtered.size());
}
@ApiOperation("处置预警(处理/发函/闭环/提醒等)")
@PostMapping("/handle")
public SingleResponse<RegulatorRiskMessageCO> handle(@Validated @RequestBody RegulatorRiskHandleCmd cmd) {
Optional<RegulatorRiskMessageCO> optional = messages.stream()
.filter(m -> m.getId().equals(cmd.getId()))
.findFirst();
if (!optional.isPresent()) {
return SingleResponse.error("404", "预警不存在");
}
RegulatorRiskMessageCO message = optional.get();
message.setStatus(resolveStatus(cmd));
return SingleResponse.success(message);
}
private List<RegulatorRiskOverviewCO.StatCardCO> buildStats() {
long untreated = messages.stream().filter(m -> isUntreated(m.getStatus())).count();
long urgent = messages.stream().filter(m -> "urgent".equals(m.getLevel())).count();
long important = messages.stream().filter(m -> "important".equals(m.getLevel())).count();
long handledToday = messages.stream().filter(m -> isHandledToday(m.getStatus())).count();
return Arrays.asList(
stat("未处理预警", String.valueOf(untreated), "需监管人员处置"),
stat("紧急消息", String.valueOf(urgent), "证书过期、社保异常"),
stat("重要提醒", String.valueOf(important), "证书临期、变更停滞"),
stat("今日已处置", String.valueOf(handledToday), "含发函、确认、转办")
);
}
private List<RegulatorRiskPriorityAlertCO> buildHighPriorityAlerts(RegulatorRiskPriorityAlertQuery query) {
Map<String, String[]> titles = new HashMap<>();
titles.put("W-2026-008", new String[]{"评价师证书已过期", "来源:评价师信息管理.证书有效期;触发:有效期至 2026-06-01。"});
titles.put("W-2026-007", new String[]{"社保多处缴纳", "来源:资质保持监控.社保缴纳单位;触发:同一人员两家机构。"});
int limit = query.getLimit() == null || query.getLimit() < 1 ? 2 : query.getLimit();
return messages.stream()
.filter(m -> matchPriorityQuery(query, m))
.limit(limit)
.map(m -> toPriorityAlert(m, titles))
.collect(Collectors.toList());
}
private boolean matchPriorityQuery(RegulatorRiskPriorityAlertQuery query, RegulatorRiskMessageCO item) {
if (StringUtils.hasText(query.getLevel()) && !query.getLevel().equals(item.getLevel())) {
return false;
}
if (StringUtils.hasText(query.getWarningType()) && !query.getWarningType().equals(item.getWarningType())) {
return false;
}
if (StringUtils.hasText(query.getSourceModule()) && !query.getSourceModule().equals(item.getSourceModule())) {
return false;
}
if (Boolean.TRUE.equals(query.getOnlyUntreated()) && !isUntreated(item.getStatus())) {
return false;
}
if (StringUtils.hasText(query.getKeyword())) {
String kw = query.getKeyword().toLowerCase();
String text = (item.getId() + item.getContent() + item.getWarningType() + item.getSourceModule()).toLowerCase();
if (!text.contains(kw)) {
return false;
}
}
return true;
}
private RegulatorRiskPriorityAlertCO toPriorityAlert(RegulatorRiskMessageCO m, Map<String, String[]> titles) {
RegulatorRiskPriorityAlertCO alert = new RegulatorRiskPriorityAlertCO();
String[] preset = titles.get(m.getId());
if (preset != null) {
alert.setTitle(preset[0]);
alert.setDescription(preset[1]);
} else {
alert.setTitle(m.getWarningType());
alert.setDescription("来源:" + m.getSourceModule() + "" + m.getContent());
}
alert.setMessageId(m.getId());
alert.setLevel(alertLevel(m.getLevel()));
alert.setWarningType(m.getWarningType());
alert.setSourceModule(m.getSourceModule());
alert.setStatus(m.getStatus());
return alert;
}
private String alertLevel(String level) {
if ("urgent".equals(level)) {
return "danger";
}
if ("important".equals(level)) {
return "warning";
}
return "info";
}
private boolean matchQuery(RegulatorRiskMessagePageQuery query, RegulatorRiskMessageCO item) {
if (StringUtils.hasText(query.getKeyword())) {
String kw = query.getKeyword().toLowerCase();
String text = (item.getId() + item.getContent() + item.getWarningType()).toLowerCase();
if (!text.contains(kw)) {
return false;
}
}
if (StringUtils.hasText(query.getWarningType()) && !query.getWarningType().equals(item.getWarningType())) {
return false;
}
if (StringUtils.hasText(query.getLevel()) && !query.getLevel().equals(item.getLevel())) {
return false;
}
if (StringUtils.hasText(query.getFilterKind()) && !"all".equals(query.getFilterKind())) {
if ("urgent".equals(query.getFilterKind()) && !"urgent".equals(item.getLevel())) {
return false;
}
if ("unread".equals(query.getFilterKind()) && !isUntreated(item.getStatus())) {
return false;
}
}
return true;
}
private String resolveStatus(RegulatorRiskHandleCmd cmd) {
if (StringUtils.hasText(cmd.getStatus())) {
return cmd.getStatus();
}
String type = cmd.getHandleType();
if (type.contains("闭环")) {
return "已闭环";
}
if (type.contains("发函")) {
return "已发函";
}
if (type.contains("提醒")) {
return "已提醒";
}
if (type.contains("转办") || type.contains("转备案")) {
return "处理中";
}
return "已处置";
}
private boolean isUntreated(String status) {
return Arrays.asList("未处理", "待确认", "处理中").contains(status);
}
private boolean isHandledToday(String status) {
return Arrays.asList("已闭环", "已发函", "已提醒", "已处置").contains(status);
}
private RegulatorRiskOverviewCO.StatCardCO stat(String label, String value, String hint) {
RegulatorRiskOverviewCO.StatCardCO card = new RegulatorRiskOverviewCO.StatCardCO();
card.setLabel(label);
card.setValue(value);
card.setHint(hint);
return card;
}
private RegulatorRiskMessageCO message(String id, String type, String level, String module,
List<String> fields, String content, String status, String moduleKey) {
RegulatorRiskMessageCO co = new RegulatorRiskMessageCO();
co.setId(id);
co.setWarningType(type);
co.setLevel(level);
co.setLevelName(levelName(level));
co.setSourceModule(module);
co.setSourceFields(fields);
co.setContent(content);
co.setStatus(status);
co.setSourceModuleKey(moduleKey);
return co;
}
private String levelName(String level) {
if ("urgent".equals(level)) {
return "紧急";
}
if ("important".equals(level)) {
return "重要";
}
return "一般";
}
}

View File

@ -0,0 +1,40 @@
package org.qinan.safetyeval.client.co.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class RegulatorRiskMessageCO {
@ApiModelProperty("预警编号")
private String id;
@ApiModelProperty("预警类型")
private String warningType;
@ApiModelProperty("等级编码urgent/important/normal")
private String level;
@ApiModelProperty("等级名称:紧急/重要/一般")
private String levelName;
@ApiModelProperty("来源模块")
private String sourceModule;
@ApiModelProperty("来源字段")
private List<String> sourceFields;
@ApiModelProperty("预警内容")
private String content;
@ApiModelProperty("状态")
private String status;
@ApiModelProperty("来源模块跳转标识")
private String sourceModuleKey;
}

View File

@ -0,0 +1,32 @@
package org.qinan.safetyeval.client.co.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class RegulatorRiskOverviewCO {
@ApiModelProperty("统计卡片")
private List<StatCardCO> stats;
@ApiModelProperty("消息处理动作说明")
private String processGuide;
@Data
public static class StatCardCO {
@ApiModelProperty("标签")
private String label;
@ApiModelProperty("数值")
private String value;
@ApiModelProperty("提示")
private String hint;
}
}

View File

@ -0,0 +1,32 @@
package org.qinan.safetyeval.client.co.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
public class RegulatorRiskPriorityAlertCO {
@ApiModelProperty("标题")
private String title;
@ApiModelProperty("描述")
private String description;
@ApiModelProperty("关联预警编号")
private String messageId;
@ApiModelProperty("级别danger/warning/info")
private String level;
@ApiModelProperty("预警类型")
private String warningType;
@ApiModelProperty("来源模块")
private String sourceModule;
@ApiModelProperty("消息状态")
private String status;
}

View File

@ -0,0 +1,30 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
*
*/
@Data
public class RegulatorRiskHandleCmd {
@NotBlank
@ApiModelProperty("预警编号")
private String id;
@NotBlank
@ApiModelProperty("处置方式:发函核查/转备案审核/要求整改/标记闭环/已发函/已提醒等")
private String handleType;
@ApiModelProperty("办理期限 yyyy-MM-dd")
private String deadline;
@ApiModelProperty("处置意见")
private String opinion;
@ApiModelProperty("处置后状态,不传则根据 handleType 推断")
private String status;
}

View File

@ -0,0 +1,26 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.qinan.safetyeval.client.dto.BasePageQuery;
/**
*
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegulatorRiskMessagePageQuery extends BasePageQuery {
@ApiModelProperty("关键词:机构/人员/项目/预警编号")
private String keyword;
@ApiModelProperty("预警类型:证书过期/社保异常/信息变更/项目超期/账号限制")
private String warningType;
@ApiModelProperty("等级urgent/important/normal")
private String level;
@ApiModelProperty("Tab筛选all/urgent/unread")
private String filterKind;
}

View File

@ -0,0 +1,29 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
public class RegulatorRiskPriorityAlertQuery {
@ApiModelProperty("等级urgent/important/normal默认 urgent")
private String level = "urgent";
@ApiModelProperty("预警类型:证书过期/社保异常/信息变更/项目超期/账号限制")
private String warningType;
@ApiModelProperty("来源模块")
private String sourceModule;
@ApiModelProperty("关键词:标题/预警编号/预警内容")
private String keyword;
@ApiModelProperty("仅未处理true 时筛选 未处理/待确认/处理中,默认 true")
private Boolean onlyUntreated = true;
@ApiModelProperty("返回条数上限,默认 2")
private Integer limit = 2;
}

View File

@ -156,6 +156,7 @@ safety-eval:
- /qual-filing-personnel-cert - /qual-filing-personnel-cert
- /file-storage - /file-storage
- /file - /file
- /regulator
authenticated-paths: [] authenticated-paths: []
authenticated-prefixes: [] authenticated-prefixes: []
public-api: public-api: