Compare commits

...

2 Commits

Author SHA1 Message Date
huwei b1595c3b8f Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/support/InstitutionOrgSupport.java
2026-07-28 09:10:43 +08:00
huwei 5c7d3fb0fc 检查通知等 2026-07-28 09:09:27 +08:00
69 changed files with 4735 additions and 133 deletions

View File

@ -0,0 +1,13 @@
-- 机构端报告库原型字段补齐eval_report
-- 若表已存在,执行本增量脚本
-- 日期2026-07-23
ALTER TABLE `eval_report`
ADD COLUMN `industry_code` varchar(32) DEFAULT NULL COMMENT '所属行业编码' AFTER `eval_type_name`,
ADD COLUMN `industry_name` varchar(50) DEFAULT NULL COMMENT '所属行业名称(危化/矿山/冶炼/工贸等)' AFTER `industry_code`,
ADD COLUMN `evaluated_unit_name` varchar(200) DEFAULT NULL COMMENT '被评价单位名称' AFTER `customer_id`,
ADD COLUMN `issue_date` date DEFAULT NULL COMMENT '报告签发日期' AFTER `archive_time`,
ADD COLUMN `page_count` int DEFAULT NULL COMMENT '报告页数' AFTER `issue_date`,
ADD COLUMN `secret_level_code` varchar(32) DEFAULT NULL COMMENT '保密属性编码(NORMAL/TRADE_SECRET/PRIVACY/CLASSIFIED)' AFTER `page_count`,
ADD COLUMN `secret_level_name` varchar(50) DEFAULT NULL COMMENT '保密属性名称' AFTER `secret_level_code`,
ADD COLUMN `submit_flag` tinyint DEFAULT 2 COMMENT '是否已报送监管(1是2否)' AFTER `complete_flag`;

View File

@ -0,0 +1,11 @@
-- 监督检查告知:对齐原型筛选/展示(来源预警编号、告知附件)
-- 状态扩展0待下发来源扩展4风险预警业务码无需改列类型
ALTER TABLE `insp_notice`
ADD COLUMN `source_ref_no` varchar(64) DEFAULT NULL COMMENT '来源预警/业务编号(如W-PROJ-2026-009)' AFTER `source_ref_id`,
ADD COLUMN `notice_urls` varchar(2000) DEFAULT NULL COMMENT '告知附件URL逗号分隔' AFTER `notice_content`;
ALTER TABLE `insp_notice`
MODIFY COLUMN `source_type_code` int DEFAULT NULL COMMENT '来源方式编码(1年度计划2专项安排3其他4风险预警)',
MODIFY COLUMN `status_code` int DEFAULT NULL COMMENT '状态编码(0待下发1已下发2已确认3已反馈4已办结)',
MODIFY COLUMN `result_code` int DEFAULT NULL COMMENT '检查结果编码(1未发现问题/合格2责令整改/不合格3转入风险预警/限期整改)';

View File

