diff --git a/docs/iWork用印流程集成开发文档.md b/docs/iWork用印流程集成开发文档.md
new file mode 100644
index 00000000..c435a76c
--- /dev/null
+++ b/docs/iWork用印流程集成开发文档.md
@@ -0,0 +1,514 @@
+# iWork 用印流程集成开发文档
+
+## 1. 概述
+
+本文档描述了 ZT Cloud 平台与 iWork 系统的用印流程集成方案,包括流程发起、回调处理、消息通知及重试机制。
+
+### 1.1 功能特性
+
+- **流程发起**:支持用印专用流程和通用流程两种创建方式
+- **回调处理**:接收 iWork 回调,自动通知业务模块
+- **消息队列**:基于 RocketMQ 的异步消息通知机制
+- **自动重试**:失败回调自动重试,支持配置重试次数和间隔
+- **日志追踪**:完整记录流程创建和回调处理全生命周期
+
+### 1.2 整体架构
+
+```
+┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+│ 业务系统 │────▶│ System 模块 │────▶│ iWork 系统 │
+│ (调用方) │ │ (集成层) │ │ (OA 流程) │
+└─────────────────┘ └─────────────────┘ └─────────────────┘
+ ▲ ▲ │
+ │ │ │
+ │ └───────────────────────┘
+ │ iWork 流程完成后回调
+ │ ┌─────────────────┐
+ │ │ RocketMQ │
+ │ │ (消息队列) │
+ └───────────────┴─────────────────┘
+```
+
+### 1.3 完整流程时序图
+
+```
+┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
+│ 业务系统 │ │ System │ │ iWork │ │RocketMQ│ │业务消费者│
+└───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘
+ │ │ │ │ │
+ │ 1.发起用印流程 │ │ │ │
+ │──────────────▶│ │ │ │
+ │ │ 2.创建流程 │ │ │
+ │ │──────────────▶│ │ │
+ │ │ 返回requestId│ │ │
+ │ │◀──────────────│ │ │
+ │ 返回结果 │ │ │ │
+ │◀──────────────│ │ │ │
+ │ │ │ │ │
+ │ │ │ 3.OA流程审批 │ │
+ │ │ │ (异步进行) │ │
+ │ │ │ │ │
+ │ │ 4.流程完成回调 │ │ │
+ │ │◀──────────────│ │ │
+ │ │ │ │ │
+ │ │ 5.发送MQ消息 │ │ │
+ │ │──────────────────────────────▶│ │
+ │ │ │ │ 6.投递消息 │
+ │ │ │ │──────────────▶│
+ │ │ │ │ │
+ │ │ │ │ 7.返回处理结果 │
+ │ │ │ │◀──────────────│
+ │ │ 8.接收结果 │ │ │
+ │ │◀──────────────────────────────│ │
+ │ │ │ │ │
+ │ │ 9.更新日志状态 │ │ │
+ │ │ (成功/重试) │ │ │
+ └───────────────┴───────────────┴───────────────┴───────────────┘
+```
+
+**流程说明**:
+
+1. **发起流程**:业务系统调用 System 模块的流程创建接口
+2. **创建流程**:System 模块调用 iWork API 创建 OA 流程,获取 `requestId`
+3. **OA 审批**:流程在 iWork 系统中流转(审批、签章等),此过程异步进行
+4. **iWork 回调**:流程完成后,iWork 系统主动回调 System 模块的回调接口
+5. **MQ 通知**:System 模块将回调数据通过 RocketMQ 发送给业务消费者
+6. **业务处理**:业务消费者接收消息并处理(如保存签章文件、更新业务状态)
+7. **返回结果**:业务消费者处理完成后,发送处理结果消息
+8. **接收结果**:System 模块接收处理结果
+9. **状态更新**:根据结果更新日志状态,失败则触发重试机制
+
+## 2. 数据库设计
+
+### 2.1 流程日志表 (system_iwork_workflow_log)
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | BIGINT | 主键 |
+| request_id | VARCHAR(128) | iWork 请求编号(唯一) |
+| workflow_id | BIGINT | 流程模板 ID |
+| business_code | VARCHAR(128) | 业务编码 |
+| biz_callback_key | VARCHAR(255) | 业务回调标识(MQ tag) |
+| raw_request | VARCHAR(2000) | 创建请求原文 |
+| status | VARCHAR(32) | 流程状态 |
+| callback_status | INTEGER | 回调处理状态 |
+| retry_count | INTEGER | 已重试次数 |
+| max_retry | INTEGER | 最大重试次数 |
+| last_error_message | VARCHAR(512) | 最后错误信息 |
+| raw_callback | VARCHAR(2000) | 回调原文 |
+| last_callback_time | TIMESTAMP | 最近回调时间 |
+| tenant_id | BIGINT | 租户编号 |
+
+### 2.2 回调状态枚举 (callback_status)
+
+| 值 | 状态 | 说明 |
+|----|------|------|
+| 0 | CREATE_PENDING | 创建中 |
+| 1 | CREATE_SUCCESS | 创建成功 |
+| 2 | CREATE_FAILED | 创建失败 |
+| 3 | CALLBACK_PENDING | 回调待处理 |
+| 4 | CALLBACK_SUCCESS | 回调处理成功 |
+| 5 | CALLBACK_FAILED | 回调处理失败 |
+| 6 | CALLBACK_RETRYING | 回调重试中 |
+| 7 | CALLBACK_RETRY_FAILED | 回调重试失败 |
+
+### 2.3 状态流转图
+
+```
+ ┌─────────────────────────────────────────────────────┐
+ │ 流程创建阶段 │
+ │ ┌──────────┐ 成功 ┌──────────┐ │
+ │ │ PENDING │ ─────────▶ │ SUCCESS │ │
+ │ │ (0) │ │ (1) │ │
+ │ └──────────┘ └──────────┘ │
+ │ │ │
+ │ │ 失败 │
+ │ ▼ │
+ │ ┌──────────┐ │
+ │ │ FAILED │ │
+ │ │ (2) │ │
+ │ └──────────┘ │
+ └─────────────────────────────────────────────────────┘
+ │
+ │ iWork 回调
+ ▼
+ ┌─────────────────────────────────────────────────────┐
+ │ 回调处理阶段 │
+ │ ┌──────────┐ 成功 ┌──────────┐ │
+ │ │ CALLBACK │ ─────────▶ │ CALLBACK │ │
+ │ │ PENDING │ │ SUCCESS │ │
+ │ │ (3) │ │ (4) │ │
+ │ └──────────┘ └──────────┘ │
+ │ │ │
+ │ │ 失败 │
+ │ ▼ │
+ │ ┌──────────┐ 重试中 ┌──────────┐ │
+ │ │ CALLBACK │ ◀───────▶ │ CALLBACK │ │
+ │ │ FAILED │ │ RETRYING │ │
+ │ │ (5) │ │ (6) │ │
+ │ └──────────┘ └──────────┘ │
+ │ │ │
+ │ │ 重试次数耗尽 │
+ │ ▼ │
+ │ ┌──────────┐ │
+ │ │ RETRY │ │
+ │ │ FAILED(7)│ │
+ │ └──────────┘ │
+ └─────────────────────────────────────────────────────┘
+```
+
+## 3. API 接口说明
+
+### 3.1 用印流程创建
+
+**接口地址**:`POST /admin-api/system/integration/iwork/workflow/create`
+
+**请求参数**:
+
+```json
+{
+ "operatorUserId": "1001",
+ "jbr": "1001",
+ "yybm": "2001",
+ "fb": "3001",
+ "sqsj": "2025-01-30",
+ "yyqx": "内部使用",
+ "yyfkUrl": "https://example.com/attachment.pdf",
+ "yysy": "合同盖章",
+ "xyywjUrl": "https://example.com/contract.pdf",
+ "yysx": "公章",
+ "ywxtdjbh": "DJ-2025-0001",
+ "bizCallbackKey": "seal-callback"
+}
+```
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| operatorUserId | 是 | 操作人 iWork 用户 ID |
+| jbr | 是 | 用印申请人 |
+| yybm | 是 | 用印部门 ID |
+| fb | 是 | 用印单位(分部 ID) |
+| sqsj | 是 | 申请时间 (yyyy-MM-dd) |
+| yyqx | 是 | 用印去向 |
+| xyywjUrl | 是 | 用印材料附件 URL |
+| yysx | 是 | 用印事项 |
+| ywxtdjbh | 是 | 业务系统单据编号 |
+| bizCallbackKey | 否 | 业务回调标识 |
+| yyfkUrl | 否 | 用印依据附件 URL |
+| yysy | 否 | 用印事由 |
+
+### 3.2 通用流程创建
+
+**接口地址**:`POST /admin-api/system/integration/iwork/workflow/create-generic`
+
+**请求参数**:
+
+```json
+{
+ "operatorUserId": "1001",
+ "workflowId": 54,
+ "payload": {
+ "requestName": "用印-DJ-2025-0001",
+ "mainData": [
+ {"fieldName": "jbr", "fieldValue": "1001"},
+ {"fieldName": "yybm", "fieldValue": "2001"}
+ ]
+ },
+ "ywxtdjbh": "DJ-2025-0001",
+ "bizCallbackKey": "seal-callback"
+}
+```
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| operatorUserId | 是 | 操作人 iWork 用户 ID |
+| workflowId | 是 | 流程模板 ID |
+| payload | 是 | 透传给 iWork 的业务参数 |
+| ywxtdjbh | 否 | 业务编码 |
+| bizCallbackKey | 否 | 业务回调标识 |
+
+### 3.3 iWork 回调接口
+
+**接口地址**:`POST /admin-api/system/integration/iwork/callback/file`
+
+**说明**:此接口供 iWork 系统回调,无需认证(@PermitAll, @TenantIgnore)
+
+**iWork 侧配置**:需要在 iWork 系统中配置回调地址,当流程完成时自动调用此接口。
+
+**请求参数**:
+
+```json
+{
+ "requestId": "3603649",
+ "businessCode": "DJ-2025-0001",
+ "fileUrl": "https://iwork.example.com/signed-file.pdf",
+ "fileName": "已签章合同.pdf",
+ "status": "COMPLETED"
+}
+```
+
+| 参数 | 必填 | 说明 |
+|------|------|------|
+| requestId | 是 | iWork 请求编号(与创建流程时返回的一致) |
+| businessCode | 是 | 业务编码(与创建流程时传入的 ywxtdjbh 一致) |
+| fileUrl | 是 | 签章后文件 URL |
+| fileName | 否 | 文件名称 |
+| status | 否 | 业务状态 |
+
+**回调处理逻辑**:
+
+1. 根据 `requestId` 查询流程创建日志,获取 `bizCallbackKey`
+2. 更新日志状态为 `CALLBACK_PENDING`
+3. 发送 MQ 消息通知业务模块(仅当 `bizCallbackKey` 存在时)
+4. 返回处理结果
+
+## 4. MQ 消息机制
+
+### 4.1 消息流程图
+
+```
+┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
+│ iWork 回调 │───▶│ System 模块 │───▶│ RocketMQ │───▶│ 业务消费者 │
+│ │ │ (Producer) │ │ │ │ │
+└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
+ │
+ ▼
+┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
+│ 更新日志状态 │◀───│ System 模块 │◀───│ RocketMQ │◀───│ 返回处理结果 │
+│ │ │ (Listener) │ │ │ │ │
+└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
+```
+
+### 4.2 Topic 定义
+
+| Topic | 说明 |
+|-------|------|
+| SYSTEM_IWORK_BIZ_CALLBACK | 回调通知消息(System → 业务模块) |
+| SYSTEM_IWORK_BIZ_CALLBACK_RESULT | 处理结果消息(业务模块 → System) |
+
+### 4.3 回调通知消息 (IWorkBizCallbackMessage)
+
+```java
+{
+ "requestId": "3603649",
+ "bizCallbackKey": "seal-callback",
+ "payload": { /* 回调原始数据 */ },
+ "attempt": 0,
+ "maxAttempts": 3
+}
+```
+
+**Tag 规则**:消息 tag = `bizCallbackKey`,业务模块按 tag 订阅
+
+### 4.4 处理结果消息 (IWorkBizCallbackResultMessage)
+
+```java
+{
+ "requestId": "3603649",
+ "bizCallbackKey": "seal-callback",
+ "success": true,
+ "errorMessage": null,
+ "attempt": 0,
+ "maxAttempts": 3,
+ "payload": { /* 原始数据,用于重试 */ }
+}
+```
+
+## 5. 业务模块接入指南
+
+### 5.1 添加依赖
+
+```xml
+
+ com.zt.plat
+ zt-module-system-api
+
+```
+
+### 5.2 实现消费者
+
+```java
+@Slf4j
+@Component
+@RequiredArgsConstructor
+@RocketMQMessageListener(
+ topic = IWorkBizCallbackMessage.TOPIC,
+ consumerGroup = IWorkBizCallbackMessage.TOPIC + "_YOUR_BIZ_KEY",
+ selectorExpression = "your-biz-callback-key" // 与 bizCallbackKey 一致
+)
+public class YourBizCallbackConsumer implements RocketMQListener {
+
+ private final RocketMQTemplate rocketMQTemplate;
+
+ @Override
+ public void onMessage(IWorkBizCallbackMessage message) {
+ log.info("收到 iWork 回调: requestId={}", message.getRequestId());
+
+ IWorkBizCallbackResultMessage result;
+ try {
+ // 处理业务逻辑
+ processCallback(message);
+
+ result = IWorkBizCallbackResultMessage.builder()
+ .requestId(message.getRequestId())
+ .bizCallbackKey(message.getBizCallbackKey())
+ .success(true)
+ .attempt(message.getAttempt())
+ .maxAttempts(message.getMaxAttempts())
+ .payload(message.getPayload())
+ .build();
+ } catch (Exception e) {
+ log.error("处理回调失败", e);
+ result = IWorkBizCallbackResultMessage.builder()
+ .requestId(message.getRequestId())
+ .bizCallbackKey(message.getBizCallbackKey())
+ .success(false)
+ .errorMessage(e.getMessage())
+ .attempt(message.getAttempt())
+ .maxAttempts(message.getMaxAttempts())
+ .payload(message.getPayload())
+ .build();
+ }
+
+ // 发送处理结果
+ rocketMQTemplate.syncSend(IWorkBizCallbackResultMessage.TOPIC, result);
+ }
+
+ private void processCallback(IWorkBizCallbackMessage message) {
+ // 业务处理逻辑
+ // 1. 解析 payload 获取回调数据
+ // 2. 更新业务状态
+ // 3. 保存签章文件等
+ }
+}
+```
+
+### 5.3 关键配置项
+
+| 配置项 | 说明 |
+|--------|------|
+| consumerGroup | 消费者组,建议格式:`TOPIC + "_" + bizCallbackKey` |
+| selectorExpression | Tag 过滤,必须与发起流程时的 `bizCallbackKey` 一致 |
+
+### 5.4 注意事项
+
+1. **bizCallbackKey 唯一性**:每个业务场景使用独立的 bizCallbackKey
+2. **幂等处理**:消费者需实现幂等,同一 requestId 可能重复投递
+3. **必须返回结果**:处理完成后必须发送 `IWorkBizCallbackResultMessage`
+4. **错误信息**:失败时填写 errorMessage,便于问题排查
+
+## 6. 重试机制
+
+### 6.1 重试流程
+
+```
+业务处理失败 → 返回 success=false → System Listener 接收
+ ↓
+ 检查 attempt < maxAttempts?
+ ↓ ↓
+ 是 否
+ ↓ ↓
+ 延迟后重新投递 标记最终失败
+```
+
+### 6.2 配置参数
+
+```yaml
+iwork:
+ callback:
+ retry:
+ max-attempts: 3 # 最大重试次数
+ delay-seconds: 5 # 重试间隔(秒)
+```
+
+### 6.3 手工重试
+
+**接口地址**:`POST /admin-api/system/integration/iwork/log/retry`
+
+```json
+{
+ "requestId": "3603649"
+}
+```
+
+## 7. 日志查询
+
+### 7.1 分页查询接口
+
+**接口地址**:`POST /admin-api/system/integration/iwork/log/page`
+
+**请求参数**:
+
+```json
+{
+ "requestId": "3603649",
+ "businessCode": "DJ-2025-0001",
+ "bizCallbackKey": "seal-callback",
+ "status": 4,
+ "pageNo": 1,
+ "pageSize": 10
+}
+```
+
+## 8. 本地开发调试
+
+### 8.1 隔离测试环境
+
+为避免与测试环境消息冲突,本地开发时需修改:
+
+1. **Listener 消费者组**:添加本地标识后缀
+```java
+consumerGroup = IWorkBizCallbackResultMessage.TOPIC + "_CONSUMER_local"
+```
+
+2. **Listener Tag 过滤**:使用本地专用 tag
+```java
+selectorExpression = "local_test"
+```
+
+3. **业务消费者**:同样使用本地专用 bizCallbackKey
+```java
+selectorExpression = "your-biz-key_local"
+```
+
+4. **数据库记录**:将 `biz_callback_key` 设为本地专用值
+
+### 8.2 调试建议
+
+- 使用独立的 `bizCallbackKey` 避免消息串扰
+- 检查 RocketMQ 控制台确认消息投递情况
+- 关注日志中的 `requestId` 进行链路追踪
+
+## 9. 常见问题
+
+### Q1: 业务消费者收不到消息?
+
+检查项:
+- `selectorExpression` 是否与 `bizCallbackKey` 一致
+- 消费者组名是否正确
+- RocketMQ 连接是否正常
+
+### Q2: 收到重复消息?
+
+可能原因:
+- 多个环境的 Listener 都在消费同一 topic
+- 解决:使用独立的消费者组和 tag 过滤
+
+### Q3: 重试不生效?
+
+检查项:
+- 是否正确返回了 `IWorkBizCallbackResultMessage`
+- `success` 字段是否为 `false`
+- 配置的 `max-attempts` 是否大于当前 `attempt`
+
+## 10. 相关代码位置
+
+| 组件 | 路径 |
+|------|------|
+| Controller | `zt-module-system-server/.../controller/admin/integration/iwork/IWorkIntegrationController.java` |
+| Service | `zt-module-system-server/.../service/integration/iwork/impl/IWorkIntegrationServiceImpl.java` |
+| 日志 Service | `zt-module-system-server/.../service/integration/iwork/impl/IWorkWorkflowLogServiceImpl.java` |
+| MQ Producer | `zt-module-system-server/.../mq/iwork/IWorkBizCallbackProducer.java` |
+| MQ Listener | `zt-module-system-server/.../mq/iwork/IWorkBizCallbackListener.java` |
+| 消息定义 | `zt-module-system-api/.../mq/iwork/IWorkBizCallbackMessage.java` |
+| 配置类 | `zt-module-system-server/.../framework/integration/iwork/config/IWorkProperties.java` |
diff --git a/sql/dm/iwork_workflow_log.sql b/sql/dm/iwork_workflow_log_20260130.sql
similarity index 54%
rename from sql/dm/iwork_workflow_log.sql
rename to sql/dm/iwork_workflow_log_20260130.sql
index 6af5cd50..1f0074fc 100644
--- a/sql/dm/iwork_workflow_log.sql
+++ b/sql/dm/iwork_workflow_log_20260130.sql
@@ -1,12 +1,19 @@
--- iWork 流程创建日志表(达梦数据库)
+-- iWork 流程日志表(达梦数据库)
+-- 合并了流程创建日志和回调日志
CREATE TABLE system_iwork_workflow_log (
id BIGINT NOT NULL,
- request_id VARCHAR(64) NOT NULL,
+ request_id VARCHAR(128) NOT NULL,
workflow_id BIGINT,
business_code VARCHAR(128),
biz_callback_key VARCHAR(255),
raw_request VARCHAR(2000),
status VARCHAR(32),
+ callback_status INTEGER,
+ retry_count INTEGER DEFAULT 0,
+ max_retry INTEGER,
+ last_error_message VARCHAR(512),
+ raw_callback VARCHAR(2000),
+ last_callback_time TIMESTAMP,
creator VARCHAR(64) DEFAULT '',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) DEFAULT '',
@@ -17,14 +24,20 @@ CREATE TABLE system_iwork_workflow_log (
);
-- 添加注释
-COMMENT ON TABLE system_iwork_workflow_log IS 'iWork 流程创建日志';
+COMMENT ON TABLE system_iwork_workflow_log IS 'iWork 流程日志';
COMMENT ON COLUMN system_iwork_workflow_log.id IS '主键';
-COMMENT ON COLUMN system_iwork_workflow_log.request_id IS 'iWork 返回的请求编号';
+COMMENT ON COLUMN system_iwork_workflow_log.request_id IS 'iWork 请求编号';
COMMENT ON COLUMN system_iwork_workflow_log.workflow_id IS '流程模板 ID';
COMMENT ON COLUMN system_iwork_workflow_log.business_code IS '业务编码';
COMMENT ON COLUMN system_iwork_workflow_log.biz_callback_key IS '业务回调标识';
-COMMENT ON COLUMN system_iwork_workflow_log.raw_request IS '创建请求原始参数';
+COMMENT ON COLUMN system_iwork_workflow_log.raw_request IS '创建请求原文';
COMMENT ON COLUMN system_iwork_workflow_log.status IS '流程状态';
+COMMENT ON COLUMN system_iwork_workflow_log.callback_status IS '回调处理状态';
+COMMENT ON COLUMN system_iwork_workflow_log.retry_count IS '已重试次数';
+COMMENT ON COLUMN system_iwork_workflow_log.max_retry IS '最大重试次数';
+COMMENT ON COLUMN system_iwork_workflow_log.last_error_message IS '最后错误信息';
+COMMENT ON COLUMN system_iwork_workflow_log.raw_callback IS '回调原文';
+COMMENT ON COLUMN system_iwork_workflow_log.last_callback_time IS '最近回调时间';
COMMENT ON COLUMN system_iwork_workflow_log.creator IS '创建者';
COMMENT ON COLUMN system_iwork_workflow_log.create_time IS '创建时间';
COMMENT ON COLUMN system_iwork_workflow_log.updater IS '更新者';
@@ -33,6 +46,8 @@ COMMENT ON COLUMN system_iwork_workflow_log.deleted IS '是否删除';
COMMENT ON COLUMN system_iwork_workflow_log.tenant_id IS '租户编号';
-- 创建唯一索引
-CREATE UNIQUE INDEX uk_request_id ON system_iwork_workflow_log(request_id);
-
+CREATE UNIQUE INDEX uk_iwork_workflow_log_request_id ON system_iwork_workflow_log(request_id);
+-- 创建普通索引
+-- CREATE INDEX idx_iwork_workflow_log_business_code ON system_iwork_workflow_log(business_code);
+-- CREATE INDEX idx_iwork_workflow_log_biz_callback_key ON system_iwork_workflow_log(biz_callback_key);
diff --git a/sql/dm/system_iwork_seal_log_dm8.sql b/sql/dm/system_iwork_seal_log_dm8.sql
deleted file mode 100644
index 5451ee8e..00000000
--- a/sql/dm/system_iwork_seal_log_dm8.sql
+++ /dev/null
@@ -1,43 +0,0 @@
--- iWork 用印回调日志(DM8)
--- 表:system_iwork_seal_log
--- 序列:system_iwork_seal_log_seq
-
--- 清理旧对象(若存在)
-DROP TABLE IF EXISTS system_iwork_seal_log;
-
-CREATE TABLE system_iwork_seal_log (
- id BIGINT NOT NULL,
- request_id VARCHAR(128) NOT NULL,
- business_code VARCHAR(128),
- biz_callback_key VARCHAR(255),
- status INTEGER,
- retry_count INTEGER DEFAULT 0,
- max_retry INTEGER,
- last_error_message VARCHAR(512),
- raw_callback VARCHAR(2000),
- last_callback_time DATETIME,
- creator VARCHAR(64),
- create_time DATETIME DEFAULT SYSDATE,
- updater VARCHAR(64),
- update_time DATETIME DEFAULT SYSDATE,
- deleted SMALLINT DEFAULT 0 NOT NULL,
- PRIMARY KEY (id),
- UNIQUE (request_id)
-);
-
-COMMENT ON TABLE system_iwork_seal_log IS 'iWork 用印回调日志';
-COMMENT ON COLUMN system_iwork_seal_log.id IS '主键';
-COMMENT ON COLUMN system_iwork_seal_log.request_id IS 'iWork requestId 唯一标识';
-COMMENT ON COLUMN system_iwork_seal_log.business_code IS '业务单号';
-COMMENT ON COLUMN system_iwork_seal_log.biz_callback_key IS '业务回调标识';
-COMMENT ON COLUMN system_iwork_seal_log.status IS '状态枚举';
-COMMENT ON COLUMN system_iwork_seal_log.retry_count IS '已重试次数';
-COMMENT ON COLUMN system_iwork_seal_log.max_retry IS '最大重试次数快照';
-COMMENT ON COLUMN system_iwork_seal_log.last_error_message IS '最后错误信息';
-COMMENT ON COLUMN system_iwork_seal_log.raw_callback IS '回调原文截断';
-COMMENT ON COLUMN system_iwork_seal_log.last_callback_time IS '最近回调时间';
-COMMENT ON COLUMN system_iwork_seal_log.creator IS '创建者';
-COMMENT ON COLUMN system_iwork_seal_log.create_time IS '创建时间';
-COMMENT ON COLUMN system_iwork_seal_log.updater IS '更新者';
-COMMENT ON COLUMN system_iwork_seal_log.update_time IS '最后更新时间';
-COMMENT ON COLUMN system_iwork_seal_log.deleted IS '是否删除';
diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java
index 6c95cc7c..063610e6 100644
--- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java
+++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/config/ZtDataPermissionAutoConfiguration.java
@@ -14,6 +14,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import java.util.List;
@@ -45,6 +46,7 @@ public class ZtDataPermissionAutoConfiguration {
}
@Bean
+ @ConditionalOnMissingBean
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
// 创建菜单数据权限处理器
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/menudatapermission/config/MenuDataPermissionConfiguration.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/menudatapermission/config/MenuDataPermissionConfiguration.java
index 172fba3d..7f3ad205 100644
--- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/menudatapermission/config/MenuDataPermissionConfiguration.java
+++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/menudatapermission/config/MenuDataPermissionConfiguration.java
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionIntercepto
import com.zt.plat.framework.datapermission.core.menudatapermission.handler.MenuDataPermissionHandler;
import com.zt.plat.framework.mybatis.core.util.MyBatisUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -20,6 +21,7 @@ public class MenuDataPermissionConfiguration {
@Bean
@ConditionalOnBean(MybatisPlusInterceptor.class)
+ @ConditionalOnMissingBean
public MenuDataPermissionHandler menuDataPermissionHandler(MybatisPlusInterceptor interceptor) {
// 创建菜单数据权限处理器
MenuDataPermissionHandler handler = new MenuDataPermissionHandler();
diff --git a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
index 60951994..274b9249 100644
--- a/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
+++ b/zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
@@ -96,7 +96,6 @@ public class DeptDataPermissionRule implements DataPermissionRule {
/**
* 基于用户的表字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
- * key:表名
* value:字段名
*/
private final Map userColumns = new HashMap<>();
@@ -262,7 +261,11 @@ public class DeptDataPermissionRule implements DataPermissionRule {
if (Boolean.FALSE.equals(self)) {
return null;
}
- String columnName = userColumns.get(tableName);
+ String userColumnsKey = tableName;
+ if (StrUtil.isNotBlank(workCode)) {
+ userColumnsKey = userColumnsKey + "_work_code";
+ }
+ String columnName = userColumns.get(userColumnsKey);
if (StrUtil.isEmpty(columnName)) {
return null;
}
diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java
index ed2f0c94..cbea9553 100644
--- a/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java
+++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java
@@ -77,7 +77,12 @@ public class DbSqlSessionFactory implements SessionFactory {
// 当前系统适配 dm,如果存在 schema 为空的情况,从 connection 获取
try {
if (getDatabaseSchema() == null || getDatabaseSchema().length() == 0){
- setDatabaseSchema(dbSqlSession.getSqlSession().getConnection().getSchema());
+ String schemaFromUrl = extractSchemaFromJdbcUrl(dbSqlSession.getSqlSession().getConnection());
+ if (schemaFromUrl != null && schemaFromUrl.length() > 0) {
+ setDatabaseSchema(schemaFromUrl);
+ } else {
+ setDatabaseSchema(dbSqlSession.getSqlSession().getConnection().getSchema());
+ }
}
dbSqlSession.getSqlSession().getConnection().getSchema();
} catch (SQLException e) {
@@ -351,4 +356,39 @@ public class DbSqlSessionFactory implements SessionFactory {
public void setUsePrefixId(boolean usePrefixId) {
this.usePrefixId = usePrefixId;
}
+
+ private String extractSchemaFromJdbcUrl(java.sql.Connection connection) {
+ if (connection == null) {
+ return null;
+ }
+ try {
+ String url = connection.getMetaData().getURL();
+ if (url == null || url.isEmpty()) {
+ return null;
+ }
+ int queryIndex = url.indexOf('?');
+ if (queryIndex < 0 || queryIndex == url.length() - 1) {
+ return null;
+ }
+ String query = url.substring(queryIndex + 1);
+ String[] parts = query.split("[&;]");
+ for (String part : parts) {
+ int eqIndex = part.indexOf('=');
+ if (eqIndex <= 0 || eqIndex == part.length() - 1) {
+ continue;
+ }
+ String key = part.substring(0, eqIndex).trim().toLowerCase(Locale.ROOT);
+ if ("schema".equals(key) || "currentschema".equals(key) || "current_schema".equals(key)) {
+ String value = part.substring(eqIndex + 1).trim();
+ if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
+ value = value.substring(1, value.length() - 1);
+ }
+ return value;
+ }
+ }
+ } catch (SQLException ignored) {
+ return null;
+ }
+ return null;
+ }
}
diff --git a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java
index 6b68ebe6..f0057d1c 100644
--- a/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java
+++ b/zt-module-infra/zt-module-infra-server/src/main/java/com/zt/plat/module/infra/controller/admin/file/FileController.java
@@ -31,6 +31,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
+import static com.zt.plat.framework.common.pojo.CommonResult.error;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.zt.plat.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
@@ -52,13 +53,13 @@ public class FileController {
private FileService fileService;
@GetMapping("/get")
- @Operation(summary = "获取文件预览地址", description = "根据 fileId 返回文件预览 url(kkfile),支持加密文件预览,需要传递验证码 code,加密文件预览地址默认5分钟内有效,可在配置文件中添加zt.file.preview-expire-seconds配置")
+ @Operation(summary = "获取文件预览地址", description = "根据 fileId 返回文件预览 url(kkfile),支持加密文件预览,加密文件预览地址默认5分钟内有效,可在配置文件中添加zt.file.preview-expire-seconds配置有效时间")
public CommonResult getPreviewUrl(@RequestParam("fileId") Long fileId,
@RequestParam(value = "code", required = false) String code,
HttpServletRequest request) throws Exception {
FileDO fileDO = fileService.getActiveFileById(fileId);
if (fileDO == null) {
- return CommonResult.error(HttpStatus.NOT_FOUND.value(), "文件不存在");
+ return error(HttpStatus.NOT_FOUND.value(), "文件不存在");
}
// 统计下载次数
@@ -68,26 +69,24 @@ public class FileController {
FileRespVO fileRespVO = BeanUtils.toBean(fileDO, FileRespVO.class);
// 加密文件:塞入“临时解密预览 URL”
- if (Boolean.TRUE.equals(fileRespVO.getIsEncrypted())) { // FileDO 通过 aesIv 判断加密
+ if (Boolean.TRUE.equals(fileRespVO.getIsEncrypted()) // FileDO 通过 aesIv 判断加密
+ && cn.hutool.core.util.StrUtil.isNotBlank(code)) { // 预览文件会调用两次该接口,只有code不为空时候才塞url
- if (cn.hutool.core.util.StrUtil.isBlank(code)) {
+ /*if (cn.hutool.core.util.StrUtil.isBlank(code)) {
return CommonResult.error(HttpStatus.BAD_REQUEST.value(), "加密文件预览需要验证码 code");
- }
+ }*/
+
// 验证通过:发放给 kkfile 用的短期 token(kkfile 不带登录态)
Long userId = getLoginUserId();
boolean flag = fileService.verifyCode(fileId, userId, code);
if(!flag){
- return CommonResult.customize(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), "验证码错误");
+ return error(HttpStatus.BAD_REQUEST.value(), "验证码错误");
}
-
String token = fileService.generatePreviewToken(fileId, userId);
-
String baseUrl = buildPublicBaseUrl(request); // 见下方函数
-
String fullfilename = java.net.URLEncoder
.encode(fileDO.getName(), java.nio.charset.StandardCharsets.UTF_8)
.replace("+", "%20");
-
String decryptUrl = baseUrl + "/admin-api/infra/file/preview-decrypt"
+ "?fileId=" + fileId
+ "&token=" + token
@@ -215,14 +214,14 @@ public class FileController {
try {
sendTypeEnum = VerifyCodeSendType.valueOf(sendType.trim().toUpperCase());
} catch (IllegalArgumentException ex) {
- return CommonResult.error(HttpStatus.BAD_REQUEST.value(),
+ return error(HttpStatus.BAD_REQUEST.value(),
"sendType 参数不合法,可选:SMS / E_OFFICE");
}
}
FileDO activeFileById = fileService.getActiveFileById(fileId);
if (activeFileById == null) {
- return CommonResult.error(HttpStatus.NOT_FOUND.value(), "文件不存在");
+ return error(HttpStatus.NOT_FOUND.value(), "文件不存在");
}
FileRespVO fileRespVO = BeanUtils.toBean(activeFileById, FileRespVO.class);
diff --git a/zt-module-system/zt-module-system-server-app/src/main/resources/application.yaml b/zt-module-system/zt-module-system-server-app/src/main/resources/application.yaml
index 482543a6..853e32cd 100644
--- a/zt-module-system/zt-module-system-server-app/src/main/resources/application.yaml
+++ b/zt-module-system/zt-module-system-server-app/src/main/resources/application.yaml
@@ -229,7 +229,6 @@ zt:
- system_seq_dtl
- system_seq_rcd
- system_sync_log
- - system_iwork_seal_log
ignore-caches:
- user_role_ids
- permission_menu_ids
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java
index 4918aa6d..82232f9b 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/IWorkIntegrationController.java
@@ -3,10 +3,11 @@ package com.zt.plat.module.system.controller.admin.integration.iwork;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.tenant.core.aop.TenantIgnore;
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
-import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
import com.zt.plat.module.system.service.integration.iwork.IWorkOrgRestService;
import com.zt.plat.module.system.service.integration.iwork.IWorkSyncService;
+import com.zt.plat.module.system.service.integration.iwork.IWorkWorkflowLogService;
+import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
import lombok.extern.slf4j.Slf4j;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -37,7 +38,7 @@ public class IWorkIntegrationController {
private final IWorkIntegrationService integrationService;
private final IWorkOrgRestService orgRestService;
private final IWorkSyncService syncService;
- private final IWorkCallbackLogService callbackLogService;
+ private final IWorkWorkflowLogService workflowLogService;
@PostMapping("/auth/register")
@Operation(summary = "注册 iWork 凭证,获取服务端公钥与 secret")
@@ -99,17 +100,17 @@ public class IWorkIntegrationController {
@PreAuthorize("@ss.hasPermission('system:iwork:log:query')")
@PostMapping("/log/page")
- @Operation(summary = "iWork 回调日志分页查询")
+ @Operation(summary = "iWork 流程日志分页查询")
public CommonResult> pageLogs(@Valid @RequestBody IWorkCallbackLogPageReqVO reqVO) {
- com.zt.plat.framework.common.pojo.PageResult page = callbackLogService.page(reqVO);
+ com.zt.plat.framework.common.pojo.PageResult page = workflowLogService.page(reqVO);
java.util.List mapped = new java.util.ArrayList<>();
- for (com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO log : page.getList()) {
+ for (IWorkWorkflowLogDO log : page.getList()) {
IWorkCallbackLogRespVO vo = new IWorkCallbackLogRespVO();
vo.setId(log.getId());
vo.setRequestId(log.getRequestId());
vo.setBusinessCode(log.getBusinessCode());
vo.setBizCallbackKey(log.getBizCallbackKey());
- vo.setStatus(log.getStatus());
+ vo.setStatus(log.getCallbackStatus());
vo.setRetryCount(log.getRetryCount());
vo.setMaxRetry(log.getMaxRetry());
vo.setLastErrorMessage(log.getLastErrorMessage());
@@ -126,7 +127,7 @@ public class IWorkIntegrationController {
@PostMapping("/log/retry")
@Operation(summary = "iWork 回调手工重试")
public CommonResult retry(@Valid @RequestBody IWorkWorkflowVoidReqVO reqVO) {
- callbackLogService.resetAndDispatch(reqVO.getRequestId());
+ workflowLogService.resetAndDispatch(reqVO.getRequestId());
return success(true);
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkWorkflowCallbackReqVO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkWorkflowCallbackReqVO.java
deleted file mode 100644
index 66cf7d87..00000000
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/integration/iwork/vo/IWorkWorkflowCallbackReqVO.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-import lombok.Data;
-
-@Schema(description = "iWork 流程回调请求")
-@Data
-public class IWorkWorkflowCallbackReqVO {
-
- @Schema(description = "iWork requestId,唯一标识", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotBlank(message = "requestId 不能为空")
- private String requestId;
-
- @Schema(description = "业务单号 (ywxtdjbh)")
- private String businessCode;
-
- @Schema(description = "业务回调标识 bizCallbackKey")
- private String bizCallbackKey;
-
- @Schema(description = "回调状态/结果码")
- private String status;
-
- @Schema(description = "原始回调文本(可截断存储)")
- private String rawBody;
-}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkSealLogDO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkSealLogDO.java
deleted file mode 100644
index 09e1d325..00000000
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkSealLogDO.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.zt.plat.module.system.dal.dataobject.iwork;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.zt.plat.framework.mybatis.core.dataobject.BaseDO;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-import java.time.LocalDateTime;
-
-/**
- * iWork 用印流程回调日志。
- */
-@TableName("system_iwork_seal_log")
-@KeySequence("system_iwork_seal_log_seq")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class IWorkSealLogDO extends BaseDO {
-
- @TableId(type = IdType.ASSIGN_ID)
- private Long id;
-
- /**
- * iWork 返回的请求编号,唯一业务标识。
- */
- private String requestId;
-
- /**
- * 业务单号(ywxtdjbh)。
- */
- private String businessCode;
-
- /**
- * 业务回调标识。
- */
- private String bizCallbackKey;
-
- /**
- * 状态枚举,参考 IWorkCallbackStatusEnum。
- */
- private Integer status;
-
- /**
- * 已执行的自动/手工重试次数。
- */
- private Integer retryCount;
-
- /**
- * 最大重试次数(快照)。
- */
- private Integer maxRetry;
-
- /**
- * 最后一次错误信息。
- */
- private String lastErrorMessage;
-
- /**
- * 回调原始负载(截断)。
- */
- private String rawCallback;
-
- /**
- * 最近一次回调时间。
- */
- private LocalDateTime lastCallbackTime;
-
-}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkWorkflowLogDO.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkWorkflowLogDO.java
index 516d2bfa..0fe6e6a6 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkWorkflowLogDO.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/dataobject/iwork/IWorkWorkflowLogDO.java
@@ -9,9 +9,11 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
+import java.time.LocalDateTime;
+
/**
- * iWork 流程创建日志。
- * 用于记录流程创建时的关键信息,供回调时查询使用。
+ * iWork 流程日志。
+ * 合并了流程创建日志和回调日志,记录完整的流程生命周期。
*/
@TableName("system_iwork_workflow_log")
@KeySequence("system_iwork_workflow_log_seq")
@@ -24,27 +26,27 @@ public class IWorkWorkflowLogDO extends BaseDO {
private Long id;
/**
- * iWork 返回的请求编号,唯一业务标识。
+ * iWork 请求编号,唯一业务标识
*/
private String requestId;
/**
- * 流程模板 ID。
+ * 流程模板 ID
*/
private Long workflowId;
/**
- * 业务编码(用于关联业务数据)。
+ * 业务编码(用于关联业务数据)
*/
private String businessCode;
/**
- * 业务回调标识(用于 MQ 消息路由)。
+ * 业务回调标识(用于 MQ 消息路由)
*/
private String bizCallbackKey;
/**
- * 创建请求的原始参数(JSON 格式,截断存储)。
+ * 创建请求的原始参数(JSON 格式,截断存储)
*/
private String rawRequest;
@@ -52,4 +54,36 @@ public class IWorkWorkflowLogDO extends BaseDO {
* 流程状态:CREATED-已创建, CALLBACK_RECEIVED-已收到回调, COMPLETED-已完成
*/
private String status;
+
+ // ========== 回调相关字段 ==========
+
+ /**
+ * 回调处理状态:1-待处理, 2-处理中, 3-成功, 4-重试中, 5-失败
+ */
+ private Integer callbackStatus;
+
+ /**
+ * 已重试次数
+ */
+ private Integer retryCount;
+
+ /**
+ * 最大重试次数
+ */
+ private Integer maxRetry;
+
+ /**
+ * 最后错误信息
+ */
+ private String lastErrorMessage;
+
+ /**
+ * 回调原文(JSON 格式,截断存储)
+ */
+ private String rawCallback;
+
+ /**
+ * 最近回调时间
+ */
+ private LocalDateTime lastCallbackTime;
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkSealLogMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkSealLogMapper.java
deleted file mode 100644
index 55ac9ee6..00000000
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkSealLogMapper.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.zt.plat.module.system.dal.mysql.iwork;
-
-import com.zt.plat.framework.common.pojo.PageResult;
-import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
-import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
-import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
-import org.apache.ibatis.annotations.Mapper;
-
-@Mapper
-public interface IWorkSealLogMapper extends BaseMapperX {
-
- default IWorkSealLogDO selectByRequestId(String requestId) {
- return selectOne(IWorkSealLogDO::getRequestId, requestId);
- }
-
- default PageResult selectPage(IWorkCallbackLogPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .eqIfPresent(IWorkSealLogDO::getRequestId, reqVO.getRequestId())
- .eqIfPresent(IWorkSealLogDO::getBusinessCode, reqVO.getBusinessCode())
- .eqIfPresent(IWorkSealLogDO::getBizCallbackKey, reqVO.getBizCallbackKey())
- .eqIfPresent(IWorkSealLogDO::getStatus, reqVO.getStatus())
- .betweenIfPresent(IWorkSealLogDO::getCreateTime, reqVO.getCreateTime())
- .betweenIfPresent(IWorkSealLogDO::getLastCallbackTime, reqVO.getLastCallbackTime())
- .orderByDesc(IWorkSealLogDO::getId));
- }
-}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkWorkflowLogMapper.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkWorkflowLogMapper.java
index c8158377..750087b9 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkWorkflowLogMapper.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/dal/mysql/iwork/IWorkWorkflowLogMapper.java
@@ -1,6 +1,9 @@
package com.zt.plat.module.system.dal.mysql.iwork;
+import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
+import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
import org.apache.ibatis.annotations.Mapper;
@@ -10,4 +13,15 @@ public interface IWorkWorkflowLogMapper extends BaseMapperX
default IWorkWorkflowLogDO selectByRequestId(String requestId) {
return selectOne(IWorkWorkflowLogDO::getRequestId, requestId);
}
+
+ default PageResult selectPage(IWorkCallbackLogPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(IWorkWorkflowLogDO::getRequestId, reqVO.getRequestId())
+ .eqIfPresent(IWorkWorkflowLogDO::getBusinessCode, reqVO.getBusinessCode())
+ .eqIfPresent(IWorkWorkflowLogDO::getBizCallbackKey, reqVO.getBizCallbackKey())
+ .eqIfPresent(IWorkWorkflowLogDO::getCallbackStatus, reqVO.getStatus())
+ .betweenIfPresent(IWorkWorkflowLogDO::getCreateTime, reqVO.getCreateTime())
+ .betweenIfPresent(IWorkWorkflowLogDO::getLastCallbackTime, reqVO.getLastCallbackTime())
+ .orderByDesc(IWorkWorkflowLogDO::getId));
+ }
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/datapermission/config/DataPermissionConfiguration.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/datapermission/config/DataPermissionConfiguration.java
index def9db9b..78725a5e 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/datapermission/config/DataPermissionConfiguration.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/framework/datapermission/config/DataPermissionConfiguration.java
@@ -24,6 +24,7 @@ public class DataPermissionConfiguration {
rule.addDeptColumn(DeptDO.class, "id");
// user
rule.addUserColumn(AdminUserDO.class, "id");
+ rule.addUserColumn("system_users_work_code", "workcode");
};
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/mq/iwork/IWorkBizCallbackListener.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/mq/iwork/IWorkBizCallbackListener.java
index c92bfe45..0713f4b9 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/mq/iwork/IWorkBizCallbackListener.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/mq/iwork/IWorkBizCallbackListener.java
@@ -1,9 +1,7 @@
package com.zt.plat.module.system.mq.iwork;
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
-import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
-import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
-import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackResultMessage;
+import com.zt.plat.module.system.service.integration.iwork.IWorkWorkflowLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
@@ -24,7 +22,7 @@ import java.util.concurrent.TimeUnit;
@RocketMQMessageListener(topic = IWorkBizCallbackResultMessage.TOPIC, consumerGroup = IWorkBizCallbackResultMessage.TOPIC + "_CONSUMER")
public class IWorkBizCallbackListener implements RocketMQListener, InitializingBean {
- private final IWorkCallbackLogService logService;
+ private final IWorkWorkflowLogService workflowLogService;
private final IWorkProperties properties;
private final IWorkBizCallbackProducer producer;
private ScheduledExecutorService scheduler;
@@ -38,16 +36,16 @@ public class IWorkBizCallbackListener implements RocketMQListener 0 ? message.getMaxAttempts() : properties.getCallback().getRetry().getMaxAttempts();
if (attempt < maxAttempts) {
- logService.markFailure(message.getRequestId(), message.getErrorMessage(), true, maxAttempts);
+ workflowLogService.markCallbackFailure(message.getRequestId(), message.getErrorMessage(), true, maxAttempts);
IWorkBizCallbackMessage next = IWorkBizCallbackMessage.builder()
.requestId(message.getRequestId())
@@ -60,7 +58,7 @@ public class IWorkBizCallbackListener implements RocketMQListener producer.send(next), delay, TimeUnit.SECONDS);
} else {
- logService.markFailure(message.getRequestId(), message.getErrorMessage(), false, maxAttempts);
+ workflowLogService.markCallbackFailure(message.getRequestId(), message.getErrorMessage(), false, maxAttempts);
}
}
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkCallbackLogService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkCallbackLogService.java
deleted file mode 100644
index c87ba5de..00000000
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkCallbackLogService.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.zt.plat.module.system.service.integration.iwork;
-
-import com.zt.plat.framework.common.pojo.PageResult;
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
-import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
-
-public interface IWorkCallbackLogService {
-
- IWorkSealLogDO upsertOnCallback(IWorkWorkflowCallbackReqVO reqVO, int maxRetry, String rawBody);
-
- void markSuccess(String requestId);
-
- void markFailure(String requestId, String error, boolean retrying, int maxRetry);
-
- void incrementRetry(String requestId);
-
- PageResult page(IWorkCallbackLogPageReqVO reqVO);
-
- void resetAndDispatch(String requestId);
-}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkWorkflowLogService.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkWorkflowLogService.java
index fd577337..bbce47f2 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkWorkflowLogService.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/IWorkWorkflowLogService.java
@@ -1,9 +1,11 @@
package com.zt.plat.module.system.service.integration.iwork;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
/**
- * iWork 流程创建日志 Service
+ * iWork 流程日志 Service
*/
public interface IWorkWorkflowLogService {
@@ -13,7 +15,7 @@ public interface IWorkWorkflowLogService {
void saveWorkflowLog(IWorkWorkflowLogDO logDO);
/**
- * 根据 requestId 查询流程创建日志
+ * 根据 requestId 查询流程日志
*/
IWorkWorkflowLogDO getByRequestId(String requestId);
@@ -21,4 +23,36 @@ public interface IWorkWorkflowLogService {
* 更新流程状态
*/
void updateStatus(String requestId, String status);
+
+ // ========== 回调相关方法 ==========
+
+ /**
+ * 更新回调信息(收到回调时调用)
+ */
+ void updateCallback(String requestId, String rawCallback, int maxRetry);
+
+ /**
+ * 标记回调成功
+ */
+ void markCallbackSuccess(String requestId);
+
+ /**
+ * 标记回调失败
+ */
+ void markCallbackFailure(String requestId, String errorMessage, boolean retrying, int maxRetry);
+
+ /**
+ * 增加重试次数
+ */
+ void incrementRetry(String requestId);
+
+ /**
+ * 分页查询流程日志
+ */
+ PageResult page(IWorkCallbackLogPageReqVO reqVO);
+
+ /**
+ * 重置并重新派发回调
+ */
+ void resetAndDispatch(String requestId);
}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImpl.java
deleted file mode 100644
index d46dbbe6..00000000
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImpl.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.zt.plat.module.system.service.integration.iwork.impl;
-
-import com.zt.plat.framework.common.pojo.PageResult;
-import com.zt.plat.framework.common.util.json.JsonUtils;
-import com.zt.plat.framework.common.util.object.ObjectUtils;
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
-import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
-import com.zt.plat.module.system.dal.mysql.iwork.IWorkSealLogMapper;
-import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
-import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
-import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
-import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
-import com.zt.plat.module.system.service.integration.iwork.enums.IWorkCallbackStatusEnum;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.StringUtils;
-
-import java.time.LocalDateTime;
-import java.util.Optional;
-
-@Service
-@RequiredArgsConstructor
-public class IWorkCallbackLogServiceImpl implements IWorkCallbackLogService {
-
- private static final int RAW_MAX = 2000;
-
- private final IWorkSealLogMapper logMapper;
- private final IWorkBizCallbackProducer producer;
- private final IWorkProperties properties;
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public IWorkSealLogDO upsertOnCallback(IWorkWorkflowCallbackReqVO reqVO, int maxRetry, String rawBody) {
- IWorkSealLogDO existing = logMapper.selectByRequestId(reqVO.getRequestId());
- IWorkSealLogDO log = Optional.ofNullable(existing).orElseGet(IWorkSealLogDO::new);
- log.setRequestId(reqVO.getRequestId());
- log.setBusinessCode(reqVO.getBusinessCode());
- log.setBizCallbackKey(reqVO.getBizCallbackKey());
- log.setStatus(IWorkCallbackStatusEnum.CALLBACK_PENDING.getStatus());
- log.setRetryCount(ObjectUtils.defaultIfNull(log.getRetryCount(), 0));
- log.setMaxRetry(maxRetry);
- log.setRawCallback(truncate(rawBody));
- log.setLastCallbackTime(LocalDateTime.now());
- if (log.getId() == null) {
- logMapper.insert(log);
- } else {
- logMapper.updateById(log);
- }
- return log;
- }
-
- @Override
- public void markSuccess(String requestId) {
- IWorkSealLogDO log = new IWorkSealLogDO();
- log.setRequestId(requestId);
- log.setStatus(IWorkCallbackStatusEnum.CALLBACK_SUCCESS.getStatus());
- log.setLastErrorMessage(null);
- log.setLastCallbackTime(LocalDateTime.now());
- logMapper.update(log, new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper()
- .eq(IWorkSealLogDO::getRequestId, requestId));
- }
-
- @Override
- public void markFailure(String requestId, String error, boolean retrying, int maxRetry) {
- IWorkSealLogDO log = new IWorkSealLogDO();
- log.setRequestId(requestId);
- log.setStatus(retrying ? IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus() : IWorkCallbackStatusEnum.CALLBACK_FAILED.getStatus());
- log.setLastErrorMessage(error);
- log.setLastCallbackTime(LocalDateTime.now());
- log.setMaxRetry(maxRetry);
- logMapper.update(log, new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper()
- .eq(IWorkSealLogDO::getRequestId, requestId));
- }
-
- @Override
- public void incrementRetry(String requestId) {
- IWorkSealLogDO db = logMapper.selectByRequestId(requestId);
- if (db == null) {
- return;
- }
- IWorkSealLogDO log = new IWorkSealLogDO();
- log.setId(db.getId());
- log.setRetryCount(ObjectUtils.defaultIfNull(db.getRetryCount(), 0) + 1);
- log.setLastCallbackTime(LocalDateTime.now());
- logMapper.updateById(log);
- }
-
- @Override
- public PageResult page(IWorkCallbackLogPageReqVO reqVO) {
- return logMapper.selectPage(reqVO);
- }
-
- @Override
- public void resetAndDispatch(String requestId) {
- IWorkSealLogDO db = logMapper.selectByRequestId(requestId);
- if (db == null) {
- return;
- }
- IWorkSealLogDO log = new IWorkSealLogDO();
- log.setId(db.getId());
- log.setRetryCount(0);
- log.setStatus(IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus());
- log.setLastCallbackTime(LocalDateTime.now());
- logMapper.updateById(log);
-
- int maxAttempts = properties.getCallback().getRetry().getMaxAttempts();
- Object payload;
- try {
- payload = JsonUtils.parseObject(db.getRawCallback(), Object.class);
- } catch (Exception ex) {
- payload = db.getRawCallback();
- }
- producer.send(IWorkBizCallbackMessage.builder()
- .requestId(db.getRequestId())
- .bizCallbackKey(db.getBizCallbackKey())
- .payload(payload)
- .attempt(0)
- .maxAttempts(maxAttempts)
- .build());
- }
-
- private String truncate(String raw) {
- if (!StringUtils.hasText(raw)) {
- return raw;
- }
- return raw.length() > RAW_MAX ? raw.substring(0, RAW_MAX) : raw;
- }
-}
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java
index d184dd7e..67a191c2 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkIntegrationServiceImpl.java
@@ -20,7 +20,6 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
-import com.zt.plat.module.system.service.integration.iwork.IWorkCallbackLogService;
import com.zt.plat.module.system.service.integration.iwork.IWorkIntegrationService;
import com.zt.plat.module.system.service.integration.iwork.IWorkWorkflowLogService;
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
@@ -65,7 +64,6 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
private final FileApi fileApi;
private final BusinessFileApi businessFileApi;
- private final IWorkCallbackLogService callbackLogService;
private final IWorkWorkflowLogService workflowLogService;
private final IWorkBizCallbackProducer bizCallbackProducer;
@@ -250,23 +248,17 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
TenantUtils.execute(tenantId, () -> attachmentIdRef.set(saveCallbackAttachment(fileUrl, reqVO.getFileName(), referenceBusinessFile)));
Long attachmentId = attachmentIdRef.get();
- // 3. 更新回调日志
+ // 3. 更新流程日志(回调信息 + 状态)
int maxRetry = properties.getCallback().getRetry().getMaxAttempts();
String rawBody = buildCallbackRawBody(reqVO);
- IWorkWorkflowCallbackReqVO logReqVO = buildCallbackLogReqVO(reqVO);
- if (StringUtils.hasText(bizCallbackKey)) {
- logReqVO.setBizCallbackKey(bizCallbackKey);
- }
- callbackLogService.upsertOnCallback(logReqVO, maxRetry, rawBody);
-
- // 4. 更新流程创建日志状态
if (workflowLog != null) {
+ workflowLogService.updateCallback(reqVO.getRequestId(), rawBody, maxRetry);
String status = StringUtils.hasText(reqVO.getStatus()) ? reqVO.getStatus() : "CALLBACK_RECEIVED";
workflowLogService.updateStatus(reqVO.getRequestId(), status);
- log.info("[handleFileCallback] 已更新流程状态: requestId={}, status={}", reqVO.getRequestId(), status);
+ log.info("[handleFileCallback] 已更新流程日志: requestId={}, status={}", reqVO.getRequestId(), status);
}
- // 5. 发送 MQ 通知业务系统(仅当 bizCallbackKey 存在时发送)
+ // 4. 发送 MQ 通知业务系统(仅当 bizCallbackKey 存在时发送)
if (StringUtils.hasText(bizCallbackKey)) {
IWorkBizCallbackMessage message = IWorkBizCallbackMessage.builder()
.requestId(reqVO.getRequestId())
@@ -286,16 +278,6 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
return attachmentId;
}
- private IWorkWorkflowCallbackReqVO buildCallbackLogReqVO(IWorkFileCallbackReqVO reqVO) {
- IWorkWorkflowCallbackReqVO logReqVO = new IWorkWorkflowCallbackReqVO();
- logReqVO.setRequestId(reqVO.getRequestId());
- logReqVO.setBusinessCode(reqVO.getBusinessCode());
- logReqVO.setBizCallbackKey(reqVO.getBizCallbackKey());
- logReqVO.setStatus(reqVO.getStatus());
- logReqVO.setRawBody(reqVO.getRawBody());
- return logReqVO;
- }
-
private String buildCallbackRawBody(IWorkFileCallbackReqVO reqVO) {
if (StringUtils.hasText(reqVO.getRawBody())) {
return reqVO.getRawBody();
diff --git a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkWorkflowLogServiceImpl.java b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkWorkflowLogServiceImpl.java
index 6f3bd117..6587669c 100644
--- a/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkWorkflowLogServiceImpl.java
+++ b/zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkWorkflowLogServiceImpl.java
@@ -1,16 +1,27 @@
package com.zt.plat.module.system.service.integration.iwork.impl;
+import com.zt.plat.framework.common.pojo.PageResult;
+import com.zt.plat.framework.common.util.json.JsonUtils;
+import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkCallbackLogPageReqVO;
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
import com.zt.plat.module.system.dal.mysql.iwork.IWorkWorkflowLogMapper;
+import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
+import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackMessage;
+import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
import com.zt.plat.module.system.service.integration.iwork.IWorkWorkflowLogService;
+import com.zt.plat.module.system.service.integration.iwork.enums.IWorkCallbackStatusEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
+import java.time.LocalDateTime;
+
@Service
@RequiredArgsConstructor
public class IWorkWorkflowLogServiceImpl implements IWorkWorkflowLogService {
private final IWorkWorkflowLogMapper workflowLogMapper;
+ private final IWorkProperties properties;
+ private final IWorkBizCallbackProducer bizCallbackProducer;
@Override
public void saveWorkflowLog(IWorkWorkflowLogDO logDO) {
@@ -32,4 +43,105 @@ public class IWorkWorkflowLogServiceImpl implements IWorkWorkflowLogService {
workflowLogMapper.updateById(update);
}
}
+
+ // ========== 回调相关方法 ==========
+
+ @Override
+ public void updateCallback(String requestId, String rawCallback, int maxRetry) {
+ IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
+ if (existing != null) {
+ IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
+ update.setId(existing.getId());
+ update.setCallbackStatus(IWorkCallbackStatusEnum.CALLBACK_PENDING.getStatus());
+ update.setRawCallback(truncate(rawCallback, 2000));
+ update.setMaxRetry(maxRetry);
+ update.setRetryCount(existing.getRetryCount() != null ? existing.getRetryCount() : 0);
+ update.setLastCallbackTime(LocalDateTime.now());
+ workflowLogMapper.updateById(update);
+ }
+ }
+
+ @Override
+ public void markCallbackSuccess(String requestId) {
+ IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
+ if (existing != null) {
+ IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
+ update.setId(existing.getId());
+ update.setCallbackStatus(IWorkCallbackStatusEnum.CALLBACK_SUCCESS.getStatus());
+ update.setLastErrorMessage(null);
+ update.setLastCallbackTime(LocalDateTime.now());
+ workflowLogMapper.updateById(update);
+ }
+ }
+
+ @Override
+ public void markCallbackFailure(String requestId, String errorMessage, boolean retrying, int maxRetry) {
+ IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
+ if (existing != null) {
+ IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
+ update.setId(existing.getId());
+ update.setCallbackStatus(retrying
+ ? IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus()
+ : IWorkCallbackStatusEnum.CALLBACK_FAILED.getStatus());
+ update.setLastErrorMessage(errorMessage);
+ update.setMaxRetry(maxRetry);
+ update.setLastCallbackTime(LocalDateTime.now());
+ workflowLogMapper.updateById(update);
+ }
+ }
+
+ @Override
+ public void incrementRetry(String requestId) {
+ IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
+ if (existing != null) {
+ IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
+ update.setId(existing.getId());
+ update.setRetryCount((existing.getRetryCount() != null ? existing.getRetryCount() : 0) + 1);
+ update.setLastCallbackTime(LocalDateTime.now());
+ workflowLogMapper.updateById(update);
+ }
+ }
+
+ private String truncate(String raw, int maxLen) {
+ if (raw == null || raw.length() <= maxLen) {
+ return raw;
+ }
+ return raw.substring(0, maxLen);
+ }
+
+ @Override
+ public PageResult page(IWorkCallbackLogPageReqVO reqVO) {
+ return workflowLogMapper.selectPage(reqVO);
+ }
+
+ @Override
+ public void resetAndDispatch(String requestId) {
+ IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
+ if (existing == null) {
+ return;
+ }
+ // 重置状态
+ IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
+ update.setId(existing.getId());
+ update.setRetryCount(0);
+ update.setCallbackStatus(IWorkCallbackStatusEnum.CALLBACK_RETRYING.getStatus());
+ update.setLastCallbackTime(LocalDateTime.now());
+ workflowLogMapper.updateById(update);
+
+ // 重新派发
+ int maxAttempts = properties.getCallback().getRetry().getMaxAttempts();
+ Object payload;
+ try {
+ payload = JsonUtils.parseObject(existing.getRawCallback(), Object.class);
+ } catch (Exception ex) {
+ payload = existing.getRawCallback();
+ }
+ bizCallbackProducer.send(IWorkBizCallbackMessage.builder()
+ .requestId(existing.getRequestId())
+ .bizCallbackKey(existing.getBizCallbackKey())
+ .payload(payload)
+ .attempt(0)
+ .maxAttempts(maxAttempts)
+ .build());
+ }
}
diff --git a/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImplTest.java b/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImplTest.java
deleted file mode 100644
index f0287595..00000000
--- a/zt-module-system/zt-module-system-server/src/test/java/com/zt/plat/module/system/service/integration/iwork/impl/IWorkCallbackLogServiceImplTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.zt.plat.module.system.service.integration.iwork.impl;
-
-import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkWorkflowCallbackReqVO;
-import com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO;
-import com.zt.plat.module.system.dal.mysql.iwork.IWorkSealLogMapper;
-import com.zt.plat.module.system.framework.integration.iwork.config.IWorkProperties;
-import com.zt.plat.module.system.mq.iwork.IWorkBizCallbackProducer;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
-
-class IWorkCallbackLogServiceImplTest {
-
- private IWorkSealLogMapper mapper;
- private IWorkBizCallbackProducer producer;
- private IWorkProperties properties;
- private IWorkCallbackLogServiceImpl service;
-
- @BeforeEach
- void setup() {
- mapper = mock(IWorkSealLogMapper.class);
- producer = mock(IWorkBizCallbackProducer.class);
- properties = new IWorkProperties();
- service = new IWorkCallbackLogServiceImpl(mapper, producer, properties);
- }
-
- @Test
- void upsertOnCallback_shouldTruncateRaw() {
- String longRaw = "x".repeat(2100);
- IWorkWorkflowCallbackReqVO req = new IWorkWorkflowCallbackReqVO();
- req.setRequestId("REQ-1");
- req.setBizCallbackKey("key");
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(IWorkSealLogDO.class);
- when(mapper.selectByRequestId("REQ-1")).thenReturn(null);
-
- service.upsertOnCallback(req, 3, longRaw);
-
- verify(mapper).insert(captor.capture());
- IWorkSealLogDO saved = captor.getValue();
- assertThat(saved.getRawCallback()).hasSize(2000);
- assertThat(saved.getMaxRetry()).isEqualTo(3);
- }
-
- @Test
- void incrementRetry_shouldIncreaseCount() {
- IWorkSealLogDO existing = new IWorkSealLogDO();
- existing.setId(1L);
- existing.setRequestId("REQ-2");
- existing.setRetryCount(1);
- when(mapper.selectByRequestId("REQ-2")).thenReturn(existing);
-
- service.incrementRetry("REQ-2");
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(IWorkSealLogDO.class);
- verify(mapper).updateById(captor.capture());
- assertThat(captor.getValue().getRetryCount()).isEqualTo(2);
- }
-}
diff --git a/zt-server/src/main/resources/application-dev.yaml b/zt-server/src/main/resources/application-dev.yaml
index 2a3d7761..c62b86e8 100644
--- a/zt-server/src/main/resources/application-dev.yaml
+++ b/zt-server/src/main/resources/application-dev.yaml
@@ -47,14 +47,14 @@ spring:
primary: master
datasource:
master:
- url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO
+ url: jdbc:dm://172.17.11.98:20870?schema=JYGK_TEST
username: SYSDBA
- password: pgbsci6ddJ6Sqj@e
+ password: P@ssword25
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
lazy: true # 开启懒加载,保证启动速度
- url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO
+ url: jdbc:dm://172.17.11.98:20870?schema=JYGK_TEST
username: SYSDBA
- password: pgbsci6ddJ6Sqj@e
+ password: P@ssword25
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data: