Merge remote-tracking branch 'ztcloud/test' into dev
This commit is contained in:
38
sql/dm/iwork_workflow_log.sql
Normal file
38
sql/dm/iwork_workflow_log.sql
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
-- iWork 流程创建日志表(达梦数据库)
|
||||||
|
CREATE TABLE system_iwork_workflow_log (
|
||||||
|
id BIGINT NOT NULL,
|
||||||
|
request_id VARCHAR(64) NOT NULL,
|
||||||
|
workflow_id BIGINT,
|
||||||
|
business_code VARCHAR(128),
|
||||||
|
biz_callback_key VARCHAR(255),
|
||||||
|
raw_request VARCHAR(2000),
|
||||||
|
status VARCHAR(32),
|
||||||
|
creator VARCHAR(64) DEFAULT '',
|
||||||
|
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updater VARCHAR(64) DEFAULT '',
|
||||||
|
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||||
|
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 添加注释
|
||||||
|
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.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.status 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 '更新者';
|
||||||
|
COMMENT ON COLUMN system_iwork_workflow_log.update_time IS '更新时间';
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
@@ -85,6 +85,7 @@ public class FileController {
|
|||||||
String baseUrl = buildPublicBaseUrl(request); // 见下方函数
|
String baseUrl = buildPublicBaseUrl(request); // 见下方函数
|
||||||
String decryptUrl = baseUrl + "/admin-api/infra/file/preview-decrypt?fileId=" + fileId + "&token=" + token;
|
String decryptUrl = baseUrl + "/admin-api/infra/file/preview-decrypt?fileId=" + fileId + "&token=" + token;
|
||||||
fileRespVO.setUrl(decryptUrl);
|
fileRespVO.setUrl(decryptUrl);
|
||||||
|
log.info("decryptUrl={}, urlFieldAfterSet={}", decryptUrl, fileRespVO.getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
return success(fileRespVO);
|
return success(fileRespVO);
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public class FileRespVO {
|
|||||||
private String previewUrl;
|
private String previewUrl;
|
||||||
|
|
||||||
public String getPreviewUrl() {
|
public String getPreviewUrl() {
|
||||||
|
log.info("GET /infra/file/get this.downloadable={}, this.url={}, isEncrypted={}",
|
||||||
|
this.downloadable, this.url, this.isEncrypted);
|
||||||
// 不可下载不返回 previewUrl
|
// 不可下载不返回 previewUrl
|
||||||
if (Boolean.FALSE.equals(this.downloadable) ) {
|
if (Boolean.FALSE.equals(this.downloadable) ) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package com.zt.plat.module.system.mq.iwork;
|
package com.zt.plat.module.system.mq.iwork;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class IWorkBizCallbackMessage {
|
public class IWorkBizCallbackMessage {
|
||||||
|
|
||||||
/** 统一回调主题 */
|
/** 统一回调主题 */
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ zt:
|
|||||||
- system_seq_dtl
|
- system_seq_dtl
|
||||||
- system_seq_rcd
|
- system_seq_rcd
|
||||||
- system_sync_log
|
- system_sync_log
|
||||||
|
- system_iwork_seal_log
|
||||||
ignore-caches:
|
ignore-caches:
|
||||||
- user_role_ids
|
- user_role_ids
|
||||||
- permission_menu_ids
|
- permission_menu_ids
|
||||||
|
|||||||
@@ -77,10 +77,16 @@ public class IWorkIntegrationController {
|
|||||||
return success(integrationService.createWorkflow(reqVO));
|
return success(integrationService.createWorkflow(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/workflow/create-generic")
|
||||||
|
@Operation(summary = "通用流程创建:支持透传任意业务参数和调用不同的 workflowId")
|
||||||
|
public CommonResult<IWorkOperationRespVO> createGenericWorkflow(@Valid @RequestBody IWorkGenericWorkflowCreateReqVO reqVO) {
|
||||||
|
return success(integrationService.createGenericWorkflow(reqVO));
|
||||||
|
}
|
||||||
|
|
||||||
@PermitAll
|
@PermitAll
|
||||||
@TenantIgnore
|
@TenantIgnore
|
||||||
@PostMapping("/callback/file")
|
@PostMapping("/callback/file")
|
||||||
@Operation(summary = "iWork 文件回调:根据文件 URL 保存为附件并创建业务附件关联")
|
@Operation(summary = "iWork 文件回调:根据文件 URL 保存为附件并创建业务附件关联,记录日志并发送 MQ 通知业务系统")
|
||||||
public CommonResult<Long> callbackFile(@Valid @RequestBody IWorkFileCallbackReqVO reqVO) {
|
public CommonResult<Long> callbackFile(@Valid @RequestBody IWorkFileCallbackReqVO reqVO) {
|
||||||
return success(integrationService.handleFileCallback(reqVO));
|
return success(integrationService.handleFileCallback(reqVO));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.zt.plat.module.system.controller.admin.integration.iwork.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 iWork 流程创建请求 VO
|
||||||
|
* <p>
|
||||||
|
* 支持透传任意业务参数,直接将整个 payload 转发给 iWork
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(description = "通用 iWork 流程创建请求")
|
||||||
|
public class IWorkGenericWorkflowCreateReqVO extends IWorkBaseReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "流程模板 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "54")
|
||||||
|
@NotNull(message = "流程模板 ID 不能为空")
|
||||||
|
private Long workflowId;
|
||||||
|
|
||||||
|
@Schema(description = "透传给 iWork 的业务参数,直接作为请求体发送", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
|
example = "{\"requestName\":\"用印-DJ-2025-0001\",\"mainData\":[{\"fieldName\":\"jbr\",\"fieldValue\":\"1001\"}]}")
|
||||||
|
@NotNull(message = "业务参数不能为空")
|
||||||
|
private Map<String, Object> payload;
|
||||||
|
|
||||||
|
@Schema(description = "业务编码,用于回调时关联业务数据", example = "DJ-2025-0001")
|
||||||
|
private String ywxtdjbh;
|
||||||
|
|
||||||
|
@Schema(description = "业务回调标识,用于 MQ 消息路由到对应业务消费者", example = "seal-callback")
|
||||||
|
private String bizCallbackKey;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 流程创建日志。
|
||||||
|
* 用于记录流程创建时的关键信息,供回调时查询使用。
|
||||||
|
*/
|
||||||
|
@TableName("system_iwork_workflow_log")
|
||||||
|
@KeySequence("system_iwork_workflow_log_seq")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
public class IWorkWorkflowLogDO extends BaseDO {
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 返回的请求编号,唯一业务标识。
|
||||||
|
*/
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程模板 ID。
|
||||||
|
*/
|
||||||
|
private Long workflowId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务编码(用于关联业务数据)。
|
||||||
|
*/
|
||||||
|
private String businessCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务回调标识(用于 MQ 消息路由)。
|
||||||
|
*/
|
||||||
|
private String bizCallbackKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建请求的原始参数(JSON 格式,截断存储)。
|
||||||
|
*/
|
||||||
|
private String rawRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程状态:CREATED-已创建, CALLBACK_RECEIVED-已收到回调, COMPLETED-已完成
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.zt.plat.module.system.dal.mysql.iwork;
|
||||||
|
|
||||||
|
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface IWorkWorkflowLogMapper extends BaseMapperX<IWorkWorkflowLogDO> {
|
||||||
|
|
||||||
|
default IWorkWorkflowLogDO selectByRequestId(String requestId) {
|
||||||
|
return selectOne(IWorkWorkflowLogDO::getRequestId, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.zt.plat.module.system.mq.iwork;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
|
||||||
|
import org.apache.rocketmq.spring.core.RocketMQListener;
|
||||||
|
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 业务回调消息测试消费者
|
||||||
|
* <p>
|
||||||
|
* 用于测试 IWorkBizCallbackMessage 消息的消费,
|
||||||
|
* 消费成功后会发送 IWorkBizCallbackResultMessage 通知 system 模块。
|
||||||
|
* <p>
|
||||||
|
* 注意:这是一个测试消费者,生产环境应由具体业务模块实现自己的消费者。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RocketMQMessageListener(
|
||||||
|
topic = IWorkBizCallbackMessage.TOPIC,
|
||||||
|
consumerGroup = IWorkBizCallbackMessage.TOPIC + "_ceshi_bz_01",
|
||||||
|
selectorExpression = "ceshi_bz_01" // 只订阅 tag=ceshi_bz_01 的消息
|
||||||
|
)
|
||||||
|
public class IWorkBizCallbackTestConsumer implements RocketMQListener<IWorkBizCallbackMessage> {
|
||||||
|
|
||||||
|
private final RocketMQTemplate rocketMQTemplate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(IWorkBizCallbackMessage message) {
|
||||||
|
log.info("[IWorkBizCallbackTestConsumer] 收到回调消息: requestId={}, bizCallbackKey={}, attempt={}/{}",
|
||||||
|
message.getRequestId(),
|
||||||
|
message.getBizCallbackKey(),
|
||||||
|
message.getAttempt(),
|
||||||
|
message.getMaxAttempts());
|
||||||
|
log.info("[IWorkBizCallbackTestConsumer] payload={}", message.getPayload());
|
||||||
|
|
||||||
|
// 模拟业务处理
|
||||||
|
boolean success = processCallback(message);
|
||||||
|
|
||||||
|
// 发送处理结果给 system 模块
|
||||||
|
IWorkBizCallbackResultMessage result = IWorkBizCallbackResultMessage.builder()
|
||||||
|
.requestId(message.getRequestId())
|
||||||
|
.bizCallbackKey(message.getBizCallbackKey())
|
||||||
|
.payload(message.getPayload())
|
||||||
|
.attempt(message.getAttempt())
|
||||||
|
.maxAttempts(message.getMaxAttempts())
|
||||||
|
.success(success)
|
||||||
|
.errorMessage(success ? null : "测试模拟失败")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
log.info("[IWorkBizCallbackTestConsumer] 准备发送结果到 topic={}", IWorkBizCallbackResultMessage.TOPIC);
|
||||||
|
rocketMQTemplate.syncSend(IWorkBizCallbackResultMessage.TOPIC+":123", result);
|
||||||
|
log.info("[IWorkBizCallbackTestConsumer] 已发送处理结果: success={}, requestId={}", success, message.getRequestId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模拟业务处理逻辑
|
||||||
|
*/
|
||||||
|
private boolean processCallback(IWorkBizCallbackMessage message) {
|
||||||
|
// 随机返回成功或失败,用于测试重试逻辑
|
||||||
|
boolean success = Math.random() > 0.5;
|
||||||
|
log.info("[IWorkBizCallbackTestConsumer] 模拟处理结果: {}", success ? "成功" : "失败");
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuth
|
|||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkAuthTokenRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFileCallbackReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkFileCallbackReqVO;
|
||||||
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkGenericWorkflowCreateReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOperationRespVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOaCheckTokenReqVO;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOaCheckTokenReqVO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOaRawResponse;
|
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.IWorkOaRawResponse;
|
||||||
@@ -39,6 +40,11 @@ public interface IWorkIntegrationService {
|
|||||||
*/
|
*/
|
||||||
IWorkOperationRespVO createWorkflow(IWorkWorkflowCreateReqVO reqVO);
|
IWorkOperationRespVO createWorkflow(IWorkWorkflowCreateReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用流程创建:支持透传任意业务参数和调用不同的 workflowId
|
||||||
|
*/
|
||||||
|
IWorkOperationRespVO createGenericWorkflow(IWorkGenericWorkflowCreateReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 iWork 中对已有流程执行作废或干预。
|
* 在 iWork 中对已有流程执行作废或干预。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork;
|
||||||
|
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iWork 流程创建日志 Service
|
||||||
|
*/
|
||||||
|
public interface IWorkWorkflowLogService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存流程创建日志
|
||||||
|
*/
|
||||||
|
void saveWorkflowLog(IWorkWorkflowLogDO logDO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 requestId 查询流程创建日志
|
||||||
|
*/
|
||||||
|
IWorkWorkflowLogDO getByRequestId(String requestId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新流程状态
|
||||||
|
*/
|
||||||
|
void updateStatus(String requestId, String status);
|
||||||
|
}
|
||||||
@@ -18,7 +18,12 @@ import com.zt.plat.module.infra.api.file.dto.FileCreateReqDTO;
|
|||||||
import com.zt.plat.module.infra.api.file.dto.FileRespDTO;
|
import com.zt.plat.module.infra.api.file.dto.FileRespDTO;
|
||||||
import com.zt.plat.module.system.controller.admin.integration.iwork.vo.*;
|
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.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.IWorkIntegrationService;
|
||||||
|
import com.zt.plat.module.system.service.integration.iwork.IWorkWorkflowLogService;
|
||||||
|
import com.zt.plat.module.system.dal.dataobject.iwork.IWorkWorkflowLogDO;
|
||||||
import com.zt.plat.framework.tenant.core.util.TenantUtils;
|
import com.zt.plat.framework.tenant.core.util.TenantUtils;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -60,6 +65,9 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
|||||||
|
|
||||||
private final FileApi fileApi;
|
private final FileApi fileApi;
|
||||||
private final BusinessFileApi businessFileApi;
|
private final BusinessFileApi businessFileApi;
|
||||||
|
private final IWorkCallbackLogService callbackLogService;
|
||||||
|
private final IWorkWorkflowLogService workflowLogService;
|
||||||
|
private final IWorkBizCallbackProducer bizCallbackProducer;
|
||||||
|
|
||||||
private final Cache<String, RegistrationState> registrationCache = Caffeine.newBuilder()
|
private final Cache<String, RegistrationState> registrationCache = Caffeine.newBuilder()
|
||||||
.maximumSize(32)
|
.maximumSize(32)
|
||||||
@@ -141,7 +149,45 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
|||||||
Map<String, Object> payload = buildCreatePayload(reqVO);
|
Map<String, Object> payload = buildCreatePayload(reqVO);
|
||||||
String createWorkflowPath = requireConfiguredPath(properties.getPaths().getCreateWorkflow(), "iwork.paths.create-workflow");
|
String createWorkflowPath = requireConfiguredPath(properties.getPaths().getCreateWorkflow(), "iwork.paths.create-workflow");
|
||||||
String responseBody = executeFormRequest(createWorkflowPath, null, appId, session, payload);
|
String responseBody = executeFormRequest(createWorkflowPath, null, appId, session, payload);
|
||||||
return buildOperationResponse(responseBody);
|
IWorkOperationRespVO respVO = buildOperationResponse(responseBody);
|
||||||
|
|
||||||
|
// 创建成功后记录日志
|
||||||
|
if (respVO.isSuccess() && respVO.getPayload() != null
|
||||||
|
&& respVO.getPayload().getData() != null
|
||||||
|
&& respVO.getPayload().getData().getRequestId() != null) {
|
||||||
|
saveWorkflowLog(respVO, reqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWorkOperationRespVO createGenericWorkflow(IWorkGenericWorkflowCreateReqVO reqVO) {
|
||||||
|
assertConfigured();
|
||||||
|
String appId = resolveAppId();
|
||||||
|
ClientKeyPair clientKeyPair = resolveClientKeyPair(appId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
|
||||||
|
String operatorUserId = resolveOperatorUserId(reqVO.getOperatorUserId());
|
||||||
|
IWorkSession session = createSession(appId, clientKeyPair, operatorUserId, Boolean.TRUE.equals(reqVO.getForceRefreshToken()));
|
||||||
|
|
||||||
|
// 构建透传参数,将 workflowId 加入 payload
|
||||||
|
Map<String, Object> payload = new LinkedHashMap<>();
|
||||||
|
payload.put("workflowId", reqVO.getWorkflowId());
|
||||||
|
if (reqVO.getPayload() != null) {
|
||||||
|
payload.putAll(reqVO.getPayload());
|
||||||
|
}
|
||||||
|
|
||||||
|
String createWorkflowPath = requireConfiguredPath(properties.getPaths().getCreateWorkflow(), "iwork.paths.create-workflow");
|
||||||
|
String responseBody = executeFormRequest(createWorkflowPath, null, appId, session, payload);
|
||||||
|
IWorkOperationRespVO respVO = buildOperationResponse(responseBody);
|
||||||
|
|
||||||
|
// 创建成功后记录日志,保存 requestId、businessCode、bizCallbackKey
|
||||||
|
if (respVO.isSuccess() && respVO.getPayload() != null
|
||||||
|
&& respVO.getPayload().getData() != null
|
||||||
|
&& respVO.getPayload().getData().getRequestId() != null) {
|
||||||
|
saveWorkflowCreateLog(respVO, reqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return respVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -175,6 +221,14 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
|||||||
throw new ServiceException(IWORK_CONFIGURATION_INVALID.getCode(), "业务编码不能为空");
|
throw new ServiceException(IWORK_CONFIGURATION_INVALID.getCode(), "业务编码不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. 通过 requestId 查询流程创建日志,获取 bizCallbackKey(iWork 回调不会回传此字段)
|
||||||
|
String bizCallbackKey = null;
|
||||||
|
IWorkWorkflowLogDO workflowLog = workflowLogService.getByRequestId(reqVO.getRequestId());
|
||||||
|
if (workflowLog != null && StringUtils.hasText(workflowLog.getBizCallbackKey())) {
|
||||||
|
bizCallbackKey = workflowLog.getBizCallbackKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 保存文件附件
|
||||||
BusinessFileRespDTO referenceBusinessFile = loadBusinessFileByBusinessCode(businessCode);
|
BusinessFileRespDTO referenceBusinessFile = loadBusinessFileByBusinessCode(businessCode);
|
||||||
Long tenantId = referenceBusinessFile.getTenantId();
|
Long tenantId = referenceBusinessFile.getTenantId();
|
||||||
if (tenantId == null) {
|
if (tenantId == null) {
|
||||||
@@ -183,7 +237,113 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
|||||||
|
|
||||||
AtomicReference<Long> attachmentIdRef = new AtomicReference<>();
|
AtomicReference<Long> attachmentIdRef = new AtomicReference<>();
|
||||||
TenantUtils.execute(tenantId, () -> attachmentIdRef.set(saveCallbackAttachment(fileUrl, reqVO.getFileName(), referenceBusinessFile)));
|
TenantUtils.execute(tenantId, () -> attachmentIdRef.set(saveCallbackAttachment(fileUrl, reqVO.getFileName(), referenceBusinessFile)));
|
||||||
return attachmentIdRef.get();
|
Long attachmentId = attachmentIdRef.get();
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
String status = StringUtils.hasText(reqVO.getStatus()) ? reqVO.getStatus() : "CALLBACK_RECEIVED";
|
||||||
|
workflowLogService.updateStatus(reqVO.getRequestId(), status);
|
||||||
|
log.info("[handleFileCallback] 已更新流程状态: requestId={}, status={}", reqVO.getRequestId(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 发送 MQ 通知业务系统(仅当 bizCallbackKey 存在时发送)
|
||||||
|
if (StringUtils.hasText(bizCallbackKey)) {
|
||||||
|
IWorkBizCallbackMessage message = IWorkBizCallbackMessage.builder()
|
||||||
|
.requestId(reqVO.getRequestId())
|
||||||
|
.bizCallbackKey(bizCallbackKey)
|
||||||
|
.payload(reqVO)
|
||||||
|
.attempt(0)
|
||||||
|
.maxAttempts(maxRetry)
|
||||||
|
.build();
|
||||||
|
bizCallbackProducer.send(message);
|
||||||
|
log.info("[handleFileCallback] 已发送 MQ 通知,requestId={}, bizCallbackKey={}",
|
||||||
|
reqVO.getRequestId(), bizCallbackKey);
|
||||||
|
} else {
|
||||||
|
log.warn("[handleFileCallback] 未找到 bizCallbackKey,跳过 MQ 通知,requestId={}",
|
||||||
|
reqVO.getRequestId());
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(reqVO);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("[buildCallbackRawBody] 序列化回调请求失败", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveWorkflowCreateLog(IWorkOperationRespVO respVO, IWorkGenericWorkflowCreateReqVO reqVO) {
|
||||||
|
try {
|
||||||
|
String requestId = String.valueOf(respVO.getPayload().getData().getRequestId());
|
||||||
|
String rawRequest = objectMapper.writeValueAsString(reqVO);
|
||||||
|
|
||||||
|
IWorkWorkflowLogDO logDO = new IWorkWorkflowLogDO();
|
||||||
|
logDO.setRequestId(requestId);
|
||||||
|
logDO.setWorkflowId(reqVO.getWorkflowId());
|
||||||
|
logDO.setBusinessCode(reqVO.getYwxtdjbh());
|
||||||
|
logDO.setBizCallbackKey(reqVO.getBizCallbackKey());
|
||||||
|
logDO.setRawRequest(truncateRawRequest(rawRequest));
|
||||||
|
logDO.setStatus("CREATED");
|
||||||
|
|
||||||
|
workflowLogService.saveWorkflowLog(logDO);
|
||||||
|
log.info("[createGenericWorkflow] 已记录流程创建日志,requestId={}, businessCode={}, bizCallbackKey={}",
|
||||||
|
requestId, reqVO.getYwxtdjbh(), reqVO.getBizCallbackKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[createGenericWorkflow] 记录流程创建日志失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveWorkflowLog(IWorkOperationRespVO respVO, IWorkWorkflowCreateReqVO reqVO) {
|
||||||
|
try {
|
||||||
|
String requestId = String.valueOf(respVO.getPayload().getData().getRequestId());
|
||||||
|
String rawRequest = objectMapper.writeValueAsString(reqVO);
|
||||||
|
|
||||||
|
IWorkWorkflowLogDO logDO = new IWorkWorkflowLogDO();
|
||||||
|
logDO.setRequestId(requestId);
|
||||||
|
logDO.setWorkflowId(Long.parseLong(properties.getWorkflow().getSealWorkflowId()));
|
||||||
|
logDO.setBusinessCode(reqVO.getYwxtdjbh());
|
||||||
|
logDO.setBizCallbackKey(reqVO.getBizCallbackKey());
|
||||||
|
logDO.setRawRequest(truncateRawRequest(rawRequest));
|
||||||
|
logDO.setStatus("CREATED");
|
||||||
|
|
||||||
|
workflowLogService.saveWorkflowLog(logDO);
|
||||||
|
log.info("[createWorkflow] 已记录流程创建日志,requestId={}, workflowId={}, businessCode={}, bizCallbackKey={}",
|
||||||
|
requestId, properties.getWorkflow().getSealWorkflowId(), reqVO.getYwxtdjbh(), reqVO.getBizCallbackKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[createWorkflow] 记录流程创建日志失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String truncateRawRequest(String raw) {
|
||||||
|
if (!StringUtils.hasText(raw)) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw.length() > 2000 ? raw.substring(0, 2000) : raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -760,7 +920,7 @@ public class IWorkIntegrationServiceImpl implements IWorkIntegrationService {
|
|||||||
return value.booleanValue();
|
return value.booleanValue();
|
||||||
}
|
}
|
||||||
String text = value.asText();
|
String text = value.asText();
|
||||||
return Objects.equals("0", text) || Objects.equals("1", text) || Objects.equals("success", text);
|
return Objects.equals("0", text) || Objects.equals("1", text) || Objects.equals("success", text) || Objects.equals("SUCCESS", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveMessage(JsonNode node) {
|
private String resolveMessage(JsonNode node) {
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.zt.plat.module.system.service.integration.iwork.impl;
|
||||||
|
|
||||||
|
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.service.integration.iwork.IWorkWorkflowLogService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IWorkWorkflowLogServiceImpl implements IWorkWorkflowLogService {
|
||||||
|
|
||||||
|
private final IWorkWorkflowLogMapper workflowLogMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveWorkflowLog(IWorkWorkflowLogDO logDO) {
|
||||||
|
workflowLogMapper.insert(logDO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IWorkWorkflowLogDO getByRequestId(String requestId) {
|
||||||
|
return workflowLogMapper.selectByRequestId(requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateStatus(String requestId, String status) {
|
||||||
|
IWorkWorkflowLogDO existing = workflowLogMapper.selectByRequestId(requestId);
|
||||||
|
if (existing != null) {
|
||||||
|
IWorkWorkflowLogDO update = new IWorkWorkflowLogDO();
|
||||||
|
update.setId(existing.getId());
|
||||||
|
update.setStatus(status);
|
||||||
|
workflowLogMapper.updateById(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi;
|
|||||||
import com.zt.plat.module.bpm.api.task.BpmTaskApi;
|
import com.zt.plat.module.bpm.api.task.BpmTaskApi;
|
||||||
import com.zt.plat.module.infra.api.businessfile.BusinessFileApi;
|
import com.zt.plat.module.infra.api.businessfile.BusinessFileApi;
|
||||||
import com.zt.plat.module.infra.api.file.FileApi;
|
import com.zt.plat.module.infra.api.file.FileApi;
|
||||||
|
import com.zt.plat.module.infra.api.file.dto.FileRespDTO;
|
||||||
import com.zt.plat.module.system.api.dept.DeptApi;
|
import com.zt.plat.module.system.api.dept.DeptApi;
|
||||||
import com.zt.plat.module.system.api.iwork.dto.IWorkOperationRespDTO;
|
import com.zt.plat.module.system.api.iwork.dto.IWorkOperationRespDTO;
|
||||||
import com.zt.plat.module.system.api.permission.PermissionApi;
|
import com.zt.plat.module.system.api.permission.PermissionApi;
|
||||||
@@ -128,7 +129,12 @@ public class DemoContractController extends AbstractFileUploadController impleme
|
|||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
public CommonResult<DemoContractRespVO> getDemoContract(@RequestParam("id") Long id) {
|
public CommonResult<DemoContractRespVO> getDemoContract(@RequestParam("id") Long id) {
|
||||||
DemoContractDO demoContract = demoContractService.getDemoContract(id);
|
DemoContractDO demoContract = demoContractService.getDemoContract(id);
|
||||||
return success(BeanUtils.toBean(demoContract, DemoContractRespVO.class));
|
DemoContractRespVO respVO = BeanUtils.toBean(demoContract, DemoContractRespVO.class);
|
||||||
|
// 查询业务附件
|
||||||
|
if (respVO != null && respVO.getId() != null) {
|
||||||
|
respVO.setFiles(demoContractService.getContractFiles(respVO.getId()));
|
||||||
|
}
|
||||||
|
return success(respVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.zt.plat.module.template.controller.admin.contract.vo;
|
package com.zt.plat.module.template.controller.admin.contract.vo;
|
||||||
|
|
||||||
|
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -77,4 +78,7 @@ public class DemoContractRespVO {
|
|||||||
@ExcelProperty("岗位ID")
|
@ExcelProperty("岗位ID")
|
||||||
private Long postId;
|
private Long postId;
|
||||||
|
|
||||||
|
@Schema(description = "附件列表")
|
||||||
|
private List<BusinessFileWithUrlRespDTO> files;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.zt.plat.module.template.controller.admin.contract.vo.*;
|
|||||||
import com.zt.plat.module.template.dal.dataobject.contract.DemoContractDO;
|
import com.zt.plat.module.template.dal.dataobject.contract.DemoContractDO;
|
||||||
import com.zt.plat.framework.common.pojo.PageResult;
|
import com.zt.plat.framework.common.pojo.PageResult;
|
||||||
import com.zt.plat.framework.common.pojo.PageParam;
|
import com.zt.plat.framework.common.pojo.PageParam;
|
||||||
|
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合同 Service 接口
|
* 合同 Service 接口
|
||||||
@@ -59,4 +60,12 @@ public interface DemoContractService {
|
|||||||
*/
|
*/
|
||||||
PageResult<DemoContractDO> getDemoContractPage(DemoContractPageReqVO pageReqVO);
|
PageResult<DemoContractDO> getDemoContractPage(DemoContractPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合同附件列表
|
||||||
|
*
|
||||||
|
* @param contractId 合同ID
|
||||||
|
* @return 附件列表
|
||||||
|
*/
|
||||||
|
List<BusinessFileWithUrlRespDTO> getContractFiles(Long contractId);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||||
import com.zt.plat.framework.common.pojo.PageResult;
|
import com.zt.plat.framework.common.pojo.PageResult;
|
||||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||||
|
import com.zt.plat.module.infra.api.businessfile.BusinessFileApi;
|
||||||
|
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFilePageReqDTO;
|
||||||
|
import com.zt.plat.module.infra.api.businessfile.dto.BusinessFileWithUrlRespDTO;
|
||||||
import com.zt.plat.module.system.api.sequence.SequenceApi;
|
import com.zt.plat.module.system.api.sequence.SequenceApi;
|
||||||
import com.zt.plat.module.template.controller.admin.contract.vo.DemoContractPageReqVO;
|
import com.zt.plat.module.template.controller.admin.contract.vo.DemoContractPageReqVO;
|
||||||
import com.zt.plat.module.template.controller.admin.contract.vo.DemoContractRespVO;
|
import com.zt.plat.module.template.controller.admin.contract.vo.DemoContractRespVO;
|
||||||
@@ -34,6 +37,9 @@ public class DemoContractServiceImpl implements DemoContractService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SequenceApi sequenceApi;
|
private SequenceApi sequenceApi;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BusinessFileApi businessFileApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DemoContractRespVO createDemoContract(DemoContractSaveReqVO createReqVO) {
|
public DemoContractRespVO createDemoContract(DemoContractSaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
@@ -93,4 +99,20 @@ public class DemoContractServiceImpl implements DemoContractService {
|
|||||||
return demoContractMapper.selectPage(pageReqVO);
|
return demoContractMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BusinessFileWithUrlRespDTO> getContractFiles(Long contractId) {
|
||||||
|
if (contractId == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
BusinessFilePageReqDTO reqDTO = new BusinessFilePageReqDTO();
|
||||||
|
reqDTO.setBusinessId(contractId);
|
||||||
|
reqDTO.setPageNo(1);
|
||||||
|
reqDTO.setPageSize(100);
|
||||||
|
CommonResult<PageResult<BusinessFileWithUrlRespDTO>> result = businessFileApi.getBusinessFilePageWithUrl(reqDTO);
|
||||||
|
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return result.getData().getList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user