@ -21,19 +21,27 @@ create table `eval_report` (
`org_id` bigint not null comment '机构id',
`project_id` bigint default null comment '关联项目id(可空,历史报告)',
`customer_id` bigint default null comment '客户id',
`evaluated_unit_name` varchar(200) default null comment '被评价单位名称',
`report_no` varchar(64) default null comment '报告编号',
`report_name` varchar(200) not null comment '报告名称',
`report_year` int default null comment '报告年度',
`eval_type_code` varchar(32) default null comment '评价类型编码(PRE安全预评价 ACCEPT安全验收评价 STATUS安全现状评价)',
`eval_type_name` varchar(50) default null comment '评价类型名称(安全预评价/安全验收评价/安全现状评价)',
`industry_code` varchar(32) default null comment '所属行业编码',
`industry_name` varchar(50) default null comment '所属行业名称',
`business_scope` varchar(500) default null comment '业务范围',
`file_url` varchar(500) default null comment '主文件地址',
`file_name` varchar(200) default null comment '主文件名',
`public_flag` tinyint default 2 comment '是否公开(1是2否)',
`complete_flag` tinyint default null comment '资料是否完整(1是2否)',
`submit_flag` tinyint default 2 comment '是否已报送监管(1是2否)',
`report_status_code` int default null comment '报告状态编码(1已入库2抽查中3待整改4已复核)',
`report_status_name` varchar(50) default null comment '报告状态名称',
`archive_time` datetime default null comment '入库时间',
`issue_date` date default null comment '报告签发日期',
`page_count` int default null comment '报告页数',
`secret_level_code` varchar(32) default null comment '保密属性编码',
`secret_level_name` varchar(50) default null comment '保密属性名称',
`delete_enum` varchar(32) default null comment '删除标识true false',
`remarks` varchar(255) default null comment '备注',
`create_name` varchar(50) default null comment '创建人姓名',
@ -120,19 +128,21 @@ create table `insp_notice` (
`notice_no` varchar(64) not null comment '告知编号',
`insp_type_code` varchar(32) default null comment '检查类型编码(PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查)',
`insp_type_name` varchar(50) default null comment '检查类型名称(项目过程检查/资质保持检查/专项检查)',
`source_type_code` int default null comment '来源方式编码(1年度计划2专项安排3其他)',
`source_type_code` int default null comment '来源方式编码(1年度计划2专项安排3其他4风险预警)',
`source_type_name` varchar(50) default null comment '来源方式名称',
`source_ref_id` bigint default null comment '来源业务id(可选)',
`source_ref_no` varchar(64) default null comment '来源预警/业务编号(如W-PROJ-2026-009)',
`project_id` bigint default null comment '关联项目id',
`plan_insp_time` datetime default null comment '计划检查时间',
`leader_name` varchar(50) default null comment '检查组长',
`notice_content` varchar(2000) default null comment '告知内容',
`status_code` int default null comment '状态编码(1已下发2已确认3已反馈4已办结)',
`notice_urls` varchar(2000) default null comment '告知附件URL逗号分隔',
`status_code` int default null comment '状态编码(0待下发1已下发2已确认3已反馈4已办结)',
`status_name` varchar(50) default null comment '状态名称',
`confirm_flag` tinyint default 2 comment '机构是否确认(1是2否)',
`confirm_time` datetime default null comment '确认时间',
`send_time` datetime default null comment '下发时间',
`result_code` int default null comment '检查结果编码(1合格2不合格3限期整改)',
`result_code` int default null comment '检查结果编码(1未发现问题/合格2责令整改/不合格3转入风险预警/限期整改)',
`result_name` varchar(50) default null comment '检查结果名称',
`result_content` varchar(2000) default null comment '结果说明',
`result_urls` varchar(2000) default null comment '结果附件',

View File

@ -30,22 +30,30 @@ flowchart TB
POST_GBS_LINK --> GBS_ROLE_REF
```
## 2. 硬性约束
1. **存在硬编码角色权限的业务逻辑时**,必须先在 GBS 创建对应角色,再在本系统 `GbsRoleEnum` 中登记;业务代码只从枚举取值,禁止散落硬编码角色码字符串。
2. **业务系统通过岗位与 GBS 角色关联**;当岗位重新分配 GBS 角色、岗位编辑且已有 GBS 角色关联且岗位下存在人员、或人员岗位发生变更且新岗位已关联 GBS 角色时,触发对人员 GBS 角色的更新(覆盖式 `updateUserRole`)。
3. GBS `UserFacade` **当前无批量改角色接口**,实现上优先探测批量能力;若无则 **依次** 调用 `gbsUserFacadeClient.updateUserRole`(封装为 `updateUserRolesSequentially`)。
## 3. 数据模型
`org_position` 新增字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `gbs_role_id` | bigint | 关联 GBS 角色ID |
| `gbs_role_code` | varchar(64) | 关联 GBS 角色编码 |
| 字段 | 类型 | 说明 |
| --------------- | ------------ | ---------------- |
| `gbs_role_id` | bigint | 关联 GBS 角色ID |
| `gbs_role_code` | varchar(64) | 关联 GBS 角色编码 |
| `gbs_role_name` | varchar(100) | 关联 GBS 角色名称(展示用) |
MySQL全量
```sql
@ -61,16 +69,20 @@ ALTER TABLE `org_position`
## 4. 接口
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/safetyEval/org-position/assign-gbs-role` | 为岗位分配 GBS 角色,并同步岗位下人员 |
| GET | `/safetyEval/org-position/gbs-roles` | 拉取 GBS 角色列表(下拉) |
| POST | `/safetyEval/org-position/modify` | 原编辑接口;若岗位已有 GBS 角色且有人员,则同步人员角色 |
| 方法 | 路径 | 说明 |
| ---- | ------------------------------------------ | ------------------------------ |
| POST | `/safetyEval/org-position/assign-gbs-role` | 为岗位分配 GBS 角色,并同步岗位下人员 |
| GET | `/safetyEval/org-position/gbs-roles` | 拉取 GBS 角色列表(下拉) |
| POST | `/safetyEval/org-position/modify` | 原编辑接口;若岗位已有 GBS 角色且有人员,则同步人员角色 |
分配入参 `OrgPositionAssignGbsRoleCmd``id`、`gbsRoleId`、`gbsRoleCode`、`gbsRoleName`(可选,空则尝试按 code 反查名称)。
## 5. 业务流程
### 5.1 分配角色
```mermaid
@ -93,6 +105,10 @@ sequenceDiagram
EXE-->>UI: 岗位 CO含角色
```
### 5.2 编辑岗位时同步
```mermaid
@ -106,6 +122,10 @@ flowchart TD
E --> Z
```
### 5.4 人员新增时角色
```mermaid
@ -117,6 +137,10 @@ flowchart TD
C -->|否| D
```
### 5.3 硬编码角色使用约定
```mermaid
@ -127,6 +151,10 @@ flowchart LR
CODE --> LOGIC[开展业务逻辑]
```
## 6. 前端使用流程
路径:`/safetyEval/container/EnterpriseInfo/DepartmentPosition`
@ -136,13 +164,18 @@ flowchart LR
3. 弹窗从 `/gbs-roles` 加载下拉,选择后提交 `/assign-gbs-role`
4. 若该岗位已有人员,后端依次更新其 GBS 角色后返回成功。
## 7. 关键代码索引
| 层级 | 位置 |
|------|------|
| 枚举 | `client/enums/GbsRoleEnum` |
| API | `OrgPositionApi#assignGbsRole` / `#listGbsRoles` |
| 执行 | `OrgPositionExecutor` |
| 层级 | 位置 |
| ------ | --------------------------------------------------------------------- |
| 枚举 | `client/enums/GbsRoleEnum` |
| API | `OrgPositionApi#assignGbsRole` / `#listGbsRoles` |
| 执行 | `OrgPositionExecutor` |
| GBS 用户 | `GbsUserFacadeClient#updateUserRole` / `#updateUserRolesSequentially` |
| GBS 角色 | `GbsRoleFacadeClient#listRoles` |
| 前端 | `DepartmentPosition` + `api/orgPosition` |
| GBS 角色 | `GbsRoleFacadeClient#listRoles` |
| 前端 | `DepartmentPosition` + `api/orgPosition` |

View File

@ -0,0 +1,50 @@
# 机构端报告库 · 原型对照与缺口说明
> 原型模块:`mod-institution-report-library``institution.html`
> OpenAPIApifox[`docs/openapi/机构端-安评报告库.openapi.yaml`](../openapi/机构端-安评报告库.openapi.yaml)
> 库表增量:[`docs/db/alter-eval-report-library-fields.sql`](../db/alter-eval-report-library-fields.sql)
## 1. 原型展示与功能清单
| 区域 | 原型内容 | 后端 |
|------|----------|------|
| 页头 | 标题 +「上传历史报告」 | `POST /add` |
| 汇总卡 | 总数 / 近三年 / 已报送 / 待完善 / 抽查中 | `GET /summary` |
| 筛选 | 名称编号、年度、评价类型、行业、报送状态 | `GET /page` 参数 |
| 列表列 | 编号、名称、年度、类型、行业、被评价单位、签发日期、报送状态 | `EvalReportLibraryCO` |
| 操作 | 查看、下载 | `GET /get``fileUrl` 下载) |
| 上传弹窗 | 文件+编号/名称/年度/类型/行业/单位/签发日/页数/是否报送/保密/备注 | `POST /add` |
| 报送 | 未报送→已报送 | `POST /submit` |
| 抽查整改 | (列表详情联动) | `/eval-report-spotcheck/**` |
## 2. 字段映射
| 原型 | 字段 |
|------|------|
| 所属行业 | `industry_code` / `industry_name` |
| 被评价单位 | `evaluated_unit_name` |
| 签发日期 | `issue_date` |
| 页数 | `page_count` |
| 保密属性 | `secret_level_code` / `secret_level_name` |
| 是否报送监管 | `submit_flag`1是 2否 |
| 列表报送状态文案 | `displayStatusName`(计算:未报送/已报送/抽查中/整改中/已复核) |
| 抽查流转状态 | `report_status_code`1~4既有 |
## 3. 接口一览
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/safetyEval/institution/eval-report/summary` | 汇总 |
| GET | `/safetyEval/institution/eval-report/page` | 分页 |
| GET | `/safetyEval/institution/eval-report/get` | 详情 |
| POST | `/safetyEval/institution/eval-report/add` | 上传历史报告 |
| POST | `/safetyEval/institution/eval-report/submit` | 报送监管 |
| GET | `/safetyEval/institution/eval-report-spotcheck/list` | 抽查列表 |
| GET | `/safetyEval/institution/eval-report-spotcheck/get` | 抽查详情 |
| POST | `/safetyEval/institution/eval-report-spotcheck/rectify` | 整改反馈 |
## 4. 上线注意
1. 执行 `alter-eval-report-library-fields.sql`(存量库)。
2. 历史数据 `submit_flag` 默认 2过程控制归档/监管补录写入时置 1。
3. 上传文件走统一 OSS/文件上传拿 URL本接口只存元数据与 URL。

View File

@ -0,0 +1,61 @@
# 监督检查告知 / 通知 — 原型对照
## 模块映射
| 端 | 原型 | 后端 |
|----|------|------|
| 监管 | `mod-inspection-notice` 监督检查告知 | `/safetyEval/regulator/insp-notice/**` |
| 机构 | 消息入口 `mod-reg-message``pageCode=reg-message``bizType=insp_notice`+ 告知办理 | `/safetyEval/institution/insp-notice/**` |
说明:机构端原型页「监督检查通知」是**监管消息中心**,除监督检查告知外,还包含风险预警产生的整改通知、核查函、补正通知等。后者属风险预警域,**不在本 insp_notice 接口范围内**。本接口仅覆盖 `insp_notice` 业务闭环。
## 监管端字段与能力
| 原型 | 后端 |
|------|------|
| 筛选:告知编号、评价机构、检查类型、来源方式、来源预警编号、检查负责人、检查时间起止、机构确认、材料反馈、办理状态 | `GET /page` 对应 query |
| 列表:编号/机构/类型/计划时间/来源/负责人/确认/反馈/状态 | `RegulatorInspNoticeCO`(含 `orgName`、`sourceRefNo`、`feedbackFlag` |
| 新建 / 编辑待下发 | `POST /save`status=0 |
| 下发 | `POST /issue`(新建直发,或传 `id` 下发草稿) |
| 查看 / 保存备注 | `GET /get`、`POST /remark` |
| 登记结果 | `POST /close` |
| 提醒 | `POST /urge` |
| 下载记录 | `GET /download-record`CSV |
| 导出台账 | `POST /export-ledger`CSV |
| 材料只读 | `GET /materials` |
### 状态
| 原型 | statusCode | statusName |
|------|------------|------------|
| 待下发 | 0 | 待下发 |
| 已下发 | 1 | 已下发 |
| (机构已确认,列表可仍归「已下发」类展示) | 2 | 已确认 |
| 已反馈 | 3 | 已反馈 |
| 已完成 | 4 | 已办结 |
### 来源方式
| 原型 | sourceTypeCode |
|------|----------------|
| 年度计划 | 1 |
| 专项安排 | 2 |
| 其他 | 3 |
| 风险预警 | 4 |
## 机构端insp_notice能力
| 能力 | 接口 |
|------|------|
| 本机构告知分页(不含草稿) | `GET /page` |
| 详情(含结果、监管备注) | `GET /get` |
| 确认 | `POST /confirm` |
| 上传材料 URL | `POST /material/upload` |
| 提交材料反馈 | `POST /feedback` |
| 材料列表 | `GET /materials` |
消息推送:下发/催办 → 机构消息(`pageCode=reg-message`);反馈 → 监管消息(`pageCode=inspection-notice`)。详见 `plans/phase2-prototype-api/baseline/p3-insp-rules.md`
## 库表增量
执行 `docs/db/alter-insp-notice-prototype-fields.sql``source_ref_no`、`notice_urls`)。

View File

@ -0,0 +1,44 @@
# 监管端安评报告数据库 · 原型对照
> 原型:`regulator.html` → `mod-report-library`
> OpenAPI[`docs/openapi/监管端-安评报告数据库.openapi.yaml`](../openapi/监管端-安评报告数据库.openapi.yaml)
> 机构端对照:[`docs/openapi/机构端-安评报告库.openapi.yaml`](../openapi/机构端-安评报告库.openapi.yaml)
## 1. 原型能力
| 区域 | 内容 | 后端策略 |
|------|------|----------|
| 说明 | 接收机构正式报告AI 辅助研判 | AI **不做后端** |
| 汇总 | 近三年 / 本年度 | `GET /summary` |
| 筛选 | 名称编号、报送机构、年度、类型、行业、报送时间起止 | `GET /page` |
| 列表 | 编号、名称、机构、类型、行业、签发日、报送时间 | CO 含 `orgName` 等 |
| 操作 | 查看报告、AI分析 | 查看=`/get`AI=前端静态 |
| 工具栏 | 批量下载、导出目录 | `POST /batch-download`ZIP、`POST /export-catalog`CSV |
| 补录 | (业务闭环需要) | `POST /add`,字段对齐机构端 |
| 抽查闭环 | 本页列表无直出按钮,但与机构整改对应 | `/eval-report-spotcheck/**` |
## 2. 与机构端对应关系
```text
机构上传/归档/报送(submit_flag=1)
监管报告数据库可见(默认仅已报送)
监管发起抽查 → 机构整改 → 监管复核
```
| 机构端 | 监管端 |
|--------|--------|
| `POST .../eval-report/add`(可仅存库) | 库中默认不可见直至报送 |
| `POST .../eval-report/submit` | 进入监管库 |
| 过程控制 archive | `submit_flag=1` 进入监管库 |
| `POST .../spotcheck/rectify` | `POST .../spotcheck/start` + `review` |
## 3. 本次相对原 P4 的补齐
- 汇总接口、关键词/机构名/行业/报送时间筛选
- 列表回填 `orgName`
- 补录字段与机构端上传对齐(行业、被评价单位、签发日、页数、保密等)
- **批量下载**:按勾选 ID 打包 ZIP最多 50 份,拉取各报告 `fileUrl`
- **导出目录**CSVExcel 可开);勾选优先,否则按当前筛选/全部已报送(最多 5000 行)
- 独立 Apifox OpenAPI含抽查闭环与上述导出下载

View File

@ -0,0 +1,412 @@
openapi: 3.0.3
info:
title: 机构端-安评报告库 API
description: |
对齐原型页 `institution-report-library`(机构端「报告库」)的展示与功能。
## 原型能力对照
| 原型 | 接口 |
|------|------|
| 顶栏汇总(总数/近三年/已报送/待完善/抽查中) | `GET /summary` |
| 列表筛选(名称编号/年度/评价类型/行业/报送状态) | `GET /page` |
| 查看详情 | `GET /get` |
| 下载 | 详情 `fileUrl` 由前端下载(无单独下载流接口) |
| 上传历史报告 | `POST /add`(文件先走统一上传拿 URL |
| 报送监管 | `POST /submit` |
| 抽查记录/整改 | `/eval-report-spotcheck/**` |
**Apifox 导入**:项目设置 → 导入数据 → OpenAPI → 选择本文件。
统一前缀:`/safetyEval/institution`
成功:`success=true` 且 `code="0"`
version: 1.1.0
contact:
name: safety-eval-service
servers:
- url: http://localhost:8095
description: 本地开发
- url: http://192.168.20.100:30140
description: 测试网关100
tags:
- name: 机构端-安评报告库
description: 报告库列表/详情/上传/报送/汇总
- name: 机构端-报告抽查整改
description: 抽查记录查询与整改反馈
paths:
/safetyEval/institution/eval-report/summary:
get:
tags: [机构端-安评报告库]
summary: 报告库页顶栏汇总
operationId: institutionEvalReportSummary
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInstitutionEvalReportSummaryCO'
example:
success: true
code: "0"
data:
totalCount: 126
recentThreeYearCount: 98
submittedCount: 86
incompleteCount: 5
spotcheckingCount: 3
/safetyEval/institution/eval-report/page:
get:
tags: [机构端-安评报告库]
summary: 分页查询本机构报告
operationId: institutionEvalReportPage
parameters:
- $ref: '#/components/parameters/current'
- $ref: '#/components/parameters/size'
- name: keyword
in: query
description: 报告名称 / 编号
schema: { type: string }
- name: reportYear
in: query
schema: { type: integer, example: 2026 }
- name: evalTypeCode
in: query
description: PRE / ACCEPT / STATUS
schema: { type: string, example: PRE }
- name: industryCode
in: query
description: 所属行业编码,如 HAZCHEM
schema: { type: string }
- name: reportStatusCode
in: query
description: 1已入库 2抽查中 3待整改 4已复核
schema: { type: integer }
- name: displayStatus
in: query
description: |
原型「报送状态」筛选:
`UN_SUBMITTED` 未报送 /
`SUBMITTED` 已报送 /
`SPOTCHECKING` 抽查中 /
`RECTIFYING` 整改中 /
`REVIEWED` 已复核
schema:
type: string
enum: [UN_SUBMITTED, SUBMITTED, SPOTCHECKING, RECTIFYING, REVIEWED]
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/PageResponseEvalReportLibraryCO'
/safetyEval/institution/eval-report/get:
get:
tags: [机构端-安评报告库]
summary: 报告详情
description: 含 `fileUrl`/`fileName`,前端下载直接使用文件地址。
operationId: institutionEvalReportGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportLibraryCO'
/safetyEval/institution/eval-report/add:
post:
tags: [机构端-安评报告库]
summary: 上传历史报告
description: |
对齐原型弹窗「上传历史报告」。
文件请先调用统一文件上传接口拿到 `fileUrl` 再提交本接口。
`submitToRegulator=true` 时 `submitFlag=1`(已报送);否则 `submitFlag=2`(未报送)。
同机构下 `reportNo` 不可重复。
operationId: institutionEvalReportAdd
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/InstitutionEvalReportAddCmd'
example:
reportNo: CQAP-XZ-2026-001
reportName: 华安化工园区建设项目安全预评价报告
reportYear: 2026
evalTypeCode: PRE
evalTypeName: 安全预评价
industryCode: HAZCHEM
industryName: 危险化学品
evaluatedUnitName: 重庆华安化工有限公司
issueDate: '2026-07-16'
pageCount: 186
fileUrl: https://oss.example.com/reports/xxx.pdf
fileName: 华安化工预评价.pdf
secretLevelCode: NORMAL
secretLevelName: 普通
remarks: 正式签章版
submitToRegulator: false
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportLibraryCO'
/safetyEval/institution/eval-report/submit:
post:
tags: [机构端-安评报告库]
summary: 报送监管
description: 将 `submitFlag=2`(未报送)的报告改为已报送;已报送则报错 06-07-009。
operationId: institutionEvalReportSubmit
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/InstitutionEvalReportSubmitCmd'
example:
id: 10001
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportLibraryCO'
/safetyEval/institution/eval-report-spotcheck/list:
get:
tags: [机构端-报告抽查整改]
summary: 按报告查询抽查记录
operationId: institutionSpotcheckList
parameters:
- name: reportId
in: query
required: true
schema: { type: integer, format: int64 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCOList'
/safetyEval/institution/eval-report-spotcheck/get:
get:
tags: [机构端-报告抽查整改]
summary: 抽查详情
operationId: institutionSpotcheckGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCO'
/safetyEval/institution/eval-report-spotcheck/rectify:
post:
tags: [机构端-报告抽查整改]
summary: 提交整改反馈
description: 报告须处于抽查中且抽查结果为不合格。
operationId: institutionSpotcheckRectify
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/InstitutionSpotcheckRectifyCmd'
example:
spotcheckId: 20001
rectifyFeedback: 已按抽查意见完成修订并重新盖章
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCO'
components:
parameters:
current:
name: current
in: query
description: 页码(从 1 开始)
schema: { type: integer, default: 1 }
size:
name: size
in: query
description: 每页条数
schema: { type: integer, default: 10 }
schemas:
SingleResponseBase:
type: object
properties:
success: { type: boolean, example: true }
code: { type: string, example: "0" }
message: { type: string }
errCode: { type: string }
errMessage: { type: string }
EvalReportLibraryCO:
type: object
properties:
id: { type: string }
orgId: { type: string }
projectId: { type: string }
customerId: { type: string }
evaluatedUnitName: { type: string, description: 被评价单位 }
reportNo: { type: string }
reportName: { type: string }
reportYear: { type: integer }
evalTypeCode: { type: string, example: PRE }
evalTypeName: { type: string, example: 安全预评价 }
industryCode: { type: string, example: HAZCHEM }
industryName: { type: string, example: 危险化学品 }
businessScope: { type: string }
fileUrl: { type: string }
fileName: { type: string }
publicFlag: { type: integer, description: "1是2否" }
completeFlag: { type: integer, description: "1完整2待完善" }
submitFlag: { type: integer, description: "1已报送2未报送" }
reportStatusCode: { type: integer, description: "1已入库2抽查中3待整改4已复核" }
reportStatusName: { type: string }
displayStatusName:
type: string
description: 列表展示报送状态(未报送/已报送/抽查中/整改中/已复核)
example: 已报送
archiveTime: { type: string, format: date-time }
issueDate: { type: string, format: date, description: 签发日期 }
pageCount: { type: integer }
secretLevelCode: { type: string, example: NORMAL }
secretLevelName: { type: string, example: 普通 }
remarks: { type: string }
InstitutionEvalReportSummaryCO:
type: object
properties:
totalCount: { type: integer, format: int64 }
recentThreeYearCount: { type: integer, format: int64 }
submittedCount: { type: integer, format: int64 }
incompleteCount: { type: integer, format: int64 }
spotcheckingCount: { type: integer, format: int64 }
InstitutionEvalReportAddCmd:
type: object
required: [reportNo, reportName, evaluatedUnitName, issueDate, fileUrl]
properties:
reportNo: { type: string }
reportName: { type: string }
reportYear: { type: integer }
evalTypeCode: { type: string }
evalTypeName: { type: string }
industryCode: { type: string }
industryName: { type: string }
evaluatedUnitName: { type: string }
customerId: { type: integer, format: int64 }
issueDate: { type: string, format: date }
pageCount: { type: integer }
fileUrl: { type: string }
fileName: { type: string }
secretLevelCode: { type: string }
secretLevelName: { type: string }
remarks: { type: string }
submitToRegulator: { type: boolean, description: true=同步报送监管 }
InstitutionEvalReportSubmitCmd:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
EvalReportSpotcheckCO:
type: object
properties:
id: { type: string }
orgId: { type: string }
reportId: { type: string }
checkResultCode: { type: integer, description: "1合格2不合格" }
checkResultName: { type: string }
checkOpinion: { type: string }
rectifyRequire: { type: string }
rectifyFeedback: { type: string }
reviewResultCode: { type: integer, description: "1通过2不通过" }
reviewResultName: { type: string }
checkerId: { type: string }
checkerName: { type: string }
checkTime: { type: string, format: date-time }
InstitutionSpotcheckRectifyCmd:
type: object
required: [spotcheckId, rectifyFeedback]
properties:
spotcheckId: { type: integer, format: int64 }
rectifyFeedback: { type: string }
SingleResponseEvalReportLibraryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
$ref: '#/components/schemas/EvalReportLibraryCO'
SingleResponseInstitutionEvalReportSummaryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
$ref: '#/components/schemas/InstitutionEvalReportSummaryCO'
PageResponseEvalReportLibraryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/EvalReportLibraryCO'
total:
type: integer
format: int64
SingleResponseEvalReportSpotcheckCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
$ref: '#/components/schemas/EvalReportSpotcheckCO'
SingleResponseEvalReportSpotcheckCOList:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/EvalReportSpotcheckCO'

View File

@ -0,0 +1,726 @@
openapi: 3.0.3
info:
title: 监督检查告知 / 通知 API
description: |
对齐原型:
- 监管端 `mod-inspection-notice`(监督检查告知)
- 机构端消息入口 `reg-message` 中 `bizType=insp_notice` 的办理能力
## 原型能力对照(监管)
| 原型 | 后端 |
|------|------|
| 筛选编号/机构/类型/来源/预警号/负责人/时间/确认/反馈/状态 | `GET .../regulator/insp-notice/page` |
| 列表含机构名、来源预警号、确认与反馈 | `RegulatorInspNoticeCO` |
| 新建/编辑待下发 | `POST .../save` |
| 下发 | `POST .../issue`(新建或 `id` 下发草稿) |
| 查看 / 保存备注 | `GET .../get`、`POST .../remark` |
| 登记结果 | `POST .../close` |
| 提醒 | `POST .../urge` |
| 下载记录 | `GET .../download-record` |
| 导出台账 | `POST .../export-ledger` |
| 材料只读 | `GET .../materials` |
## 原型能力对照(机构 · insp_notice
| 能力 | 后端 |
|------|------|
| 本机构告知列表/详情 | `GET .../institution/insp-notice/page|get` |
| 确认 / 上传材料 / 反馈 | `POST .../confirm|material/upload|feedback` |
| 材料列表 | `GET .../materials` |
**说明**:机构端原型「监督检查通知」整页还聚合风险预警类整改/核查/补正消息,不在本文件范围内。
**字典**
- 状态:`0待下发 1已下发 2已确认 3已反馈 4已办结`(原型「已完成」=`4`
- 来源:`1年度计划 2专项安排 3其他 4风险预警`
- 结果:`1未发现问题/合格 2责令整改/不合格 3转入风险预警/限期整改`
- 确认/反馈:`1是/已提交 2否/未提交`
**Apifox 导入**:项目设置 → 导入数据 → OpenAPI → 选择本文件。
成功:`success=true` 且 `code="0"`(文件流接口直接返回附件,失败时返回 JSON
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: 告知新建/下发/跟踪/办结/导出
- name: 机构端-监督检查告知
description: 确认、材料反馈、查看结果
paths:
/safetyEval/regulator/insp-notice/save:
post:
tags: [监管端-监督检查告知]
summary: 暂存/编辑待下发告知
operationId: regulatorInspNoticeSave
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeSaveCmd'
example:
orgId: 10001
inspTypeCode: SPECIAL
inspTypeName: 专项检查
sourceTypeCode: 1
sourceTypeName: 年度计划
planInspTime: "2026-08-05T09:00:00"
leaderName: 周强
noticeContent: 请准备专项检查材料
materialNames: [项目过程记录, 人员到场记录]
responses:
'200':
description: 成功status=0 待下发)
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/issue:
post:
tags: [监管端-监督检查告知]
summary: 新建并下发,或下发草稿
description: |
- 传 `id`:将 status=0 草稿下发为已下发,并推送机构消息
- 不传 `id`:直接新建并下发(需 `orgId`
operationId: regulatorInspNoticeIssue
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeIssueCmd'
examples:
issueDraft:
summary: 下发草稿
value: { id: 20001 }
createAndIssue:
summary: 新建并下发
value:
orgId: 10001
inspTypeCode: PROJECT_PROCESS
inspTypeName: 项目过程检查
sourceTypeCode: 4
sourceTypeName: 风险预警
sourceRefNo: W-PROJ-2026-009
planInspTime: "2026-07-22T09:30:00"
leaderName: 刘敏
noticeContent: 请准备项目过程记录、人员到场记录及相关证明材料
materialNames: [联系人及准备材料清单]
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/page:
get:
tags: [监管端-监督检查告知]
summary: 分页查询告知
operationId: regulatorInspNoticePage
parameters:
- $ref: '#/components/parameters/current'
- $ref: '#/components/parameters/size'
- name: noticeNo
in: query
schema: { type: string }
- name: orgId
in: query
schema: { type: integer, format: int64 }
- name: orgName
in: query
description: 评价机构名称(模糊)
schema: { type: string }
- name: keyword
in: query
schema: { type: string }
- name: inspTypeCode
in: query
schema: { type: string, enum: [PROJECT_PROCESS, QUAL_KEEP, SPECIAL] }
- name: sourceTypeCode
in: query
schema: { type: integer, enum: [1, 2, 3, 4] }
- name: sourceRefNo
in: query
description: 来源预警编号
schema: { type: string }
- name: leaderName
in: query
schema: { type: string }
- name: planInspTimeStart
in: query
description: yyyy-MM-dd
schema: { type: string }
- name: planInspTimeEnd
in: query
description: yyyy-MM-dd
schema: { type: string }
- name: confirmFlag
in: query
description: 1已确认 2未确认
schema: { type: integer, enum: [1, 2] }
- name: feedbackFlag
in: query
description: 1已提交 2未提交
schema: { type: integer, enum: [1, 2] }
- name: statusCode
in: query
description: 0待下发 1已下发 2已确认 3已反馈 4已办结
schema: { type: integer, enum: [0, 1, 2, 3, 4] }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/PageResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/get:
get:
tags: [监管端-监督检查告知]
summary: 查询告知详情
operationId: regulatorInspNoticeGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/close:
post:
tags: [监管端-监督检查告知]
summary: 登记结果并办结
description: 前置状态须为已确认(2)或已反馈(3)
operationId: regulatorInspNoticeClose
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeCloseCmd'
example:
id: 20001
resultCode: 2
resultName: 责令整改
resultContent: 现场检查发现问题,限期整改
resultUrls: ["https://example.com/record.pdf"]
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/remark:
post:
tags: [监管端-监督检查告知]
summary: 保存监管备注
operationId: regulatorInspNoticeRemark
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeRemarkCmd'
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorInspNoticeCO'
/safetyEval/regulator/insp-notice/materials:
get:
tags: [监管端-监督检查告知]
summary: 材料只读列表
operationId: regulatorInspNoticeMaterials
parameters:
- name: noticeId
in: query
required: true
schema: { type: integer, format: int64 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInspNoticeMaterialCOList'
/safetyEval/regulator/insp-notice/urge:
post:
tags: [监管端-监督检查告知]
summary: 催办(重发机构消息)
operationId: regulatorInspNoticeUrge
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeUrgeCmd'
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseVoid'
/safetyEval/regulator/insp-notice/export-ledger:
post:
tags: [监管端-监督检查告知]
summary: 导出台账CSV
description: 勾选优先;否则按筛选;最多 5000 行。直接下载文件。
operationId: regulatorInspNoticeExportLedger
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorInspNoticeExportCmd'
responses:
'200':
description: CSV 文件流;失败时 JSON
content:
text/csv:
schema: { type: string, format: binary }
application/json:
schema:
$ref: '#/components/schemas/SingleResponseBase'
/safetyEval/regulator/insp-notice/download-record:
get:
tags: [监管端-监督检查告知]
summary: 下载检查记录CSV
operationId: regulatorInspNoticeDownloadRecord
parameters:
- name: id
in: query
required: true
schema: { type: integer, format: int64 }
responses:
'200':
description: CSV 文件流;失败时 JSON
content:
text/csv:
schema: { type: string, format: binary }
application/json:
schema:
$ref: '#/components/schemas/SingleResponseBase'
/safetyEval/institution/insp-notice/page:
get:
tags: [机构端-监督检查告知]
summary: 分页查询本机构告知(不含待下发草稿)
operationId: institutionInspNoticePage
parameters:
- $ref: '#/components/parameters/current'
- $ref: '#/components/parameters/size'
- name: noticeNo
in: query
schema: { type: string }
- name: keyword
in: query
schema: { type: string }
- name: inspTypeCode
in: query
schema: { type: string }
- name: sourceTypeCode
in: query
schema: { type: integer }
- name: sourceRefNo
in: query
schema: { type: string }
- name: confirmFlag
in: query
schema: { type: integer }
- name: feedbackFlag
in: query
schema: { type: integer }
- name: statusCode
in: query
description: 1已下发 2已确认 3已反馈 4已办结
schema: { type: integer, enum: [1, 2, 3, 4] }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/PageResponseInstitutionInspNoticeCO'
/safetyEval/institution/insp-notice/get:
get:
tags: [机构端-监督检查告知]
summary: 查询告知详情
operationId: institutionInspNoticeGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInstitutionInspNoticeCO'
/safetyEval/institution/insp-notice/confirm:
post:
tags: [机构端-监督检查告知]
summary: 确认告知
description: 前置 status=1 已下发 → 2 已确认
operationId: institutionInspNoticeConfirm
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInstitutionInspNoticeCO'
/safetyEval/institution/insp-notice/material/upload:
post:
tags: [机构端-监督检查告知]
summary: 上传材料(仅 URL
description: 前置告知状态为已确认或已反馈
operationId: institutionInspNoticeMaterialUpload
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/InstitutionInspNoticeMaterialUploadCmd'
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInspNoticeMaterialCO'
/safetyEval/institution/insp-notice/feedback:
post:
tags: [机构端-监督检查告知]
summary: 提交材料反馈
description: 前置 status=2必填材料须已上传
operationId: institutionInspNoticeFeedback
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/InstitutionInspNoticeFeedbackCmd'
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInstitutionInspNoticeCO'
/safetyEval/institution/insp-notice/materials:
get:
tags: [机构端-监督检查告知]
summary: 材料列表
operationId: institutionInspNoticeMaterials
parameters:
- name: noticeId
in: query
required: true
schema: { type: integer, format: int64 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseInspNoticeMaterialCOList'
components:
parameters:
current:
name: current
in: query
schema: { type: integer, format: int64, default: 1 }
size:
name: size
in: query
schema: { type: integer, format: int64, default: 10 }
schemas:
SingleResponseBase:
type: object
properties:
success: { type: boolean, example: true }
code: { type: string, example: "0" }
errMessage: { type: string, nullable: true }
traceId: { type: string, nullable: true }
RegulatorInspNoticeCO:
type: object
properties:
id: { type: string }
orgId: { type: string }
orgName: { type: string, description: 评价机构名称 }
noticeNo: { type: string, example: JC2026000018 }
inspTypeCode: { type: string, example: PROJECT_PROCESS }
inspTypeName: { type: string, example: 项目过程检查 }
sourceTypeCode: { type: integer, example: 4 }
sourceTypeName: { type: string, example: 风险预警 }
sourceRefId: { type: string }
sourceRefNo: { type: string, example: W-PROJ-2026-009 }
projectId: { type: string }
planInspTime: { type: string, format: date-time }
leaderName: { type: string }
noticeContent: { type: string }
noticeUrls: { type: string }
statusCode: { type: integer, example: 3 }
statusName: { type: string, example: 已反馈 }
confirmFlag: { type: integer, example: 1 }
feedbackFlag: { type: integer, example: 1, description: 1已提交 2未提交 }
confirmTime: { type: string, format: date-time }
sendTime: { type: string, format: date-time }
resultCode: { type: integer }
resultName: { type: string }
resultContent: { type: string }
resultUrls: { type: string }
resultRecorderName: { type: string }
resultTime: { type: string, format: date-time }
remarks: { type: string }
InstitutionInspNoticeCO:
type: object
properties:
id: { type: string }
noticeNo: { type: string }
inspTypeCode: { type: string }
inspTypeName: { type: string }
sourceTypeCode: { type: integer }
sourceTypeName: { type: string }
sourceRefNo: { type: string }
planInspTime: { type: string, format: date-time }
leaderName: { type: string }
noticeContent: { type: string }
noticeUrls: { type: string }
statusCode: { type: integer }
statusName: { type: string }
confirmFlag: { type: integer }
feedbackFlag: { type: integer }
confirmTime: { type: string, format: date-time }
sendTime: { type: string, format: date-time }
resultCode: { type: integer }
resultName: { type: string }
resultContent: { type: string }
resultUrls: { type: string }
resultTime: { type: string, format: date-time }
remarks: { type: string }
InspNoticeMaterialCO:
type: object
properties:
id: { type: string }
noticeId: { type: string }
materialName: { type: string }
requiredFlag: { type: integer, description: 1必需 2非必需 }
uploadStatusCode: { type: integer, description: 1待上传 2已上传 }
uploadStatusName: { type: string }
fileUrl: { type: string }
RegulatorInspNoticeSaveCmd:
type: object
required: [orgId]
properties:
id: { type: integer, format: int64, description: 空=新建草稿,非空=编辑待下发 }
orgId: { type: integer, format: int64 }
inspTypeCode: { type: string }
inspTypeName: { type: string }
sourceTypeCode: { type: integer }
sourceTypeName: { type: string }
sourceRefId: { type: integer, format: int64 }
sourceRefNo: { type: string }
projectId: { type: integer, format: int64 }
planInspTime: { type: string, format: date-time }
leaderName: { type: string }
noticeContent: { type: string }
noticeUrls:
type: array
items: { type: string }
remarks: { type: string }
materialNames:
type: array
items: { type: string }
description: 仅新建草稿时预置材料行
RegulatorInspNoticeIssueCmd:
type: object
properties:
id: { type: integer, format: int64, description: 传则下发草稿 }
orgId: { type: integer, format: int64, description: 新建下发时必填 }
inspTypeCode: { type: string }
inspTypeName: { type: string }
sourceTypeCode: { type: integer }
sourceTypeName: { type: string }
sourceRefId: { type: integer, format: int64 }
sourceRefNo: { type: string }
projectId: { type: integer, format: int64 }
planInspTime: { type: string, format: date-time }
leaderName: { type: string }
noticeContent: { type: string }
noticeUrls:
type: array
items: { type: string }
remarks: { type: string }
materialNames:
type: array
items: { type: string }
RegulatorInspNoticeCloseCmd:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
resultCode: { type: integer, description: "1未发现问题 2责令整改 3转入风险预警" }
resultName: { type: string }
resultContent: { type: string }
resultUrls:
type: array
items: { type: string }
RegulatorInspNoticeRemarkCmd:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
remarks: { type: string }
RegulatorInspNoticeUrgeCmd:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
RegulatorInspNoticeExportCmd:
type: object
properties:
ids:
type: array
items: { type: integer, format: int64 }
scope: { type: string, enum: [SELECTED, FILTERED, ALL] }
noticeNo: { type: string }
orgId: { type: integer, format: int64 }
orgName: { type: string }
keyword: { type: string }
inspTypeCode: { type: string }
sourceTypeCode: { type: integer }
sourceRefNo: { type: string }
leaderName: { type: string }
planInspTimeStart: { type: string }
planInspTimeEnd: { type: string }
confirmFlag: { type: integer }
feedbackFlag: { type: integer }
statusCode: { type: integer }
InstitutionInspNoticeMaterialUploadCmd:
type: object
required: [materialId, fileUrl]
properties:
materialId: { type: integer, format: int64 }
fileUrl: { type: string }
InstitutionInspNoticeFeedbackCmd:
type: object
required: [id]
properties:
id: { type: integer, format: int64 }
materials:
type: array
items:
$ref: '#/components/schemas/InstitutionInspNoticeMaterialUploadCmd'
SingleResponseRegulatorInspNoticeCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/RegulatorInspNoticeCO' }
SingleResponseInstitutionInspNoticeCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/InstitutionInspNoticeCO' }
PageResponseRegulatorInspNoticeCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/RegulatorInspNoticeCO' }
total: { type: integer, format: int64 }
PageResponseInstitutionInspNoticeCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/InstitutionInspNoticeCO' }
total: { type: integer, format: int64 }
SingleResponseInspNoticeMaterialCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/InspNoticeMaterialCO' }
SingleResponseInspNoticeMaterialCOList:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/InspNoticeMaterialCO' }
SingleResponseVoid:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { nullable: true }

View File

@ -0,0 +1,510 @@
openapi: 3.0.3
info:
title: 监管端-安评报告数据库 API
description: |
对齐监管端原型页 `mod-report-library`(安评报告数据库)。
## 原型能力对照
| 原型 | 后端 |
|------|------|
| 近三年报告 / 本年度 | `GET /summary` |
| 筛选:名称编号、报送机构、年度、评价类型、行业、报送时间起止 | `GET /page` |
| 列表列:编号/名称/机构/类型/行业/签发日/报送时间 | `EvalReportLibraryCO`(含 `orgName` |
| 查看报告 | `GET /get``fileUrl` |
| AI分析 | **前端静态,无后端接口** |
| 批量下载 / 导出目录 | `POST /batch-download`ZIP、`POST /export-catalog`CSV |
| 补录历史报告 | `POST /add`(与机构端上传字段对齐,强制已报送) |
| 抽查发起/复核(与机构端整改闭环,本页列表未直出按钮) | `/eval-report-spotcheck/**` |
与机构端关系:机构 `submit`/`archive`/`add(submitToRegulator)` → `submit_flag=1` 后进入监管库;监管抽查 → 机构整改 → 监管复核。
**Apifox 导入**:项目设置 → 导入数据 → OpenAPI → 选择本文件。
成功:`success=true` 且 `code="0"`(文件流接口直接返回附件,失败时返回 JSON
version: 1.1.0
contact:
name: safety-eval-service
servers:
- url: http://localhost:8095
description: 本地开发
- url: http://192.168.20.100:30140
description: 测试网关100
tags:
- name: 监管端-安评报告库
description: 报告数据库列表/详情/汇总/补录
- name: 监管端-报告抽查
description: 发起抽查与复核(机构整改闭环)
paths:
/safetyEval/regulator/eval-report/summary:
get:
tags: [监管端-安评报告库]
summary: 报告数据库页顶栏汇总
operationId: regulatorEvalReportSummary
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseRegulatorEvalReportSummaryCO'
example:
success: true
code: "0"
data:
recentThreeYearCount: 286
currentYearCount: 92
submittedTotalCount: 310
/safetyEval/regulator/eval-report/page:
get:
tags: [监管端-安评报告库]
summary: 分页查询已报送报告
description: 默认仅返回 `submitFlag=1`(机构已报送/归档入库)的报告。
operationId: regulatorEvalReportPage
parameters:
- $ref: '#/components/parameters/current'
- $ref: '#/components/parameters/size'
- name: keyword
in: query
description: 报告名称 / 编号
schema: { type: string }
- name: orgId
in: query
description: 机构 id精确
schema: { type: integer, format: int64 }
- name: orgName
in: query
description: 报送机构名称(模糊,按机构表 unitName 解析)
schema: { type: string }
- name: reportYear
in: query
schema: { type: integer, example: 2026 }
- name: evalTypeCode
in: query
description: PRE / ACCEPT / STATUS
schema: { type: string, example: PRE }
- name: industryCode
in: query
schema: { type: string, example: HAZCHEM }
- name: reportStatusCode
in: query
description: 1已入库 2抽查中 3待整改 4已复核
schema: { type: integer }
- name: archiveTimeStart
in: query
description: 报送时间起 yyyy-MM-dd
schema: { type: string, example: '2026-01-01' }
- name: archiveTimeEnd
in: query
description: 报送时间止 yyyy-MM-dd
schema: { type: string, example: '2026-12-31' }
- name: submitFlag
in: query
description: 默认 1一般无需传
schema: { type: integer, example: 1 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/PageResponseEvalReportLibraryCO'
/safetyEval/regulator/eval-report/get:
get:
tags: [监管端-安评报告库]
summary: 报告详情
operationId: regulatorEvalReportGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportLibraryCO'
/safetyEval/regulator/eval-report/add:
post:
tags: [监管端-安评报告库]
summary: 补录历史报告
description: |
监管侧补录;`submitFlag` 固定为已报送。
字段与机构端上传对齐(行业、被评价单位、签发日、页数、保密等)。
文件先走统一上传拿 `fileUrl`。
operationId: regulatorEvalReportAdd
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorEvalReportAddCmd'
example:
orgId: 10001
reportNo: CQAP-2026-0715
reportName: 华安化工园区安全预评价报告
reportYear: 2026
evalTypeCode: PRE
evalTypeName: 安全预评价
industryCode: HAZCHEM
industryName: 危险化学品
evaluatedUnitName: 重庆华安化工有限公司
issueDate: '2026-07-15'
pageCount: 186
fileUrl: https://oss.example.com/reports/xxx.pdf
fileName: 华安化工预评价.pdf
secretLevelCode: NORMAL
secretLevelName: 普通
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportLibraryCO'
/safetyEval/regulator/eval-report/batch-download:
post:
tags: [监管端-安评报告库]
summary: 批量下载报告文件ZIP
description: |
按勾选报告 ID 拉取各自 `fileUrl` 打包为 ZIP。
上限 50 份;无有效文件地址时报错 06-07-012。
成功直接返回 `application/zip` 附件流。
operationId: regulatorEvalReportBatchDownload
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorEvalReportBatchDownloadCmd'
example:
ids: [10001, 10002, 10003]
responses:
'200':
description: ZIP 文件流,或业务失败时的 JSON
content:
application/zip:
schema:
type: string
format: binary
application/json:
schema:
$ref: '#/components/schemas/SingleResponseBase'
/safetyEval/regulator/eval-report/export-catalog:
post:
tags: [监管端-安评报告库]
summary: 导出报告目录CSV
description: |
导出「安评报告数据库目录」。
- `ids` 非空或 `scope=SELECTED`:导出勾选
- `scope=FILTERED`(默认无 ids导出当前筛选结果
- `scope=ALL`:导出全部已报送
上限 5000 行UTF-8 BOMExcel 可直接打开。
operationId: regulatorEvalReportExportCatalog
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorEvalReportExportCmd'
example:
scope: FILTERED
reportYear: 2026
industryCode: HAZCHEM
responses:
'200':
description: CSV 文件流,或业务失败时的 JSON
content:
text/csv:
schema:
type: string
format: binary
application/json:
schema:
$ref: '#/components/schemas/SingleResponseBase'
/safetyEval/regulator/eval-report-spotcheck/start:
post:
tags: [监管端-报告抽查]
summary: 发起抽查
description: 报告须已入库(status=1);不合格须填整改要求,并消息通知机构。
operationId: regulatorSpotcheckStart
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorSpotcheckStartCmd'
example:
reportId: 10001
checkResultCode: 2
checkResultName: 不合格
checkOpinion: 危险辨识不完整
rectifyRequire: 请补充重大危险源辨识章节并重新签章
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCO'
/safetyEval/regulator/eval-report-spotcheck/review:
post:
tags: [监管端-报告抽查]
summary: 复核抽查
operationId: regulatorSpotcheckReview
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegulatorSpotcheckReviewCmd'
example:
spotcheckId: 20001
reviewResultCode: 1
reviewResultName: 通过
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCO'
/safetyEval/regulator/eval-report-spotcheck/list:
get:
tags: [监管端-报告抽查]
summary: 按报告查询抽查记录
operationId: regulatorSpotcheckList
parameters:
- name: reportId
in: query
required: true
schema: { type: integer, format: int64 }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCOList'
/safetyEval/regulator/eval-report-spotcheck/get:
get:
tags: [监管端-报告抽查]
summary: 抽查详情
operationId: regulatorSpotcheckGet
parameters:
- name: id
in: query
required: true
schema: { type: string }
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/SingleResponseEvalReportSpotcheckCO'
components:
parameters:
current:
name: current
in: query
schema: { type: integer, default: 1 }
size:
name: size
in: query
schema: { type: integer, default: 10 }
schemas:
SingleResponseBase:
type: object
properties:
success: { type: boolean, example: true }
code: { type: string, example: "0" }
message: { type: string }
errCode: { type: string }
errMessage: { type: string }
EvalReportLibraryCO:
type: object
properties:
id: { type: string }
orgId: { type: string }
orgName: { type: string, description: 报送机构名称 }
projectId: { type: string }
customerId: { type: string }
evaluatedUnitName: { type: string }
reportNo: { type: string }
reportName: { type: string }
reportYear: { type: integer }
evalTypeCode: { type: string }
evalTypeName: { type: string }
industryCode: { type: string }
industryName: { type: string }
fileUrl: { type: string }
fileName: { type: string }
submitFlag: { type: integer, description: "1已报送2未报送" }
reportStatusCode: { type: integer }
reportStatusName: { type: string }
displayStatusName: { type: string }
archiveTime: { type: string, format: date-time, description: 报送/入库时间 }
issueDate: { type: string, format: date, description: 签发日期 }
pageCount: { type: integer }
secretLevelCode: { type: string }
secretLevelName: { type: string }
remarks: { type: string }
RegulatorEvalReportSummaryCO:
type: object
properties:
recentThreeYearCount: { type: integer, format: int64 }
currentYearCount: { type: integer, format: int64 }
submittedTotalCount: { type: integer, format: int64 }
RegulatorEvalReportAddCmd:
type: object
required: [orgId, reportName, fileUrl]
properties:
orgId: { type: integer, format: int64 }
projectId: { type: integer, format: int64 }
reportName: { type: string }
reportNo: { type: string }
reportYear: { type: integer }
evalTypeCode: { type: string }
evalTypeName: { type: string }
industryCode: { type: string }
industryName: { type: string }
evaluatedUnitName: { type: string }
customerId: { type: integer, format: int64 }
issueDate: { type: string, format: date }
pageCount: { type: integer }
businessScope: { type: string }
fileUrl: { type: string }
fileName: { type: string }
secretLevelCode: { type: string }
secretLevelName: { type: string }
remarks: { type: string }
RegulatorEvalReportBatchDownloadCmd:
type: object
required: [ids]
properties:
ids:
type: array
items: { type: integer, format: int64 }
maxItems: 50
RegulatorEvalReportExportCmd:
allOf:
- $ref: '#/components/schemas/RegulatorEvalReportPageQueryFields'
- type: object
properties:
ids:
type: array
items: { type: integer, format: int64 }
scope:
type: string
enum: [SELECTED, FILTERED, ALL]
description: 有 ids 时默认 SELECTED无 ids 默认 FILTERED
RegulatorEvalReportPageQueryFields:
type: object
properties:
orgId: { type: integer, format: int64 }
orgName: { type: string }
keyword: { type: string }
reportYear: { type: integer }
evalTypeCode: { type: string }
industryCode: { type: string }
reportStatusCode: { type: integer }
archiveTimeStart: { type: string }
archiveTimeEnd: { type: string }
submitFlag: { type: integer }
RegulatorSpotcheckStartCmd:
type: object
required: [reportId, checkResultCode]
properties:
reportId: { type: integer, format: int64 }
checkResultCode: { type: integer, description: "1合格2不合格" }
checkResultName: { type: string }
checkOpinion: { type: string }
rectifyRequire: { type: string, description: 不合格必填 }
RegulatorSpotcheckReviewCmd:
type: object
required: [spotcheckId, reviewResultCode]
properties:
spotcheckId: { type: integer, format: int64 }
reviewResultCode: { type: integer, description: "1通过2不通过" }
reviewResultName: { type: string }
EvalReportSpotcheckCO:
type: object
properties:
id: { type: string }
orgId: { type: string }
reportId: { type: string }
checkResultCode: { type: integer }
checkResultName: { type: string }
checkOpinion: { type: string }
rectifyRequire: { type: string }
rectifyFeedback: { type: string }
reviewResultCode: { type: integer }
reviewResultName: { type: string }
checkerId: { type: string }
checkerName: { type: string }
checkTime: { type: string, format: date-time }
SingleResponseEvalReportLibraryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/EvalReportLibraryCO' }
SingleResponseRegulatorEvalReportSummaryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/RegulatorEvalReportSummaryCO' }
PageResponseEvalReportLibraryCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/EvalReportLibraryCO' }
total: { type: integer, format: int64 }
SingleResponseEvalReportSpotcheckCO:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data: { $ref: '#/components/schemas/EvalReportSpotcheckCO' }
SingleResponseEvalReportSpotcheckCOList:
allOf:
- $ref: '#/components/schemas/SingleResponseBase'
- type: object
properties:
data:
type: array
items: { $ref: '#/components/schemas/EvalReportSpotcheckCO' }

35
docs/tmp/insp-modals.js Normal file
View File

@ -0,0 +1,35 @@
','30%','抽查合格率退回修改次数'],['风险处置','20%','未闭环预警逾期反馈'],['监督检查','20%','检查结论整改完成情况']]);
} else if (type === 'inspectionCreate') {
title.textContent = name;
html = section('告知信息', '告知下发后同步至机构端消息中心,机构需确认检查时间并按要求准备材料。') + '<div class="form-grid"><div class="field"><label>评价机构</label><select><option>重庆安评技术研究院有限公司</option><option>重庆恒安安全评价有限公司</option><option>重庆渝安风险评估中心</option></select></div><div class="field"><label>检查类型</label><select><option>项目过程检查</option><option>资质保持检查</option><option>专项检查</option></select></div><div class="field"><label>计划检查时间</label><input type="datetime-local" value="2026-07-25T09:00"></div><div class="field"><label>来源预警</label><select><option>无</option><option>W-PROJ-2026-021</option><option>W-QUAL-2026-012</option></select></div><div class="field full"><label>检查事项</label><textarea>请准备项目过程记录、人员到场记录及相关证明材料。</textarea></div><div class="field full"><label>告知附件</label><input type="file" accept=".pdf,.doc,.docx"></div></div>';
foot += actionButton('下发机构', '监督检查告知已下发');
} else if (type === 'inspectionResult') {
title.textContent = '登记监督检查结果 - ' + name;
html = section('检查结果', '记录实际检查情况、不符合项、整改要求和后续处置。') + '<div class="form-grid"><div class="field"><label>实际检查时间</label><input type="datetime-local" value="2026-07-22T09:30"></div><div class="field"><label>检查结论</label><select><option>未发现问题</option><option>责令整改</option><option>转入风险预警</option></select></div><div class="field full"><label>检查情况</label><textarea placeholder="填写现场检查情况和发现问题"></textarea></div><div class="field full"><label>整改要求</label><textarea placeholder="需要整改时填写整改事项和期限"></textarea></div><div class="field"><label>整改期限</label><input type="date" value="2026-08-05"></div><div class="field full"><label>检查记录附件</label><input type="file" multiple accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"></div></div>';
foot += actionButton('提交检查结果', '监督检查结果已登记'); } else if (type === 'inspectionDetail') {
title.textContent = '监督检查告知 - ' + name;
html = section('告知详情', '展示告知内容、机构确认和材料反馈情况。') + tableHtml(['事项','内容','状态'], [['检查时间','2026-07-22 09:30','机构已确认'],['检查类型','项目过程检查','已下发'],['来源预警','现场勘查异常','已关联'],['机构反馈','联系人及准备材料清单','已反馈']]) + '<div class="form-grid" style="margin-top:1rem;"><div class="field full"><label>监管备注</label><textarea placeholder="填写检查前备注或补充要求"></textarea></div></div>';
foot += actionButton('保存备注', '检查备注已保存');
} else if (type === 'trainingCourseCreate' || type === 'trainingCourseEdit') {
title.textContent = type === 'trainingCourseCreate' ? '新增培训课程' : '编辑课程 - ' + name;
html = '<div class="form-grid"><div class="field full"><label>课程名称</label><input value="' + (type === 'trainingCourseEdit' ? name : '') + '" placeholder="输入课程名称"></div><div class="field"><label>课程分类</label><select><option>法律法规</option><option>专业能力</option><option>过程控制</option></select></div><div class="field"><label>课程学时</label><input type="number" min="1" value="4"></div><div class="field"><label>适用人员</label><select><option>全体评价人员</option><option>机构负责人</option><option>技术负责人</option><option>过程控制负责人</option></select></div><div class="field"><label>考试要求</label><select><option>需要考试</option><option>无需考试</option></select></div><div class="field full"><label>课程课件</label><input type="file" multiple accept=".pdf,.ppt,.pptx,.mp4"></div><div class="field full"><label>课程简介</label><textarea placeholder="填写课程目标和主要内容"></textarea></div></div>';
foot += actionButton(type === 'trainingCourseCreate' ? '保存课程' : '保存修改', '课程信息已保存');
} else if (type === 'trainingTaskCreate') {
title.textContent = name;
html = '<div class="form-grid"><div class="field full"><label>任务名称</label><input placeholder="例如2026年度安全评价专业培训"></div><div class="field"><label>选择课程</label><select><option>安全评价通则与责任要求</option><option>评价机构过程控制实务</option></select></div><div class="field"><label>下发范围</label><select><option>全市备案评价人员</option><option>机构负责人</option><option>过程控制负责人</option></select></div><div class="field"><label>完成期限</label><input type="date" value="2026-09-30"></div><div class="field"><label>逾期提醒</label><select><option>提前7天</option><option>提前3天</option><option>不提醒</option></select></div><div class="field full"><label>任务说明</label><textarea placeholder="填写培训要求"></textarea></div></div>';
foot += actionButton('确认下发', '培训任务已下发');
} else if (type === 'trainingStudents') {
title.textContent = '班级学员列表 - ' + name;
html = section('学员完成情况', '查看班级学员的学习进度、完成学时和考试结果。') + tableHtml(['姓名','所属机构','部门','完成进度','考试结果'], [['张建国','重庆安评技术研究院有限公司','评价一部','100%','92分/合格'],['李明华','重庆恒安安全评价有限公司','评价二部','80%','待考试'],['王海','重庆渝安风险评估中心','过程控制部','60%','未完成']]);
foot += actionButton('导出学员名单', '班级学员名单已生成');
} else if (type === 'trainingCourses') {
title.textContent = '班级课程列表 - ' + name;
html = section('课程配置', '展示班级包含的必修课程、学时和考试要求。') + tableHtml(['课程名称','培训类型','学时','学习要求'], [['安全生产举报和奖励办法','法律法规','0.7','必修/考试'],['安全评价过程控制实务','专业能力','1.0','必修/考试'],['典型事故案例警示教育','事故警示','0.3','必修/无需考试']]);
foot += actionButton('导出课程清单', '班级课程清单已生成');
} else if (type === 'trainingExtend') {
title.textContent = '培训班级延期 - ' + name;
html = '<div class="form-grid"><div class="field"><label>原结束时间</label><input value="2026-06-30" disabled></div><div class="field"><label>延期至</label><input type="date" value="2026-07-15"></div><div class="field full"><label>延期原因</label><textarea placeholder="填写延期原因"></textarea></div><div class="field full"><label>通知方式</label><label><input type="checkbox" checked> 通知参训单位</label><label><input type="checkbox" checked> 通知未完成学员</label></div></div>';
foot += actionButton('确认延期', '培训班级结束时间已更新');
} else if (type === 'trainingQr') {
title.textContent = '班级二维码 - ' + name;
html = section('扫码参训', '学员扫码后进入班级课程,二维码有效期与班级结束时间一致。') + '<div style="width:180px;height:180px;margin:1rem auto;background:repeating-conic-gradient(#111 0 25%,#fff 0 50%) 0/18px 18px;border:12px solid #fff;box-shadow:0 0 0 1px #d1d5db"></div><p style="text-align:center;color:var(--color-text-se

View File

@ -0,0 +1,190 @@
id="mod-reg-message">
<div class="page-header"><div><h2>📄 监督检查通知</h2><div style="font-size:.82rem;color:var(--color-text-secondary);margin-top:.2rem;">接收监管端下发的整改通知、监督检查通知、核查函及其他通知文件,机构在线反馈结果、上传证明材料并查看监管复核意见。</div></div><div class="boss-tabs"><button class="boss-tab active" onclick="switchBossTab(this,'msg-all')">全部通知</button><button class="boss-tab" onclick="switchBossTab(this,'msg-unread')">待处理</button><button class="boss-tab" onclick="switchBossTab(this,'msg-processing')">处理中</button><button class="boss-tab" onclick="switchBossTab(this,'msg-feedback')">待监管复核</button><button class="boss-tab" onclick="switchBossTab(this,'msg-closed')">已办结</button></div></div>
<div class="boss-grid cols-4" style="margin-bottom:1rem;"><div class="boss-card boss-metric"><div><div class="label">监管消息总数</div><div class="value">9</div><div class="hint">来源:监管端风险预警中心</div></div><div class="icon">✉️</div></div><div class="boss-card boss-metric"><div><div class="label">待处理</div><div class="value boss-kpi-danger">3</div><div class="hint">需机构直接处理</div></div><div class="icon"></div></div><div class="boss-card boss-metric"><div><div class="label">待监管复核</div><div class="value boss-kpi-warning">2</div><div class="hint">材料已提交</div></div><div class="icon"></div></div><div class="boss-card boss-metric"><div><div class="label">已办结</div><div class="value boss-kpi-success">4</div><div class="hint">办结消息可归档</div></div><div class="icon"></div></div></div>
<div class="boss-note"><strong>消息来源:</strong>监管端风险预警中心。监管人员在风险预警中心发出整改通知、核查函、补正通知或办结结果后,机构端在此接收并处理;资质保持监控页面只作为监管信息来源。</div>
<div class="search-form"><div class="row"><div class="field"><label>通知关键词</label><input type="text" data-filter-input="msg-q" placeholder="通知编号/预警编号/事项"></div><div class="field"><label>通知类型</label><select data-filter-input="msg-type"><option value="">全部</option><option>整改通知</option><option>核查函</option><option>补正通知</option><option>办结通知</option></select></div><div class="field"><label>处理状态</label><select data-filter-input="msg-state"><option value="">全部</option><option value="unread">待处理</option><option value="processing">处理中</option><option value="feedback">待监管复核</option><option value="closed">已办结</option></select></div><div class="actions"><button class="btn btn-primary" onclick="filterBossRows('reg-message-table')">🔍 搜索</button><button class="btn btn-ghost" onclick="resetBossFilters('mod-reg-message')">↺ 重置</button></div></div></div>
<div class="boss-toolbar"><div class="left"><span style="font-size:.85rem;color:var(--color-text-secondary);">监管消息列表:展示来源预警、来源字段、办理期限和机构处理状态。</span></div><div class="right"><button class="btn btn-ghost btn-sm" onclick="openBossModal('regMessageExport','监管消息清单')">导出消息</button></div></div>
<div class="data-table"><table id="reg-message-table"><thead><tr><th style="width:34px"><input type="checkbox" onclick="toggleBossChecks(this,'reg-message-table')"></th><th>通知编号</th><th>通知类型</th><th>来源预警</th><th>来源字段</th><th>监管要求</th><th>办理期限</th><th>状态</th><th style="width:90px;text-align:center;">操作</th></tr></thead><tbody>
<tr data-keywords="M-QUAL-2026-012 W-QUAL-2026-012 整改通知 资质保持 unread 待处理 重庆渝安" data-state="unread"><td><input type="checkbox"></td><td>M-QUAL-2026-012</td><td><span class="tag tag-danger">整改通知</span></td><td>W-QUAL-2026-012</td><td><div class="source-list"><span class="source-pill">固定资产</span><span class="source-pill">工作场所</span><span class="source-pill">专职人数</span></div></td><td>补充固定资产、场所面积、档案室面积和专职人员证明材料。</td><td>2026-07-25</td><td><span class="tag tag-danger" data-msg-status>待处理</span></td><td class="action-btns" style="justify-content:center;"><button class="btn btn-primary btn-sm" onclick="openBossModal('regMessageProcess','M-QUAL-2026-012')">处理</button></td></tr>
<tr data-keywords="M-QUAL-2026-011 W-QUAL-2026-011 整改通知 资质保持 processing 处理中 重庆恒安" data-state="processing"><td><input type="checkbox"></td><td>M-QUAL-2026-011</td><td><span class="tag tag-warning">整改通知</span></td><td>W-QUAL-2026-011</td><td><div class="source-list"><span class="source-pill">职称等级</span><span class="source-pill">注册安全工程师比例</span><span class="source-pill">业务范围数量</span></div></td><td>提交高级职称人员补充计划、劳动合同和社保清单。</td><td>2026-07-22</td><td><span class="tag tag-warning" data-msg-status>处理中</span></td><td class="action-btns" style="justify-content:center;"><button class="btn btn-primary btn-sm" onclick="openBossModal('regMessageProcess','M-QUAL-2026-011')">处理</button></td></tr>
<tr data-keywords="M-QUAL-2026-013 W-QUAL-2026-013 属地核验 feedback 待监管复核 北京中安" data-state="feedback"><td><input type="checkbox"></td><td>M-QUAL-2026-013</td><td><span class="tag tag-info">属地核验</span></td><td>W-QUAL-2026-013</td><td><div class="source-list"><span class="source-pill">本市社保人数</span><span class="source-pill">租赁合同</span><span class="source-pill">注册地</span></div></td><td>已提交重庆场所、社保人员和档案室说明,等待监管复核。</td><td>2026-07-20</td><td><span class="tag tag-info" data-msg-status>待监管复核</span></td><td class="action-btns" style="justify-content:center;"><button class="btn btn-primary btn-sm" onclick="openBossModal('regMessageProcess','M-QUAL-2026-013')">处理</button></td></tr>
<tr data-keywords="M-QUAL-2026-010 W-QUAL-2026-010 补正通知 returned processing 监管退回" data-state="processing"><td><input type="checkbox"></td><td>M-QUAL-2026-010</td><td><span class="tag tag-warning">补正通知</span></td><td>W-QUAL-2026-010</td><td><div class="source-list"><span class="source-pill">证书附件</span><span class="source-pill">社保清单</span></div></td><td>监管退回:证书延续材料缺少发证机关页,请补充后再次提交。</td><td>2026-07-18</td><td><span class="tag tag-warning" data-msg-status>监管退回</span></td><td class="action-btns" style="justify-content:center;"><button class="btn btn-primary btn-sm" onclick="openBossModal('regMessageProcess','M-QUAL-2026-010')">处理</button></td></tr>
<tr data-keywords="M-QUAL-2026-009 W-QUAL-2026-009 办结通知 closed 已办结" data-state="closed"><td><input type="checkbox"></td><td>M-QUAL-2026-009</td><td><span class="tag tag-success">办结通知</span></td><td>W-QUAL-2026-009</td><td><div class="source-list"><span class="source-pill">整改说明</span><span class="source-pill">复核结论</span></div></td><td>监管已确认整改材料,预警闭环,办结结果归档。</td><td>2026-07-12</td><td><span class="tag tag-success" data-msg-status>已办结</span></td><td class="action-btns" style="justify-content:center;"><button class="btn btn-primary btn-sm" onclick="openBossModal('regMessageProcess','M-QUAL-2026-009')">处理</button></td></tr>
</tbody></table></div>
</div> </main>
</div>
</div>
<!-- 模态框: 资质备案申请向导5步 -->
<div class="modal-overlay" id="modal-filing-guide">
<div class="modal" style="width:800px;">
<div class="modal-header"><h3>📋 资质备案申请</h3><button class="modal-close" onclick="closeModal('modal-filing-guide')"></button></div>
<div class="modal-body">
<div class="step-indicator" style="margin-bottom:1.5rem;">
<div class="step active" id="step1-label"><span class="num" id="step1-num">1</span> 备案基本信息</div>
<span class="connector"></span>
<div class="step" id="step2-label"><span class="num" id="step2-num">2</span> 申请材料清单</div>
<span class="connector"></span>
<div class="step" id="step3-label"><span class="num" id="step3-num">3</span> 法定代表人承诺书</div>
<span class="connector"></span>
<div class="step" id="step4-label"><span class="num" id="step4-num">4</span> 备案人员管理</div>
<span class="connector"></span>
<div class="step" id="step5-label"><span class="num" id="step5-num">5</span> 装备清单</div>
</div>
<div class="step-content" id="step1-content">
<h4 style="margin-bottom:1rem;">备案基本信息</h4>
<div class="form-grid">
<div class="field"><label>备案属地 <span style="color:red">*</span></label><select><option>重庆市渝北区</option><option>重庆市南岸区</option><option>重庆市江北区</option></select></div>
<div class="field"><label>备案单位类型</label><select><option>本地单位</option><option>异地单位</option></select></div>
<div class="field full"><label>备案单位</label><input type="text" value="重庆安评技术研究院有限公司"></div>
<div class="field full"><label>单位基本情况介绍</label><textarea rows="4" placeholder="请输入单位基本情况介绍...">重庆安评技术研究院成立于2015年是一家专业从事安全评价、安全咨询、安全技术开发与转让的综合性安全技术服务机构...</textarea></div>
<div class="field full"><label>附件上传支持PDF格式</label><button class="btn btn-ghost btn-sm" onclick="alert('选择文件上传')">📎 上传文件</button></div>
</div>
</div>
<div class="step-content" id="step2-content" style="display:none;">
<h4 style="margin-bottom:1rem;">申请材料清单</h4>
<div class="data-table" style="box-shadow:none;border:1px solid var(--color-border);">
<table>
<thead><tr><th style="width:30px"><input type="checkbox"></th><th>材料名称</th><th>是否必需</th><th>状态</th><th>操作</th></tr></thead>
<tbody>
<tr><td><input type="checkbox" checked></td><td>安全评价机构资质申请书</td><td><span class="tag tag-danger">必需</span></td><td><span class="tag tag-success">已上传</span></td><td><button class="btn btn-ghost btn-sm">预览</button></td></tr>
<tr><td><input type="checkbox" checked></td><td>法人营业执照副本</td><td><span class="tag tag-danger">必需</span></td><td><span class="tag tag-success">已上传</span></td><td><button class="btn btn-ghost btn-sm">预览</button></td></tr>
<tr><td><input type="checkbox" checked></td><td>技术人员资质证书</td><td><span class="tag tag-danger">必需</span></td><td><span class="tag tag-success">已上传</span></td><td><button class="btn btn-ghost btn-sm">预览</button></td></tr>
<tr><td><input type="checkbox" checked></td><td>社保缴纳证明材料</td><td><span class="tag tag-danger">必需</span></td><td><span class="tag tag-warning">待上传</span></td><td><button class="btn btn-ghost btn-sm">📎 上传</button></td></tr>
<tr><td><input type="checkbox"></td><td>办公场所证明材料</td><td><span class="tag tag-info">建议</span></td><td></td><td><button class="btn btn-ghost btn-sm">📎 上传</button></td></tr>
</tbody>
</table>
</div>
</div>
<div class="step-content" id="step3-content" style="display:none;">
<h4 style="margin-bottom:1rem;">申请单位法定代表人承诺书</h4>
<div style="border:1px solid var(--color-border);border-radius:var(--radius);padding:1rem;background:#fafafa;margin-bottom:1rem;">
<p style="font-size:.85rem;line-height:1.8;">本申请人自愿申请安全评价机构资质备案,郑重承诺如下:<br><br>一、本申请人所提交的全部申请材料真实、准确、完整,无隐瞒、虚假陈述或重大遗漏;<br>二、本申请人符合《安全评价机构管理规定》等法律法规规定的资质条件;<br>三、本申请人将严格遵守国家有关安全生产的法律法规和技术标准;<br>四、本申请人愿意接受并配合监管部门的监督检查和评审工作。<br><br>如有违反上述承诺,本申请人愿意承担相应的法律责任。</p>
</div>
<div class="form-grid">
<div class="field"><label>法定代表人</label><input type="text" value="刘强"></div>
<div class="field"><label>签署日期</label><input type="date" value="2026-06-25"></div>
<div class="field full"><label>电子签名</label><div style="border:1px dashed var(--color-border);border-radius:var(--radius);padding:1rem;text-align:center;color:var(--color-text-secondary);">点击此处绘制电子签名</div></div>
</div>
</div>
<div class="step-content" id="step4-content" style="display:none;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem;">
<h4>备案人员信息</h4>
<button class="btn btn-primary btn-sm" onclick="openModal('modal-filing-person-add')"> 添加人员信息</button>
</div>
<div class="data-table" style="box-shadow:none;border:1px solid var(--color-border);">
<table>
<thead><tr><th style="width:50px">序号</th><th>人员姓名</th><th>类型</th><th>职务</th><th>职称</th><th style="width:150px">操作</th></tr></thead>
<tbody>
<tr><td>1</td><td>张建国</td><td>评价师</td><td>高级评价师</td><td>高级工程师</td><td class="action-btns"><button class="btn btn-ghost btn-sm">查看</button><button class="btn btn-ghost btn-sm">修改</button></td></tr>
<tr><td>2</td><td>李明华</td><td>评价师</td><td>评价师</td><td>工程师</td><td class="action-btns"><button class="btn btn-ghost btn-sm">查看</button><button class="btn btn-ghost btn-sm">修改</button></td></tr>
<tr><td>3</td><td>王丽萍</td><td>管理人员</td><td>人事主管</td><td></td><td class="action-btns"><button class="btn btn-ghost btn-sm">查看</button><button class="btn btn-ghost btn-sm">修改</button></td></tr>
</tbody>
</table>
</div>
</div>
<div class="step-content" id="step5-content" style="display:none;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem;">
<h4>申请单位装备清单</h4>
<button class="btn btn-ghost btn-sm" onclick="alert('弹出企业已有设备列表,选择本次备案设备')"> 添加设备</button>
</div>
<div class="data-table" style="box-shadow:none;border:1px solid var(--color-border);margin-top:.5rem;">
<table>
<thead><tr><th style="width:50px">序号</th><th>装备名称</th><th>规格型号</th><th>生产厂家</th><th>计量检定情况</th><th>操作</th></tr></thead>
<tbody>
<tr><td>1</td><td>气相色谱仪</td><td>GC-2018</td><td>岛津</td><td><button class="btn btn-ghost btn-sm">📎 上传报告</button></td><td><button class="btn btn-ghost btn-sm" onclick="alert('移除设备')">移除</button></td></tr>
<tr><td>2</td><td>噪声计</td><td>HS-6288</td><td>杭州爱华</td><td><button class="btn btn-ghost btn-sm">📎 上传报告</button></td><td><button class="btn btn-ghost btn-sm">移除</button></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="modal-footer" style="justify-content:space-between;">
<div>
<button class="btn btn-ghost" onclick="closeModal('modal-filing-guide')">取消</button>
<button class="btn btn-ghost" onclick="alert('已暂存')">💾 暂存</button>
</div>
<div>
<button class="btn btn-ghost" id="prev-step-btn" style="display:none;" onclick="prevStep()">← 上一步</button>
<button class="btn btn-primary" id="next-step-btn" onclick="nextStep()">下一步 →</button>
<button class="btn btn-primary" id="submit-step-btn" style="display:none;" onclick="submitFiling()">📤 提交备案</button>
</div>
</div>
</div>
</div>
<!-- 模态框: 添加备案人员信息 -->
<div class="modal-overlay" id="modal-filing-person-add">
<div class="modal" style="width:700px;">
<div class="modal-header"><h3> 添加备案人员</h3><button class="modal-close" onclick="closeModal('modal-filing-person-add')"></button></div>
<div class="modal-body">
<div class="form-grid">
<div class="field"><label>人员类型</label><select><option>专职评价师</option><option>基础人员</option></select></div>
<div class="field"><label>资质范围</label><select><option>矿山开采业</option><option>危险化学品行业</option><option>烟花爆竹行业</option><option>金属冶炼与加工</option></select></div>
<div class="field"><label>姓名</label><input type="text" placeholder="人员姓名"></div>
<div class="field"><label>性别</label><select><option></option><option></option></select></div>
<div class="field"><label>出生日期</label><input type="date"></div>
<div class="field"><label>身份证号</label><input type="text" placeholder="18位身份证号"></div>
<div class="field"><label>学历</label><select><option>全日制 · 专科</option><option>全日制 · 本科</option><option>全日制 · 硕士</option><option>全日制 · 博士</option><option>在职教育 · 本科</option></select></div>
<div class="field"><label>毕业院校</label><input type="text" placeholder="毕业院校"></div>
<div class="field"><label>专业</label><input type="text" placeholder="所学专业"></div>
<div class="field"><label>职务</label><input type="text" placeholder="职务"></div>
<div class="field full"><label>现住地址</label><input type="text" placeholder="现住地址"></div>
<div class="field full"><label>办公地址</label><input type="text" value="重庆市渝北区龙山街道100号"></div>
<div class="field full"><label>出版学术专著、专利、科技发明等情况</label><textarea rows="2" placeholder="请描述"></textarea></div>
<div class="field full"><label>自我申报的专业能力及认定方式</label><textarea rows="2" placeholder="请描述"></textarea></div>
<div class="field full"><label>申报专业能力证明材料</label><button class="btn btn-ghost btn-sm" onclick="alert('上传附件')">📎 上传附件</button></div>
<div class="field full"><label>主要学习工作经历</label><textarea rows="2" placeholder="请描述学习工作经历"></textarea></div>
<div class="field"><label>职业资格等级及证书编号</label><input type="text" placeholder="如:一级/12345"></div>
<div class="field"><label>是否注册安全工程师</label><select><option></option><option></option></select></div>
<div class="field full"><label>电子签名</label><div style="border:1px dashed var(--color-border);border-radius:var(--radius);padding:.75rem 1rem;text-align:center;"><span style="color:#94a3b8;">未签名</span> <span style="font-size:0.75rem;color:var(--color-text-secondary);margin-left:0.3rem;">(点击进行签字)</span></div></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('modal-filing-person-add')">取消</button>
<button class="btn btn-primary" onclick="closeModal('modal-filing-person-add');alert('备案人员信息已添加!')">保存</button>
</div>
</div>
</div>
<!-- 模态框: 备案确认5步向导-复用) -->
<div class="modal-overlay" id="modal-filing-confirm">
<div class="modal" style="width:800px;">
<div class="modal-header"><h3>✅ 备案确认</h3><button class="modal-close" onclick="closeModal('modal-filing-confirm')"></button></div>
<div class="modal-body">
<div class="step-indicator" style="margin-bottom:1.5rem;">
<div class="step active"><span class="num">1</span> 备案基本信息</div>
<span class="connector"></span>
<div class="step"><span class="num">2</span> 申请材料清单</div>
<span class="connector"></span>
<div class="step"><span class="num">3</span> 法定代表人承诺书</div>
<span class="connector"></span>
<div class="step"><span class="num">4</span> 备案人员管理</div>
<span class="connector"></span>
<div class="step"><span class="num">5</span> 装备清单</div>
</div>
<div class="form-grid">
<div class="field"><label>备案属地</label><select><option>重庆市渝北区</option></select></div>
<div class="field"><label>备案单位类型</label><select><option>本地单位</option></select></div>
<div class="field"><label>上次备案时间</label><input type="date" value="2026-01-15"></div>
<div class="field full"><label>备案单位</label><input type="text" value="重庆安评技术研究院有限公司"></div>
<div class="field full"><label>单位基本情况介绍</label><textarea rows="3" placeholder="单位基本情况...">重庆安评技术研究院成立于2015年...</textarea></div>
<div class="field full"><label>附件上传</label><button class="btn btn-ghost btn-sm">📎 上传文件</button></div>
</div>
</div>
<div class="modal-footer" style="justify-content:space-between;">
<div><button class="btn btn-ghost" onclick="closeModal('modal-filing-confirm')">取消</button></div>
<div><button class="btn btn-ghost">← 上一步</button><button class="btn btn-primary">下一步 →</button></div>
</div>
</div>
</div>
<!-- 模态框: 备案变更历史 -->
<div class="modal-overlay" id="modal-filing-change-history">
<div class="modal" style="width:560px;">
<div class="modal-header"><h3>📋 备案变更记录</h3><button class="modal-close" onclick="closeModal('modal-filing-change-history')"></button></div>
<div class="modal-body">
<div class="data-table" style="box-shadow:none;border:1px solid var(--color-border);">
<table><thead><tr><th style="width:50px">序号</th><th>变更事项</th><th>变更时间</th><th>操作人</th></tr></thead>
<tbody>
<tr><td>1</td><td><span cla

31
docs/tmp/reg-insp.html Normal file
View File

@ -0,0 +1,31 @@
id="mod-inspection-notice">
<div class="page-header"><div><h2>📨 监督检查告知</h2><p class="reg-sub">根据风险预警、年度计划或专项安排下发检查告知,跟踪机构确认、材料反馈和检查结果。</p></div><button class="btn btn-primary" onclick="openRegModal('inspectionCreate','新建监督检查告知')">新建检查告知</button></div>
<div class="search-form"><div class="row">
<div class="field"><label>告知编号</label><input data-inspection-filter="number" placeholder="输入告知编号"></div>
<div class="field"><label>评价机构</label><input data-inspection-filter="institution" placeholder="输入机构名称"></div>
<div class="field"><label>检查类型</label><select data-inspection-filter="type"><option value="">全部</option><option>项目过程检查</option><option>资质保持检查</option><option>专项检查</option></select></div>
<div class="field"><label>来源方式</label><select data-inspection-filter="source"><option value="">全部</option><option>风险预警</option><option>年度计划</option><option>专项安排</option></select></div>
<div class="field"><label>来源预警编号</label><input data-inspection-filter="warning" placeholder="输入预警编号"></div>
<div class="field"><label>检查负责人</label><input data-inspection-filter="leader" placeholder="输入负责人姓名"></div>
<div class="field"><label>检查时间起</label><input type="date" data-inspection-filter="start"></div>
<div class="field"><label>检查时间止</label><input type="date" data-inspection-filter="end"></div>
<div class="field"><label>机构确认</label><select data-inspection-filter="confirm"><option value="">全部</option><option>未确认</option><option>已确认</option></select></div>
<div class="field"><label>材料反馈</label><select data-inspection-filter="feedback"><option value="">全部</option><option>未提交</option><option>已提交</option></select></div>
<div class="field"><label>办理状态</label><select data-inspection-filter="status"><option value="">全部</option><option>待下发</option><option>已下发</option><option>已反馈</option><option>已完成</option></select></div>
<div class="actions"><button class="btn btn-primary" onclick="filterInspectionNotices()">🔍 搜索</button><button class="btn btn-ghost" onclick="resetInspectionNotices()">↺ 重置</button></div>
</div></div>
<div class="toolbar"><div class="left"><span class="total">共 4 条检查告知</span></div><div class="right"><button class="btn btn-ghost btn-sm" onclick="openRegModal('export','监督检查告知台账')">导出台账</button></div></div>
<div class="data-table"><table id="inspection-notice-table"><thead><tr><th>告知编号</th><th>评价机构</th><th>检查类型</th><th>计划检查时间</th><th>来源</th><th>检查负责人</th><th>机构确认</th><th>材料反馈</th><th>办理状态</th><th>操作</th></tr></thead><tbody>
<tr data-number="JC-2026-021" data-institution="重庆安评技术研究院有限公司" data-type="专项检查" data-source="年度计划" data-warning="" data-leader="周强" data-date="2026-08-05" data-confirm="未确认" data-feedback="未提交" data-status="待下发"><td>JC-2026-021</td><td>重庆安评技术研究院有限公司</td><td>专项检查</td><td>2026-08-05 09:00</td><td>年度计划</td><td>周强</td><td><span class="tag tag-warning">未确认</span></td><td>未提交</td><td><span class="tag tag-warning">待下发</span></td><td><button class="btn btn-primary btn-sm" onclick="openRegModal('inspectionCreate','编辑 JC-2026-021')">编辑</button> <button class="btn btn-ghost btn-sm" onclick="updateInspectionStatus(this,'已下发','tag tag-info','检查告知已下发机构')">下发</button></td></tr>
<tr data-number="JC-2026-018" data-institution="重庆恒安安全评价有限公司" data-type="项目过程检查" data-source="风险预警" data-warning="W-PROJ-2026-009" data-leader="刘敏" data-date="2026-07-22" data-confirm="已确认" data-feedback="已提交" data-status="已反馈"><td>JC-2026-018</td><td>重庆恒安安全评价有限公司</td><td>项目过程检查</td><td>2026-07-22 09:30</td><td>风险预警<br><span class="source-pill">W-PROJ-2026-009</span></td><td>刘敏</td><td><span class="tag tag-success">已确认</span></td><td>已提交</td><td><span class="tag tag-info">已反馈</span></td><td><button class="btn btn-primary btn-sm" onclick="openRegModal('inspectionDetail','JC-2026-018')">查看</button> <button class="btn btn-ghost btn-sm" onclick="openRegModal('inspectionResult','JC-2026-018')">登记结果</button></td></tr>
<tr data-number="JC-2026-019" data-institution="重庆渝安风险评估中心" data-type="资质保持检查" data-source="风险预警" data-warning="W-QUAL-2026-012" data-leader="陈涛" data-date="2026-07-25" data-confirm="未确认" data-feedback="未提交" data-status="已下发"><td>JC-2026-019</td><td>重庆渝安风险评估中心</td><td>资质保持检查</td><td>2026-07-25 14:00</td><td>风险预警<br><span class="source-pill">W-QUAL-2026-012</span></td><td>陈涛</td><td><span class="tag tag-warning">未确认</span></td><td>未提交</td><td><span class="tag tag-info">已下发</span></td><td><button class="btn btn-primary btn-sm" onclick="openRegModal('inspectionDetail','JC-2026-019')">查看</button> <button class="btn btn-ghost btn-sm" onclick="showRegToast('已再次提醒机构确认')">提醒</button></td></tr>
<tr data-number="JC-2026-012" data-institution="北京中安评价中心重庆分公司" data-type="资质保持检查" data-source="专项安排" data-warning="" data-leader="周强" data-date="2026-06-18" data-confirm="已确认" data-feedback="已提交" data-status="已完成"><td>JC-2026-012</td><td>北京中安评价中心重庆分公司</td><td>资质保持检查</td><td>2026-06-18 09:00</td><td>专项安排</td><td>周强</td><td><span class="tag tag-success">已确认</span></td><td>已提交</td><td><span class="tag tag-success">已完成</span></td><td><button class="btn btn-primary btn-sm" onclick="openRegModal('inspectionDetail','JC-2026-012')">查看</button> <button class="btn btn-ghost btn-sm" onclick="showRegToast('检查记录已加入下载任务')">下载记录</button></td></tr>
</tbody></table></div>
</div>
<div class="module-page" id="mod-training-course">
<div class="training-list-head"><span class="training-back" onclick="switchModule('dashboard')">← 返回</span><h2>课程列表</h2></div>
<div class="search-form"><div class="row"><div class="field"><label>课程名称</label><input placeholder="请输入名称"></div><div class="field"><label>培训类型</label><select><option value="">请选择</option><option>法律法规</option><option>专业能力</option><option>事故警示</option><option>过程控制</option></select></div><div class="field"><label>课程状态</label><select><option value="">请选择课程状态</option><option>启用</option><option>禁用</option></select></div><div class="actions"><button class="btn btn-primary" onclick="filterNearestTable(this)">🔍 搜索</button><button class="btn" style="background:#10b981;color:#fff" onclick="resetNearestFilters(this)">↻ 重置</button></div></div></div>
<div class="data-table training-table"><table><thead><tr><th><input type="checkbox" onclick="toggleRegChecks(this,'training-course-table')"></th><th>序号</th><th>课程名称</th><th>课程描述</th><th>课程状态</th><th>总课时</th><th>上传单位</th><th>操作</th></tr></thead><tbody id="training-course-table">
<tr><td><input type="checkbox"></td><td>1</td><td class="course-name">安全生产举报和奖励办法专题培训</td><td class="course-desc">安全生产举报奖励制度、受理范围及内部报告奖励工作要求。</td><td><span class="tag tag-success">● 启用</span></td><td>0.7</td><td>重庆市应急管理局</td><td><div class="compact-actions"><button class="btn btn-primary btn-sm" onclick="toggleRowStatus(this,'● 禁用','tag tag-warning','课程已禁用')">禁用</button><button class="btn btn-primary btn-sm" onclick="openRegModal('trainingCourseEdit','安全生产举报和奖励办法专题培训')">编辑</button><button class="btn btn-danger btn-sm" onclick="deleteRegRow(this,'课程已删除')">删除</button></div></td></tr>
<tr><td><input type="checkbox"></td><td>2</td><td class="course-name">2026年春节前安全培训</td><td class="course-desc">节前安全培训和工作重点提醒。</td><td><span class="tag tag-success">● 启用</span></td><td>0.2</td><td>重庆市应急管理局</td><td><div class="compact-actions"><button class="btn btn-primary btn-sm" onclick="toggleRowStatus(this,'● 禁用','tag tag-warning','课程已禁用')">禁用</button><button class="btn btn-primary btn-sm" onclick="openRegModal('trainingCourseEdit','2026年春节前安全培训')">编辑</button><button class="btn btn-danger btn-sm" onclick="deleteRegRow(this,'课程已删除')">删除</button></div></td></tr>
<tr><td><input type="checkbox"></td><td>3</td><td class="course-name">典型事故案例分析</td><td class="course-desc">化工、矿山和工贸行业典型事故案例警示。</td><td><span class="tag tag-success">● 启用</span></td><td>0.3</td><td>重庆市应急管理局</td><td><div class="compact-actions"><button class="btn btn-primary btn-sm" onclick="toggleRowStatus(this,'● 禁用','tag tag-warning','课程已禁用')">禁用</button><button class="btn btn-primary btn-sm" onclick="openRegModal('training

View File

@ -4,17 +4,21 @@
```mermaid
stateDiagram-v2
[*] --> 已下发: 监管 issue
[*] --> 待下发: 监管 save
待下发 --> 已下发: 监管 issue(id)
[*] --> 已下发: 监管 issue直接新建下发
已下发 --> 已确认: 机构 confirm
已确认 --> 已反馈: 机构 feedback
已确认 --> 已办结: 监管 close至少已确认
已反馈 --> 已办结: 监管 close
已下发 --> [*]: 不可直接办结
待下发 --> [*]: 不可确认/办结
```
| 动作 | 前置 status | 结果 status | 关键字段 |
|------|-------------|-------------|----------|
| 监管下发 `issue` | — | 1 已下发 | `notice_no`、`send_time` |
| 监管暂存 `save` | — / 0 | 0 待下发 | `notice_no`(新建时生成) |
| 监管下发 `issue` | — 或 0 | 1 已下发 | `notice_no`、`send_time`;推送机构消息 |
| 机构确认 `confirm` | 1 | 2 已确认 | `confirm_flag=1`、`confirm_time` |
| 机构反馈 `feedback` | 2 | 3 已反馈 | 必填材料 `file_url` 已上传 |
| 监管办结 `close` | 2 或 3 | 4 已办结 | `result_*`、`result_time` |

View File

@ -5,9 +5,12 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.qinan.safetyeval.client.api.institution.InstitutionEvalReportApi;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.InstitutionEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportAddCmd;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportPageQuery;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportSubmitCmd;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -21,15 +24,33 @@ public class InstitutionEvalReportController {
@Resource
private InstitutionEvalReportApi institutionEvalReportApi;
@ApiOperation("报告库页顶栏汇总")
@GetMapping("/summary")
public SingleResponse<InstitutionEvalReportSummaryCO> summary() {
return institutionEvalReportApi.summary();
}
@ApiOperation("分页查询本机构报告")
@GetMapping("/page")
public PageResponse<EvalReportLibraryCO> page(@Validated InstitutionEvalReportPageQuery query) {
return institutionEvalReportApi.page(query);
}
@ApiOperation("查询报告详情")
@ApiOperation("查询报告详情(含 fileUrl前端据此下载")
@GetMapping("/get")
public SingleResponse<EvalReportLibraryCO> get(@ApiParam("主键ID") @RequestParam String id) {
return institutionEvalReportApi.get(Long.parseLong(id));
}
@ApiOperation("上传历史报告(对齐原型「上传历史报告」)")
@PostMapping("/add")
public SingleResponse<EvalReportLibraryCO> add(@Validated @RequestBody InstitutionEvalReportAddCmd cmd) {
return institutionEvalReportApi.add(cmd);
}
@ApiOperation("报送监管(未报送 → 已报送)")
@PostMapping("/submit")
public SingleResponse<EvalReportLibraryCO> submit(@Validated @RequestBody InstitutionEvalReportSubmitCmd cmd) {
return institutionEvalReportApi.submit(cmd);
}
}

View File

@ -9,10 +9,18 @@ import org.qinan.safetyeval.client.co.institution.InstitutionInspNoticeCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.institution.*;
import org.qinan.safetyeval.app.executor.support.InstitutionInspNoticeFileSupport;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.infrastructure.support.InspNoticeIdSupport;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Api(tags = "机构端-监督检查告知")
@ -20,9 +28,14 @@ import java.util.List;
@RequestMapping("/safetyEval/institution/insp-notice")
public class InstitutionInspNoticeController {
private static final DateTimeFormatter FILE_TS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
@Resource
private InstitutionInspNoticeApi institutionInspNoticeApi;
@Resource
private InstitutionInspNoticeFileSupport institutionInspNoticeFileSupport;
@ApiOperation("分页查询本机构告知")
@GetMapping("/page")
public PageResponse<InstitutionInspNoticeCO> page(@Validated InstitutionInspNoticePageQuery query) {
@ -32,7 +45,7 @@ public class InstitutionInspNoticeController {
@ApiOperation("查询告知详情")
@GetMapping("/get")
public SingleResponse<InstitutionInspNoticeCO> get(@ApiParam("主键ID") @RequestParam String id) {
return institutionInspNoticeApi.get(Long.parseLong(id));
return institutionInspNoticeApi.get(InspNoticeIdSupport.parseNoticeId(id));
}
@ApiOperation("确认告知")
@ -56,7 +69,34 @@ public class InstitutionInspNoticeController {
@ApiOperation("材料列表")
@GetMapping("/materials")
public SingleResponse<List<InspNoticeMaterialCO>> materials(@ApiParam("告知ID") @RequestParam Long noticeId) {
return institutionInspNoticeApi.materials(noticeId);
public SingleResponse<List<InspNoticeMaterialCO>> materials(@ApiParam("告知ID") @RequestParam String noticeId) {
return institutionInspNoticeApi.materials(InspNoticeIdSupport.parseNoticeId(noticeId));
}
@ApiOperation("导出消息清单CSV")
@PostMapping("/export-messages")
public void exportMessages(@Validated @RequestBody InstitutionInspNoticeExportCmd cmd,
HttpServletResponse response) throws IOException {
String fileName = "监管消息清单_" + FILE_TS.format(LocalDateTime.now()) + ".csv";
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
try {
institutionInspNoticeFileSupport.writeMessageCsv(cmd, response.getOutputStream());
response.flushBuffer();
} catch (BizException ex) {
response.reset();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\":false,\"code\":\"" + ex.getCode()
+ "\",\"errMessage\":\"" + escapeJson(ex.getMessage()) + "\"}");
}
}
private String escapeJson(String message) {
if (message == null) {
return "";
}
return message.replace("\\", "\\\\").replace("\"", "\\\"");
}
}

View File

@ -3,40 +3,108 @@ package org.qinan.safetyeval.adapter.web.regulator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.qinan.safetyeval.app.executor.support.RegulatorEvalReportFileSupport;
import org.qinan.safetyeval.client.api.regulator.RegulatorEvalReportApi;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.RegulatorEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportAddCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportBatchDownloadCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportExportCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportPageQuery;
import org.qinan.safetyeval.domain.exception.BizException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Api(tags = "监管端-安评报告库")
@RestController
@RequestMapping("/safetyEval/regulator/eval-report")
public class RegulatorEvalReportController {
private static final DateTimeFormatter FILE_TS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
@Resource
private RegulatorEvalReportApi regulatorEvalReportApi;
@ApiOperation("分页查询报告")
@Resource
private RegulatorEvalReportFileSupport regulatorEvalReportFileSupport;
@ApiOperation("报告数据库页顶栏汇总(近三年/本年度/已报送总数)")
@GetMapping("/summary")
public SingleResponse<RegulatorEvalReportSummaryCO> summary() {
return regulatorEvalReportApi.summary();
}
@ApiOperation("分页查询已报送报告(跨机构;默认 submitFlag=1")
@GetMapping("/page")
public PageResponse<EvalReportLibraryCO> page(@Validated RegulatorEvalReportPageQuery query) {
return regulatorEvalReportApi.page(query);
}
@ApiOperation("查询报告详情")
@ApiOperation("查询报告详情(含 fileUrl单份下载前端可用 fileUrl")
@GetMapping("/get")
public SingleResponse<EvalReportLibraryCO> get(@ApiParam("主键ID") @RequestParam String id) {
return regulatorEvalReportApi.get(Long.parseLong(id));
}
@ApiOperation("补录历史报告")
@ApiOperation("补录历史报告(字段与机构端上传对齐,强制已报送)")
@PostMapping("/add")
public SingleResponse<EvalReportLibraryCO> add(@Validated @RequestBody RegulatorEvalReportAddCmd cmd) {
return regulatorEvalReportApi.add(cmd);
}
@ApiOperation("批量下载报告文件ZIP最多50份")
@PostMapping("/batch-download")
public void batchDownload(@Validated @RequestBody RegulatorEvalReportBatchDownloadCmd cmd,
HttpServletResponse response) throws IOException {
String fileName = "安评报告批量下载_" + FILE_TS.format(LocalDateTime.now()) + ".zip";
response.setContentType("application/zip");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
try {
regulatorEvalReportFileSupport.writeBatchZip(cmd.getIds(), response.getOutputStream());
response.flushBuffer();
} catch (BizException ex) {
response.reset();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\":false,\"code\":\"" + ex.getCode()
+ "\",\"errMessage\":\"" + escapeJson(ex.getMessage()) + "\"}");
}
}
@ApiOperation("导出报告目录CSVExcel可打开勾选优先否则按筛选最多5000行")
@PostMapping("/export-catalog")
public void exportCatalog(@Validated @RequestBody RegulatorEvalReportExportCmd cmd,
HttpServletResponse response) throws IOException {
String fileName = "安评报告数据库目录_" + FILE_TS.format(LocalDateTime.now()) + ".csv";
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
try {
regulatorEvalReportFileSupport.writeCatalogCsv(cmd, response.getOutputStream());
response.flushBuffer();
} catch (BizException ex) {
response.reset();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\":false,\"code\":\"" + ex.getCode()
+ "\",\"errMessage\":\"" + escapeJson(ex.getMessage()) + "\"}");
}
}
private String escapeJson(String message) {
if (message == null) {
return "";
}
return message.replace("\\", "\\\\").replace("\"", "\\\"");
}
}

View File

@ -3,16 +3,24 @@ package org.qinan.safetyeval.adapter.web.regulator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.qinan.safetyeval.app.executor.support.RegulatorInspNoticeFileSupport;
import org.qinan.safetyeval.client.api.regulator.RegulatorInspNoticeApi;
import org.qinan.safetyeval.client.co.insp.InspNoticeMaterialCO;
import org.qinan.safetyeval.client.co.regulator.RegulatorInspNoticeCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.*;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.infrastructure.support.InspNoticeIdSupport;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Api(tags = "监管端-监督检查告知")
@ -20,10 +28,21 @@ import java.util.List;
@RequestMapping("/safetyEval/regulator/insp-notice")
public class RegulatorInspNoticeController {
private static final DateTimeFormatter FILE_TS = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
@Resource
private RegulatorInspNoticeApi regulatorInspNoticeApi;
@ApiOperation("新建并下发告知")
@Resource
private RegulatorInspNoticeFileSupport regulatorInspNoticeFileSupport;
@ApiOperation("暂存/编辑待下发告知")
@PostMapping("/save")
public SingleResponse<RegulatorInspNoticeCO> save(@Validated @RequestBody RegulatorInspNoticeSaveCmd cmd) {
return regulatorInspNoticeApi.save(cmd);
}
@ApiOperation("新建并下发告知;或对草稿下发(传 id")
@PostMapping("/issue")
public SingleResponse<RegulatorInspNoticeCO> issue(@Validated @RequestBody RegulatorInspNoticeIssueCmd cmd) {
return regulatorInspNoticeApi.issue(cmd);
@ -38,7 +57,7 @@ public class RegulatorInspNoticeController {
@ApiOperation("查询告知详情")
@GetMapping("/get")
public SingleResponse<RegulatorInspNoticeCO> get(@ApiParam("主键ID") @RequestParam String id) {
return regulatorInspNoticeApi.get(Long.parseLong(id));
return regulatorInspNoticeApi.get(InspNoticeIdSupport.parseNoticeId(id));
}
@ApiOperation("登记结果并办结")
@ -47,10 +66,16 @@ public class RegulatorInspNoticeController {
return regulatorInspNoticeApi.close(cmd);
}
@ApiOperation("保存监管备注")
@PostMapping("/remark")
public SingleResponse<RegulatorInspNoticeCO> remark(@Validated @RequestBody RegulatorInspNoticeRemarkCmd cmd) {
return regulatorInspNoticeApi.remark(cmd);
}
@ApiOperation("材料只读列表")
@GetMapping("/materials")
public SingleResponse<List<InspNoticeMaterialCO>> materials(@ApiParam("告知ID") @RequestParam Long noticeId) {
return regulatorInspNoticeApi.materials(noticeId);
public SingleResponse<List<InspNoticeMaterialCO>> materials(@ApiParam("告知ID") @RequestParam String noticeId) {
return regulatorInspNoticeApi.materials(InspNoticeIdSupport.parseNoticeId(noticeId));
}
@ApiOperation("催办(重发机构消息)")
@ -58,4 +83,52 @@ public class RegulatorInspNoticeController {
public SingleResponse<Void> urge(@Validated @RequestBody RegulatorInspNoticeUrgeCmd cmd) {
return regulatorInspNoticeApi.urge(cmd);
}
@ApiOperation("导出台账CSVExcel可打开勾选优先否则按筛选最多5000行")
@PostMapping("/export-ledger")
public void exportLedger(@Validated @RequestBody RegulatorInspNoticeExportCmd cmd,
HttpServletResponse response) throws IOException {
String fileName = "监督检查告知台账_" + FILE_TS.format(LocalDateTime.now()) + ".csv";
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
try {
regulatorInspNoticeFileSupport.writeLedgerCsv(cmd, response.getOutputStream());
response.flushBuffer();
} catch (BizException ex) {
response.reset();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\":false,\"code\":\"" + ex.getCode()
+ "\",\"errMessage\":\"" + escapeJson(ex.getMessage()) + "\"}");
}
}
@ApiOperation("下载检查记录CSV")
@GetMapping("/download-record")
public void downloadRecord(@ApiParam("告知ID") @RequestParam String id,
HttpServletResponse response) throws IOException {
Long noticeId = InspNoticeIdSupport.parseNoticeId(id);
String fileName = "监督检查记录_" + id + "_" + FILE_TS.format(LocalDateTime.now()) + ".csv";
response.setContentType("text/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
try {
regulatorInspNoticeFileSupport.writeRecordCsv(noticeId, response.getOutputStream());
response.flushBuffer();
} catch (BizException ex) {
response.reset();
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\":false,\"code\":\"" + ex.getCode()
+ "\",\"errMessage\":\"" + escapeJson(ex.getMessage()) + "\"}");
}
}
private String escapeJson(String message) {
if (message == null) {
return "";
}
return message.replace("\\", "\\\\").replace("\"", "\\\"");
}
}

View File

@ -438,6 +438,7 @@ public class EvalProcessControlExecutor implements EvalProcessControlApi {
report.setEvalTypeName(evalType != null ? evalType.getValue() : null);
report.setPublicFlag(2);
report.setCompleteFlag(1);
report.setSubmitFlag(1);
report.setReportStatusCode(1);
report.setReportStatusName("已入库");
report.setArchiveTime(LocalDateTime.now());

View File

@ -3,9 +3,12 @@ package org.qinan.safetyeval.app.executor.institution;
import org.qinan.safetyeval.app.executor.support.ReportProfileCoConverter;
import org.qinan.safetyeval.client.api.institution.InstitutionEvalReportApi;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.InstitutionEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportAddCmd;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportPageQuery;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportSubmitCmd;
import org.qinan.safetyeval.domain.entity.EvalReportEntity;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.query.EvalReportQuery;
@ -15,7 +18,9 @@ import org.qinan.safetyeval.infrastructure.support.InstitutionOrgSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.util.stream.Collectors;
@Service
@ -37,7 +42,9 @@ public class InstitutionEvalReportExecutor implements InstitutionEvalReportApi {
domainQuery.setOrgId(institutionOrgSupport.resolveCurrentOrgId());
domainQuery.setReportYear(query.getReportYear());
domainQuery.setEvalTypeCode(query.getEvalTypeCode());
domainQuery.setReportStatusCode(query.getReportStatusCode());
domainQuery.setIndustryCode(query.getIndustryCode());
domainQuery.setKeyword(query.getKeyword());
applyDisplayStatus(domainQuery, query.getDisplayStatus(), query.getReportStatusCode());
PageResult<EvalReportEntity> pageResult = evalReportDomainService.page(domainQuery);
return PageResponse.of(
pageResult.getRecords().stream().map(ReportProfileCoConverter::toReportCO).collect(Collectors.toList()),
@ -50,4 +57,145 @@ public class InstitutionEvalReportExecutor implements InstitutionEvalReportApi {
institutionOrgSupport.assertOrgAccess(entity.getOrgId(), ErrorCode.EVAL_REPORT_ORG_MISMATCH);
return SingleResponse.success(ReportProfileCoConverter.toReportCO(entity));
}
@Override
public SingleResponse<InstitutionEvalReportSummaryCO> summary() {
Long orgId = institutionOrgSupport.resolveCurrentOrgId();
InstitutionEvalReportSummaryCO co = new InstitutionEvalReportSummaryCO();
co.setTotalCount(countBase(orgId, null, null, null, null));
int yearFrom = LocalDate.now().getYear() - 2;
co.setRecentThreeYearCount(countBase(orgId, null, null, null, yearFrom));
co.setSubmittedCount(countBase(orgId, 1, null, null, null));
co.setIncompleteCount(countByCompleteFlag(orgId, 2));
co.setSpotcheckingCount(countBase(orgId, null, EvalReportDomainService.STATUS_SPOTCHECKING, null, null));
return SingleResponse.success(co);
}
@Override
public SingleResponse<EvalReportLibraryCO> add(InstitutionEvalReportAddCmd cmd) {
Long orgId = institutionOrgSupport.resolveCurrentOrgId();
EvalReportEntity entity = new EvalReportEntity();
entity.setOrgId(orgId);
entity.setReportNo(cmd.getReportNo().trim());
entity.setReportName(cmd.getReportName().trim());
entity.setReportYear(cmd.getReportYear() != null ? cmd.getReportYear() : LocalDate.now().getYear());
entity.setEvalTypeCode(cmd.getEvalTypeCode());
entity.setEvalTypeName(resolveEvalTypeName(cmd.getEvalTypeCode(), cmd.getEvalTypeName()));
entity.setIndustryCode(cmd.getIndustryCode());
entity.setIndustryName(cmd.getIndustryName());
entity.setEvaluatedUnitName(cmd.getEvaluatedUnitName().trim());
entity.setCustomerId(cmd.getCustomerId());
entity.setIssueDate(cmd.getIssueDate());
entity.setPageCount(cmd.getPageCount());
entity.setFileUrl(cmd.getFileUrl());
entity.setFileName(cmd.getFileName());
entity.setSecretLevelCode(cmd.getSecretLevelCode());
entity.setSecretLevelName(resolveSecretName(cmd.getSecretLevelCode(), cmd.getSecretLevelName()));
entity.setRemarks(cmd.getRemarks());
boolean submit = Boolean.TRUE.equals(cmd.getSubmitToRegulator());
entity.setSubmitFlag(submit ? 1 : 2);
EvalReportEntity result = evalReportDomainService.addForInstitution(entity);
return SingleResponse.success(ReportProfileCoConverter.toReportCO(result));
}
@Override
public SingleResponse<EvalReportLibraryCO> submit(InstitutionEvalReportSubmitCmd cmd) {
EvalReportEntity existing = evalReportDomainService.get(cmd.getId());
institutionOrgSupport.assertOrgAccess(existing.getOrgId(), ErrorCode.EVAL_REPORT_ORG_MISMATCH);
EvalReportEntity result = evalReportDomainService.submitToRegulator(cmd.getId());
return SingleResponse.success(ReportProfileCoConverter.toReportCO(result));
}
private void applyDisplayStatus(EvalReportQuery query, String displayStatus, Integer reportStatusCode) {
if (StringUtils.hasText(displayStatus)) {
String status = displayStatus.trim().toUpperCase();
if ("UN_SUBMITTED".equals(status) || "未报送".equals(displayStatus.trim())) {
query.setSubmitFlag(2);
return;
}
if ("SUBMITTED".equals(status) || "已报送".equals(displayStatus.trim())) {
query.setSubmitFlag(1);
query.setReportStatusCode(EvalReportDomainService.STATUS_ARCHIVED);
return;
}
if ("SPOTCHECKING".equals(status) || "抽查中".equals(displayStatus.trim())) {
query.setReportStatusCode(EvalReportDomainService.STATUS_SPOTCHECKING);
return;
}
if ("RECTIFYING".equals(status) || "整改中".equals(displayStatus.trim())) {
query.setReportStatusCode(EvalReportDomainService.STATUS_RECTIFY);
return;
}
if ("REVIEWED".equals(status) || "已复核".equals(displayStatus.trim())) {
query.setReportStatusCode(EvalReportDomainService.STATUS_REVIEWED);
return;
}
}
if (reportStatusCode != null) {
query.setReportStatusCode(reportStatusCode);
}
}
private EvalReportQuery baseQuery(Long orgId) {
EvalReportQuery q = new EvalReportQuery();
q.setOrgId(orgId);
q.setPageNum(1L);
q.setPageSize(1L);
return q;
}
private long countBase(Long orgId, Integer submitFlag, Integer statusCode, String industryCode, Integer yearFrom) {
EvalReportQuery q = baseQuery(orgId);
q.setSubmitFlag(submitFlag);
q.setReportStatusCode(statusCode);
q.setIndustryCode(industryCode);
q.setRecentYearFrom(yearFrom);
return evalReportDomainService.count(q);
}
private long countByCompleteFlag(Long orgId, int completeFlag) {
EvalReportQuery q = baseQuery(orgId);
q.setCompleteFlag(completeFlag);
return evalReportDomainService.count(q);
}
private String resolveEvalTypeName(String code, String name) {
if (StringUtils.hasText(name)) {
return name.trim();
}
if (!StringUtils.hasText(code)) {
return null;
}
switch (code.trim().toUpperCase()) {
case "PRE":
return "安全预评价";
case "ACCEPT":
return "安全验收评价";
case "STATUS":
return "安全现状评价";
default:
return code;
}
}
private String resolveSecretName(String code, String name) {
if (StringUtils.hasText(name)) {
return name.trim();
}
if (!StringUtils.hasText(code)) {
return null;
}
switch (code.trim().toUpperCase()) {
case "NORMAL":
return "普通";
case "TRADE_SECRET":
return "含商业秘密";
case "PRIVACY":
return "涉个人隐私";
case "CLASSIFIED":
return "涉密资料";
default:
return code;
}
}
}

View File

@ -17,6 +17,7 @@ import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.InspNoticeDomainService;
import org.qinan.safetyeval.domain.service.InspNoticeMaterialDomainService;
import org.qinan.safetyeval.infrastructure.adapter.auth.AuthUserContextAdapter;
import org.qinan.safetyeval.infrastructure.support.InspNoticeIdSupport;
import org.qinan.safetyeval.infrastructure.support.InspNoticeMessageHelper;
import org.qinan.safetyeval.infrastructure.support.InstitutionOrgSupport;
import org.springframework.beans.factory.annotation.Autowired;
@ -64,8 +65,17 @@ public class InstitutionInspNoticeExecutor implements InstitutionInspNoticeApi {
domainQuery.setPageSize(query.getSize());
domainQuery.setOrgId(institutionOrgSupport.resolveCurrentOrgId());
domainQuery.setNoticeNo(query.getNoticeNo());
domainQuery.setKeyword(query.getKeyword());
domainQuery.setStatusCode(query.getStatusCode());
domainQuery.setInspTypeCode(query.getInspTypeCode());
domainQuery.setSourceTypeCode(query.getSourceTypeCode());
domainQuery.setSourceRefNo(query.getSourceRefNo());
domainQuery.setConfirmFlag(query.getConfirmFlag());
domainQuery.setFeedbackFlag(query.getFeedbackFlag());
// 机构端不展示待下发草稿
if (query.getStatusCode() == null) {
domainQuery.setMinStatusCode(InspNoticeDomainService.STATUS_ISSUED);
}
PageResult<InspNoticeEntity> pageResult = inspNoticeDomainService.page(domainQuery);
return PageResponse.of(
pageResult.getRecords().stream().map(this::toCO).collect(Collectors.toList()),
@ -74,37 +84,41 @@ public class InstitutionInspNoticeExecutor implements InstitutionInspNoticeApi {
@Override
public SingleResponse<InstitutionInspNoticeCO> confirm(InstitutionInspNoticeConfirmCmd cmd) {
InspNoticeEntity existing = inspNoticeDomainService.get(cmd.getId());
Long noticeId = InspNoticeIdSupport.parseNoticeId(cmd.getId());
InspNoticeEntity existing = inspNoticeDomainService.get(noticeId);
inspNoticeDomainService.assertOrgAccess(existing, institutionOrgSupport.resolveCurrentOrgId());
InspNoticeEntity result = inspNoticeDomainService.confirm(cmd.getId());
InspNoticeEntity result = inspNoticeDomainService.confirm(noticeId);
return SingleResponse.success(toCO(result));
}
@Override
public SingleResponse<InstitutionInspNoticeCO> feedback(InstitutionInspNoticeFeedbackCmd cmd) {
InspNoticeEntity existing = inspNoticeDomainService.get(cmd.getId());
Long noticeId = InspNoticeIdSupport.parseNoticeId(cmd.getId());
InspNoticeEntity existing = inspNoticeDomainService.get(noticeId);
inspNoticeDomainService.assertOrgAccess(existing, institutionOrgSupport.resolveCurrentOrgId());
Long lastMaterialId = null;
if (!CollectionUtils.isEmpty(cmd.getMaterials())) {
for (InstitutionInspNoticeMaterialUploadCmd item : cmd.getMaterials()) {
InspNoticeMaterialEntity material = inspNoticeMaterialDomainService.get(item.getMaterialId());
if (!cmd.getId().equals(material.getNoticeId())
Long materialId = InspNoticeIdSupport.parseMaterialId(item.getMaterialId());
InspNoticeMaterialEntity material = inspNoticeMaterialDomainService.get(materialId);
if (!noticeId.equals(material.getNoticeId())
|| !existing.getOrgId().equals(material.getOrgId())) {
throw new BizException(ErrorCode.INSP_NOTICE_MATERIAL_NOT_FOUND);
}
inspNoticeMaterialDomainService.upload(item.getMaterialId(), item.getFileUrl());
lastMaterialId = item.getMaterialId();
inspNoticeMaterialDomainService.upload(materialId, item.getFileUrl());
lastMaterialId = materialId;
}
}
inspNoticeMaterialDomainService.assertRequiredMaterialsUploaded(cmd.getId());
InspNoticeEntity result = inspNoticeDomainService.feedback(cmd.getId());
inspNoticeMaterialDomainService.assertRequiredMaterialsUploaded(noticeId);
InspNoticeEntity result = inspNoticeDomainService.feedback(noticeId);
sendRegMessage(result, lastMaterialId);
return SingleResponse.success(toCO(result));
}
@Override
public SingleResponse<InspNoticeMaterialCO> uploadMaterial(InstitutionInspNoticeMaterialUploadCmd cmd) {
InspNoticeMaterialEntity material = inspNoticeMaterialDomainService.get(cmd.getMaterialId());
Long materialId = InspNoticeIdSupport.parseMaterialId(cmd.getMaterialId());
InspNoticeMaterialEntity material = inspNoticeMaterialDomainService.get(materialId);
InspNoticeEntity notice = inspNoticeDomainService.get(material.getNoticeId());
inspNoticeDomainService.assertOrgAccess(notice, institutionOrgSupport.resolveCurrentOrgId());
if (!notice.getOrgId().equals(material.getOrgId())) {
@ -116,7 +130,7 @@ public class InstitutionInspNoticeExecutor implements InstitutionInspNoticeApi {
&& status != InspNoticeDomainService.STATUS_FEEDBACK)) {
throw new BizException(ErrorCode.INSP_NOTICE_STATUS_INVALID);
}
InspNoticeMaterialEntity result = inspNoticeMaterialDomainService.upload(cmd.getMaterialId(), cmd.getFileUrl());
InspNoticeMaterialEntity result = inspNoticeMaterialDomainService.upload(materialId, cmd.getFileUrl());
return SingleResponse.success(toMaterialCO(result));
}
@ -151,12 +165,16 @@ public class InstitutionInspNoticeExecutor implements InstitutionInspNoticeApi {
co.setInspTypeName(entity.getInspTypeName());
co.setSourceTypeCode(entity.getSourceTypeCode());
co.setSourceTypeName(entity.getSourceTypeName());
co.setSourceRefNo(entity.getSourceRefNo());
co.setPlanInspTime(entity.getPlanInspTime());
co.setLeaderName(entity.getLeaderName());
co.setNoticeContent(entity.getNoticeContent());
co.setNoticeUrls(entity.getNoticeUrls());
co.setStatusCode(entity.getStatusCode());
co.setStatusName(entity.getStatusName());
co.setConfirmFlag(entity.getConfirmFlag());
co.setFeedbackFlag(entity.getStatusCode() != null
&& entity.getStatusCode() >= InspNoticeDomainService.STATUS_FEEDBACK ? 1 : 2);
co.setConfirmTime(entity.getConfirmTime());
co.setSendTime(entity.getSendTime());
co.setResultCode(entity.getResultCode());
@ -164,6 +182,7 @@ public class InstitutionInspNoticeExecutor implements InstitutionInspNoticeApi {
co.setResultContent(entity.getResultContent());
co.setResultUrls(entity.getResultUrls());
co.setResultTime(entity.getResultTime());
co.setRemarks(entity.getRemarks());
return co;
}

View File

@ -3,19 +3,33 @@ package org.qinan.safetyeval.app.executor.regulator;
import org.qinan.safetyeval.app.executor.support.ReportProfileCoConverter;
import org.qinan.safetyeval.client.api.regulator.RegulatorEvalReportApi;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.RegulatorEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportAddCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportPageQuery;
import org.qinan.safetyeval.domain.entity.EvalReportEntity;
import org.qinan.safetyeval.domain.entity.OrgInfoEntity;
import org.qinan.safetyeval.domain.gateway.OrgInfoGateway;
import org.qinan.safetyeval.domain.query.EvalReportQuery;
import org.qinan.safetyeval.domain.query.OrgInfoQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.EvalReportDomainService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@ -25,6 +39,19 @@ public class RegulatorEvalReportExecutor implements RegulatorEvalReportApi {
@Autowired(required = false)
private EvalReportDomainService evalReportDomainService;
@Autowired(required = false)
private OrgInfoGateway orgInfoGateway;
@Override
public SingleResponse<RegulatorEvalReportSummaryCO> summary() {
RegulatorEvalReportSummaryCO co = new RegulatorEvalReportSummaryCO();
int year = LocalDate.now().getYear();
co.setSubmittedTotalCount(countSubmitted(null, null));
co.setRecentThreeYearCount(countSubmitted(year - 2, null));
co.setCurrentYearCount(countSubmitted(null, year));
return SingleResponse.success(co);
}
@Override
public PageResponse<EvalReportLibraryCO> page(RegulatorEvalReportPageQuery query) {
EvalReportQuery domainQuery = new EvalReportQuery();
@ -33,16 +60,47 @@ public class RegulatorEvalReportExecutor implements RegulatorEvalReportApi {
domainQuery.setOrgId(query.getOrgId());
domainQuery.setReportYear(query.getReportYear());
domainQuery.setEvalTypeCode(query.getEvalTypeCode());
domainQuery.setIndustryCode(query.getIndustryCode());
domainQuery.setReportStatusCode(query.getReportStatusCode());
domainQuery.setKeyword(query.getKeyword());
// 监管端报告数据库默认只看已报送
domainQuery.setSubmitFlag(query.getSubmitFlag() != null ? query.getSubmitFlag() : 1);
if (StringUtils.hasText(query.getArchiveTimeStart())) {
domainQuery.setArchiveTimeFrom(LocalDate.parse(query.getArchiveTimeStart().trim()).atStartOfDay());
}
if (StringUtils.hasText(query.getArchiveTimeEnd())) {
domainQuery.setArchiveTimeTo(LocalDate.parse(query.getArchiveTimeEnd().trim()).atTime(LocalTime.MAX));
}
if (StringUtils.hasText(query.getOrgName())) {
List<Long> orgIds = resolveOrgIdsByName(query.getOrgName().trim());
domainQuery.setOrgIds(orgIds);
if (orgIds.isEmpty()) {
return PageResponse.of(Collections.<EvalReportLibraryCO>emptyList(), 0L);
}
}
PageResult<EvalReportEntity> pageResult = evalReportDomainService.page(domainQuery);
return PageResponse.of(
pageResult.getRecords().stream().map(ReportProfileCoConverter::toReportCO).collect(Collectors.toList()),
pageResult.getTotal());
Map<Long, String> orgNameMap = buildOrgNameMap(pageResult.getRecords());
List<EvalReportLibraryCO> list = pageResult.getRecords().stream().map(entity -> {
EvalReportLibraryCO co = ReportProfileCoConverter.toReportCO(entity);
if (co != null && entity.getOrgId() != null) {
co.setOrgName(orgNameMap.get(entity.getOrgId()));
}
return co;
}).collect(Collectors.toList());
return PageResponse.of(list, pageResult.getTotal());
}
@Override
public SingleResponse<EvalReportLibraryCO> get(Long id) {
return SingleResponse.success(ReportProfileCoConverter.toReportCO(evalReportDomainService.get(id)));
EvalReportEntity entity = evalReportDomainService.get(id);
EvalReportLibraryCO co = ReportProfileCoConverter.toReportCO(entity);
if (co != null && entity.getOrgId() != null && orgInfoGateway != null) {
OrgInfoEntity org = orgInfoGateway.get(entity.getOrgId());
if (org != null) {
co.setOrgName(org.getUnitName());
}
}
return SingleResponse.success(co);
}
@Override
@ -50,16 +108,103 @@ public class RegulatorEvalReportExecutor implements RegulatorEvalReportApi {
EvalReportEntity entity = new EvalReportEntity();
entity.setOrgId(cmd.getOrgId());
entity.setProjectId(cmd.getProjectId());
entity.setCustomerId(cmd.getCustomerId());
entity.setEvaluatedUnitName(cmd.getEvaluatedUnitName());
entity.setReportName(cmd.getReportName());
entity.setReportNo(cmd.getReportNo());
entity.setReportYear(cmd.getReportYear() != null ? cmd.getReportYear() : LocalDateTime.now().getYear());
entity.setEvalTypeCode(cmd.getEvalTypeCode());
entity.setEvalTypeName(cmd.getEvalTypeName());
entity.setEvalTypeName(resolveEvalTypeName(cmd.getEvalTypeCode(), cmd.getEvalTypeName()));
entity.setIndustryCode(cmd.getIndustryCode());
entity.setIndustryName(cmd.getIndustryName());
entity.setBusinessScope(cmd.getBusinessScope());
entity.setFileUrl(cmd.getFileUrl());
entity.setFileName(cmd.getFileName());
entity.setIssueDate(cmd.getIssueDate());
entity.setPageCount(cmd.getPageCount());
entity.setSecretLevelCode(cmd.getSecretLevelCode());
entity.setSecretLevelName(cmd.getSecretLevelName());
entity.setRemarks(cmd.getRemarks());
entity.setCompleteFlag(1);
entity.setSubmitFlag(1);
if (StringUtils.hasText(cmd.getReportNo())) {
evalReportDomainService.assertReportNoUnique(cmd.getOrgId(), cmd.getReportNo(), null);
}
EvalReportEntity result = evalReportDomainService.addForRegulator(entity);
return SingleResponse.success(ReportProfileCoConverter.toReportCO(result));
return get(result.getId());
}
private long countSubmitted(Integer yearFrom, Integer exactYear) {
EvalReportQuery q = new EvalReportQuery();
q.setPageNum(1L);
q.setPageSize(1L);
q.setSubmitFlag(1);
if (yearFrom != null) {
q.setRecentYearFrom(yearFrom);
}
if (exactYear != null) {
q.setReportYear(exactYear);
}
return evalReportDomainService.count(q);
}
private List<Long> resolveOrgIdsByName(String orgName) {
if (orgInfoGateway == null || !StringUtils.hasText(orgName)) {
return Collections.emptyList();
}
OrgInfoQuery query = new OrgInfoQuery();
query.setPageNum(1L);
query.setPageSize(500L);
query.setUnitName(orgName);
PageResult<OrgInfoEntity> page = orgInfoGateway.page(query);
if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) {
return Collections.emptyList();
}
List<Long> ids = new ArrayList<Long>();
for (OrgInfoEntity org : page.getRecords()) {
if (org != null && org.getId() != null) {
ids.add(org.getId());
}
}
return ids;
}
private Map<Long, String> buildOrgNameMap(List<EvalReportEntity> records) {
Map<Long, String> map = new HashMap<Long, String>();
if (records == null || records.isEmpty() || orgInfoGateway == null) {
return map;
}
Set<Long> ids = new HashSet<Long>();
for (EvalReportEntity entity : records) {
if (entity != null && entity.getOrgId() != null) {
ids.add(entity.getOrgId());
}
}
for (Long id : ids) {
OrgInfoEntity org = orgInfoGateway.get(id);
if (org != null) {
map.put(id, org.getUnitName());
}
}
return map;
}
private String resolveEvalTypeName(String code, String name) {
if (StringUtils.hasText(name)) {
return name.trim();
}
if (!StringUtils.hasText(code)) {
return null;
}
switch (code.trim().toUpperCase()) {
case "PRE":
return "安全预评价";
case "ACCEPT":
return "安全验收评价";
case "STATUS":
return "安全现状评价";
default:
return code;
}
}
}

View File

@ -1,5 +1,6 @@
package org.qinan.safetyeval.app.executor.regulator;
import org.qinan.safetyeval.app.executor.support.RegulatorInspNoticeFileSupport;
import org.qinan.safetyeval.client.api.SafetyMessageApi;
import org.qinan.safetyeval.client.api.regulator.RegulatorInspNoticeApi;
import org.qinan.safetyeval.client.co.insp.InspNoticeMaterialCO;
@ -10,13 +11,16 @@ import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.*;
import org.qinan.safetyeval.domain.entity.InspNoticeEntity;
import org.qinan.safetyeval.domain.entity.InspNoticeMaterialEntity;
import org.qinan.safetyeval.domain.entity.OrgInfoEntity;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.OrgInfoGateway;
import org.qinan.safetyeval.domain.query.InspNoticeQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.InspNoticeDomainService;
import org.qinan.safetyeval.domain.service.InspNoticeMaterialDomainService;
import org.qinan.safetyeval.infrastructure.adapter.auth.AuthUserContextAdapter;
import org.qinan.safetyeval.infrastructure.support.InspNoticeIdSupport;
import org.qinan.safetyeval.infrastructure.support.InspNoticeMessageHelper;
import org.qinan.safetyeval.infrastructure.support.NoticeNoGenerator;
import org.springframework.beans.factory.annotation.Autowired;
@ -28,7 +32,11 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@ -50,48 +58,79 @@ public class RegulatorInspNoticeExecutor implements RegulatorInspNoticeApi {
@Autowired(required = false)
private InspNoticeMessageHelper inspNoticeMessageHelper;
@Lazy
@Autowired(required = false)
private RegulatorInspNoticeFileSupport regulatorInspNoticeFileSupport;
@Autowired(required = false)
private OrgInfoGateway orgInfoGateway;
@Resource
private SafetyMessageApi safetyMessageApi;
@Override
public SingleResponse<RegulatorInspNoticeCO> issue(RegulatorInspNoticeIssueCmd cmd) {
InspNoticeEntity entity = new InspNoticeEntity();
entity.setOrgId(cmd.getOrgId());
entity.setInspTypeCode(cmd.getInspTypeCode());
entity.setInspTypeName(cmd.getInspTypeName());
entity.setSourceTypeCode(cmd.getSourceTypeCode());
entity.setSourceTypeName(cmd.getSourceTypeName());
entity.setSourceRefId(cmd.getSourceRefId());
entity.setProjectId(cmd.getProjectId());
entity.setPlanInspTime(cmd.getPlanInspTime());
entity.setLeaderName(cmd.getLeaderName());
entity.setNoticeContent(cmd.getNoticeContent());
public SingleResponse<RegulatorInspNoticeCO> save(RegulatorInspNoticeSaveCmd cmd) {
if (cmd.getId() != null) {
InspNoticeEntity update = buildContentEntity(cmd);
update.setId(InspNoticeIdSupport.parseNoticeId(cmd.getId()));
InspNoticeEntity result = inspNoticeDomainService.updateDraft(update);
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
Long orgId = InspNoticeIdSupport.parseOrgId(cmd.getOrgId());
if (orgId == null) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "被检机构id不能为空");
}
InspNoticeEntity entity = buildContentEntity(cmd);
entity.setOrgId(orgId);
InspNoticeEntity result = saveDraftWithNoticeNo(entity);
createPresetMaterials(result, cmd.getMaterialNames());
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
@Override
public SingleResponse<RegulatorInspNoticeCO> issue(RegulatorInspNoticeIssueCmd cmd) {
if (cmd.getId() != null) {
InspNoticeEntity result = inspNoticeDomainService.issueDraft(
InspNoticeIdSupport.parseNoticeId(cmd.getId()));
sendOrgMessage(result);
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
Long orgId = InspNoticeIdSupport.parseOrgId(cmd.getOrgId());
if (orgId == null) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "被检机构id不能为空");
}
InspNoticeEntity entity = new InspNoticeEntity();
entity.setOrgId(orgId);
fillContent(entity, cmd.getInspTypeCode(), cmd.getInspTypeName(), cmd.getSourceTypeCode(),
cmd.getSourceTypeName(), cmd.getSourceRefId(), cmd.getSourceRefNo(), cmd.getProjectId(),
cmd.getPlanInspTime(), cmd.getLeaderName(), cmd.getNoticeContent(),
joinUrls(cmd.getNoticeUrls()), cmd.getRemarks());
InspNoticeEntity result = issueWithNoticeNo(entity);
createPresetMaterials(result, cmd.getMaterialNames());
sendOrgMessage(result);
return SingleResponse.success(toCO(result));
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
@Override
public SingleResponse<RegulatorInspNoticeCO> get(Long id) {
return SingleResponse.success(toCO(inspNoticeDomainService.get(id)));
InspNoticeEntity entity = inspNoticeDomainService.get(id);
return SingleResponse.success(toCO(entity, resolveOrgName(entity.getOrgId())));
}
@Override
public PageResponse<RegulatorInspNoticeCO> page(RegulatorInspNoticePageQuery query) {
InspNoticeQuery domainQuery = new InspNoticeQuery();
InspNoticeQuery domainQuery = regulatorInspNoticeFileSupport.toDomainQuery(query);
if (domainQuery.getOrgIds() != null && domainQuery.getOrgIds().isEmpty()) {
return PageResponse.of(Collections.<RegulatorInspNoticeCO>emptyList(), 0L);
}
domainQuery.setPageNum(query.getCurrent());
domainQuery.setPageSize(query.getSize());
domainQuery.setOrgId(query.getOrgId());
domainQuery.setNoticeNo(query.getNoticeNo());
domainQuery.setStatusCode(query.getStatusCode());
domainQuery.setInspTypeCode(query.getInspTypeCode());
domainQuery.setLeaderName(query.getLeaderName());
PageResult<InspNoticeEntity> pageResult = inspNoticeDomainService.page(domainQuery);
return PageResponse.of(
pageResult.getRecords().stream().map(this::toCO).collect(Collectors.toList()),
pageResult.getTotal());
Map<Long, String> orgNameMap = buildOrgNameMap(pageResult.getRecords());
List<RegulatorInspNoticeCO> list = pageResult.getRecords().stream()
.map(entity -> toCO(entity, entity.getOrgId() == null ? null : orgNameMap.get(entity.getOrgId())))
.collect(Collectors.toList());
return PageResponse.of(list, pageResult.getTotal());
}
@Override
@ -100,14 +139,21 @@ public class RegulatorInspNoticeExecutor implements RegulatorInspNoticeApi {
String recorderName = AuthUserContextAdapter.getCurrentUserName();
String resultUrls = joinUrls(cmd.getResultUrls());
InspNoticeEntity result = inspNoticeDomainService.close(
cmd.getId(),
InspNoticeIdSupport.parseNoticeId(cmd.getId()),
cmd.getResultCode(),
cmd.getResultName(),
cmd.getResultContent(),
resultUrls,
recorderId,
recorderName);
return SingleResponse.success(toCO(result));
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
@Override
public SingleResponse<RegulatorInspNoticeCO> remark(RegulatorInspNoticeRemarkCmd cmd) {
InspNoticeEntity result = inspNoticeDomainService.updateRemarks(
InspNoticeIdSupport.parseNoticeId(cmd.getId()), cmd.getRemarks());
return SingleResponse.success(toCO(result, resolveOrgName(result.getOrgId())));
}
@Override
@ -119,14 +165,86 @@ public class RegulatorInspNoticeExecutor implements RegulatorInspNoticeApi {
@Override
public SingleResponse<Void> urge(RegulatorInspNoticeUrgeCmd cmd) {
InspNoticeEntity notice = inspNoticeDomainService.get(cmd.getId());
if (notice.getStatusCode() == null || notice.getStatusCode() >= InspNoticeDomainService.STATUS_CLOSED) {
InspNoticeEntity notice = inspNoticeDomainService.get(
InspNoticeIdSupport.parseNoticeId(cmd.getId()));
Integer status = notice.getStatusCode();
if (status == null
|| status == InspNoticeDomainService.STATUS_DRAFT
|| status >= InspNoticeDomainService.STATUS_CLOSED) {
throw new BizException(ErrorCode.INSP_NOTICE_STATUS_INVALID);
}
sendOrgMessage(notice);
return SingleResponse.success();
}
private InspNoticeEntity buildContentEntity(RegulatorInspNoticeSaveCmd cmd) {
InspNoticeEntity entity = new InspNoticeEntity();
Long orgId = InspNoticeIdSupport.parseOrgId(cmd.getOrgId());
if (orgId != null) {
entity.setOrgId(orgId);
}
fillContent(entity, cmd.getInspTypeCode(), cmd.getInspTypeName(), cmd.getSourceTypeCode(),
cmd.getSourceTypeName(), cmd.getSourceRefId(), cmd.getSourceRefNo(), cmd.getProjectId(),
cmd.getPlanInspTime(), cmd.getLeaderName(), cmd.getNoticeContent(),
joinUrls(cmd.getNoticeUrls()), cmd.getRemarks());
return entity;
}
private void fillContent(InspNoticeEntity entity, String inspTypeCode, String inspTypeName,
Integer sourceTypeCode, String sourceTypeName, Long sourceRefId,
String sourceRefNo, Long projectId, java.time.LocalDateTime planInspTime,
String leaderName, String noticeContent, String noticeUrls, String remarks) {
entity.setInspTypeCode(inspTypeCode);
entity.setInspTypeName(inspTypeName);
entity.setSourceTypeCode(sourceTypeCode);
entity.setSourceTypeName(resolveSourceTypeName(sourceTypeCode, sourceTypeName));
entity.setSourceRefId(sourceRefId);
entity.setSourceRefNo(sourceRefNo);
entity.setProjectId(projectId);
entity.setPlanInspTime(planInspTime);
entity.setLeaderName(leaderName);
entity.setNoticeContent(noticeContent);
entity.setNoticeUrls(noticeUrls);
entity.setRemarks(remarks);
}
private String resolveSourceTypeName(Integer code, String name) {
if (StringUtils.hasText(name)) {
return name.trim();
}
if (code == null) {
return null;
}
switch (code) {
case 1:
return "年度计划";
case 2:
return "专项安排";
case 3:
return "其他";
case 4:
return "风险预警";
default:
return null;
}
}
private InspNoticeEntity saveDraftWithNoticeNo(InspNoticeEntity entity) {
DuplicateKeyException lastEx = null;
for (int i = 0; i < noticeNoGenerator.maxRetry(); i++) {
entity.setNoticeNo(noticeNoGenerator.generate());
try {
return inspNoticeDomainService.saveDraft(entity);
} catch (DuplicateKeyException ex) {
lastEx = ex;
}
}
if (lastEx != null) {
throw new BizException(ErrorCode.INSP_NOTICE_NO_GENERATE_FAILED, lastEx);
}
throw new BizException(ErrorCode.INSP_NOTICE_NO_GENERATE_FAILED);
}
private InspNoticeEntity issueWithNoticeNo(InspNoticeEntity entity) {
DuplicateKeyException lastEx = null;
for (int i = 0; i < noticeNoGenerator.maxRetry(); i++) {
@ -183,26 +301,59 @@ public class RegulatorInspNoticeExecutor implements RegulatorInspNoticeApi {
.collect(Collectors.joining(","));
}
private RegulatorInspNoticeCO toCO(InspNoticeEntity entity) {
private String resolveOrgName(Long orgId) {
if (orgId == null || orgInfoGateway == null) {
return null;
}
OrgInfoEntity org = orgInfoGateway.get(orgId);
return org == null ? null : org.getUnitName();
}
private Map<Long, String> buildOrgNameMap(List<InspNoticeEntity> records) {
Map<Long, String> map = new HashMap<Long, String>();
if (records == null || records.isEmpty() || orgInfoGateway == null) {
return map;
}
Set<Long> ids = new HashSet<Long>();
for (InspNoticeEntity entity : records) {
if (entity != null && entity.getOrgId() != null) {
ids.add(entity.getOrgId());
}
}
for (Long id : ids) {
OrgInfoEntity org = orgInfoGateway.get(id);
if (org != null) {
map.put(id, org.getUnitName());
}
}
return map;
}
private RegulatorInspNoticeCO toCO(InspNoticeEntity entity, String orgName) {
if (entity == null) {
return null;
}
RegulatorInspNoticeCO co = new RegulatorInspNoticeCO();
co.setId(entity.getId() != null ? String.valueOf(entity.getId()) : null);
co.setOrgId(entity.getOrgId() != null ? String.valueOf(entity.getOrgId()) : null);
co.setOrgName(orgName);
co.setNoticeNo(entity.getNoticeNo());
co.setInspTypeCode(entity.getInspTypeCode());
co.setInspTypeName(entity.getInspTypeName());
co.setSourceTypeCode(entity.getSourceTypeCode());
co.setSourceTypeName(entity.getSourceTypeName());
co.setSourceRefId(entity.getSourceRefId() != null ? String.valueOf(entity.getSourceRefId()) : null);
co.setSourceRefNo(entity.getSourceRefNo());
co.setProjectId(entity.getProjectId() != null ? String.valueOf(entity.getProjectId()) : null);
co.setPlanInspTime(entity.getPlanInspTime());
co.setLeaderName(entity.getLeaderName());
co.setNoticeContent(entity.getNoticeContent());
co.setNoticeUrls(entity.getNoticeUrls());
co.setStatusCode(entity.getStatusCode());
co.setStatusName(entity.getStatusName());
co.setConfirmFlag(entity.getConfirmFlag());
co.setFeedbackFlag(entity.getStatusCode() != null && entity.getStatusCode() >= InspNoticeDomainService.STATUS_FEEDBACK
? 1 : 2);
co.setConfirmTime(entity.getConfirmTime());
co.setSendTime(entity.getSendTime());
co.setResultCode(entity.getResultCode());
@ -211,6 +362,7 @@ public class RegulatorInspNoticeExecutor implements RegulatorInspNoticeApi {
co.setResultUrls(entity.getResultUrls());
co.setResultRecorderName(entity.getResultRecorderName());
co.setResultTime(entity.getResultTime());
co.setRemarks(entity.getRemarks());
return co;
}

View File

@ -0,0 +1,208 @@
package org.qinan.safetyeval.app.executor.support;
import org.qinan.safetyeval.client.dto.institution.InstitutionInspNoticeExportCmd;
import org.qinan.safetyeval.domain.entity.InspNoticeEntity;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.query.InspNoticeQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.InspNoticeDomainService;
import org.qinan.safetyeval.infrastructure.support.InspNoticeIdSupport;
import org.qinan.safetyeval.infrastructure.support.InstitutionOrgSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
*/
@Component
public class InstitutionInspNoticeFileSupport {
public static final int MAX_EXPORT_ROWS = 5000;
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Lazy
@Autowired(required = false)
private InspNoticeDomainService inspNoticeDomainService;
@Lazy
@Autowired(required = false)
private InstitutionOrgSupport institutionOrgSupport;
public void writeMessageCsv(InstitutionInspNoticeExportCmd cmd, OutputStream outputStream) throws IOException {
List<InspNoticeEntity> notices = resolveExportNotices(cmd);
if (notices.isEmpty()) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
try {
writer.write('\ufeff');
writer.write(csvLine(new String[]{
"消息编号", "来源预警", "来源字段", "监管要求", "办理期限", "处理状态", "办结结果"
}));
for (InspNoticeEntity notice : notices) {
if (notice == null) {
continue;
}
writer.write(csvLine(new String[]{
nullToEmpty(notice.getNoticeNo()),
nullToEmpty(notice.getSourceRefNo()),
sourceFieldLabel(notice),
nullToEmpty(notice.getNoticeContent()),
formatDate(notice.getPlanInspTime()),
statusDisplayName(notice),
resultLabel(notice)
}));
}
writer.flush();
} finally {
writer.close();
}
}
private List<InspNoticeEntity> resolveExportNotices(InstitutionInspNoticeExportCmd cmd) {
if (cmd == null) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
Long orgId = institutionOrgSupport.resolveCurrentOrgId();
String scope = cmd.getScope() == null ? "" : cmd.getScope().trim().toUpperCase();
boolean hasIds = !CollectionUtils.isEmpty(cmd.getIds());
if ("SELECTED".equals(scope) || (hasIds && !"FILTERED".equals(scope) && !"ALL".equals(scope))) {
if (!hasIds) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
if (cmd.getIds().size() > MAX_EXPORT_ROWS) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_TOO_LARGE);
}
List<Long> ids = new ArrayList<Long>();
for (String raw : cmd.getIds()) {
ids.add(InspNoticeIdSupport.parseNoticeId(raw));
}
InspNoticeQuery q = new InspNoticeQuery();
q.setPageNum(1L);
q.setPageSize((long) MAX_EXPORT_ROWS);
q.setOrgId(orgId);
q.setIds(ids);
q.setMinStatusCode(InspNoticeDomainService.STATUS_ISSUED);
PageResult<InspNoticeEntity> page = inspNoticeDomainService.page(q);
return page == null || page.getRecords() == null
? Collections.<InspNoticeEntity>emptyList()
: page.getRecords();
}
InspNoticeQuery query = new InspNoticeQuery();
query.setOrgId(orgId);
query.setPageNum(1L);
query.setPageSize((long) MAX_EXPORT_ROWS);
query.setMinStatusCode(InspNoticeDomainService.STATUS_ISSUED);
if (!"ALL".equals(scope)) {
query.setNoticeNo(cmd.getNoticeNo());
query.setKeyword(cmd.getKeyword());
query.setStatusCode(cmd.getStatusCode());
query.setInspTypeCode(cmd.getInspTypeCode());
query.setSourceTypeCode(cmd.getSourceTypeCode());
query.setSourceRefNo(cmd.getSourceRefNo());
query.setConfirmFlag(cmd.getConfirmFlag());
query.setFeedbackFlag(cmd.getFeedbackFlag());
if (cmd.getStatusCode() != null) {
query.setMinStatusCode(null);
}
}
PageResult<InspNoticeEntity> page = inspNoticeDomainService.page(query);
if (page == null || page.getRecords() == null) {
return Collections.emptyList();
}
return page.getRecords();
}
private static String sourceFieldLabel(InspNoticeEntity notice) {
StringBuilder sb = new StringBuilder();
if (StringUtils.hasText(notice.getSourceTypeName())) {
sb.append(notice.getSourceTypeName());
}
if (StringUtils.hasText(notice.getInspTypeName())) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(notice.getInspTypeName());
}
return sb.length() == 0 ? "监督检查" : sb.toString();
}
private static String statusDisplayName(InspNoticeEntity notice) {
Integer code = notice.getStatusCode();
if (code == null) {
return nullToEmpty(notice.getStatusName());
}
if (code == 1) {
return "待处理";
}
if (code == 2) {
return "处理中";
}
if (code == 3) {
return "待监管复核";
}
if (code == 4) {
return "已办结";
}
return nullToEmpty(notice.getStatusName());
}
private static String resultLabel(InspNoticeEntity notice) {
if (!StringUtils.hasText(notice.getResultName()) && !StringUtils.hasText(notice.getResultContent())) {
return "";
}
if (!StringUtils.hasText(notice.getResultName())) {
return nullToEmpty(notice.getResultContent());
}
if (!StringUtils.hasText(notice.getResultContent())) {
return notice.getResultName();
}
return notice.getResultName() + "" + notice.getResultContent();
}
private static String formatDate(LocalDateTime value) {
return value == null ? "" : DATE_FMT.format(value);
}
private static String nullToEmpty(String value) {
return value == null ? "" : value;
}
private static String csvLine(String[] cols) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cols.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(escapeCsv(cols[i]));
}
sb.append('\n');
return sb.toString();
}
private static String escapeCsv(String value) {
if (value == null) {
return "";
}
boolean needQuote = value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r");
String escaped = value.replace("\"", "\"\"");
return needQuote ? "\"" + escaped + "\"" : escaped;
}
}

