Merge remote-tracking branch 'ztcloud/test' into dev

# Conflicts:
#	zt-framework/zt-spring-boot-starter-biz-data-permission/src/main/java/com/zt/plat/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
This commit is contained in:
yangchaojin
2026-02-02 16:34:03 +08:00
24 changed files with 823 additions and 455 deletions

View File

@@ -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<com.zt.plat.framework.common.pojo.PageResult<IWorkCallbackLogRespVO>> pageLogs(@Valid @RequestBody IWorkCallbackLogPageReqVO reqVO) {
com.zt.plat.framework.common.pojo.PageResult<com.zt.plat.module.system.dal.dataobject.iwork.IWorkSealLogDO> page = callbackLogService.page(reqVO);
com.zt.plat.framework.common.pojo.PageResult<IWorkWorkflowLogDO> page = workflowLogService.page(reqVO);
java.util.List<IWorkCallbackLogRespVO> 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<Boolean> retry(@Valid @RequestBody IWorkWorkflowVoidReqVO reqVO) {
callbackLogService.resetAndDispatch(reqVO.getRequestId());
workflowLogService.resetAndDispatch(reqVO.getRequestId());
return success(true);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<IWorkSealLogDO> {
default IWorkSealLogDO selectByRequestId(String requestId) {
return selectOne(IWorkSealLogDO::getRequestId, requestId);
}
default PageResult<IWorkSealLogDO> selectPage(IWorkCallbackLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<IWorkSealLogDO>()
.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));
}
}

View File

@@ -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<IWorkWorkflowLogDO>
default IWorkWorkflowLogDO selectByRequestId(String requestId) {
return selectOne(IWorkWorkflowLogDO::getRequestId, requestId);
}
default PageResult<IWorkWorkflowLogDO> selectPage(IWorkCallbackLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<IWorkWorkflowLogDO>()
.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));
}
}

View File

@@ -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");
};
}

View File

@@ -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<IWorkBizCallbackResultMessage>, 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<IWorkBizCallba
public void onMessage(IWorkBizCallbackResultMessage message) {
String key = message.getBizCallbackKey();
if (message.isSuccess()) {
logService.markSuccess(message.getRequestId());
workflowLogService.markCallbackSuccess(message.getRequestId());
return;
}
int attempt = message.getAttempt() + 1;
logService.incrementRetry(message.getRequestId());
workflowLogService.incrementRetry(message.getRequestId());
int maxAttempts = message.getMaxAttempts() > 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<IWorkBizCallba
int delay = properties.getCallback().getRetry().getDelaySeconds();
scheduler.schedule(() -> producer.send(next), delay, TimeUnit.SECONDS);
} else {
logService.markFailure(message.getRequestId(), message.getErrorMessage(), false, maxAttempts);
workflowLogService.markCallbackFailure(message.getRequestId(), message.getErrorMessage(), false, maxAttempts);
}
}
}

View File

@@ -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<IWorkSealLogDO> page(IWorkCallbackLogPageReqVO reqVO);
void resetAndDispatch(String requestId);
}

View File

@@ -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<IWorkWorkflowLogDO> page(IWorkCallbackLogPageReqVO reqVO);
/**
* 重置并重新派发回调
*/
void resetAndDispatch(String requestId);
}

View File

@@ -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<IWorkSealLogDO>()
.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<IWorkSealLogDO>()
.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<IWorkSealLogDO> 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;
}
}

View File

@@ -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();

View File

@@ -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<IWorkWorkflowLogDO> 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());
}
}

View File

@@ -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<IWorkSealLogDO> 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<IWorkSealLogDO> captor = ArgumentCaptor.forClass(IWorkSealLogDO.class);
verify(mapper).updateById(captor.capture());
assertThat(captor.getValue().getRetryCount()).isEqualTo(2);
}
}