Compare commits
5 Commits
6987a0e790
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 0797785176 | |||
| 86adb47392 | |||
| 780cd56ffe | |||
| 8cf9140f56 | |||
| 2f56ca07cb |
+12
@@ -39,6 +39,7 @@ public interface IBusinessProcessClient {
|
||||
String QUERY_BUSINESS_PROCESS_SNAPSHOT = API_PREFIX + "/queryBusinessProcessSnapshot";
|
||||
String QUERY_APPROVED_RECORD_LIST = API_PREFIX + "/queryApprovedRecordsNoAttachments";
|
||||
String GET_CURRENT_NODES = API_PREFIX + "/getCurrentNodes";
|
||||
String GET_PROCESS_INFO = API_PREFIX + "/getProcessInfo";
|
||||
|
||||
/**
|
||||
* 提交业务流程
|
||||
@@ -114,4 +115,15 @@ public interface IBusinessProcessClient {
|
||||
@GetMapping(GET_CURRENT_NODES)
|
||||
FR<Object> getCurrentNodes(@RequestParam("processInstanceId") String processInstanceId,
|
||||
@RequestParam(value = "loginName", required = false) String loginName);
|
||||
|
||||
/**
|
||||
* 获取流程实例详情
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param loginName MK登录名(手机号)
|
||||
* @return 流程实例详情
|
||||
*/
|
||||
@GetMapping(GET_PROCESS_INFO)
|
||||
FR<Object> getProcessInfo(@RequestParam("processInstanceId") String processInstanceId,
|
||||
@RequestParam(value = "loginName", required = false) String loginName);
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.springblade.transport.feign;
|
||||
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.transport.pojo.dto.CustomerProcessNodeSyncDTO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* 客商档案 Feign接口
|
||||
*/
|
||||
@FeignClient(value = "blade-transport")
|
||||
public interface ICustomerArchiveClient {
|
||||
|
||||
String API_PREFIX = "/feign/client/customerArchive";
|
||||
String SYNC_PROCESS_NODE = API_PREFIX + "/syncProcessNode";
|
||||
|
||||
/**
|
||||
* 按 MK 当前节点同步客商当前节点、当前处理人和审批状态
|
||||
*
|
||||
* @param param 同步参数
|
||||
* @return 是否成功
|
||||
*/
|
||||
@PostMapping(SYNC_PROCESS_NODE)
|
||||
FR<Boolean> syncProcessNode(@RequestBody CustomerProcessNodeSyncDTO param);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package org.springblade.transport.feign;
|
||||
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.transport.pojo.dto.MkProcessSyncDTO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* MK 业务流程同步 Feign
|
||||
*/
|
||||
@FeignClient(value = "blade-transport")
|
||||
public interface IMkProcessClient {
|
||||
|
||||
String API_PREFIX = "/feign/client/mkProcess";
|
||||
String APPLY = API_PREFIX + "/apply";
|
||||
|
||||
@PostMapping(APPLY)
|
||||
FR<Boolean> apply(@RequestBody MkProcessSyncDTO param);
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.springblade.transport.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 客商流程当前节点同步参数
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "客商流程当前节点同步参数")
|
||||
public class CustomerProcessNodeSyncDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "客商ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "当前节点")
|
||||
private String currentNode;
|
||||
|
||||
@Schema(description = "当前处理人")
|
||||
private String currentProcessor;
|
||||
|
||||
@Schema(description = "审批状态")
|
||||
private String approvalStatus;
|
||||
|
||||
@Schema(description = "MK 流程实例详情,传入后按 currentHandlers.fdName 和 handlerInfos.nodeName 回写")
|
||||
private Object processInfo;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package org.springblade.transport.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* MK 流程节点同步参数
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "MK 流程节点同步参数")
|
||||
public class MkProcessSyncDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "业务类型,如 project-apply")
|
||||
private String bizType;
|
||||
|
||||
@Schema(description = "业务主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "动作:sync / approve / reject")
|
||||
private String action;
|
||||
|
||||
@Schema(description = "处理人姓名")
|
||||
private String processorName;
|
||||
|
||||
@Schema(description = "MK 流程实例详情")
|
||||
private Object processInfo;
|
||||
}
|
||||
@@ -62,6 +62,10 @@
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-mk-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-transport-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 其他依赖 -->
|
||||
<dependency>
|
||||
|
||||
+6
@@ -60,6 +60,12 @@ public class BusinessProcessController extends BladeController {
|
||||
return R.data(businessProcessService.processSubmit(param));
|
||||
}
|
||||
|
||||
@PostMapping("/processDelete")
|
||||
@ApiOperationSupport(order = 7)
|
||||
public R<Boolean> processDelete(@RequestBody MKProcessCreateDTO param) {
|
||||
return R.data(businessProcessService.processDelete(param == null ? null : param.getFormInstanceId()));
|
||||
}
|
||||
|
||||
@GetMapping("/isEditView")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "是否编辑页", description = "传入业务id")
|
||||
|
||||
+8
@@ -86,4 +86,12 @@ public class BusinessProcessClient implements IBusinessProcessClient {
|
||||
@RequestParam(value = "loginName", required = false) String loginName) {
|
||||
return FR.data(businessProcessService.getCurrentNodes(processInstanceId, loginName));
|
||||
}
|
||||
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@GetMapping(GET_PROCESS_INFO)
|
||||
@Override
|
||||
public FR<Object> getProcessInfo(@RequestParam("processInstanceId") String processInstanceId,
|
||||
@RequestParam(value = "loginName", required = false) String loginName) {
|
||||
return FR.data(businessProcessService.getProcessInfo(processInstanceId, loginName));
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -34,6 +34,14 @@ public interface IBusinessProcessService extends IService<BusinessProcess> {
|
||||
*/
|
||||
String processSubmit(MKProcessCreateDTO param);
|
||||
|
||||
/**
|
||||
* 按业务表单实例 id 删除 MK 流程(不删除本地业务流程记录,供驳回后重新提交使用)
|
||||
*
|
||||
* @param formInstanceId 业务表单实例 id
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean processDelete(String formInstanceId);
|
||||
|
||||
/**
|
||||
* 获取流程当前节点详情
|
||||
*
|
||||
@@ -43,6 +51,15 @@ public interface IBusinessProcessService extends IService<BusinessProcess> {
|
||||
*/
|
||||
List<?> getCurrentNodes(String processInstanceId, String loginName);
|
||||
|
||||
/**
|
||||
* 获取流程实例详情
|
||||
*
|
||||
* @param processInstanceId 流程实例id
|
||||
* @param loginName MK登录名(手机号),可为空,为空时按流程发起人解析
|
||||
* @return 流程实例详情
|
||||
*/
|
||||
Object getProcessInfo(String processInstanceId, String loginName);
|
||||
|
||||
/**
|
||||
* 修改业务流程状态
|
||||
* @param param
|
||||
|
||||
+166
-2
@@ -15,6 +15,7 @@ import org.springblade.process.pojo.enums.ApproveStatusEnum;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.log.utils.AssertUtils;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.process.convert.ApprovalConvert;
|
||||
import org.springblade.process.convert.BusinessProcessConvert;
|
||||
@@ -41,12 +42,17 @@ import org.springblade.thirdparty.mk.pojo.dto.approval.MKProcessDTO;
|
||||
import org.springblade.thirdparty.mk.pojo.dto.sort.*;
|
||||
import org.springblade.thirdparty.mk.pojo.vo.*;
|
||||
import org.springblade.thirdparty.mk.service.IMKService;
|
||||
import org.springblade.transport.feign.ICustomerArchiveClient;
|
||||
import org.springblade.transport.feign.IMkProcessClient;
|
||||
import org.springblade.transport.pojo.dto.CustomerProcessNodeSyncDTO;
|
||||
import org.springblade.transport.pojo.dto.MkProcessSyncDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 业务流程关联表 服务实现类
|
||||
@@ -64,6 +70,8 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
private final MKProperties mkProperties;
|
||||
private final ApprovalConvert approvalConvert;
|
||||
private final IUserService userService;
|
||||
private final ICustomerArchiveClient customerArchiveClient;
|
||||
private final IMkProcessClient mkProcessClient;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
@@ -128,8 +136,17 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
AssertUtils.notBlank(loginName, "当前用户手机号为空,无法提交审核流");
|
||||
param.setSubmitIdentity(loginName);
|
||||
param.setLoginName(loginName);
|
||||
log.info("调用mk processSubmit 参数:{}", JSON.toJSONString(param));
|
||||
String processInstanceId = mkService.processSubmit(param);
|
||||
String bizType = param.getBizType();
|
||||
MKProcessCreateDTO mkParam = new MKProcessCreateDTO();
|
||||
mkParam.setFormInstanceId(param.getFormInstanceId());
|
||||
mkParam.setSubject(param.getSubject());
|
||||
mkParam.setSubmitIdentity(loginName);
|
||||
mkParam.setLoginName(loginName);
|
||||
mkParam.setTemplateCode(param.getTemplateCode());
|
||||
mkParam.setFormValues(param.getFormValues());
|
||||
mkParam.setTempVarData(param.getTempVarData());
|
||||
log.info("调用mk processSubmit 参数:{}", JSON.toJSONString(mkParam));
|
||||
String processInstanceId = mkService.processSubmit(mkParam);
|
||||
AssertUtils.notBlank(processInstanceId, "提交流程失败,未返回流程实例id");
|
||||
|
||||
Long bizId;
|
||||
@@ -164,9 +181,42 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
businessProcess.setApproveStatus(ApproveStatusEnum.APPROVING.getValue());
|
||||
this.saveOrUpdate(businessProcess);
|
||||
this.getCurrentNodes(processInstanceId, loginName);
|
||||
Object processInfo = this.getProcessInfo(processInstanceId, loginName);
|
||||
this.syncBizFromProcessInfo(bizType, param.getFormInstanceId(), processInfo);
|
||||
return processInstanceId;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean processDelete(String formInstanceId) {
|
||||
AssertUtils.notBlank(formInstanceId, "表单实例id不能为空");
|
||||
Long bizId;
|
||||
try {
|
||||
bizId = Long.valueOf(formInstanceId);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ServiceException("表单实例id格式不正确");
|
||||
}
|
||||
BusinessProcess businessProcess = this.getOne(Wrappers.<BusinessProcess>lambdaQuery()
|
||||
.eq(BusinessProcess::getBizId, bizId)
|
||||
);
|
||||
if (businessProcess == null || StringUtil.isBlank(businessProcess.getProcessInstanceId())) {
|
||||
log.warn("客商驳回后删除流程跳过,未找到流程实例 formInstanceId={}", formInstanceId);
|
||||
return true;
|
||||
}
|
||||
String loginName = businessProcess.getPromoterLoginName();
|
||||
if (StringUtil.isBlank(loginName)) {
|
||||
loginName = resolveCurrentUserPhone();
|
||||
}
|
||||
AssertUtils.notBlank(loginName, "当前用户手机号为空,无法删除审核流");
|
||||
log.info("调用mk processDelete processInstanceId={} loginName={} formInstanceId={}",
|
||||
businessProcess.getProcessInstanceId(), loginName, formInstanceId);
|
||||
boolean deleted = mkService.processDelete(businessProcess.getProcessInstanceId(), loginName);
|
||||
if (!deleted) {
|
||||
throw new ServiceException("删除MK流程失败");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<?> getCurrentNodes(String processInstanceId, String loginName) {
|
||||
if (StringUtils.isBlank(processInstanceId)) {
|
||||
@@ -193,6 +243,120 @@ public class BusinessProcessServiceImpl extends ServiceImpl<BusinessProcessMappe
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProcessInfo(String processInstanceId, String loginName) {
|
||||
if (StringUtils.isBlank(processInstanceId)) {
|
||||
log.warn("查询流程实例详情失败,processInstanceId为空");
|
||||
return null;
|
||||
}
|
||||
String resolvedLoginName = loginName;
|
||||
if (StringUtils.isBlank(resolvedLoginName)) {
|
||||
BusinessProcess businessProcess = this.getBusinessProcessByProcessInstanceId(processInstanceId);
|
||||
resolvedLoginName = resolvePromoterLoginName(businessProcess, null);
|
||||
}
|
||||
if (StringUtils.isBlank(resolvedLoginName)) {
|
||||
log.warn("查询流程实例详情失败,loginName为空 processInstanceId={}", processInstanceId);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Object processInfo = mkService.getProcessInfo(processInstanceId, resolvedLoginName);
|
||||
log.info("获取流程实例详情 processInstanceId={} loginName={} result={}",
|
||||
processInstanceId, resolvedLoginName, JSON.toJSONString(processInfo));
|
||||
return processInfo;
|
||||
} catch (Exception e) {
|
||||
log.error("查询流程实例详情异常 processInstanceId={} loginName={}", processInstanceId, resolvedLoginName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void syncBizFromProcessInfo(String bizType, String formInstanceId, Object processInfo) {
|
||||
if (StringUtils.isBlank(formInstanceId) || processInfo == null) {
|
||||
log.warn("同步业务当前节点跳过,formInstanceId或流程实例详情为空 formInstanceId={}", formInstanceId);
|
||||
return;
|
||||
}
|
||||
Long bizId;
|
||||
try {
|
||||
bizId = Long.valueOf(formInstanceId);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("同步业务当前节点失败,表单实例id不是数字:{}", formInstanceId);
|
||||
return;
|
||||
}
|
||||
String resolvedBizType = StringUtils.isBlank(bizType) ? "customer-archive" : bizType;
|
||||
if ("customer-archive".equals(resolvedBizType)) {
|
||||
this.syncCustomerArchiveFromProcessInfo(formInstanceId, processInfo);
|
||||
return;
|
||||
}
|
||||
MkProcessSyncDTO param = new MkProcessSyncDTO();
|
||||
param.setBizType(resolvedBizType);
|
||||
param.setId(bizId);
|
||||
param.setAction("sync");
|
||||
param.setProcessInfo(processInfo);
|
||||
try {
|
||||
FR<Boolean> result = mkProcessClient.apply(param);
|
||||
log.info("同步业务当前节点完成 bizType={} bizId={} result={}",
|
||||
resolvedBizType, bizId, JSON.toJSONString(result));
|
||||
} catch (Exception e) {
|
||||
log.error("同步业务当前节点异常 bizType={} bizId={}", resolvedBizType, bizId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void syncCustomerArchiveFromProcessInfo(String formInstanceId, Object processInfo) {
|
||||
if (StringUtils.isBlank(formInstanceId) || processInfo == null) {
|
||||
log.warn("同步客商当前节点跳过,formInstanceId或流程实例详情为空 formInstanceId={}", formInstanceId);
|
||||
return;
|
||||
}
|
||||
Long customerId;
|
||||
try {
|
||||
customerId = Long.valueOf(formInstanceId);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("同步客商当前节点失败,表单实例id不是数字:{}", formInstanceId);
|
||||
return;
|
||||
}
|
||||
CustomerProcessNodeSyncDTO param = new CustomerProcessNodeSyncDTO();
|
||||
param.setId(customerId);
|
||||
param.setProcessInfo(processInfo);
|
||||
param.setApprovalStatus("reviewing");
|
||||
try {
|
||||
FR<Boolean> result = customerArchiveClient.syncProcessNode(param);
|
||||
log.info("同步客商当前节点完成 customerId={} result={}", customerId, JSON.toJSONString(result));
|
||||
} catch (Exception e) {
|
||||
log.error("同步客商当前节点异常 customerId={}", customerId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private String readMkNodeName(Object node) {
|
||||
if (node instanceof MKNodeVO vo) {
|
||||
return vo.getNodeName();
|
||||
}
|
||||
if (node instanceof Map<?, ?> map) {
|
||||
Object value = map.get("nodeName");
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Stream<String> readMkHandlerNames(Object node) {
|
||||
List<MKNodeHandlerVO> handlers = null;
|
||||
if (node instanceof MKNodeVO vo) {
|
||||
handlers = vo.getNodeHandlers();
|
||||
} else if (node instanceof Map<?, ?> map && map.get("nodeHandlers") instanceof List<?> list) {
|
||||
return list.stream().map(item -> {
|
||||
if (item instanceof MKNodeHandlerVO handler) {
|
||||
return handler.getHandlerName();
|
||||
}
|
||||
if (item instanceof Map<?, ?> handlerMap) {
|
||||
Object value = handlerMap.get("handlerName");
|
||||
return value == null ? null : String.valueOf(value);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
if (CollectionUtil.isEmpty(handlers)) {
|
||||
return Stream.empty();
|
||||
}
|
||||
return handlers.stream().map(MKNodeHandlerVO::getHandlerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前登录用户实体读取真实手机号(绕过接口返回脱敏)
|
||||
*/
|
||||
|
||||
+5
-55
@@ -35,13 +35,11 @@ import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.constant.AuthConstant;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.process.feign.IBusinessProcessClient;
|
||||
import org.springblade.transport.pojo.vo.CustomerArchiveVO;
|
||||
import org.springblade.transport.pojo.vo.CustomerChangeRecordVO;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springblade.transport.service.impl.CustomerArchivePublicProcessService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -49,8 +47,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -68,7 +64,7 @@ import java.util.Map;
|
||||
public class CustomerArchivePublicController {
|
||||
|
||||
private final ICustomerArchiveService customerArchiveService;
|
||||
private final IBusinessProcessClient businessProcessClient;
|
||||
private final CustomerArchivePublicProcessService customerArchivePublicProcessService;
|
||||
|
||||
/**
|
||||
* 公开详情
|
||||
@@ -92,61 +88,15 @@ public class CustomerArchivePublicController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 公开接收流程页 postMessage 数据(当前仅打印,便于联调)
|
||||
* 公开接收流程页 postMessage 数据
|
||||
*/
|
||||
@PostMapping("/process-message")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "公开接收流程消息", description = "无需登录,接收后查询当前节点并打印")
|
||||
@Operation(summary = "公开接收流程消息", description = "无需登录,立即查询当前节点,5秒后查询流程实例详情并同步客商")
|
||||
public R processMessage(@RequestBody Map<String, Object> body) {
|
||||
log.info("客商公开页收到流程消息:{}", JSON.toJSONString(body));
|
||||
Map<String, Object> formValues = asMap(body == null ? null : body.get("formValues"));
|
||||
String processId = firstText(formValues, "processId");
|
||||
if (StringUtil.isBlank(processId) && body != null) {
|
||||
processId = firstText(body, "processId");
|
||||
}
|
||||
String loginName = firstText(formValues, "mkLoginName", "loginName");
|
||||
if (StringUtil.isBlank(processId)) {
|
||||
log.warn("客商公开页流程消息未找到 processId,跳过查询当前节点");
|
||||
return R.success("ok");
|
||||
}
|
||||
try {
|
||||
FR<Object> result = businessProcessClient.getCurrentNodes(processId, loginName);
|
||||
log.info("客商公开页流程消息当前节点详情 processId={} loginName={} result={}",
|
||||
processId, loginName, JSON.toJSONString(result == null ? null : result.getData()));
|
||||
} catch (Exception e) {
|
||||
log.error("客商公开页查询当前节点失败 processId={} loginName={}", processId, loginName, e);
|
||||
}
|
||||
customerArchivePublicProcessService.handleProcessMessage(body);
|
||||
return R.success("ok");
|
||||
}
|
||||
|
||||
private static Map<String, Object> asMap(Object value) {
|
||||
if (!(value instanceof Map<?, ?> map)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
map.forEach((key, nested) -> {
|
||||
if (key != null) {
|
||||
result.put(String.valueOf(key), nested);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String firstText(Map<String, Object> source, String... keys) {
|
||||
if (source == null || keys == null) {
|
||||
return null;
|
||||
}
|
||||
for (String key : keys) {
|
||||
Object value = source.get(key);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
if (StringUtil.isNotBlank(text) && !"null".equalsIgnoreCase(text)) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.springblade.transport.controller;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.constant.AuthConstant;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.mk.MkProcessMessageService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MK 业务流程公开查看
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@TenantIgnore
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@RequestMapping("/mk-process/public")
|
||||
@Tag(name = "MK业务流程公开查看", description = "MK业务流程公开查看")
|
||||
public class MkProcessPublicController {
|
||||
|
||||
private final MkProcessMessageService mkProcessMessageService;
|
||||
|
||||
@GetMapping("/{bizType}/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "公开详情", description = "无需登录")
|
||||
public R detail(@PathVariable String bizType,
|
||||
@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.data(mkProcessMessageService.handler(bizType).publicDetail(id));
|
||||
}
|
||||
|
||||
@PostMapping("/{bizType}/process-message")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "公开接收流程消息", description = "无需登录,立即查询当前节点,5秒后查询流程实例详情并同步业务")
|
||||
public R processMessage(@PathVariable String bizType, @RequestBody Map<String, Object> body) {
|
||||
log.info("公开页收到流程消息 bizType={} body={}", bizType, JSON.toJSONString(body));
|
||||
mkProcessMessageService.handleProcessMessage(bizType, body);
|
||||
return R.success("ok");
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -105,7 +105,7 @@ public class ProjectApplyController extends BladeController {
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "新增或修改", description = "传入projectApply")
|
||||
public R submit(@RequestBody ProjectApply projectApply) {
|
||||
return R.status(projectApplyService.submit(projectApply));
|
||||
return projectApplyService.submit(projectApply) ? R.data(projectApply) : R.fail("保存失败");
|
||||
}
|
||||
|
||||
@PostMapping("/submit-approval")
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ public class WaybillController extends BladeController {
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "新增或修改", description = "传入waybill")
|
||||
public R submit(@RequestBody Waybill waybill) {
|
||||
return R.status(waybillService.submit(waybill));
|
||||
return waybillService.submit(waybill) ? R.data(waybill) : R.fail("保存失败");
|
||||
}
|
||||
|
||||
@PostMapping("/save-draft")
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.springblade.transport.feign;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.constant.AuthConstant;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.transport.pojo.dto.CustomerProcessNodeSyncDTO;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 客商档案 Feign实现
|
||||
*/
|
||||
@Hidden
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class CustomerArchiveClient implements ICustomerArchiveClient {
|
||||
|
||||
private final ICustomerArchiveService customerArchiveService;
|
||||
|
||||
@TenantIgnore
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@PostMapping(SYNC_PROCESS_NODE)
|
||||
@Override
|
||||
public FR<Boolean> syncProcessNode(@RequestBody CustomerProcessNodeSyncDTO param) {
|
||||
if (param == null) {
|
||||
return FR.data(false);
|
||||
}
|
||||
if (param.getProcessInfo() != null) {
|
||||
return FR.data(customerArchiveService.syncProcessNodeFromProcessInfo(param.getId(), param.getProcessInfo()));
|
||||
}
|
||||
return FR.data(customerArchiveService.syncProcessNode(
|
||||
param.getId(), param.getCurrentNode(), param.getCurrentProcessor(), param.getApprovalStatus()));
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package org.springblade.transport.feign;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.constant.AuthConstant;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.transport.mk.IMkProcessBizHandler;
|
||||
import org.springblade.transport.mk.MkProcessMessageService;
|
||||
import org.springblade.transport.pojo.dto.MkProcessSyncDTO;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* MK 业务流程同步 Feign 实现
|
||||
*/
|
||||
@Hidden
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
public class MkProcessClient implements IMkProcessClient {
|
||||
|
||||
private final MkProcessMessageService mkProcessMessageService;
|
||||
|
||||
@TenantIgnore
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@PostMapping(APPLY)
|
||||
@Override
|
||||
public FR<Boolean> apply(@RequestBody MkProcessSyncDTO param) {
|
||||
if (param == null || param.getId() == null) {
|
||||
return FR.data(false);
|
||||
}
|
||||
IMkProcessBizHandler handler = mkProcessMessageService.handler(param.getBizType());
|
||||
String action = param.getAction() == null ? "sync" : param.getAction();
|
||||
return switch (action) {
|
||||
case "approve" -> FR.data(handler.approveFromProcess(param.getId(), param.getProcessorName()));
|
||||
case "reject" -> FR.data(handler.rejectFromProcess(param.getId(), param.getProcessorName()));
|
||||
default -> FR.data(handler.syncFromProcessInfo(param.getId(), param.getProcessInfo()));
|
||||
};
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.springblade.transport.mk;
|
||||
|
||||
/**
|
||||
* MK 业务流程处理器
|
||||
*/
|
||||
public interface IMkProcessBizHandler {
|
||||
|
||||
String bizType();
|
||||
|
||||
Object publicDetail(Long id);
|
||||
|
||||
boolean syncFromProcessInfo(Long id, Object processInfo);
|
||||
|
||||
boolean approveFromProcess(Long id, String processorName);
|
||||
|
||||
boolean rejectFromProcess(Long id, String processorName);
|
||||
}
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
package org.springblade.transport.mk;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.pojo.entity.PreSettlement;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.service.IContractManageService;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springblade.transport.service.IFormalSettlementService;
|
||||
import org.springblade.transport.service.IPaymentApplicationService;
|
||||
import org.springblade.transport.service.IPreSettlementService;
|
||||
import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 各业务 MK 流程处理器
|
||||
*/
|
||||
public final class MkProcessBizHandlers {
|
||||
|
||||
private MkProcessBizHandlers() {
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class CustomerHandler implements IMkProcessBizHandler {
|
||||
private final ICustomerArchiveService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "customer-archive";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.publicDetail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
return service.syncProcessNodeFromProcessInfo(id, processInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
return service.approveFromProcess(id, processorName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
return service.rejectFromProcess(id, processorName);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class ProjectHandler implements IMkProcessBizHandler {
|
||||
private final IProjectApplyService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "project-apply";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.detail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
ProjectApply entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
if (!"change_reviewing".equals(entity.getApprovalStatus())) {
|
||||
entity.setApprovalStatus("reviewing");
|
||||
}
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
ProjectApply entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())
|
||||
|| "change_approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
boolean change = "change_reviewing".equals(entity.getApprovalStatus());
|
||||
entity.setApprovalStatus(change ? "change_approved" : "approved");
|
||||
entity.setEffectiveType("formal");
|
||||
entity.setCurrentNode("审核通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
entity.setApprovedTime(LocalDateTime.now());
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
ProjectApply entity = service.getById(id);
|
||||
if (entity == null || "rejected".equals(entity.getApprovalStatus())
|
||||
|| "change_rejected".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
boolean change = "change_reviewing".equals(entity.getApprovalStatus());
|
||||
entity.setApprovalStatus(change ? "change_rejected" : "rejected");
|
||||
entity.setCurrentNode("审核不通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class ContractHandler implements IMkProcessBizHandler {
|
||||
private final IContractManageService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "contract-manage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.detail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
ContractManage entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
if (!"change_reviewing".equals(entity.getApprovalStatus())) {
|
||||
entity.setApprovalStatus("reviewing");
|
||||
}
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
ContractManage entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())
|
||||
|| "change_approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
boolean change = "change_reviewing".equals(entity.getApprovalStatus());
|
||||
entity.setApprovalStatus(change ? "change_approved" : "approved");
|
||||
entity.setCurrentNode("审核通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
ContractManage entity = service.getById(id);
|
||||
if (entity == null || "rejected".equals(entity.getApprovalStatus())
|
||||
|| "change_rejected".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
boolean change = "change_reviewing".equals(entity.getApprovalStatus());
|
||||
entity.setApprovalStatus(change ? "change_rejected" : "rejected");
|
||||
entity.setCurrentNode("审核不通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class WaybillHandler implements IMkProcessBizHandler {
|
||||
private final IWaybillService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "waybill-manage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
Waybill entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
if (Func.isEmpty(node)) {
|
||||
return false;
|
||||
}
|
||||
entity.setCurrentProcessNode(node);
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
Waybill entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
entity.setCurrentProcessNode("审核通过");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
Waybill entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
entity.setCurrentProcessNode("审核不通过");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class PreSettlementHandler implements IMkProcessBizHandler {
|
||||
private final IPreSettlementService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "pre-settlement";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.detail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
PreSettlement entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
entity.setApprovalStatus("reviewing");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
PreSettlement entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("approved");
|
||||
entity.setCurrentNode("审核通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
entity.setApprovedTime(LocalDateTime.now());
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
PreSettlement entity = service.getById(id);
|
||||
if (entity == null || "returned".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("returned");
|
||||
entity.setCurrentNode("审核不通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class FormalSettlementHandler implements IMkProcessBizHandler {
|
||||
private final IFormalSettlementService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "formal-settlement";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.detail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
FormalSettlement entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
entity.setApprovalStatus("reviewing");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
FormalSettlement entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("approved");
|
||||
entity.setCurrentNode("审核通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
entity.setApprovedTime(LocalDateTime.now());
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
FormalSettlement entity = service.getById(id);
|
||||
if (entity == null || "returned".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("returned");
|
||||
entity.setCurrentNode("审核不通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public static class PaymentHandler implements IMkProcessBizHandler {
|
||||
private final IPaymentApplicationService service;
|
||||
|
||||
@Override
|
||||
public String bizType() {
|
||||
return "payment-application";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object publicDetail(Long id) {
|
||||
return service.detail(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean syncFromProcessInfo(Long id, Object processInfo) {
|
||||
PaymentApplication entity = service.getById(id);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
String node = MkProcessNodeHelper.currentNode(processInfo);
|
||||
String processor = MkProcessNodeHelper.currentProcessor(processInfo);
|
||||
if (Func.isEmpty(node) && Func.isEmpty(processor)) {
|
||||
return false;
|
||||
}
|
||||
if (Func.isNotEmpty(node)) {
|
||||
entity.setCurrentNode(node);
|
||||
}
|
||||
if (Func.isNotEmpty(processor)) {
|
||||
entity.setCurrentProcessor(processor);
|
||||
}
|
||||
entity.setApprovalStatus("reviewing");
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
PaymentApplication entity = service.getById(id);
|
||||
if (entity == null || "approved".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("approved");
|
||||
entity.setCurrentNode("审核通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
PaymentApplication entity = service.getById(id);
|
||||
if (entity == null || "returned".equals(entity.getApprovalStatus())) {
|
||||
return entity != null;
|
||||
}
|
||||
entity.setApprovalStatus("returned");
|
||||
entity.setCurrentNode("审核不通过");
|
||||
entity.setCurrentProcessor(MkProcessNodeHelper.text(processorName, "系统"));
|
||||
return service.updateById(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
package org.springblade.transport.mk;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.process.feign.IBusinessProcessClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 公开页流程消息:当前节点立即查询,流程实例详情延迟 5 秒查询后同步业务状态。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public class MkProcessMessageService {
|
||||
|
||||
private static final long PROCESS_INFO_DELAY_SECONDS = 5L;
|
||||
|
||||
private final IBusinessProcessClient businessProcessClient;
|
||||
private final List<IMkProcessBizHandler> handlers;
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, runnable -> {
|
||||
Thread thread = new Thread(runnable, "mk-process-info-" + THREAD_INDEX.incrementAndGet());
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
private static final AtomicInteger THREAD_INDEX = new AtomicInteger();
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
public IMkProcessBizHandler handler(String bizType) {
|
||||
Map<String, IMkProcessBizHandler> mapping = handlers.stream()
|
||||
.collect(Collectors.toMap(IMkProcessBizHandler::bizType, Function.identity(), (a, b) -> a));
|
||||
IMkProcessBizHandler handler = mapping.get(bizType);
|
||||
if (handler == null) {
|
||||
throw new IllegalArgumentException("不支持的MK业务类型:" + bizType);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void handleProcessMessage(String bizType, Map<String, Object> body) {
|
||||
IMkProcessBizHandler handler = handler(bizType);
|
||||
Map<String, Object> formValues = asMap(body == null ? null : body.get("formValues"));
|
||||
String processId = firstText(formValues, "processId");
|
||||
if (StringUtil.isBlank(processId) && body != null) {
|
||||
processId = firstText(body, "processId");
|
||||
}
|
||||
String loginName = firstText(formValues, "mkLoginName", "loginName");
|
||||
if (StringUtil.isBlank(processId)) {
|
||||
log.warn("公开页流程消息未找到 processId,跳过查询当前节点 bizType={}", bizType);
|
||||
return;
|
||||
}
|
||||
String bizIdText = firstText(asMap(body == null ? null : body.get("formData")), "id", "formInstanceId");
|
||||
queryCurrentNodes(bizType, processId, loginName);
|
||||
scheduleProcessInfo(handler, bizType, processId, loginName, bizIdText, formValues);
|
||||
}
|
||||
|
||||
private void queryCurrentNodes(String bizType, String processId, String loginName) {
|
||||
try {
|
||||
FR<Object> result = businessProcessClient.getCurrentNodes(processId, loginName);
|
||||
Object nodeData = result == null ? null : result.getData();
|
||||
log.info("公开页流程消息当前节点详情 bizType={} processId={} loginName={} result={}",
|
||||
bizType, processId, loginName, JSON.toJSONString(nodeData));
|
||||
} catch (Exception e) {
|
||||
log.error("公开页查询当前节点失败 bizType={} processId={} loginName={}", bizType, processId, loginName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
||||
String loginName, String bizIdText, Map<String, Object> formValues) {
|
||||
Map<String, Object> formValuesCopy = new HashMap<>(formValues == null ? Map.of() : formValues);
|
||||
log.info("公开页将在{}秒后查询流程实例详情 bizType={} processId={}", PROCESS_INFO_DELAY_SECONDS, bizType, processId);
|
||||
scheduler.schedule(
|
||||
() -> queryProcessInfo(handler, bizType, processId, loginName, bizIdText, formValuesCopy),
|
||||
PROCESS_INFO_DELAY_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
private void queryProcessInfo(IMkProcessBizHandler handler, String bizType, String processId,
|
||||
String loginName, String bizIdText, Map<String, Object> formValues) {
|
||||
try {
|
||||
FR<Object> processInfoResult = businessProcessClient.getProcessInfo(processId, loginName);
|
||||
Object processInfo = processInfoResult == null ? null : processInfoResult.getData();
|
||||
log.info("公开页流程消息流程实例详情 bizType={} processId={} loginName={} result={}",
|
||||
bizType, processId, loginName, JSON.toJSONString(processInfo));
|
||||
if (isProcessFinished(processInfo)) {
|
||||
applyProcessResult(handler, bizType, bizIdText, processId, formValues, true);
|
||||
} else if (isProcessRejected(processInfo)) {
|
||||
applyProcessResult(handler, bizType, bizIdText, processId, formValues, false);
|
||||
} else if (StringUtil.isNotBlank(bizIdText)) {
|
||||
try {
|
||||
handler.syncFromProcessInfo(Long.valueOf(bizIdText), processInfo);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("公开页流程消息业务id格式不正确 bizType={} id={}", bizType, bizIdText);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("公开页查询流程实例详情失败 bizType={} processId={} loginName={}", bizType, processId, loginName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isProcessFinished(Object processInfo) {
|
||||
return "30".equals(firstText(asMap(processInfo), "fdProcessStatus"));
|
||||
}
|
||||
|
||||
private static boolean isProcessRejected(Object processInfo) {
|
||||
Map<String, Object> info = asMap(processInfo);
|
||||
if (!"20".equals(firstText(info, "fdProcessStatus"))) {
|
||||
return false;
|
||||
}
|
||||
if (hasItems(info.get("currentHandlers"))) {
|
||||
return false;
|
||||
}
|
||||
return !hasItems(asMap(info.get("fdTaskInfo")).get("handlerInfos"));
|
||||
}
|
||||
|
||||
private static boolean hasItems(Object value) {
|
||||
return value instanceof Collection<?> collection && !collection.isEmpty();
|
||||
}
|
||||
|
||||
private void applyProcessResult(IMkProcessBizHandler handler, String bizType, String bizIdText,
|
||||
String processId, Map<String, Object> formValues, boolean approved) {
|
||||
String action = approved ? "审核通过" : "审核驳回";
|
||||
if (StringUtil.isBlank(bizIdText)) {
|
||||
log.warn("流程{}但未找到业务id,跳过同步 bizType={} processId={}", action, bizType, processId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String processorName = firstText(formValues, "mkUserName", "mkLoginName");
|
||||
Long bizId = Long.valueOf(bizIdText);
|
||||
if (approved) {
|
||||
handler.approveFromProcess(bizId, processorName);
|
||||
} else {
|
||||
handler.rejectFromProcess(bizId, processorName);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("流程{}但业务id格式不正确 bizType={} id={}", action, bizType, bizIdText);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> asMap(Object value) {
|
||||
if (!(value instanceof Map<?, ?> map)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
map.forEach((key, nested) -> {
|
||||
if (key != null) {
|
||||
result.put(String.valueOf(key), nested);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String firstText(Map<String, Object> source, String... keys) {
|
||||
if (source == null || keys == null) {
|
||||
return null;
|
||||
}
|
||||
for (String key : keys) {
|
||||
Object value = source.get(key);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
if (StringUtil.isNotBlank(text) && !"null".equalsIgnoreCase(text)) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.springblade.transport.mk;
|
||||
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 解析 MK 流程实例详情中的当前节点、当前处理人
|
||||
*/
|
||||
public final class MkProcessNodeHelper {
|
||||
|
||||
private MkProcessNodeHelper() {
|
||||
}
|
||||
|
||||
public static String currentProcessor(Object processInfo) {
|
||||
return joinDistinct(extractTexts(asMap(processInfo).get("currentHandlers"), "fdName", "name"), "、");
|
||||
}
|
||||
|
||||
public static String currentNode(Object processInfo) {
|
||||
Map<String, Object> taskInfo = asMap(asMap(processInfo).get("fdTaskInfo"));
|
||||
return joinDistinct(extractTexts(taskInfo.get("handlerInfos"), "nodeName"), "、");
|
||||
}
|
||||
|
||||
public static String text(String value, String fallback) {
|
||||
return Func.isEmpty(value) ? fallback : value.trim();
|
||||
}
|
||||
|
||||
public static Map<String, Object> asMap(Object value) {
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
map.forEach((key, nested) -> {
|
||||
if (key != null) {
|
||||
result.put(String.valueOf(key), nested);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
if (value == null) {
|
||||
return Map.of();
|
||||
}
|
||||
try {
|
||||
Map<String, Object> parsed = JsonUtil.toMap(JsonUtil.toJson(value));
|
||||
return parsed == null ? Map.of() : parsed;
|
||||
} catch (Exception ignored) {
|
||||
return Map.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> extractTexts(Object listObj, String... keys) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (!(listObj instanceof Collection<?> collection) || keys == null) {
|
||||
return result;
|
||||
}
|
||||
for (Object item : collection) {
|
||||
Map<String, Object> map = asMap(item);
|
||||
for (String key : keys) {
|
||||
String text = Func.toStr(map.get(key), "").trim();
|
||||
if (Func.isNotEmpty(text)) {
|
||||
result.add(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String joinDistinct(List<String> values, String delimiter) {
|
||||
return values.stream().filter(Func::isNotEmpty).distinct().collect(Collectors.joining(delimiter));
|
||||
}
|
||||
}
|
||||
+47
@@ -100,6 +100,53 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
|
||||
*/
|
||||
boolean submitApproval(Long id);
|
||||
|
||||
/**
|
||||
* 按 MK 当前节点同步客商当前节点、当前处理人和审批状态
|
||||
*
|
||||
* @param id 客商ID
|
||||
* @param currentNode 当前节点
|
||||
* @param currentProcessor 当前处理人
|
||||
* @param approvalStatus 审批状态,可为空,为空时保持为审核中
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean syncProcessNode(Long id, String currentNode, String currentProcessor, String approvalStatus);
|
||||
|
||||
/**
|
||||
* 按 MK getCurrentNodes 返回结果同步客商当前节点
|
||||
*
|
||||
* @param id 客商ID
|
||||
* @param currentNodes MK 当前节点详情
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean syncProcessNodeFromMk(Long id, Object currentNodes);
|
||||
|
||||
/**
|
||||
* 按 MK 流程实例详情回写当前节点、当前处理人
|
||||
*
|
||||
* @param id 客商ID
|
||||
* @param processInfo MK getProcessInfo 返回数据
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean syncProcessNodeFromProcessInfo(Long id, Object processInfo);
|
||||
|
||||
/**
|
||||
* MK 流程结束(fdProcessStatus=30)时将客商置为审核通过
|
||||
*
|
||||
* @param id 客商ID
|
||||
* @param processorName 当前处理人,可为空
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean approveFromProcess(Long id, String processorName);
|
||||
|
||||
/**
|
||||
* MK 流程状态为 20 时将客商置为审核驳回
|
||||
*
|
||||
* @param id 客商ID
|
||||
* @param processorName 当前处理人,可为空
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean rejectFromProcess(Long id, String processorName);
|
||||
|
||||
/**
|
||||
* 撤回审批并恢复草稿状态
|
||||
*
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
*/
|
||||
package org.springblade.transport.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
import org.springblade.core.tool.api.FR;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.process.feign.IBusinessProcessClient;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 客商公开页流程消息处理:当前节点立即查询,流程实例详情延迟查询。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@TenantIgnore
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerArchivePublicProcessService {
|
||||
|
||||
private static final long PROCESS_INFO_DELAY_SECONDS = 5L;
|
||||
|
||||
private final ICustomerArchiveService customerArchiveService;
|
||||
private final IBusinessProcessClient businessProcessClient;
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, runnable -> {
|
||||
Thread thread = new Thread(runnable, "customer-archive-process-info-" + THREAD_INDEX.incrementAndGet());
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
private static final AtomicInteger THREAD_INDEX = new AtomicInteger();
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
public void handleProcessMessage(Map<String, Object> body) {
|
||||
Map<String, Object> formValues = asMap(body == null ? null : body.get("formValues"));
|
||||
String processId = firstText(formValues, "processId");
|
||||
if (StringUtil.isBlank(processId) && body != null) {
|
||||
processId = firstText(body, "processId");
|
||||
}
|
||||
String loginName = firstText(formValues, "mkLoginName", "loginName");
|
||||
if (StringUtil.isBlank(processId)) {
|
||||
log.warn("客商公开页流程消息未找到 processId,跳过查询当前节点");
|
||||
return;
|
||||
}
|
||||
String customerIdText = firstText(asMap(body == null ? null : body.get("formData")), "id");
|
||||
queryCurrentNodes(processId, loginName);
|
||||
scheduleProcessInfo(processId, loginName, customerIdText, formValues);
|
||||
}
|
||||
|
||||
private void queryCurrentNodes(String processId, String loginName) {
|
||||
try {
|
||||
FR<Object> result = businessProcessClient.getCurrentNodes(processId, loginName);
|
||||
Object nodeData = result == null ? null : result.getData();
|
||||
log.info("客商公开页流程消息当前节点详情 processId={} loginName={} result={}",
|
||||
processId, loginName, JSON.toJSONString(nodeData));
|
||||
} catch (Exception e) {
|
||||
log.error("客商公开页查询当前节点失败 processId={} loginName={}", processId, loginName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleProcessInfo(String processId, String loginName, String customerIdText,
|
||||
Map<String, Object> formValues) {
|
||||
Map<String, Object> formValuesCopy = new HashMap<>(formValues == null ? Map.of() : formValues);
|
||||
log.info("客商公开页将在{}秒后查询流程实例详情 processId={}", PROCESS_INFO_DELAY_SECONDS, processId);
|
||||
scheduler.schedule(
|
||||
() -> queryProcessInfo(processId, loginName, customerIdText, formValuesCopy),
|
||||
PROCESS_INFO_DELAY_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
|
||||
private void queryProcessInfo(String processId, String loginName, String customerIdText,
|
||||
Map<String, Object> formValues) {
|
||||
try {
|
||||
FR<Object> processInfoResult = businessProcessClient.getProcessInfo(processId, loginName);
|
||||
Object processInfo = processInfoResult == null ? null : processInfoResult.getData();
|
||||
log.info("客商公开页流程消息流程实例详情 processId={} loginName={} result={}",
|
||||
processId, loginName, JSON.toJSONString(processInfo));
|
||||
if (isProcessFinished(processInfo)) {
|
||||
applyProcessResult(customerIdText, processId, formValues, true);
|
||||
} else if (isProcessRejected(processInfo)) {
|
||||
applyProcessResult(customerIdText, processId, formValues, false);
|
||||
} else if (StringUtil.isNotBlank(customerIdText)) {
|
||||
try {
|
||||
customerArchiveService.syncProcessNodeFromProcessInfo(Long.valueOf(customerIdText), processInfo);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("客商公开页流程消息客商id格式不正确:{}", customerIdText);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("客商公开页查询流程实例详情失败 processId={} loginName={}", processId, loginName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isProcessFinished(Object processInfo) {
|
||||
return "30".equals(firstText(asMap(processInfo), "fdProcessStatus"));
|
||||
}
|
||||
|
||||
private static boolean isProcessRejected(Object processInfo) {
|
||||
Map<String, Object> info = asMap(processInfo);
|
||||
if (!"20".equals(firstText(info, "fdProcessStatus"))) {
|
||||
return false;
|
||||
}
|
||||
if (hasItems(info.get("currentHandlers"))) {
|
||||
return false;
|
||||
}
|
||||
return !hasItems(asMap(info.get("fdTaskInfo")).get("handlerInfos"));
|
||||
}
|
||||
|
||||
private static boolean hasItems(Object value) {
|
||||
return value instanceof Collection<?> collection && !collection.isEmpty();
|
||||
}
|
||||
|
||||
private void applyProcessResult(String customerIdText, String processId, Map<String, Object> formValues,
|
||||
boolean approved) {
|
||||
String action = approved ? "审核通过" : "审核驳回";
|
||||
if (StringUtil.isBlank(customerIdText)) {
|
||||
log.warn("流程{}但未找到客商id,跳过同步 processId={}", action, processId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String processorName = firstText(formValues, "mkUserName", "mkLoginName");
|
||||
Long customerId = Long.valueOf(customerIdText);
|
||||
if (approved) {
|
||||
customerArchiveService.approveFromProcess(customerId, processorName);
|
||||
} else {
|
||||
customerArchiveService.rejectFromProcess(customerId, processorName);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("流程{}但客商id格式不正确:{}", action, customerIdText);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> asMap(Object value) {
|
||||
if (!(value instanceof Map<?, ?> map)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
map.forEach((key, nested) -> {
|
||||
if (key != null) {
|
||||
result.put(String.valueOf(key), nested);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String firstText(Map<String, Object> source, String... keys) {
|
||||
if (source == null || keys == null) {
|
||||
return null;
|
||||
}
|
||||
for (String key : keys) {
|
||||
Object value = source.get(key);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = String.valueOf(value).trim();
|
||||
if (StringUtil.isNotBlank(text) && !"null".equalsIgnoreCase(text)) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+209
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
@@ -85,6 +86,7 @@ import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -101,6 +103,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveMapper, CustomerArchive> implements ICustomerArchiveService {
|
||||
@@ -214,12 +217,151 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class));
|
||||
CustomerArchive after = copyCustomer(before);
|
||||
after.setApprovalStatus(APPROVAL_REVIEWING);
|
||||
if (Func.isEmpty(after.getCurrentNode()) || Objects.equals(after.getCurrentNode(), "草稿")) {
|
||||
after.setCurrentNode("客商准入审批");
|
||||
after.setCurrentProcessor("待处理");
|
||||
}
|
||||
addChangeRecord(id, "提交客商准入审批", before, after);
|
||||
return updateById(after);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean syncProcessNode(Long id, String currentNode, String currentProcessor, String approvalStatus) {
|
||||
if (Func.isEmpty(id)) {
|
||||
log.warn("同步客商当前节点失败,客商ID为空");
|
||||
return false;
|
||||
}
|
||||
CustomerArchive customer = getById(id);
|
||||
if (customer == null || Objects.equals(customer.getIsDeleted(), 1)) {
|
||||
log.warn("同步客商当前节点失败,客商不存在 id={}", id);
|
||||
return false;
|
||||
}
|
||||
String nextStatus = Func.isEmpty(approvalStatus) ? APPROVAL_REVIEWING : approvalStatus;
|
||||
boolean updated = this.lambdaUpdate()
|
||||
.eq(CustomerArchive::getId, id)
|
||||
.set(Func.isNotEmpty(currentNode), CustomerArchive::getCurrentNode, currentNode)
|
||||
.set(Func.isNotEmpty(currentProcessor), CustomerArchive::getCurrentProcessor, currentProcessor)
|
||||
.set(Func.isNotEmpty(nextStatus), CustomerArchive::getApprovalStatus, nextStatus)
|
||||
.update();
|
||||
log.info("同步客商当前节点 id={} currentNode={} currentProcessor={} approvalStatus={} result={}",
|
||||
id, currentNode, currentProcessor, nextStatus, updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean syncProcessNodeFromMk(Long id, Object currentNodes) {
|
||||
if (Func.isEmpty(id) || currentNodes == null) {
|
||||
return false;
|
||||
}
|
||||
List<Map<String, Object>> nodes = normalizeMkNodeList(currentNodes);
|
||||
if (nodes.isEmpty()) {
|
||||
log.warn("同步客商当前节点跳过,节点详情为空 id={}", id);
|
||||
return false;
|
||||
}
|
||||
List<String> nodeNames = new ArrayList<>();
|
||||
List<String> handlerNames = new ArrayList<>();
|
||||
for (Map<String, Object> node : nodes) {
|
||||
String nodeName = Func.toStr(node.get("nodeName"), "").trim();
|
||||
if (Func.isNotEmpty(nodeName)) {
|
||||
nodeNames.add(nodeName);
|
||||
}
|
||||
Object handlers = node.get("nodeHandlers");
|
||||
if (!(handlers instanceof Collection<?> handlerList)) {
|
||||
continue;
|
||||
}
|
||||
for (Object handler : handlerList) {
|
||||
Map<String, Object> handlerMap = asStringObjectMap(handler);
|
||||
String handlerName = Func.toStr(handlerMap.get("handlerName"), "").trim();
|
||||
if (Func.isNotEmpty(handlerName)) {
|
||||
handlerNames.add(handlerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return syncProcessNode(id, joinDistinct(nodeNames), joinDistinct(handlerNames), APPROVAL_REVIEWING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean syncProcessNodeFromProcessInfo(Long id, Object processInfo) {
|
||||
if (Func.isEmpty(id) || processInfo == null) {
|
||||
return false;
|
||||
}
|
||||
Map<String, Object> info = asStringObjectMap(processInfo);
|
||||
if (info.isEmpty()) {
|
||||
log.warn("同步客商当前节点跳过,流程实例详情为空 id={}", id);
|
||||
return false;
|
||||
}
|
||||
String currentProcessor = joinDistinct(extractTexts(info.get("currentHandlers"), "fdName", "name"), "、");
|
||||
Map<String, Object> taskInfo = asStringObjectMap(info.get("fdTaskInfo"));
|
||||
String currentNode = joinDistinct(extractTexts(taskInfo.get("handlerInfos"), "nodeName"), "、");
|
||||
if (Func.isEmpty(currentNode) && Func.isEmpty(currentProcessor)) {
|
||||
log.warn("同步客商当前节点跳过,未解析到节点名称或处理人 id={}", id);
|
||||
return false;
|
||||
}
|
||||
return syncProcessNode(id, currentNode, currentProcessor, APPROVAL_REVIEWING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean approveFromProcess(Long id, String processorName) {
|
||||
if (Func.isEmpty(id)) {
|
||||
log.warn("流程结束审核通过失败,客商ID为空");
|
||||
return false;
|
||||
}
|
||||
CustomerArchive before = getById(id);
|
||||
if (before == null || Objects.equals(before.getIsDeleted(), 1)) {
|
||||
log.warn("流程结束审核通过失败,客商不存在 id={}", id);
|
||||
return false;
|
||||
}
|
||||
if (Objects.equals(before.getApprovalStatus(), APPROVAL_APPROVED)) {
|
||||
log.info("流程结束审核通过跳过,客商已是审核通过 id={}", id);
|
||||
return true;
|
||||
}
|
||||
CustomerArchive after = copyCustomer(before);
|
||||
after.setAccessType(ACCESS_FORMAL);
|
||||
after.setApprovalStatus(APPROVAL_APPROVED);
|
||||
after.setCurrentNode("审核通过");
|
||||
after.setCurrentProcessor(Func.isEmpty(processorName) ? "系统" : processorName.trim());
|
||||
after.setApprovedTime(LocalDateTime.now());
|
||||
addChangeRecord(id, "客商准入审核通过", before, after);
|
||||
boolean updated = updateById(after);
|
||||
log.info("流程结束审核通过 id={} processor={} result={}", id, after.getCurrentProcessor(), updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@TenantIgnore
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean rejectFromProcess(Long id, String processorName) {
|
||||
if (Func.isEmpty(id)) {
|
||||
log.warn("流程审核驳回失败,客商ID为空");
|
||||
return false;
|
||||
}
|
||||
CustomerArchive before = getById(id);
|
||||
if (before == null || Objects.equals(before.getIsDeleted(), 1)) {
|
||||
log.warn("流程审核驳回失败,客商不存在 id={}", id);
|
||||
return false;
|
||||
}
|
||||
if (Objects.equals(before.getApprovalStatus(), APPROVAL_REJECTED)) {
|
||||
log.info("流程审核驳回跳过,客商已是审核不通过 id={}", id);
|
||||
return true;
|
||||
}
|
||||
CustomerArchive after = copyCustomer(before);
|
||||
after.setApprovalStatus(APPROVAL_REJECTED);
|
||||
after.setCurrentNode("审核不通过");
|
||||
after.setCurrentProcessor(Func.isEmpty(processorName) ? "系统" : processorName.trim());
|
||||
addChangeRecord(id, "客商准入审核不通过", before, after);
|
||||
boolean updated = updateById(after);
|
||||
log.info("流程审核驳回 id={} processor={} result={}", id, after.getCurrentProcessor(), updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean withdrawApproval(Long id) {
|
||||
@@ -1032,6 +1174,73 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
return Objects.requireNonNull(BeanUtil.copyProperties(customer, CustomerArchive.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> normalizeMkNodeList(Object currentNodes) {
|
||||
Object source = currentNodes;
|
||||
if (source instanceof String text && Func.isNotEmpty(text.trim())) {
|
||||
source = JsonUtil.parse(text, List.class);
|
||||
}
|
||||
if (source instanceof Collection<?> collection) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Object item : collection) {
|
||||
Map<String, Object> map = asStringObjectMap(item);
|
||||
if (!map.isEmpty()) {
|
||||
result.add(map);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Map<String, Object> single = asStringObjectMap(source);
|
||||
return single.isEmpty() ? List.of() : List.of(single);
|
||||
}
|
||||
|
||||
private Map<String, Object> asStringObjectMap(Object value) {
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
map.forEach((key, nested) -> {
|
||||
if (key != null) {
|
||||
result.put(String.valueOf(key), nested);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
if (value == null) {
|
||||
return Map.of();
|
||||
}
|
||||
try {
|
||||
Map<String, Object> parsed = JsonUtil.toMap(JsonUtil.toJson(value));
|
||||
return parsed == null ? Map.of() : parsed;
|
||||
} catch (Exception ignored) {
|
||||
return Map.of();
|
||||
}
|
||||
}
|
||||
|
||||
private String joinDistinct(List<String> values) {
|
||||
return joinDistinct(values, ",");
|
||||
}
|
||||
|
||||
private String joinDistinct(List<String> values, String delimiter) {
|
||||
return values.stream().filter(Func::isNotEmpty).distinct().collect(Collectors.joining(delimiter));
|
||||
}
|
||||
|
||||
private List<String> extractTexts(Object listObj, String... keys) {
|
||||
List<String> result = new ArrayList<>();
|
||||
if (!(listObj instanceof Collection<?> collection) || keys == null) {
|
||||
return result;
|
||||
}
|
||||
for (Object item : collection) {
|
||||
Map<String, Object> map = asStringObjectMap(item);
|
||||
for (String key : keys) {
|
||||
String text = Func.toStr(map.get(key), "").trim();
|
||||
if (Func.isNotEmpty(text)) {
|
||||
result.add(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addChangeRecord(Long customerId, String content, CustomerArchive before, CustomerArchive after) {
|
||||
Map<String, Object> beforeSnapshot = customerSnapshot(before);
|
||||
Map<String, Object> afterSnapshot = customerSnapshot(after);
|
||||
|
||||
Vendored
+9
@@ -65,6 +65,15 @@ public interface IMKClient {
|
||||
@PostMapping("${thirdParty.mk.getCurrentNodesUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getCurNodesInfo}")
|
||||
MKResultVO<List<MKNodeVO>> getCurrentNodes(@RequestBody MKCurrentNodesDTO param, @RequestParam("access_token") String accessToken);
|
||||
|
||||
/**
|
||||
* 获取流程实例详情
|
||||
* @param param
|
||||
* @param accessToken
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("${thirdParty.mk.getProcessInfoUrl:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getProcessInfo}")
|
||||
MKResultVO<Object> getProcessInfo(@RequestBody MKCurrentNodesDTO param, @RequestParam("access_token") String accessToken);
|
||||
|
||||
/**
|
||||
* 获取节点处理人信息
|
||||
* @param param
|
||||
|
||||
+6
@@ -1,6 +1,7 @@
|
||||
package org.springblade.thirdparty.mk.pojo.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -44,4 +45,9 @@ public class MKProcessCreateDTO implements Serializable {
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private Object tempVarData;
|
||||
/**
|
||||
* 业务类型,仅 ERP 内部使用,不传给 MK
|
||||
*/
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
private String bizType;
|
||||
}
|
||||
|
||||
+8
@@ -66,6 +66,14 @@ public interface IMKService {
|
||||
*/
|
||||
List<MKNodeVO> getCurrentNodes(String processInstanceId, String loginName);
|
||||
|
||||
/**
|
||||
* 获取流程实例详情
|
||||
* @param processInstanceId
|
||||
* @param loginName
|
||||
* @return
|
||||
*/
|
||||
Object getProcessInfo(String processInstanceId, String loginName);
|
||||
|
||||
/**
|
||||
* 获取节点处理人信息
|
||||
* @param processInstanceId
|
||||
|
||||
+15
@@ -246,6 +246,21 @@ public class MKServiceImpl implements IMKService {
|
||||
return nodesResult.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProcessInfo(String processInstanceId, String loginName) {
|
||||
String accessToken = getToken();
|
||||
MKCurrentNodesDTO param = new MKCurrentNodesDTO();
|
||||
param.setProcessInstanceId(processInstanceId);
|
||||
param.setLoginName(loginName);
|
||||
MKResultVO<Object> processInfoResult = client.getProcessInfo(param, accessToken);
|
||||
log.info("调用mk接口 获取流程实例详情 :{}", JSONUtil.toJsonStr(processInfoResult));
|
||||
if (processInfoResult == null || !processInfoResult.isSuccess()) {
|
||||
log.error("调用mk接口 获取流程实例详情异常:{}", JSONUtil.toJsonStr(processInfoResult));
|
||||
return null;
|
||||
}
|
||||
return processInfoResult.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MKAllHandlerVO getNodeHandlers(String processInstanceId, String loginName, String nodeId) {
|
||||
String accessToken = getToken();
|
||||
|
||||
@@ -118,6 +118,7 @@ thirdParty:
|
||||
processExecuteUrl: ${MK_PROCESS_EXECUTE_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/execute}
|
||||
processDeleteUrl: ${MK_PROCESS_DELETE_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/delete}
|
||||
getCurrentNodesUrl: ${MK_CURRENT_NODES_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getCurNodesInfo}
|
||||
getProcessInfoUrl: ${MK_PROCESS_INFO_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getProcessInfo}
|
||||
getNodeHandlersUrl: ${MK_NODE_HANDLERS_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getNodeHandlerInfos}
|
||||
getManualNodeUrl: ${MK_MANUAL_NODE_URL:/openapi/sys-lbpm/sysLbpmTemplate/openSupport/getManualNode}
|
||||
pushOrgDeptUrl: ${MK_PUSH_ORG_DEPT_URL:/openapi/sys-org/v2/push/orgDept}
|
||||
|
||||
@@ -91,6 +91,7 @@ thirdParty:
|
||||
processExecuteUrl: ${MK_PROCESS_EXECUTE_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/execute}
|
||||
processDeleteUrl: ${MK_PROCESS_DELETE_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/delete}
|
||||
getCurrentNodesUrl: ${MK_CURRENT_NODES_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getCurNodesInfo}
|
||||
getProcessInfoUrl: ${MK_PROCESS_INFO_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getProcessInfo}
|
||||
getNodeHandlersUrl: ${MK_NODE_HANDLERS_URL:/openapi/sys-lbpm/sysLbpmProcess/openSupport/getNodeHandlerInfos}
|
||||
getManualNodeUrl: ${MK_MANUAL_NODE_URL:/openapi/sys-lbpm/sysLbpmTemplate/openSupport/getManualNode}
|
||||
pushOrgDeptUrl: ${MK_PUSH_ORG_DEPT_URL:/openapi/sys-org/v2/push/orgDept}
|
||||
|
||||
@@ -218,9 +218,14 @@ blade:
|
||||
- /blade-auth/oauth/logout/**
|
||||
- /blade-transport/customer-archive/public/**
|
||||
- /customer-archive/public/**
|
||||
- /blade-transport/mk-process/public/**
|
||||
- /mk-process/public/**
|
||||
- /blade-openapi/openApi/mk/process/commonCallback
|
||||
- /openApi/mk/process/commonCallback
|
||||
- /feign/client/businessProcess/getCurrentNodes
|
||||
- /feign/client/businessProcess/getProcessInfo
|
||||
- /feign/client/customerArchive/syncProcessNode
|
||||
- /feign/client/mkProcess/**
|
||||
#授权认证配置
|
||||
auth:
|
||||
- method: ALL
|
||||
|
||||
Reference in New Issue
Block a user