View File

@ -0,0 +1,343 @@
package org.qinan.safetyeval.app.executor.support;
import org.qinan.safetyeval.domain.entity.EvalReportEntity;
import org.qinan.safetyeval.domain.entity.OrgInfoEntity;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.OrgInfoGateway;
import org.qinan.safetyeval.domain.query.EvalReportQuery;
import org.qinan.safetyeval.domain.query.OrgInfoQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.EvalReportDomainService;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportExportCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportPageQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* ZIP CSV
*/
@Component
public class RegulatorEvalReportFileSupport {
public static final int MAX_BATCH_DOWNLOAD = 50;
public static final int MAX_EXPORT_ROWS = 5000;
private static final int CONNECT_TIMEOUT_MS = 15000;
private static final int READ_TIMEOUT_MS = 60000;
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Lazy
@Autowired(required = false)
private EvalReportDomainService evalReportDomainService;
@Autowired(required = false)
private OrgInfoGateway orgInfoGateway;
public void writeBatchZip(List<Long> ids, OutputStream outputStream) throws IOException {
if (ids == null || ids.isEmpty()) {
throw new BizException(ErrorCode.EVAL_REPORT_BATCH_EMPTY);
}
if (ids.size() > MAX_BATCH_DOWNLOAD) {
throw new BizException(ErrorCode.EVAL_REPORT_BATCH_TOO_LARGE);
}
List<EvalReportEntity> reports = evalReportDomainService.listByIds(ids);
if (reports == null || reports.isEmpty()) {
throw new BizException(ErrorCode.EVAL_REPORT_NOT_FOUND);
}
int withFile = 0;
for (EvalReportEntity report : reports) {
if (report != null && StringUtils.hasText(report.getFileUrl())) {
withFile++;
}
}
if (withFile == 0) {
throw new BizException(ErrorCode.EVAL_REPORT_FILE_MISSING);
}
Set<String> usedNames = new HashSet<String>();
ZipOutputStream zip = new ZipOutputStream(outputStream);
byte[] buffer = new byte[8192];
try {
for (EvalReportEntity report : reports) {
if (report == null || !StringUtils.hasText(report.getFileUrl())) {
continue;
}
String entryName = buildZipEntryName(report, usedNames);
zip.putNextEntry(new ZipEntry(entryName));
HttpURLConnection connection = null;
InputStream in = null;
try {
connection = openConnection(report.getFileUrl().trim());
in = new BufferedInputStream(connection.getInputStream());
int n;
while ((n = in.read(buffer)) != -1) {
zip.write(buffer, 0, n);
}
} catch (IOException ex) {
// 单个文件失败写入说明条目,避免整包失败
String tip = "下载失败: " + report.getFileUrl() + " / " + ex.getMessage();
zip.write(tip.getBytes(StandardCharsets.UTF_8));
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
// ignore
}
}
if (connection != null) {
connection.disconnect();
}
}
zip.closeEntry();
}
zip.finish();
} finally {
zip.close();
}
}
public void writeCatalogCsv(RegulatorEvalReportExportCmd cmd, OutputStream outputStream) throws IOException {
List<EvalReportEntity> reports = resolveExportReports(cmd);
Map<Long, String> orgNameMap = buildOrgNameMap(reports);
Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
try {
// Excel UTF-8 BOM
writer.write('\ufeff');
writer.write(csvLine(new String[]{
"报告编号", "报告名称", "报送机构", "评价类型", "所属行业",
"被评价单位", "签发日期", "报送时间", "报告状态", "文件名", "文件地址"
}));
for (EvalReportEntity report : reports) {
if (report == null) {
continue;
}
String orgName = report.getOrgId() == null ? "" : orgNameMap.get(report.getOrgId());
writer.write(csvLine(new String[]{
nullToEmpty(report.getReportNo()),
nullToEmpty(report.getReportName()),
nullToEmpty(orgName),
nullToEmpty(report.getEvalTypeName()),
nullToEmpty(report.getIndustryName()),
nullToEmpty(report.getEvaluatedUnitName()),
formatDate(report.getIssueDate()),
formatDateTime(report.getArchiveTime()),
nullToEmpty(ReportProfileCoConverter.resolveDisplayStatusName(report)),
nullToEmpty(report.getFileName()),
nullToEmpty(report.getFileUrl())
}));
}
writer.flush();
} finally {
writer.close();
}
}
private List<EvalReportEntity> resolveExportReports(RegulatorEvalReportExportCmd cmd) {
if (cmd == null) {
throw new BizException(ErrorCode.EVAL_REPORT_BATCH_EMPTY);
}
String scope = cmd.getScope() == null ? "" : cmd.getScope().trim().toUpperCase();
boolean hasIds = cmd.getIds() != null && !cmd.getIds().isEmpty();
if ("SELECTED".equals(scope) || (hasIds && !"FILTERED".equals(scope) && !"ALL".equals(scope))) {
if (!hasIds) {
throw new BizException(ErrorCode.EVAL_REPORT_BATCH_EMPTY);
}
if (cmd.getIds().size() > MAX_EXPORT_ROWS) {
throw new BizException(ErrorCode.EVAL_REPORT_BATCH_TOO_LARGE);
}
return evalReportDomainService.listByIds(cmd.getIds());
}
EvalReportQuery query = buildFilterQuery(cmd);
if ("ALL".equals(scope)) {
query.setReportYear(null);
query.setEvalTypeCode(null);
query.setIndustryCode(null);
query.setKeyword(null);
query.setReportStatusCode(null);
query.setArchiveTimeFrom(null);
query.setArchiveTimeTo(null);
query.setOrgId(null);
query.setOrgIds(null);
}
query.setSubmitFlag(cmd.getSubmitFlag() != null ? cmd.getSubmitFlag() : 1);
query.setPageNum(1L);
query.setPageSize((long) MAX_EXPORT_ROWS);
PageResult<EvalReportEntity> page = evalReportDomainService.page(query);
if (page == null || page.getRecords() == null) {
return Collections.emptyList();
}
return page.getRecords();
}
private EvalReportQuery buildFilterQuery(RegulatorEvalReportPageQuery query) {
EvalReportQuery domainQuery = new EvalReportQuery();
domainQuery.setOrgId(query.getOrgId());
domainQuery.setReportYear(query.getReportYear());
domainQuery.setEvalTypeCode(query.getEvalTypeCode());
domainQuery.setIndustryCode(query.getIndustryCode());
domainQuery.setReportStatusCode(query.getReportStatusCode());
domainQuery.setKeyword(query.getKeyword());
domainQuery.setSubmitFlag(query.getSubmitFlag() != null ? query.getSubmitFlag() : 1);
if (StringUtils.hasText(query.getArchiveTimeStart())) {
domainQuery.setArchiveTimeFrom(LocalDate.parse(query.getArchiveTimeStart().trim()).atStartOfDay());
}
if (StringUtils.hasText(query.getArchiveTimeEnd())) {
domainQuery.setArchiveTimeTo(LocalDate.parse(query.getArchiveTimeEnd().trim()).atTime(LocalTime.MAX));
}
if (StringUtils.hasText(query.getOrgName())) {
domainQuery.setOrgIds(resolveOrgIdsByName(query.getOrgName().trim()));
}
return domainQuery;
}
private List<Long> resolveOrgIdsByName(String orgName) {
if (orgInfoGateway == null || !StringUtils.hasText(orgName)) {
return Collections.emptyList();
}
OrgInfoQuery query = new OrgInfoQuery();
query.setPageNum(1L);
query.setPageSize(500L);
query.setUnitName(orgName);
PageResult<OrgInfoEntity> page = orgInfoGateway.page(query);
if (page == null || page.getRecords() == null) {
return Collections.emptyList();
}
List<Long> ids = new ArrayList<Long>();
for (OrgInfoEntity org : page.getRecords()) {
if (org != null && org.getId() != null) {
ids.add(org.getId());
}
}
return ids;
}
private Map<Long, String> buildOrgNameMap(List<EvalReportEntity> records) {
Map<Long, String> map = new HashMap<Long, String>();
if (records == null || records.isEmpty() || orgInfoGateway == null) {
return map;
}
Set<Long> ids = new HashSet<Long>();
for (EvalReportEntity entity : records) {
if (entity != null && entity.getOrgId() != null) {
ids.add(entity.getOrgId());
}
}
for (Long id : ids) {
OrgInfoEntity org = orgInfoGateway.get(id);
if (org != null) {
map.put(id, org.getUnitName());
}
}
return map;
}
private HttpURLConnection openConnection(String fileUrl) throws IOException {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
connection.setReadTimeout(READ_TIMEOUT_MS);
connection.setInstanceFollowRedirects(true);
connection.setRequestMethod("GET");
int code = connection.getResponseCode();
if (code >= 400) {
throw new IOException("HTTP " + code);
}
return connection;
}
private String buildZipEntryName(EvalReportEntity report, Set<String> usedNames) {
String base = StringUtils.hasText(report.getReportNo())
? report.getReportNo().trim()
: ("report-" + report.getId());
base = base.replaceAll("[\\\\/:*?\"<>|]", "_");
String ext = extractExtension(report.getFileName(), report.getFileUrl());
String name = base + ext;
int i = 2;
while (!usedNames.add(name)) {
name = base + "_" + i + ext;
i++;
}
return name;
}
private String extractExtension(String fileName, String fileUrl) {
String source = StringUtils.hasText(fileName) ? fileName : fileUrl;
if (!StringUtils.hasText(source)) {
return ".bin";
}
int q = source.indexOf('?');
if (q > 0) {
source = source.substring(0, q);
}
int dot = source.lastIndexOf('.');
int slash = Math.max(source.lastIndexOf('/'), source.lastIndexOf('\\'));
if (dot > slash && dot < source.length() - 1) {
String ext = source.substring(dot);
if (ext.length() <= 10) {
return ext;
}
}
return ".bin";
}
private String csvLine(String[] cols) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cols.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(csvEscape(cols[i]));
}
sb.append('\n');
return sb.toString();
}
private String csvEscape(String value) {
if (value == null) {
return "";
}
boolean needQuote = value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r");
String escaped = value.replace("\"", "\"\"");
return needQuote ? ("\"" + escaped + "\"") : escaped;
}
private String nullToEmpty(String value) {
return value == null ? "" : value;
}
private String formatDate(LocalDate date) {
return date == null ? "" : DATE_FMT.format(date);
}
private String formatDateTime(LocalDateTime dateTime) {
return dateTime == null ? "" : DATETIME_FMT.format(dateTime);
}
}

