Commit c33186569d1b848bad4680c707ac9a5aaa9eb1db
1 parent
5d6308e4
优化 mp 账号的刷新机制,使用 Job 轮询,替换 MQ 广播
Showing
14 changed files
with
62 additions
and
115 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
| ... | ... | @@ -164,7 +164,7 @@ public class CollectionUtils { |
| 164 | 164 | return from.stream().filter(predicate).findFirst().orElse(null); |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | - public static <T, V extends Comparable<? super V>> V getMaxValue(List<T> from, Function<T, V> valueFunc) { | |
| 167 | + public static <T, V extends Comparable<? super V>> V getMaxValue(Collection<T> from, Function<T, V> valueFunc) { | |
| 168 | 168 | if (CollUtil.isEmpty(from)) { |
| 169 | 169 | return null; |
| 170 | 170 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessInstanceServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.service.definition; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } } | |
| 0 | 2 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmTaskAssignRuleServiceImpl.java
| ... | ... | @@ -15,8 +15,8 @@ import cn.iocoder.yudao.module.bpm.convert.definition.BpmTaskAssignRuleConvert; |
| 15 | 15 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO; |
| 16 | 16 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; |
| 17 | 17 | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper; |
| 18 | -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; | |
| 19 | 18 | import cn.iocoder.yudao.module.bpm.enums.DictTypeConstants; |
| 19 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTaskAssignRuleTypeEnum; | |
| 20 | 20 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior.script.BpmTaskAssignScript; |
| 21 | 21 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| 22 | 22 | import cn.iocoder.yudao.module.system.api.dept.PostApi; |
| ... | ... | @@ -89,7 +89,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 89 | 89 | |
| 90 | 90 | @Override |
| 91 | 91 | public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId, |
| 92 | - String taskDefinitionKey) { | |
| 92 | + String taskDefinitionKey) { | |
| 93 | 93 | return taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, taskDefinitionKey); |
| 94 | 94 | } |
| 95 | 95 | |
| ... | ... | @@ -128,14 +128,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 128 | 128 | validTaskAssignRuleOptions(reqVO.getType(), reqVO.getOptions()); |
| 129 | 129 | // 校验是否已经配置 |
| 130 | 130 | BpmTaskAssignRuleDO existRule = |
| 131 | - taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); | |
| 131 | + taskRuleMapper.selectListByModelIdAndTaskDefinitionKey(reqVO.getModelId(), reqVO.getTaskDefinitionKey()); | |
| 132 | 132 | if (existRule != null) { |
| 133 | 133 | throw exception(TASK_ASSIGN_RULE_EXISTS, reqVO.getModelId(), reqVO.getTaskDefinitionKey()); |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | // 存储 |
| 137 | 137 | BpmTaskAssignRuleDO rule = BpmTaskAssignRuleConvert.INSTANCE.convert(reqVO) |
| 138 | - .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建 | |
| 138 | + .setProcessDefinitionId(BpmTaskAssignRuleDO.PROCESS_DEFINITION_ID_NULL); // 只有流程模型,才允许新建 | |
| 139 | 139 | taskRuleMapper.insert(rule); |
| 140 | 140 | return rule.getId(); |
| 141 | 141 | } |
| ... | ... | @@ -169,14 +169,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 169 | 169 | |
| 170 | 170 | // 遍历,匹配对应的规则 |
| 171 | 171 | Map<String, BpmTaskAssignRuleRespVO> processInstanceRuleMap = |
| 172 | - CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); | |
| 172 | + CollectionUtils.convertMap(processInstanceRules, BpmTaskAssignRuleRespVO::getTaskDefinitionKey); | |
| 173 | 173 | for (BpmTaskAssignRuleRespVO modelRule : modelRules) { |
| 174 | 174 | BpmTaskAssignRuleRespVO processInstanceRule = processInstanceRuleMap.get(modelRule.getTaskDefinitionKey()); |
| 175 | 175 | if (processInstanceRule == null) { |
| 176 | 176 | return false; |
| 177 | 177 | } |
| 178 | 178 | if (!ObjectUtil.equals(modelRule.getType(), processInstanceRule.getType()) || !ObjectUtil.equal( |
| 179 | - modelRule.getOptions(), processInstanceRule.getOptions())) { | |
| 179 | + modelRule.getOptions(), processInstanceRule.getOptions())) { | |
| 180 | 180 | return false; |
| 181 | 181 | } |
| 182 | 182 | } |
| ... | ... | @@ -192,7 +192,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 192 | 192 | // 开始复制 |
| 193 | 193 | List<BpmTaskAssignRuleDO> newRules = BpmTaskAssignRuleConvert.INSTANCE.convertList2(rules); |
| 194 | 194 | newRules.forEach(rule -> rule.setProcessDefinitionId(toProcessDefinitionId).setId(null).setCreateTime(null) |
| 195 | - .setUpdateTime(null)); | |
| 195 | + .setUpdateTime(null)); | |
| 196 | 196 | taskRuleMapper.insertBatch(newRules); |
| 197 | 197 | } |
| 198 | 198 | |
| ... | ... | @@ -215,7 +215,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 215 | 215 | if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.ROLE.getType())) { |
| 216 | 216 | roleApi.validRoleList(options); |
| 217 | 217 | } else if (ObjectUtils.equalsAny(type, BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(), |
| 218 | - BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { | |
| 218 | + BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) { | |
| 219 | 219 | deptApi.validateDeptList(options); |
| 220 | 220 | } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) { |
| 221 | 221 | postApi.validPostList(options); |
| ... | ... | @@ -225,7 +225,7 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService { |
| 225 | 225 | userGroupService.validUserGroups(options); |
| 226 | 226 | } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) { |
| 227 | 227 | dictDataApi.validateDictDataList(DictTypeConstants.TASK_ASSIGN_SCRIPT, |
| 228 | - CollectionUtils.convertSet(options, String::valueOf)); | |
| 228 | + CollectionUtils.convertSet(options, String::valueOf)); | |
| 229 | 229 | } else { |
| 230 | 230 | throw new IllegalArgumentException(format("未知的规则类型({})", type)); |
| 231 | 231 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/script/impl/BpmTaskAssignLeaderX2ScriptTest.java
| ... | ... | @@ -40,6 +40,8 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { |
| 40 | 40 | // mock 方法(startUser) |
| 41 | 41 | AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L)); |
| 42 | 42 | when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); |
| 43 | + // mock 方法(getStartUserDept)没有部门 | |
| 44 | + when(deptApi.getDept(eq(10L))).thenReturn(null); | |
| 43 | 45 | |
| 44 | 46 | // 调用 |
| 45 | 47 | Set<Long> result = script.calculateTaskCandidateUsers(execution); |
| ... | ... | @@ -56,7 +58,9 @@ public class BpmTaskAssignLeaderX2ScriptTest extends BaseMockitoUnitTest { |
| 56 | 58 | when(adminUserApi.getUser(eq(1L))).thenReturn(startUser); |
| 57 | 59 | DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) |
| 58 | 60 | .setLeaderUserId(20L)); |
| 61 | + // mock 方法(getDept) | |
| 59 | 62 | when(deptApi.getDept(eq(10L))).thenReturn(startUserDept); |
| 63 | + when(deptApi.getDept(eq(100L))).thenReturn(null); | |
| 60 | 64 | |
| 61 | 65 | // 调用 |
| 62 | 66 | Set<Long> result = script.calculateTaskCandidateUsers(execution); | ... | ... |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/account/MpAccountMapper.java
| ... | ... | @@ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 8 | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | +import org.apache.ibatis.annotations.Select; | |
| 10 | + | |
| 11 | +import java.time.LocalDateTime; | |
| 9 | 12 | |
| 10 | 13 | @Mapper |
| 11 | 14 | public interface MpAccountMapper extends BaseMapperX<MpAccountDO> { |
| ... | ... | @@ -22,4 +25,7 @@ public interface MpAccountMapper extends BaseMapperX<MpAccountDO> { |
| 22 | 25 | return selectOne(MpAccountDO::getAppId, appId); |
| 23 | 26 | } |
| 24 | 27 | |
| 28 | + @Select("SELECT COUNT(*) FROM pay_account WHERE update_time > #{maxUpdateTime}") | |
| 29 | + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); | |
| 30 | + | |
| 25 | 31 | } | ... | ... |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/consumer/MpAccountRefreshConsumer.java deleted
| 1 | -package cn.iocoder.yudao.module.mp.mq.consumer; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; | |
| 4 | -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage; | |
| 5 | -import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | |
| 6 | -import lombok.extern.slf4j.Slf4j; | |
| 7 | -import org.springframework.stereotype.Component; | |
| 8 | - | |
| 9 | -import javax.annotation.Resource; | |
| 10 | - | |
| 11 | -/** | |
| 12 | - * 针对 {@link MpAccountRefreshMessage} 的消费者 | |
| 13 | - * | |
| 14 | - * @author 芋道源码 | |
| 15 | - */ | |
| 16 | -@Component | |
| 17 | -@Slf4j | |
| 18 | -public class MpAccountRefreshConsumer extends AbstractChannelMessageListener<MpAccountRefreshMessage> { | |
| 19 | - | |
| 20 | - @Resource | |
| 21 | - private MpAccountService mpAccountService; | |
| 22 | - | |
| 23 | - @Override | |
| 24 | - public void onMessage(MpAccountRefreshMessage message) { | |
| 25 | - log.info("[onMessage][收到 Account 刷新消息]"); | |
| 26 | - mpAccountService.initLocalCache(); | |
| 27 | - } | |
| 28 | - | |
| 29 | -} |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/MpAccountRefreshMessage.java deleted
| 1 | -package cn.iocoder.yudao.module.mp.mq.message; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; | |
| 4 | -import lombok.Data; | |
| 5 | -import lombok.EqualsAndHashCode; | |
| 6 | - | |
| 7 | -/** | |
| 8 | - * 公众号账号刷新 Message | |
| 9 | - * | |
| 10 | - * @author 芋道源码 | |
| 11 | - */ | |
| 12 | -@Data | |
| 13 | -@EqualsAndHashCode(callSuper = true) | |
| 14 | -public class MpAccountRefreshMessage extends AbstractChannelMessage { | |
| 15 | - | |
| 16 | - @Override | |
| 17 | - public String getChannel() { | |
| 18 | - return "mp.account.refresh"; | |
| 19 | - } | |
| 20 | - | |
| 21 | -} |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/MpAccountProducer.java deleted
| 1 | -package cn.iocoder.yudao.module.mp.mq.producer; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | |
| 4 | -import cn.iocoder.yudao.module.mp.mq.message.MpAccountRefreshMessage; | |
| 5 | -import org.springframework.stereotype.Component; | |
| 6 | - | |
| 7 | -import javax.annotation.Resource; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * 公众号账号 Producer | |
| 11 | - * | |
| 12 | - * @author 芋道源码 | |
| 13 | - */ | |
| 14 | -@Component | |
| 15 | -public class MpAccountProducer { | |
| 16 | - | |
| 17 | - @Resource | |
| 18 | - private RedisMQTemplate redisMQTemplate; | |
| 19 | - | |
| 20 | - /** | |
| 21 | - * 发送 {@link MpAccountRefreshMessage} 消息 | |
| 22 | - */ | |
| 23 | - public void sendAccountRefreshMessage() { | |
| 24 | - MpAccountRefreshMessage message = new MpAccountRefreshMessage(); | |
| 25 | - redisMQTemplate.send(message); | |
| 26 | - } | |
| 27 | - | |
| 28 | -} |
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/MpAccountServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.mp.service.account; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjUtil; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 7 | 7 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 8 | 8 | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; |
| ... | ... | @@ -13,7 +13,6 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |
| 13 | 13 | import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper; |
| 14 | 14 | import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; |
| 15 | 15 | import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; |
| 16 | -import cn.iocoder.yudao.module.mp.mq.producer.MpAccountProducer; | |
| 17 | 16 | import com.google.common.annotations.VisibleForTesting; |
| 18 | 17 | import lombok.Getter; |
| 19 | 18 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -21,15 +20,20 @@ import me.chanjar.weixin.common.error.WxErrorException; |
| 21 | 20 | import me.chanjar.weixin.mp.api.WxMpService; |
| 22 | 21 | import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; |
| 23 | 22 | import org.springframework.context.annotation.Lazy; |
| 23 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 24 | 24 | import org.springframework.stereotype.Service; |
| 25 | 25 | import org.springframework.validation.annotation.Validated; |
| 26 | 26 | |
| 27 | 27 | import javax.annotation.PostConstruct; |
| 28 | 28 | import javax.annotation.Resource; |
| 29 | +import java.time.LocalDateTime; | |
| 29 | 30 | import java.util.List; |
| 30 | 31 | import java.util.Map; |
| 32 | +import java.util.concurrent.TimeUnit; | |
| 31 | 33 | |
| 32 | 34 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 35 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | |
| 36 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; | |
| 33 | 37 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS; |
| 34 | 38 | |
| 35 | 39 | /** |
| ... | ... | @@ -58,9 +62,6 @@ public class MpAccountServiceImpl implements MpAccountService { |
| 58 | 62 | @Lazy // 延迟加载,解决循环依赖的问题 |
| 59 | 63 | private MpServiceFactory mpServiceFactory; |
| 60 | 64 | |
| 61 | - @Resource | |
| 62 | - private MpAccountProducer mpAccountProducer; | |
| 63 | - | |
| 64 | 65 | @Override |
| 65 | 66 | @PostConstruct |
| 66 | 67 | public void initLocalCache() { |
| ... | ... | @@ -72,7 +73,30 @@ public class MpAccountServiceImpl implements MpAccountService { |
| 72 | 73 | |
| 73 | 74 | // 第二步:构建缓存。创建或更新支付 Client |
| 74 | 75 | mpServiceFactory.init(accounts); |
| 75 | - accountCache = CollectionUtils.convertMap(accounts, MpAccountDO::getAppId); | |
| 76 | + accountCache = convertMap(accounts, MpAccountDO::getAppId); | |
| 77 | + }); | |
| 78 | + } | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * 通过定时任务轮询,刷新缓存 | |
| 82 | + * | |
| 83 | + * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新 | |
| 84 | + */ | |
| 85 | + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) | |
| 86 | + public void refreshLocalCache() { | |
| 87 | + // 注意:忽略自动多租户,因为要全局初始化缓存 | |
| 88 | + TenantUtils.executeIgnore(() -> { | |
| 89 | + // 情况一:如果缓存里没有数据,则直接刷新缓存 | |
| 90 | + if (CollUtil.isEmpty(accountCache)) { | |
| 91 | + initLocalCache(); | |
| 92 | + return; | |
| 93 | + } | |
| 94 | + | |
| 95 | + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 | |
| 96 | + LocalDateTime maxTime = getMaxValue(accountCache.values(), MpAccountDO::getUpdateTime); | |
| 97 | + if (mpAccountMapper.selectCountByUpdateTimeGt(maxTime) > 0) { | |
| 98 | + initLocalCache(); | |
| 99 | + } | |
| 76 | 100 | }); |
| 77 | 101 | } |
| 78 | 102 | |
| ... | ... | @@ -85,8 +109,8 @@ public class MpAccountServiceImpl implements MpAccountService { |
| 85 | 109 | MpAccountDO account = MpAccountConvert.INSTANCE.convert(createReqVO); |
| 86 | 110 | mpAccountMapper.insert(account); |
| 87 | 111 | |
| 88 | - // 发送刷新消息 | |
| 89 | - mpAccountProducer.sendAccountRefreshMessage(); | |
| 112 | + // 刷新缓存 | |
| 113 | + initLocalCache(); | |
| 90 | 114 | return account.getId(); |
| 91 | 115 | } |
| 92 | 116 | |
| ... | ... | @@ -101,8 +125,8 @@ public class MpAccountServiceImpl implements MpAccountService { |
| 101 | 125 | MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO); |
| 102 | 126 | mpAccountMapper.updateById(updateObj); |
| 103 | 127 | |
| 104 | - // 发送刷新消息 | |
| 105 | - mpAccountProducer.sendAccountRefreshMessage(); | |
| 128 | + // 刷新缓存 | |
| 129 | + initLocalCache(); | |
| 106 | 130 | } |
| 107 | 131 | |
| 108 | 132 | @Override |
| ... | ... | @@ -112,8 +136,8 @@ public class MpAccountServiceImpl implements MpAccountService { |
| 112 | 136 | // 删除 |
| 113 | 137 | mpAccountMapper.deleteById(id); |
| 114 | 138 | |
| 115 | - // 发送刷新消息 | |
| 116 | - mpAccountProducer.sendAccountRefreshMessage(); | |
| 139 | + // 刷新缓存 | |
| 140 | + initLocalCache(); | |
| 117 | 141 | } |
| 118 | 142 | |
| 119 | 143 | private MpAccountDO validateAccountExists(Long id) { | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java
yudao-module-pay/yudao-module-pay-biz/pom.xml
| ... | ... | @@ -61,12 +61,6 @@ |
| 61 | 61 | <artifactId>yudao-spring-boot-starter-job</artifactId> |
| 62 | 62 | </dependency> |
| 63 | 63 | |
| 64 | - <!-- 消息队列相关 --> | |
| 65 | - <dependency> | |
| 66 | - <groupId>cn.iocoder.boot</groupId> | |
| 67 | - <artifactId>yudao-spring-boot-starter-mq</artifactId> | |
| 68 | - </dependency> | |
| 69 | - | |
| 70 | 64 | <!-- Test 测试相关 --> |
| 71 | 65 | <dependency> |
| 72 | 66 | <groupId>cn.iocoder.boot</groupId> | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
| ... | ... | @@ -15,7 +15,6 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR |
| 15 | 15 | import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert; |
| 16 | 16 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 17 | 17 | import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper; |
| 18 | -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; | |
| 19 | 18 | import lombok.Getter; |
| 20 | 19 | import lombok.Setter; |
| 21 | 20 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -112,7 +111,7 @@ public class PayChannelServiceImpl implements PayChannelService { |
| 112 | 111 | channelMapper.insert(channel); |
| 113 | 112 | |
| 114 | 113 | // 刷新缓存 |
| 115 | - refreshLocalCache(); | |
| 114 | + initLocalCache(); | |
| 116 | 115 | return channel.getId(); |
| 117 | 116 | } |
| 118 | 117 | |
| ... | ... | @@ -127,7 +126,7 @@ public class PayChannelServiceImpl implements PayChannelService { |
| 127 | 126 | channelMapper.updateById(channel); |
| 128 | 127 | |
| 129 | 128 | // 刷新缓存 |
| 130 | - refreshLocalCache(); | |
| 129 | + initLocalCache(); | |
| 131 | 130 | } |
| 132 | 131 | |
| 133 | 132 | /** |
| ... | ... | @@ -160,7 +159,7 @@ public class PayChannelServiceImpl implements PayChannelService { |
| 160 | 159 | channelMapper.deleteById(id); |
| 161 | 160 | |
| 162 | 161 | // 刷新缓存 |
| 163 | - refreshLocalCache(); | |
| 162 | + initLocalCache(); | |
| 164 | 163 | } |
| 165 | 164 | |
| 166 | 165 | private PayChannelDO validateChannelExists(Long id) { | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -167,7 +167,8 @@ yudao: |
| 167 | 167 | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| 168 | 168 | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| 169 | 169 | pay: |
| 170 | - callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback | |
| 170 | + order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 | |
| 171 | + refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 | |
| 171 | 172 | demo: true # 开启演示模式 |
| 172 | 173 | |
| 173 | 174 | justauth: | ... | ... |