View File

@ -0,0 +1,312 @@
package org.qinan.safetyeval.app.executor.support;
import org.qinan.safetyeval.client.dto.regulator.RegulatorInspNoticeExportCmd;
import org.qinan.safetyeval.client.dto.regulator.RegulatorInspNoticePageQuery;
import org.qinan.safetyeval.domain.entity.InspNoticeEntity;
import org.qinan.safetyeval.domain.entity.InspNoticeMaterialEntity;
import org.qinan.safetyeval.domain.entity.OrgInfoEntity;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.OrgInfoGateway;
import org.qinan.safetyeval.domain.query.InspNoticeQuery;
import org.qinan.safetyeval.domain.query.OrgInfoQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.InspNoticeDomainService;
import org.qinan.safetyeval.domain.service.InspNoticeMaterialDomainService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
*/
@Component
public class RegulatorInspNoticeFileSupport {
public static final int MAX_EXPORT_ROWS = 5000;
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Lazy
@Autowired(required = false)
private InspNoticeDomainService inspNoticeDomainService;
@Lazy
@Autowired(required = false)
private InspNoticeMaterialDomainService inspNoticeMaterialDomainService;
@Autowired(required = false)
private OrgInfoGateway orgInfoGateway;
public void writeLedgerCsv(RegulatorInspNoticeExportCmd cmd, OutputStream outputStream) throws IOException {
List<InspNoticeEntity> notices = resolveExportNotices(cmd);
if (notices.isEmpty()) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
Map<Long, String> orgNameMap = buildOrgNameMap(notices);
Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
try {
writer.write('\ufeff');
writer.write(csvLine(new String[]{
"告知编号", "评价机构", "检查类型", "计划检查时间", "来源方式", "来源预警编号",
"检查负责人", "机构确认", "材料反馈", "办理状态", "下发时间", "检查结果", "结果说明"
}));
for (InspNoticeEntity notice : notices) {
if (notice == null) {
continue;
}
String orgName = notice.getOrgId() == null ? "" : nullToEmpty(orgNameMap.get(notice.getOrgId()));
writer.write(csvLine(new String[]{
nullToEmpty(notice.getNoticeNo()),
orgName,
nullToEmpty(notice.getInspTypeName()),
formatDateTime(notice.getPlanInspTime()),
nullToEmpty(notice.getSourceTypeName()),
nullToEmpty(notice.getSourceRefNo()),
nullToEmpty(notice.getLeaderName()),
confirmLabel(notice.getConfirmFlag()),
feedbackLabel(notice.getStatusCode()),
nullToEmpty(notice.getStatusName()),
formatDateTime(notice.getSendTime()),
nullToEmpty(notice.getResultName()),
nullToEmpty(notice.getResultContent())
}));
}
writer.flush();
} finally {
writer.close();
}
}
public void writeRecordCsv(Long id, OutputStream outputStream) throws IOException {
InspNoticeEntity notice = inspNoticeDomainService.get(id);
String orgName = "";
if (notice.getOrgId() != null && orgInfoGateway != null) {
OrgInfoEntity org = orgInfoGateway.get(notice.getOrgId());
if (org != null) {
orgName = nullToEmpty(org.getUnitName());
}
}
List<InspNoticeMaterialEntity> materials = inspNoticeMaterialDomainService == null
? Collections.<InspNoticeMaterialEntity>emptyList()
: inspNoticeMaterialDomainService.listByNoticeId(id);
Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
try {
writer.write('\ufeff');
writer.write(csvLine(new String[]{"字段", "内容"}));
writeKv(writer, "告知编号", notice.getNoticeNo());
writeKv(writer, "评价机构", orgName);
writeKv(writer, "检查类型", notice.getInspTypeName());
writeKv(writer, "计划检查时间", formatDateTime(notice.getPlanInspTime()));
writeKv(writer, "来源方式", notice.getSourceTypeName());
writeKv(writer, "来源预警编号", notice.getSourceRefNo());
writeKv(writer, "检查负责人", notice.getLeaderName());
writeKv(writer, "告知内容", notice.getNoticeContent());
writeKv(writer, "告知附件", notice.getNoticeUrls());
writeKv(writer, "机构确认", confirmLabel(notice.getConfirmFlag()));
writeKv(writer, "确认时间", formatDateTime(notice.getConfirmTime()));
writeKv(writer, "材料反馈", feedbackLabel(notice.getStatusCode()));
writeKv(writer, "办理状态", notice.getStatusName());
writeKv(writer, "下发时间", formatDateTime(notice.getSendTime()));
writeKv(writer, "检查结果", notice.getResultName());
writeKv(writer, "结果说明", notice.getResultContent());
writeKv(writer, "结果附件", notice.getResultUrls());
writeKv(writer, "结果登记人", notice.getResultRecorderName());
writeKv(writer, "结果登记时间", formatDateTime(notice.getResultTime()));
writeKv(writer, "监管备注", notice.getRemarks());
writer.write(csvLine(new String[]{"材料名称", "上传状态", "文件地址"}));
for (InspNoticeMaterialEntity material : materials) {
if (material == null) {
continue;
}
writer.write(csvLine(new String[]{
nullToEmpty(material.getMaterialName()),
nullToEmpty(material.getUploadStatusName()),
nullToEmpty(material.getFileUrl())
}));
}
writer.flush();
} finally {
writer.close();
}
}
public InspNoticeQuery toDomainQuery(RegulatorInspNoticePageQuery query) {
InspNoticeQuery domainQuery = new InspNoticeQuery();
domainQuery.setOrgId(query.getOrgId());
domainQuery.setNoticeNo(query.getNoticeNo());
domainQuery.setKeyword(query.getKeyword());
domainQuery.setStatusCode(query.getStatusCode());
domainQuery.setInspTypeCode(query.getInspTypeCode());
domainQuery.setLeaderName(query.getLeaderName());
domainQuery.setSourceTypeCode(query.getSourceTypeCode());
domainQuery.setSourceRefNo(query.getSourceRefNo());
domainQuery.setConfirmFlag(query.getConfirmFlag());
domainQuery.setFeedbackFlag(query.getFeedbackFlag());
if (StringUtils.hasText(query.getPlanInspTimeStart())) {
domainQuery.setPlanInspTimeFrom(LocalDate.parse(query.getPlanInspTimeStart().trim()).atStartOfDay());
}
if (StringUtils.hasText(query.getPlanInspTimeEnd())) {
domainQuery.setPlanInspTimeTo(LocalDate.parse(query.getPlanInspTimeEnd().trim()).atTime(LocalTime.MAX));
}
if (StringUtils.hasText(query.getOrgName())) {
domainQuery.setOrgIds(resolveOrgIdsByName(query.getOrgName().trim()));
}
return domainQuery;
}
private List<InspNoticeEntity> resolveExportNotices(RegulatorInspNoticeExportCmd cmd) {
if (cmd == null) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
String scope = cmd.getScope() == null ? "" : cmd.getScope().trim().toUpperCase();
boolean hasIds = cmd.getIds() != null && !cmd.getIds().isEmpty();
if ("SELECTED".equals(scope) || (hasIds && !"FILTERED".equals(scope) && !"ALL".equals(scope))) {
if (!hasIds) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_EMPTY);
}
if (cmd.getIds().size() > MAX_EXPORT_ROWS) {
throw new BizException(ErrorCode.INSP_NOTICE_EXPORT_TOO_LARGE);
}
InspNoticeQuery q = new InspNoticeQuery();
q.setPageNum(1L);
q.setPageSize((long) MAX_EXPORT_ROWS);
q.setIds(cmd.getIds());
PageResult<InspNoticeEntity> page = inspNoticeDomainService.page(q);
return page == null || page.getRecords() == null ? Collections.<InspNoticeEntity>emptyList() : page.getRecords();
}
InspNoticeQuery query = toDomainQuery(cmd);
if ("ALL".equals(scope)) {
query.setOrgId(null);
query.setOrgIds(null);
query.setNoticeNo(null);
query.setKeyword(null);
query.setStatusCode(null);
query.setInspTypeCode(null);
query.setLeaderName(null);
query.setSourceTypeCode(null);
query.setSourceRefNo(null);
query.setPlanInspTimeFrom(null);
query.setPlanInspTimeTo(null);
query.setConfirmFlag(null);
query.setFeedbackFlag(null);
}
if (query.getOrgIds() != null && query.getOrgIds().isEmpty()) {
return Collections.emptyList();
}
query.setPageNum(1L);
query.setPageSize((long) MAX_EXPORT_ROWS);
PageResult<InspNoticeEntity> page = inspNoticeDomainService.page(query);
if (page == null || page.getRecords() == null) {
return Collections.emptyList();
}
return page.getRecords();
}
private List<Long> resolveOrgIdsByName(String orgName) {
if (orgInfoGateway == null || !StringUtils.hasText(orgName)) {
return Collections.emptyList();
}
OrgInfoQuery query = new OrgInfoQuery();
query.setPageNum(1L);
query.setPageSize(500L);
query.setUnitName(orgName);
PageResult<OrgInfoEntity> page = orgInfoGateway.page(query);
if (page == null || page.getRecords() == null) {
return Collections.emptyList();
}
List<Long> ids = new ArrayList<Long>();
for (OrgInfoEntity org : page.getRecords()) {
if (org != null && org.getId() != null) {
ids.add(org.getId());
}
}
return ids;
}
private Map<Long, String> buildOrgNameMap(List<InspNoticeEntity> records) {
Map<Long, String> map = new HashMap<Long, String>();
if (records == null || records.isEmpty() || orgInfoGateway == null) {
return map;
}
Set<Long> ids = new HashSet<Long>();
for (InspNoticeEntity entity : records) {
if (entity != null && entity.getOrgId() != null) {
ids.add(entity.getOrgId());
}
}
for (Long id : ids) {
OrgInfoEntity org = orgInfoGateway.get(id);
if (org != null) {
map.put(id, org.getUnitName());
}
}
return map;
}
private void writeKv(Writer writer, String key, String value) throws IOException {
writer.write(csvLine(new String[]{key, nullToEmpty(value)}));
}
private static String confirmLabel(Integer confirmFlag) {
if (confirmFlag != null && confirmFlag == 1) {
return "已确认";
}
return "未确认";
}
private static String feedbackLabel(Integer statusCode) {
if (statusCode != null && statusCode >= InspNoticeDomainService.STATUS_FEEDBACK) {
return "已提交";
}
return "未提交";
}
private static String formatDateTime(LocalDateTime value) {
return value == null ? "" : DATETIME_FMT.format(value);
}
private static String nullToEmpty(String value) {
return value == null ? "" : value;
}
private static String csvLine(String[] cols) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cols.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(escapeCsv(cols[i]));
}
sb.append('\n');
return sb.toString();
}
private static String escapeCsv(String value) {
if (value == null) {
return "";
}
boolean needQuote = value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r");
String escaped = value.replace("\"", "\"\"");
return needQuote ? "\"" + escaped + "\"" : escaped;
}
}

View File

@ -6,6 +6,7 @@ import org.qinan.safetyeval.client.co.report.EvalReportSpotcheckCO;
import org.qinan.safetyeval.domain.entity.EvalReportEntity;
import org.qinan.safetyeval.domain.entity.EvalReportSpotcheckEntity;
import org.qinan.safetyeval.domain.entity.OrgProfileScoreEntity;
import org.qinan.safetyeval.domain.service.EvalReportDomainService;
public final class ReportProfileCoConverter {
@ -19,24 +20,58 @@ public final class ReportProfileCoConverter {
EvalReportLibraryCO co = new EvalReportLibraryCO();
co.setId(entity.getId() != null ? String.valueOf(entity.getId()) : null);
co.setOrgId(entity.getOrgId() != null ? String.valueOf(entity.getOrgId()) : null);
co.setOrgName(null);
co.setProjectId(entity.getProjectId() != null ? String.valueOf(entity.getProjectId()) : null);
co.setCustomerId(entity.getCustomerId() != null ? String.valueOf(entity.getCustomerId()) : null);
co.setEvaluatedUnitName(entity.getEvaluatedUnitName());
co.setReportNo(entity.getReportNo());
co.setReportName(entity.getReportName());
co.setReportYear(entity.getReportYear());
co.setEvalTypeCode(entity.getEvalTypeCode());
co.setEvalTypeName(entity.getEvalTypeName());
co.setIndustryCode(entity.getIndustryCode());
co.setIndustryName(entity.getIndustryName());
co.setBusinessScope(entity.getBusinessScope());
co.setFileUrl(entity.getFileUrl());
co.setFileName(entity.getFileName());
co.setPublicFlag(entity.getPublicFlag());
co.setCompleteFlag(entity.getCompleteFlag());
co.setSubmitFlag(entity.getSubmitFlag());
co.setReportStatusCode(entity.getReportStatusCode());
co.setReportStatusName(entity.getReportStatusName());
co.setDisplayStatusName(resolveDisplayStatusName(entity));
co.setArchiveTime(entity.getArchiveTime());
co.setIssueDate(entity.getIssueDate());
co.setPageCount(entity.getPageCount());
co.setSecretLevelCode(entity.getSecretLevelCode());
co.setSecretLevelName(entity.getSecretLevelName());
co.setRemarks(entity.getRemarks());
return co;
}
/**
* / / / /
*/
public static String resolveDisplayStatusName(EvalReportEntity entity) {
if (entity == null) {
return null;
}
Integer status = entity.getReportStatusCode();
if (status != null && status == EvalReportDomainService.STATUS_SPOTCHECKING) {
return "抽查中";
}
if (status != null && status == EvalReportDomainService.STATUS_RECTIFY) {
return "整改中";
}
if (status != null && status == EvalReportDomainService.STATUS_REVIEWED) {
return "已复核";
}
if (entity.getSubmitFlag() != null && entity.getSubmitFlag() == 2) {
return "未报送";
}
return "已报送";
}
public static EvalReportSpotcheckCO toSpotcheckCO(EvalReportSpotcheckEntity entity) {
if (entity == null) {
return null;

View File

@ -1,13 +1,22 @@
package org.qinan.safetyeval.client.api.institution;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.InstitutionEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportAddCmd;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportPageQuery;
import org.qinan.safetyeval.client.dto.institution.InstitutionEvalReportSubmitCmd;
public interface InstitutionEvalReportApi {
PageResponse<EvalReportLibraryCO> page(InstitutionEvalReportPageQuery query);
SingleResponse<EvalReportLibraryCO> get(Long id);
SingleResponse<InstitutionEvalReportSummaryCO> summary();
SingleResponse<EvalReportLibraryCO> add(InstitutionEvalReportAddCmd cmd);
SingleResponse<EvalReportLibraryCO> submit(InstitutionEvalReportSubmitCmd cmd);
}

View File

@ -1,6 +1,7 @@
package org.qinan.safetyeval.client.api.regulator;
import org.qinan.safetyeval.client.co.report.EvalReportLibraryCO;
import org.qinan.safetyeval.client.co.report.RegulatorEvalReportSummaryCO;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportAddCmd;
@ -8,6 +9,8 @@ import org.qinan.safetyeval.client.dto.regulator.RegulatorEvalReportPageQuery;
public interface RegulatorEvalReportApi {
SingleResponse<RegulatorEvalReportSummaryCO> summary();
PageResponse<EvalReportLibraryCO> page(RegulatorEvalReportPageQuery query);
SingleResponse<EvalReportLibraryCO> get(Long id);

View File

@ -10,6 +10,8 @@ import java.util.List;
public interface RegulatorInspNoticeApi {
SingleResponse<RegulatorInspNoticeCO> save(RegulatorInspNoticeSaveCmd cmd);
SingleResponse<RegulatorInspNoticeCO> issue(RegulatorInspNoticeIssueCmd cmd);
SingleResponse<RegulatorInspNoticeCO> get(Long id);
@ -18,6 +20,8 @@ public interface RegulatorInspNoticeApi {
SingleResponse<RegulatorInspNoticeCO> close(RegulatorInspNoticeCloseCmd cmd);
SingleResponse<RegulatorInspNoticeCO> remark(RegulatorInspNoticeRemarkCmd cmd);
SingleResponse<List<InspNoticeMaterialCO>> materials(Long noticeId);
SingleResponse<Void> urge(RegulatorInspNoticeUrgeCmd cmd);

View File

@ -20,12 +20,15 @@ public class InstitutionInspNoticeCO {
@ApiModelProperty(value = "检查类型名称:项目过程检查/资质保持检查/专项检查", example = "项目过程检查")
private String inspTypeName;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他", example = "1")
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "4")
private Integer sourceTypeCode;
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他", example = "年度计划")
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他/风险预警", example = "风险预警")
private String sourceTypeName;
@ApiModelProperty("来源预警/业务编号")
private String sourceRefNo;
@ApiModelProperty("计划检查时间")
private LocalDateTime planInspTime;
@ -35,7 +38,10 @@ public class InstitutionInspNoticeCO {
@ApiModelProperty("告知内容")
private String noticeContent;
@ApiModelProperty(value = "状态编码1已下发 2已确认 3已反馈 4已办结", example = "1")
@ApiModelProperty("告知附件URL逗号分隔")
private String noticeUrls;
@ApiModelProperty(value = "状态编码1已下发 2已确认 3已反馈 4已办结机构端不见草稿", example = "1")
private Integer statusCode;
@ApiModelProperty(value = "状态名称:已下发/已确认/已反馈/已办结", example = "已下发")
@ -44,16 +50,19 @@ public class InstitutionInspNoticeCO {
@ApiModelProperty(value = "机构是否确认1是 2否", example = "2")
private Integer confirmFlag;
@ApiModelProperty(value = "材料反馈1已提交 2未提交", example = "2")
private Integer feedbackFlag;
@ApiModelProperty("确认时间")
private LocalDateTime confirmTime;
@ApiModelProperty("下发时间")
private LocalDateTime sendTime;
@ApiModelProperty(value = "检查结果编码1合格 2不合格 3限期整改", example = "1")
@ApiModelProperty(value = "检查结果编码1未发现问题/合格 2责令整改/不合格 3转入风险预警/限期整改", example = "1")
private Integer resultCode;
@ApiModelProperty(value = "检查结果名称:合格/不合格/限期整改", example = "合格")
@ApiModelProperty(value = "检查结果名称", example = "未发现问题")
private String resultName;
@ApiModelProperty("结果说明")
@ -64,4 +73,7 @@ public class InstitutionInspNoticeCO {
@ApiModelProperty("结果登记时间")
private LocalDateTime resultTime;
@ApiModelProperty("监管备注")
private String remarks;
}

View File

@ -14,6 +14,9 @@ public class RegulatorInspNoticeCO {
@ApiModelProperty("被检机构id")
private String orgId;
@ApiModelProperty("评价机构名称")
private String orgName;
@ApiModelProperty("告知编号")
private String noticeNo;
@ -23,15 +26,18 @@ public class RegulatorInspNoticeCO {
@ApiModelProperty(value = "检查类型名称:项目过程检查/资质保持检查/专项检查", example = "项目过程检查")
private String inspTypeName;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他", example = "1")
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "4")
private Integer sourceTypeCode;
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他", example = "年度计划")
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他/风险预警", example = "风险预警")
private String sourceTypeName;
@ApiModelProperty("来源业务id")
private String sourceRefId;
@ApiModelProperty("来源预警/业务编号")
private String sourceRefNo;
@ApiModelProperty("关联项目id")
private String projectId;
@ -44,25 +50,31 @@ public class RegulatorInspNoticeCO {
@ApiModelProperty("告知内容")
private String noticeContent;
@ApiModelProperty(value = "状态编码1已下发 2已确认 3已反馈 4已办结", example = "1")
@ApiModelProperty("告知附件URL逗号分隔")
private String noticeUrls;
@ApiModelProperty(value = "状态编码0待下发 1已下发 2已确认 3已反馈 4已办结", example = "1")
private Integer statusCode;
@ApiModelProperty(value = "状态名称:已下发/已确认/已反馈/已办结", example = "已下发")
@ApiModelProperty(value = "状态名称:待下发/已下发/已确认/已反馈/已办结(原型「已完成」=已办结)", example = "已下发")
private String statusName;
@ApiModelProperty(value = "机构是否确认1是 2否", example = "2")
private Integer confirmFlag;
@ApiModelProperty(value = "材料反馈1已提交 2未提交status>=3 为已提交)", example = "2")
private Integer feedbackFlag;
@ApiModelProperty("确认时间")
private LocalDateTime confirmTime;
@ApiModelProperty("下发时间")
private LocalDateTime sendTime;
@ApiModelProperty(value = "检查结果编码1合格 2不合格 3限期整改", example = "1")
@ApiModelProperty(value = "检查结果编码1未发现问题/合格 2责令整改/不合格 3转入风险预警/限期整改", example = "1")
private Integer resultCode;
@ApiModelProperty(value = "检查结果名称:合格/不合格/限期整改", example = "合格")
@ApiModelProperty(value = "检查结果名称", example = "未发现问题")
private String resultName;
@ApiModelProperty("结果说明")
@ -76,4 +88,7 @@ public class RegulatorInspNoticeCO {
@ApiModelProperty("结果登记时间")
private LocalDateTime resultTime;
@ApiModelProperty("监管备注")
private String remarks;
}

View File

@ -3,6 +3,7 @@ package org.qinan.safetyeval.client.co.report;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@ -14,12 +15,18 @@ public class EvalReportLibraryCO {
@ApiModelProperty("机构id")
private String orgId;
@ApiModelProperty("报送机构名称")
private String orgName;
@ApiModelProperty("项目id")
private String projectId;
@ApiModelProperty("客户id")
private String customerId;
@ApiModelProperty("被评价单位名称")
private String evaluatedUnitName;
@ApiModelProperty("报告编号")
private String reportNo;
@ -35,6 +42,12 @@ public class EvalReportLibraryCO {
@ApiModelProperty("评价类型名称")
private String evalTypeName;
@ApiModelProperty("所属行业编码")
private String industryCode;
@ApiModelProperty("所属行业名称")
private String industryName;
@ApiModelProperty("业务范围")
private String businessScope;
@ -44,18 +57,40 @@ public class EvalReportLibraryCO {
@ApiModelProperty("文件名")
private String fileName;
@ApiModelProperty("是否公开")
@ApiModelProperty("是否公开1是2否")
private Integer publicFlag;
@ApiModelProperty("资料是否完整")
@ApiModelProperty("资料是否完整1是2否")
private Integer completeFlag;
@ApiModelProperty("报告状态编码")
@ApiModelProperty("是否已报送监管1是2否")
private Integer submitFlag;
@ApiModelProperty("报告状态编码1已入库2抽查中3待整改4已复核")
private Integer reportStatusCode;
@ApiModelProperty("报告状态名称")
private String reportStatusName;
@ApiModelProperty(value = "列表展示用报送状态:未报送/已报送/抽查中/整改中/已复核",
example = "已报送")
private String displayStatusName;
@ApiModelProperty("入库时间")
private LocalDateTime archiveTime;
@ApiModelProperty("报告签发日期")
private LocalDate issueDate;
@ApiModelProperty("报告页数")
private Integer pageCount;
@ApiModelProperty("保密属性编码")
private String secretLevelCode;
@ApiModelProperty("保密属性名称")
private String secretLevelName;
@ApiModelProperty("备注")
private String remarks;
}

View File

@ -0,0 +1,26 @@
package org.qinan.safetyeval.client.co.report;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
public class InstitutionEvalReportSummaryCO {
@ApiModelProperty("报告总数")
private Long totalCount;
@ApiModelProperty("近三年报告数")
private Long recentThreeYearCount;
@ApiModelProperty("已报送监管数")
private Long submittedCount;
@ApiModelProperty("待完善分类数(资料不完整)")
private Long incompleteCount;
@ApiModelProperty("监管抽查中数量")
private Long spotcheckingCount;
}

View File

@ -0,0 +1,20 @@
package org.qinan.safetyeval.client.co.report;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
public class RegulatorEvalReportSummaryCO {
@ApiModelProperty("近三年报告数(已报送)")
private Long recentThreeYearCount;
@ApiModelProperty("本年度报告数(已报送)")
private Long currentYearCount;
@ApiModelProperty("已报送报告总数")
private Long submittedTotalCount;
}

View File

@ -0,0 +1,71 @@
package org.qinan.safetyeval.client.dto.institution;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
/**
*
*/
@Data
public class InstitutionEvalReportAddCmd {
@NotBlank(message = "报告编号不能为空")
@ApiModelProperty(value = "报告编号", required = true, example = "CQAP-XZ-2026-001")
private String reportNo;
@NotBlank(message = "报告名称不能为空")
@ApiModelProperty(value = "报告名称", required = true)
private String reportName;
@ApiModelProperty("报告年度")
private Integer reportYear;
@ApiModelProperty(value = "评价类型编码PRE/ACCEPT/STATUS", example = "PRE")
private String evalTypeCode;
@ApiModelProperty(value = "评价类型名称", example = "安全预评价")
private String evalTypeName;
@ApiModelProperty(value = "所属行业编码", example = "HAZCHEM")
private String industryCode;
@ApiModelProperty(value = "所属行业名称", example = "危险化学品")
private String industryName;
@NotBlank(message = "被评价单位不能为空")
@ApiModelProperty(value = "被评价单位名称", required = true)
private String evaluatedUnitName;
@ApiModelProperty("客户id可选")
private Long customerId;
@NotNull(message = "报告签发日期不能为空")
@ApiModelProperty(value = "报告签发日期", required = true, example = "2026-07-16")
private LocalDate issueDate;
@ApiModelProperty("报告页数")
private Integer pageCount;
@NotBlank(message = "文件地址不能为空")
@ApiModelProperty(value = "文件地址(先走文件上传拿 URL", required = true)
private String fileUrl;
@ApiModelProperty("文件名")
private String fileName;
@ApiModelProperty(value = "保密属性编码NORMAL/TRADE_SECRET/PRIVACY/CLASSIFIED", example = "NORMAL")
private String secretLevelCode;
@ApiModelProperty(value = "保密属性名称", example = "普通")
private String secretLevelName;
@ApiModelProperty("备注")
private String remarks;
@ApiModelProperty(value = "是否同步报送监管端true=已报送(status已入库)false=仅存机构库", example = "false")
private Boolean submitToRegulator;
}

View File

@ -9,12 +9,22 @@ import org.qinan.safetyeval.client.dto.BasePageQuery;
@EqualsAndHashCode(callSuper = true)
public class InstitutionEvalReportPageQuery extends BasePageQuery {
@ApiModelProperty("关键词(报告名称/编号)")
private String keyword;
@ApiModelProperty("报告年度")
private Integer reportYear;
@ApiModelProperty(value = "评价类型编码PRE安全预评价 ACCEPT安全验收评价 STATUS安全现状评价", example = "PRE")
private String evalTypeCode;
@ApiModelProperty(value = "所属行业编码", example = "HAZCHEM")
private String industryCode;
@ApiModelProperty(value = "报告状态编码1已入库 2抽查中 3待整改 4已复核", example = "1")
private Integer reportStatusCode;
@ApiModelProperty(value = "报送展示状态UN_SUBMITTED未报送 SUBMITTED已报送 SPOTCHECKING抽查中 RECTIFYING整改中 REVIEWED已复核",
example = "SUBMITTED")
private String displayStatus;
}

View File

@ -0,0 +1,17 @@
package org.qinan.safetyeval.client.dto.institution;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
*
*/
@Data
public class InstitutionEvalReportSubmitCmd {
@NotNull(message = "报告ID不能为空")
@ApiModelProperty(value = "报告ID", required = true)
private Long id;
}

View File

@ -1,7 +1,9 @@
package org.qinan.safetyeval.client.dto.institution;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
@ -9,6 +11,7 @@ import javax.validation.constraints.NotNull;
public class InstitutionInspNoticeConfirmCmd {
@NotNull
@ApiModelProperty(value = "告知id", required = true)
private Long id;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "告知id字符串避免雪花ID精度丢失", required = true)
private String id;
}

View File

@ -0,0 +1,21 @@
package org.qinan.safetyeval.client.dto.institution;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* CSV ids
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class InstitutionInspNoticeExportCmd extends InstitutionInspNoticePageQuery {
@ApiModelProperty("勾选的告知ID字符串避免雪花精度丢失")
private List<String> ids;
@ApiModelProperty(value = "导出范围SELECTED / FILTERED / ALL", example = "FILTERED")
private String scope;
}

View File

@ -1,7 +1,9 @@
package org.qinan.safetyeval.client.dto.institution;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
import java.util.List;
@ -10,8 +12,9 @@ import java.util.List;
public class InstitutionInspNoticeFeedbackCmd {
@NotNull
@ApiModelProperty(value = "告知id", required = true)
private Long id;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "告知id字符串避免雪花ID精度丢失", required = true)
private String id;
@ApiModelProperty("随反馈一并上传的材料(可选)")
private List<InstitutionInspNoticeMaterialUploadCmd> materials;

View File

@ -1,7 +1,9 @@
package org.qinan.safetyeval.client.dto.institution;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@ -10,8 +12,9 @@ import javax.validation.constraints.NotNull;
public class InstitutionInspNoticeMaterialUploadCmd {
@NotNull
@ApiModelProperty(value = "材料id", required = true)
private Long materialId;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "材料id字符串避免雪花ID精度丢失", required = true)
private String materialId;
@NotBlank
@ApiModelProperty(value = "文件URL", required = true)

View File

@ -9,12 +9,27 @@ import org.qinan.safetyeval.client.dto.BasePageQuery;
@EqualsAndHashCode(callSuper = true)
public class InstitutionInspNoticePageQuery extends BasePageQuery {
@ApiModelProperty("告知编号")
@ApiModelProperty("告知编号(模糊)")
private String noticeNo;
@ApiModelProperty("关键词:告知编号/来源预警编号/告知内容")
private String keyword;
@ApiModelProperty(value = "状态编码1已下发 2已确认 3已反馈 4已办结", example = "1")
private Integer statusCode;
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查", example = "PROJECT_PROCESS")
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS/QUAL_KEEP/SPECIAL", example = "PROJECT_PROCESS")
private String inspTypeCode;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "4")
private Integer sourceTypeCode;
@ApiModelProperty("来源预警编号(模糊)")
private String sourceRefNo;
@ApiModelProperty(value = "机构确认1已确认 2未确认", example = "2")
private Integer confirmFlag;
@ApiModelProperty(value = "材料反馈1已提交 2未提交", example = "2")
private Integer feedbackFlag;
}

View File

@ -5,7 +5,11 @@ import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
/**
*
*/
@Data
public class RegulatorEvalReportAddCmd {
@ -13,25 +17,43 @@ public class RegulatorEvalReportAddCmd {
@ApiModelProperty(value = "机构id", required = true)
private Long orgId;
@ApiModelProperty("项目id可空")
@ApiModelProperty("项目id可空,历史补录")
private Long projectId;
@NotBlank(message = "报告名称不能为空")
@ApiModelProperty(value = "报告名称", required = true)
private String reportName;
@ApiModelProperty("报告编号")
@ApiModelProperty(value = "报告编号", example = "CQAP-2026-0715")
private String reportNo;
@ApiModelProperty("报告年度")
private Integer reportYear;
@ApiModelProperty(value = "评价类型编码PRE安全预评价 ACCEPT安全验收评价 STATUS安全现状评价", example = "PRE")
@ApiModelProperty(value = "评价类型编码PRE/ACCEPT/STATUS", example = "PRE")
private String evalTypeCode;
@ApiModelProperty(value = "评价类型名称:安全预评价/安全验收评价/安全现状评价", example = "安全预评价")
@ApiModelProperty(value = "评价类型名称", example = "安全预评价")
private String evalTypeName;
@ApiModelProperty(value = "所属行业编码", example = "HAZCHEM")
private String industryCode;
@ApiModelProperty(value = "所属行业名称", example = "危险化学品")
private String industryName;
@ApiModelProperty("被评价单位名称")
private String evaluatedUnitName;
@ApiModelProperty("客户id可选")
private Long customerId;
@ApiModelProperty("报告签发日期")
private LocalDate issueDate;
@ApiModelProperty("报告页数")
private Integer pageCount;
@ApiModelProperty("业务范围")
private String businessScope;
@ -41,4 +63,13 @@ public class RegulatorEvalReportAddCmd {
@ApiModelProperty("文件名")
private String fileName;
@ApiModelProperty(value = "保密属性编码", example = "NORMAL")
private String secretLevelCode;
@ApiModelProperty(value = "保密属性名称", example = "普通")
private String secretLevelName;
@ApiModelProperty("备注")
private String remarks;
}

View File

@ -0,0 +1,18 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
/**
* ZIP
*/
@Data
public class RegulatorEvalReportBatchDownloadCmd {
@NotEmpty(message = "请选择需要下载的报告")
@ApiModelProperty(value = "报告ID列表", required = true)
private List<Long> ids;
}

View File

@ -0,0 +1,25 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* CSVExcel
* <p>
* {@code ids} ids
* </p>
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegulatorEvalReportExportCmd extends RegulatorEvalReportPageQuery {
@ApiModelProperty("勾选的报告ID非空时忽略分页按勾选导出")
private List<Long> ids;
@ApiModelProperty(value = "导出范围SELECTED=勾选 FILTERED=当前筛选 ALL=全部已报送未传时有ids则SELECTED否则FILTERED",
example = "FILTERED")
private String scope;
}

View File

@ -9,15 +9,33 @@ import org.qinan.safetyeval.client.dto.BasePageQuery;
@EqualsAndHashCode(callSuper = true)
public class RegulatorEvalReportPageQuery extends BasePageQuery {
@ApiModelProperty("机构id")
@ApiModelProperty("机构id(精确)")
private Long orgId;
@ApiModelProperty("报送机构名称(模糊)")
private String orgName;
@ApiModelProperty("关键词(报告名称/编号)")
private String keyword;
@ApiModelProperty("报告年度")
private Integer reportYear;
@ApiModelProperty(value = "评价类型编码PRE安全预评价 ACCEPT安全验收评价 STATUS安全现状评价", example = "PRE")
private String evalTypeCode;
@ApiModelProperty(value = "所属行业编码", example = "HAZCHEM")
private String industryCode;
@ApiModelProperty(value = "报告状态编码1已入库 2抽查中 3待整改 4已复核", example = "1")
private Integer reportStatusCode;
@ApiModelProperty(value = "报送时间起 yyyy-MM-dd", example = "2026-01-01")
private String archiveTimeStart;
@ApiModelProperty(value = "报送时间止 yyyy-MM-dd", example = "2026-12-31")
private String archiveTimeEnd;
@ApiModelProperty(value = "是否已报送:默认仅查已报送(1)。传 0/null 以外值可放宽", example = "1")
private Integer submitFlag;
}

View File

@ -1,7 +1,9 @@
package org.qinan.safetyeval.client.dto.regulator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
import java.util.List;
@ -10,13 +12,14 @@ import java.util.List;
public class RegulatorInspNoticeCloseCmd {
@NotNull
@ApiModelProperty(value = "告知id", required = true)
private Long id;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "告知id字符串避免雪花ID精度丢失", required = true)
private String id;
@ApiModelProperty(value = "检查结果编码1合格 2不合格 3限期整改", example = "1")
@ApiModelProperty(value = "检查结果编码1未发现问题/合格 2责令整改/不合格 3转入风险预警/限期整改", example = "1")
private Integer resultCode;
@ApiModelProperty(value = "检查结果名称:合格/不合格/限期整改", example = "合格")
@ApiModelProperty(value = "检查结果名称(原型:未发现问题/责令整改/转入风险预警)", example = "未发现问题")
private String resultName;
@ApiModelProperty("结果说明")

View File

@ -0,0 +1,21 @@
package org.qinan.safetyeval.client.dto.regulator;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* CSV ids
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegulatorInspNoticeExportCmd extends RegulatorInspNoticePageQuery {
@ApiModelProperty("勾选的告知ID非空时按勾选导出")
private List<Long> ids;
@ApiModelProperty(value = "导出范围SELECTED / FILTERED / ALL未传时有ids则SELECTED否则FILTERED", example = "FILTERED")
private String scope;
}

View File

@ -1,18 +1,23 @@
package org.qinan.safetyeval.client.dto.regulator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class RegulatorInspNoticeIssueCmd {
@NotNull
@ApiModelProperty(value = "被检机构id", required = true)
private Long orgId;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty("草稿告知id传入则将待下发草稿正式下发字符串")
private String id;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "被检机构idorg_info.id新建下发时必填字符串避免精度丢失")
private String orgId;
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查", example = "PROJECT_PROCESS")
private String inspTypeCode;
@ -20,15 +25,18 @@ public class RegulatorInspNoticeIssueCmd {
@ApiModelProperty(value = "检查类型名称:项目过程检查/资质保持检查/专项检查", example = "项目过程检查")
private String inspTypeName;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他", example = "1")
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "4")
private Integer sourceTypeCode;
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他", example = "年度计划")
@ApiModelProperty(value = "来源方式名称:年度计划/专项安排/其他/风险预警", example = "风险预警")
private String sourceTypeName;
@ApiModelProperty("来源业务id")
private Long sourceRefId;
@ApiModelProperty("来源预警/业务编号")
private String sourceRefNo;
@ApiModelProperty("关联项目id")
private Long projectId;
@ -41,6 +49,12 @@ public class RegulatorInspNoticeIssueCmd {
@ApiModelProperty("告知内容")
private String noticeContent;
@ApiModelProperty("告知附件URL列表")
private List<String> noticeUrls;
@ApiModelProperty("监管备注")
private String remarks;
@ApiModelProperty("预置材料名称列表")
private List<String> materialNames;
}

View File

@ -12,15 +12,39 @@ public class RegulatorInspNoticePageQuery extends BasePageQuery {
@ApiModelProperty("被检机构id")
private Long orgId;
@ApiModelProperty("告知编号")
@ApiModelProperty("评价机构名称(模糊)")
private String orgName;
@ApiModelProperty("告知编号(模糊)")
private String noticeNo;
@ApiModelProperty(value = "状态编码1已下发 2已确认 3已反馈 4已办结", example = "1")
@ApiModelProperty("关键词:告知编号/来源预警编号/告知内容/负责人")
private String keyword;
@ApiModelProperty(value = "状态编码0待下发 1已下发 2已确认 3已反馈 4已办结原型「已完成」传4", example = "1")
private Integer statusCode;
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查", example = "PROJECT_PROCESS")
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS/QUAL_KEEP/SPECIAL", example = "PROJECT_PROCESS")
private String inspTypeCode;
@ApiModelProperty("检查组长")
@ApiModelProperty("检查组长(模糊)")
private String leaderName;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "4")
private Integer sourceTypeCode;
@ApiModelProperty("来源预警编号(模糊)")
private String sourceRefNo;
@ApiModelProperty(value = "计划检查时间起yyyy-MM-dd", example = "2026-07-01")
private String planInspTimeStart;
@ApiModelProperty(value = "计划检查时间止yyyy-MM-dd", example = "2026-07-31")
private String planInspTimeEnd;
@ApiModelProperty(value = "机构确认1已确认 2未确认", example = "2")
private Integer confirmFlag;
@ApiModelProperty(value = "材料反馈1已提交 2未提交", example = "2")
private Integer feedbackFlag;
}

View File

@ -0,0 +1,20 @@
package org.qinan.safetyeval.client.dto.regulator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
@Data
public class RegulatorInspNoticeRemarkCmd {
@NotNull
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "告知id字符串避免雪花ID精度丢失", required = true)
private String id;
@ApiModelProperty("监管备注")
private String remarks;
}

View File

@ -0,0 +1,65 @@
package org.qinan.safetyeval.client.dto.regulator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
/**
* /status=0
*/
@Data
public class RegulatorInspNoticeSaveCmd {
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty("主键;空=新建草稿,非空=编辑待下发草稿(字符串)")
private String id;
@NotNull
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "被检机构idorg_info.id字符串避免精度丢失", required = true)
private String orgId;
@ApiModelProperty(value = "检查类型编码PROJECT_PROCESS/QUAL_KEEP/SPECIAL", example = "PROJECT_PROCESS")
private String inspTypeCode;
@ApiModelProperty(value = "检查类型名称", example = "项目过程检查")
private String inspTypeName;
@ApiModelProperty(value = "来源方式编码1年度计划 2专项安排 3其他 4风险预警", example = "1")
private Integer sourceTypeCode;
@ApiModelProperty(value = "来源方式名称", example = "年度计划")
private String sourceTypeName;
@ApiModelProperty("来源业务id")
private Long sourceRefId;
@ApiModelProperty("来源预警/业务编号")
private String sourceRefNo;
@ApiModelProperty("关联项目id")
private Long projectId;
@ApiModelProperty("计划检查时间")
private LocalDateTime planInspTime;
@ApiModelProperty("检查组长")
private String leaderName;
@ApiModelProperty("告知内容")
private String noticeContent;
@ApiModelProperty("告知附件URL列表")
private List<String> noticeUrls;
@ApiModelProperty("监管备注")
private String remarks;
@ApiModelProperty("预置材料名称列表(仅新建草稿时生效)")
private List<String> materialNames;
}

View File

@ -1,7 +1,9 @@
package org.qinan.safetyeval.client.dto.regulator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.qinan.safetyeval.client.jackson.SnowflakeIdStringDeserializer;
import javax.validation.constraints.NotNull;
@ -9,6 +11,7 @@ import javax.validation.constraints.NotNull;
public class RegulatorInspNoticeUrgeCmd {
@NotNull
@ApiModelProperty(value = "告知id", required = true)
private Long id;
@JsonDeserialize(using = SnowflakeIdStringDeserializer.class)
@ApiModelProperty(value = "告知id字符串避免雪花ID精度丢失", required = true)
private String id;
}

View File

@ -0,0 +1,27 @@
package org.qinan.safetyeval.client.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
/**
* JSON ID Long
*/
public class SnowflakeIdStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
JsonToken token = parser.getCurrentToken();
if (token == JsonToken.VALUE_STRING) {
return parser.getText().trim();
}
if (token == JsonToken.VALUE_NUMBER_INT || token == JsonToken.VALUE_NUMBER_FLOAT) {
// 用原文文本,避免 Long 精度丢失;兼容各 Jackson 版本
return parser.getText().trim();
}
return null;
}
}

View File

@ -11,19 +11,35 @@ public class EvalReportEntity {
private Long orgId;
private Long projectId;
private Long customerId;
/** 被评价单位名称(历史补录可无 customerId */
private String evaluatedUnitName;
private String reportNo;
private String reportName;
private Integer reportYear;
private String evalTypeCode;
private String evalTypeName;
/** 所属行业编码 */
private String industryCode;
/** 所属行业名称 */
private String industryName;
private String businessScope;
private String fileUrl;
private String fileName;
private Integer publicFlag;
private Integer completeFlag;
/** 是否已报送监管1是 2否 */
private Integer submitFlag;
private Integer reportStatusCode;
private String reportStatusName;
private LocalDateTime archiveTime;
/** 报告签发日期 */
private java.time.LocalDate issueDate;
/** 报告页数 */
private Integer pageCount;
/** 保密属性编码 */
private String secretLevelCode;
/** 保密属性名称 */
private String secretLevelName;
private String deleteEnum;
private String remarks;
private String createName;

View File

@ -15,10 +15,12 @@ public class InspNoticeEntity {
private Integer sourceTypeCode;
private String sourceTypeName;
private Long sourceRefId;
private String sourceRefNo;
private Long projectId;
private LocalDateTime planInspTime;
private String leaderName;
private String noticeContent;
private String noticeUrls;
private Integer statusCode;
private String statusName;
private Integer confirmFlag;
@ -31,5 +33,6 @@ public class InspNoticeEntity {
private Long resultRecorderId;
private String resultRecorderName;
private LocalDateTime resultTime;
private String remarks;
private Long tenantId;
}

View File

@ -174,6 +174,8 @@ public enum ErrorCode {
INSP_NOTICE_NO_GENERATE_FAILED("06-06-004", "告知编号生成失败"),
INSP_NOTICE_MATERIAL_NOT_FOUND("06-06-005", "监督检查材料不存在"),
INSP_NOTICE_MATERIAL_INCOMPLETE("06-06-006", "必填材料未上传完整"),
INSP_NOTICE_EXPORT_EMPTY("06-06-007", "没有可导出的监督检查告知"),
INSP_NOTICE_EXPORT_TOO_LARGE("06-06-008", "导出数量超过上限"),
// ---- 报告库与抽查phase2 P4 ----
EVAL_REPORT_NOT_FOUND("06-07-001", "安评报告不存在"),
@ -183,6 +185,11 @@ public enum ErrorCode {
EVAL_REPORT_SPOTCHECK_IN_PROGRESS("06-07-005", "该报告已有进行中的抽查"),
EVAL_REPORT_SPOTCHECK_INVALID("06-07-006", "抽查状态不允许该操作"),
EVAL_REPORT_RECTIFY_REQUIRE_MISSING("06-07-007", "不合格抽查须填写整改要求"),
EVAL_REPORT_NO_DUPLICATE("06-07-008", "报告编号已存在"),
EVAL_REPORT_ALREADY_SUBMITTED("06-07-009", "报告已报送监管,无需重复报送"),
EVAL_REPORT_BATCH_EMPTY("06-07-010", "请选择需要操作的报告"),
EVAL_REPORT_BATCH_TOO_LARGE("06-07-011", "批量操作数量超过上限"),
EVAL_REPORT_FILE_MISSING("06-07-012", "所选报告均无有效文件地址,无法打包下载"),
// ---- 机构画像得分phase2 P4 ----
ORG_PROFILE_SCORE_NOT_FOUND("06-08-001", "机构画像得分不存在"),

View File

@ -15,4 +15,13 @@ public interface EvalReportGateway {
EvalReportEntity saveInternal(EvalReportEntity entity);
PageResult<EvalReportEntity> page(EvalReportQuery query);
long count(EvalReportQuery query);
EvalReportEntity findByOrgIdAndReportNo(Long orgId, String reportNo);
/**
* ids /
*/
java.util.List<EvalReportEntity> listByIds(java.util.List<Long> ids);
}

View File

@ -17,4 +17,20 @@ public class EvalReportQuery {
private String evalTypeCode;
/** 报告状态编码1已入库 2抽查中 3待整改 4已复核示例1 */
private Integer reportStatusCode;
/** 关键词:报告名称/编号 */
private String keyword;
/** 所属行业编码 */
private String industryCode;
/** 是否已报送监管1是 2否 */
private Integer submitFlag;
/** 资料是否完整1是 2否 */
private Integer completeFlag;
/** 近 N 年含当年用于汇总report_year >= recentYearFrom */
private Integer recentYearFrom;
/** 报送/入库时间起(含) */
private java.time.LocalDateTime archiveTimeFrom;
/** 报送/入库时间止(含) */
private java.time.LocalDateTime archiveTimeTo;
/** 机构 id 列表(按机构名称解析后过滤) */
private java.util.List<Long> orgIds;
}

View File

@ -2,16 +2,34 @@ package org.qinan.safetyeval.domain.query;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class InspNoticeQuery {
private Long pageNum;
private Long pageSize;
private Long orgId;
private List<Long> orgIds;
private List<Long> ids;
private String noticeNo;
/** 状态编码1已下发 2已确认 3已反馈 4已办结示例1 */
private String keyword;
/** 状态编码0待下发 1已下发 2已确认 3已反馈 4已办结 */
private Integer statusCode;
/** 检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查示例PROJECT_PROCESS */
/** 检查类型编码 */
private String inspTypeCode;
private String leaderName;
/** 来源方式编码1年度计划 2专项安排 3其他 4风险预警 */
private Integer sourceTypeCode;
/** 来源预警/业务编号(模糊) */
private String sourceRefNo;
private LocalDateTime planInspTimeFrom;
private LocalDateTime planInspTimeTo;
/** 机构是否确认1是 2否 */
private Integer confirmFlag;
/** 材料反馈1已提交(status>=3) 2未提交(status<3) */
private Integer feedbackFlag;
/** 最小状态(含),如机构端传 1 排除草稿 */
private Integer minStatusCode;
}

View File

@ -41,9 +41,69 @@ public class EvalReportDomainService {
if (entity.getPublicFlag() == null) {
entity.setPublicFlag(2);
}
if (entity.getSubmitFlag() == null) {
entity.setSubmitFlag(1);
}
if (entity.getCompleteFlag() == null) {
entity.setCompleteFlag(1);
}
return evalReportGateway.saveInternal(entity);
}
public EvalReportEntity addForInstitution(EvalReportEntity entity) {
assertReportNoUnique(entity.getOrgId(), entity.getReportNo(), null);
entity.setReportStatusCode(STATUS_ARCHIVED);
entity.setReportStatusName("已入库");
entity.setArchiveTime(LocalDateTime.now());
if (entity.getPublicFlag() == null) {
entity.setPublicFlag(2);
}
if (entity.getSubmitFlag() == null) {
entity.setSubmitFlag(2);
}
if (entity.getCompleteFlag() == null) {
boolean incomplete = !org.springframework.util.StringUtils.hasText(entity.getIndustryCode())
&& !org.springframework.util.StringUtils.hasText(entity.getIndustryName());
entity.setCompleteFlag(incomplete ? 2 : 1);
}
return evalReportGateway.save(entity);
}
public EvalReportEntity submitToRegulator(Long id) {
EvalReportEntity existing = get(id);
if (existing.getSubmitFlag() != null && existing.getSubmitFlag() == 1) {
throw new BizException(ErrorCode.EVAL_REPORT_ALREADY_SUBMITTED);
}
EvalReportEntity update = new EvalReportEntity();
update.setId(id);
update.setOrgId(existing.getOrgId());
update.setSubmitFlag(1);
update.setReportStatusCode(STATUS_ARCHIVED);
update.setReportStatusName("已入库");
if (existing.getArchiveTime() == null) {
update.setArchiveTime(LocalDateTime.now());
}
return evalReportGateway.modify(update);
}
public void assertReportNoUnique(Long orgId, String reportNo, Long excludeId) {
if (!org.springframework.util.StringUtils.hasText(reportNo)) {
return;
}
EvalReportEntity found = evalReportGateway.findByOrgIdAndReportNo(orgId, reportNo);
if (found != null && (excludeId == null || !excludeId.equals(found.getId()))) {
throw new BizException(ErrorCode.EVAL_REPORT_NO_DUPLICATE);
}
}
public long count(EvalReportQuery query) {
return evalReportGateway.count(query);
}
public java.util.List<EvalReportEntity> listByIds(java.util.List<Long> ids) {
return evalReportGateway.listByIds(ids);
}
public EvalReportEntity updateStatus(Long id, int statusCode, String statusName) {
EvalReportEntity existing = get(id);
EvalReportEntity update = new EvalReportEntity();

View File

@ -14,6 +14,8 @@ import java.time.LocalDateTime;
@Service
public class InspNoticeDomainService {
/** 待下发(草稿) */
public static final int STATUS_DRAFT = 0;
public static final int STATUS_ISSUED = 1;
public static final int STATUS_CONFIRMED = 2;
public static final int STATUS_FEEDBACK = 3;
@ -50,6 +52,27 @@ public class InspNoticeDomainService {
return inspNoticeGateway.page(query);
}
public InspNoticeEntity saveDraft(InspNoticeEntity entity) {
entity.setStatusCode(STATUS_DRAFT);
entity.setSendTime(null);
entity.setConfirmFlag(2);
entity.setConfirmTime(null);
return add(entity);
}
public InspNoticeEntity updateDraft(InspNoticeEntity entity) {
InspNoticeEntity existing = get(entity.getId());
if (existing.getStatusCode() == null || existing.getStatusCode() != STATUS_DRAFT) {
throw new BizException(ErrorCode.INSP_NOTICE_STATUS_INVALID);
}
entity.setStatusCode(STATUS_DRAFT);
entity.setSendTime(null);
if (entity.getConfirmFlag() == null) {
entity.setConfirmFlag(2);
}
return modify(entity);
}
public InspNoticeEntity issue(InspNoticeEntity entity) {
entity.setStatusCode(STATUS_ISSUED);
entity.setSendTime(LocalDateTime.now());
@ -57,6 +80,27 @@ public class InspNoticeDomainService {
return add(entity);
}
public InspNoticeEntity issueDraft(Long id) {
InspNoticeEntity existing = get(id);
if (existing.getStatusCode() == null || existing.getStatusCode() != STATUS_DRAFT) {
throw new BizException(ErrorCode.INSP_NOTICE_STATUS_INVALID);
}
InspNoticeEntity update = new InspNoticeEntity();
update.setId(id);
update.setStatusCode(STATUS_ISSUED);
update.setSendTime(LocalDateTime.now());
update.setConfirmFlag(2);
return modify(update);
}
public InspNoticeEntity updateRemarks(Long id, String remarks) {
get(id);
InspNoticeEntity update = new InspNoticeEntity();
update.setId(id);
update.setRemarks(remarks);
return modify(update);
}
public InspNoticeEntity confirm(Long id) {
InspNoticeEntity existing = get(id);
if (existing.getStatusCode() == null || existing.getStatusCode() != STATUS_ISSUED) {
@ -113,6 +157,9 @@ public class InspNoticeDomainService {
return;
}
switch (entity.getStatusCode()) {
case STATUS_DRAFT:
entity.setStatusName("待下发");
break;
case STATUS_ISSUED:
entity.setStatusName("已下发");
break;

View File

@ -12,6 +12,8 @@ public class EvalReportDO {
private Long orgId;
private Long projectId;
private Long customerId;
/** 被评价单位名称(历史补录可无 customerId */
private String evaluatedUnitName;
private String reportNo;
private String reportName;
private Integer reportYear;
@ -19,6 +21,8 @@ public class EvalReportDO {
private String evalTypeCode;
/** 评价类型名称:安全预评价/安全验收评价/安全现状评价,示例:安全预评价 */
private String evalTypeName;
private String industryCode;
private String industryName;
private String businessScope;
private String fileUrl;
private String fileName;
@ -26,11 +30,17 @@ public class EvalReportDO {
private Integer publicFlag;
/** 资料是否完整1是 2否示例2 */
private Integer completeFlag;
/** 是否已报送监管1是 2否 */
private Integer submitFlag;
/** 报告状态编码1已入库 2抽查中 3待整改 4已复核示例1 */
private Integer reportStatusCode;
/** 报告状态名称:已入库/抽查中/待整改/已复核,示例:已入库 */
private String reportStatusName;
private LocalDateTime archiveTime;
private java.time.LocalDate issueDate;
private Integer pageCount;
private String secretLevelCode;
private String secretLevelName;
private String deleteEnum;
private String remarks;
private String createName;

View File

@ -12,30 +12,28 @@ public class InspNoticeDO {
private Long id;
private Long orgId;
private String noticeNo;
/** 检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查示例PROJECT_PROCESS */
/** 检查类型编码PROJECT_PROCESS项目过程检查 QUAL_KEEP资质保持检查 SPECIAL专项检查 */
private String inspTypeCode;
/** 检查类型名称:项目过程检查/资质保持检查/专项检查,示例:项目过程检查 */
private String inspTypeName;
/** 来源方式编码1年度计划 2专项安排 3其他示例1 */
/** 来源方式编码1年度计划 2专项安排 3其他 4风险预警 */
private Integer sourceTypeCode;
/** 来源方式名称:年度计划/专项安排/其他,示例:年度计划 */
private String sourceTypeName;
private Long sourceRefId;
private String sourceRefNo;
private Long projectId;
private LocalDateTime planInspTime;
private String leaderName;
private String noticeContent;
/** 状态编码1已下发 2已确认 3已反馈 4已办结示例1 */
private String noticeUrls;
/** 状态编码0待下发 1已下发 2已确认 3已反馈 4已办结 */
private Integer statusCode;
/** 状态名称:已下发/已确认/已反馈/已办结,示例:已下发 */
private String statusName;
/** 机构是否确认1是 2否示例2 */
/** 机构是否确认1是 2否 */
private Integer confirmFlag;
private LocalDateTime confirmTime;
private LocalDateTime sendTime;
/** 检查结果编码1合格 2不合格 3限期整改示例1 */
/** 检查结果编码1未发现问题/合格 2责令整改/不合格 3转入风险预警/限期整改 */
private Integer resultCode;
/** 检查结果名称:合格/不合格/限期整改,示例:合格 */
private String resultName;
private String resultContent;
private String resultUrls;

View File

@ -71,12 +71,67 @@ public class EvalReportGatewayImpl implements EvalReportGateway {
return PageResult.of(entities, result.getTotal(), result.getCurrent(), result.getSize());
}
@Override
public long count(EvalReportQuery query) {
Long cnt = evalReportMapper.selectCount(buildQueryWrapper(query));
return cnt == null ? 0L : cnt;
}
@Override
public EvalReportEntity findByOrgIdAndReportNo(Long orgId, String reportNo) {
if (orgId == null || !org.springframework.util.StringUtils.hasText(reportNo)) {
return null;
}
LambdaQueryWrapper<EvalReportDO> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EvalReportDO::getDeleteEnum, "false");
wrapper.eq(EvalReportDO::getOrgId, orgId);
wrapper.eq(EvalReportDO::getReportNo, reportNo.trim());
wrapper.last("limit 1");
return toEntity(evalReportMapper.selectOne(wrapper));
}
@Override
public List<EvalReportEntity> listByIds(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return java.util.Collections.emptyList();
}
List<EvalReportDO> records = evalReportMapper.selectBatchIds(ids);
if (records == null || records.isEmpty()) {
return java.util.Collections.emptyList();
}
java.util.Map<Long, EvalReportEntity> map = new java.util.HashMap<Long, EvalReportEntity>();
for (EvalReportDO dataObject : records) {
if (dataObject == null || !"false".equals(dataObject.getDeleteEnum())) {
continue;
}
EvalReportEntity entity = toEntity(dataObject);
if (entity != null && entity.getId() != null) {
map.put(entity.getId(), entity);
}
}
List<EvalReportEntity> ordered = new java.util.ArrayList<EvalReportEntity>();
for (Long id : ids) {
EvalReportEntity entity = map.get(id);
if (entity != null) {
ordered.add(entity);
}
}
return ordered;
}
private LambdaQueryWrapper<EvalReportDO> buildQueryWrapper(EvalReportQuery query) {
LambdaQueryWrapper<EvalReportDO> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EvalReportDO::getDeleteEnum, "false");
if (query.getOrgId() != null) {
wrapper.eq(EvalReportDO::getOrgId, query.getOrgId());
}
if (query.getOrgIds() != null) {
if (query.getOrgIds().isEmpty()) {
wrapper.eq(EvalReportDO::getId, -1L);
} else {
wrapper.in(EvalReportDO::getOrgId, query.getOrgIds());
}
}
if (query.getProjectId() != null) {
wrapper.eq(EvalReportDO::getProjectId, query.getProjectId());
}
@ -89,6 +144,28 @@ public class EvalReportGatewayImpl implements EvalReportGateway {
if (query.getReportStatusCode() != null) {
wrapper.eq(EvalReportDO::getReportStatusCode, query.getReportStatusCode());
}
if (org.springframework.util.StringUtils.hasText(query.getIndustryCode())) {
wrapper.eq(EvalReportDO::getIndustryCode, query.getIndustryCode());
}
if (query.getSubmitFlag() != null) {
wrapper.eq(EvalReportDO::getSubmitFlag, query.getSubmitFlag());
}
if (query.getCompleteFlag() != null) {
wrapper.eq(EvalReportDO::getCompleteFlag, query.getCompleteFlag());
}
if (query.getRecentYearFrom() != null) {
wrapper.ge(EvalReportDO::getReportYear, query.getRecentYearFrom());
}
if (query.getArchiveTimeFrom() != null) {
wrapper.ge(EvalReportDO::getArchiveTime, query.getArchiveTimeFrom());
}
if (query.getArchiveTimeTo() != null) {
wrapper.le(EvalReportDO::getArchiveTime, query.getArchiveTimeTo());
}
if (org.springframework.util.StringUtils.hasText(query.getKeyword())) {
String kw = query.getKeyword().trim();
wrapper.and(w -> w.like(EvalReportDO::getReportName, kw).or().like(EvalReportDO::getReportNo, kw));
}
wrapper.orderByDesc(EvalReportDO::getArchiveTime).orderByDesc(EvalReportDO::getCreateTime);
return wrapper;
}

View File

@ -12,6 +12,7 @@ import org.qinan.safetyeval.infrastructure.mapper.InspNoticeMapper;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
import org.qinan.safetyeval.infrastructure.support.OrgContextResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
@ -39,12 +40,16 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
@Override
public InspNoticeEntity get(Long id) {
InspNoticeDO dataObject = inspNoticeMapper.selectById(id);
if (dataObject == null || !"false".equals(dataObject.getDeleteEnum())) {
if (dataObject == null || isDeleted(dataObject.getDeleteEnum())) {
return null;
}
return toEntity(dataObject);
}
private boolean isDeleted(String deleteEnum) {
return deleteEnum != null && !"false".equals(deleteEnum);
}
@Override
public InspNoticeEntity modify(InspNoticeEntity entity) {
InspNoticeDO existing = inspNoticeMapper.selectById(entity.getId());
@ -76,11 +81,26 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
if (query.getOrgId() != null) {
wrapper.eq(InspNoticeDO::getOrgId, query.getOrgId());
}
if (!CollectionUtils.isEmpty(query.getOrgIds())) {
wrapper.in(InspNoticeDO::getOrgId, query.getOrgIds());
}
if (!CollectionUtils.isEmpty(query.getIds())) {
wrapper.in(InspNoticeDO::getId, query.getIds());
}
if (StringUtils.hasText(query.getNoticeNo())) {
wrapper.like(InspNoticeDO::getNoticeNo, query.getNoticeNo());
}
if (StringUtils.hasText(query.getKeyword())) {
String kw = query.getKeyword().trim();
wrapper.and(w -> w.like(InspNoticeDO::getNoticeNo, kw)
.or().like(InspNoticeDO::getSourceRefNo, kw)
.or().like(InspNoticeDO::getNoticeContent, kw)
.or().like(InspNoticeDO::getLeaderName, kw));
}
if (query.getStatusCode() != null) {
wrapper.eq(InspNoticeDO::getStatusCode, query.getStatusCode());
} else if (query.getMinStatusCode() != null) {
wrapper.ge(InspNoticeDO::getStatusCode, query.getMinStatusCode());
}
if (StringUtils.hasText(query.getInspTypeCode())) {
wrapper.eq(InspNoticeDO::getInspTypeCode, query.getInspTypeCode());
@ -88,6 +108,28 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
if (StringUtils.hasText(query.getLeaderName())) {
wrapper.like(InspNoticeDO::getLeaderName, query.getLeaderName());
}
if (query.getSourceTypeCode() != null) {
wrapper.eq(InspNoticeDO::getSourceTypeCode, query.getSourceTypeCode());
}
if (StringUtils.hasText(query.getSourceRefNo())) {
wrapper.like(InspNoticeDO::getSourceRefNo, query.getSourceRefNo());
}
if (query.getPlanInspTimeFrom() != null) {
wrapper.ge(InspNoticeDO::getPlanInspTime, query.getPlanInspTimeFrom());
}
if (query.getPlanInspTimeTo() != null) {
wrapper.le(InspNoticeDO::getPlanInspTime, query.getPlanInspTimeTo());
}
if (query.getConfirmFlag() != null) {
wrapper.eq(InspNoticeDO::getConfirmFlag, query.getConfirmFlag());
}
if (query.getFeedbackFlag() != null) {
if (query.getFeedbackFlag() == 1) {
wrapper.ge(InspNoticeDO::getStatusCode, 3);
} else if (query.getFeedbackFlag() == 2) {
wrapper.lt(InspNoticeDO::getStatusCode, 3);
}
}
wrapper.orderByDesc(InspNoticeDO::getSendTime).orderByDesc(InspNoticeDO::getCreateTime);
return wrapper;
}
@ -101,10 +143,12 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
dataObject.setSourceTypeCode(entity.getSourceTypeCode());
dataObject.setSourceTypeName(entity.getSourceTypeName());
dataObject.setSourceRefId(entity.getSourceRefId());
dataObject.setSourceRefNo(entity.getSourceRefNo());
dataObject.setProjectId(entity.getProjectId());
dataObject.setPlanInspTime(entity.getPlanInspTime());
dataObject.setLeaderName(entity.getLeaderName());
dataObject.setNoticeContent(entity.getNoticeContent());
dataObject.setNoticeUrls(entity.getNoticeUrls());
dataObject.setStatusCode(entity.getStatusCode());
dataObject.setStatusName(entity.getStatusName());
dataObject.setConfirmFlag(entity.getConfirmFlag());
@ -117,6 +161,7 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
dataObject.setResultRecorderId(entity.getResultRecorderId());
dataObject.setResultRecorderName(entity.getResultRecorderName());
dataObject.setResultTime(entity.getResultTime());
dataObject.setRemarks(entity.getRemarks());
dataObject.setTenantId(entity.getTenantId());
return dataObject;
}
@ -134,10 +179,12 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
entity.setSourceTypeCode(dataObject.getSourceTypeCode());
entity.setSourceTypeName(dataObject.getSourceTypeName());
entity.setSourceRefId(dataObject.getSourceRefId());
entity.setSourceRefNo(dataObject.getSourceRefNo());
entity.setProjectId(dataObject.getProjectId());
entity.setPlanInspTime(dataObject.getPlanInspTime());
entity.setLeaderName(dataObject.getLeaderName());
entity.setNoticeContent(dataObject.getNoticeContent());
entity.setNoticeUrls(dataObject.getNoticeUrls());
entity.setStatusCode(dataObject.getStatusCode());
entity.setStatusName(dataObject.getStatusName());
entity.setConfirmFlag(dataObject.getConfirmFlag());
@ -150,6 +197,7 @@ public class InspNoticeGatewayImpl implements InspNoticeGateway {
entity.setResultRecorderId(dataObject.getResultRecorderId());
entity.setResultRecorderName(dataObject.getResultRecorderName());
entity.setResultTime(dataObject.getResultTime());
entity.setRemarks(dataObject.getRemarks());
entity.setTenantId(dataObject.getTenantId());
return entity;
}

View File

@ -0,0 +1,47 @@
package org.qinan.safetyeval.infrastructure.support;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.springframework.util.StringUtils;
/**
* ID 使 Number
*/
public final class InspNoticeIdSupport {
private InspNoticeIdSupport() {
}
public static Long parseNoticeId(String id) {
if (!StringUtils.hasText(id)) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "告知id不能为空");
}
try {
return Long.parseLong(id.trim());
} catch (NumberFormatException ex) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "告知id格式无效");
}
}
public static Long parseOrgId(String orgId) {
if (!StringUtils.hasText(orgId)) {
return null;
}
try {
return Long.parseLong(orgId.trim());
} catch (NumberFormatException ex) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "被检机构id格式无效");
}
}
public static Long parseMaterialId(String materialId) {
if (!StringUtils.hasText(materialId)) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "材料id不能为空");
}
try {
return Long.parseLong(materialId.trim());
} catch (NumberFormatException ex) {
throw new BizException(ErrorCode.REGULATOR_EVAL_PARAM_INVALID, "材料id格式无效");
}
}
}

View File

@ -1,8 +1,11 @@
package org.qinan.safetyeval.infrastructure.support;
import org.qinan.safetyeval.domain.entity.OrgInfoEntity;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.OrgInfoGateway;
import org.qinan.safetyeval.infrastructure.adapter.ThreadLocalUserInfoAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
@ -11,11 +14,23 @@ import org.springframework.stereotype.Component;
@Component
public class InstitutionOrgSupport {
@Autowired(required = false)
private OrgInfoGateway orgInfoGateway;
public Long resolveCurrentOrgId() {
Long orgId = ThreadLocalUserInfoAdapter.getOrgId();
if (orgId == null) {
orgId = ThreadLocalUserInfoAdapter.get();
}
if (orgId == null && orgInfoGateway != null) {
OrgInfoEntity orgInfo = orgInfoGateway.getInfo();
if (orgInfo != null) {
orgId = orgInfo.getId();
}
}
if (orgId == null) {
throw new BizException(ErrorCode.ORG_INFO_NOT_FOUND);
}
return orgId;
}