refactor(bpm): 重构流程附件功能

- 新增 resolveEntityType 方法动态解析实体类型
-优化 updateAttachment 方法,增加日志记录和异常处理
- 移除 BpmProcessInstanceController 中的静态实体类型映射
- 使用服务层方法统一处理实体类型解析和附件更新
This commit is contained in:
aikai 2025-07-02 11:35:32 +08:00
parent b8339c9056
commit a71cd74ecc
3 changed files with 84 additions and 93 deletions

View File

@ -1,10 +1,8 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.BpmOALeaveController;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.attachment.BpmOAAttachmentReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.print.BpmProcessInstancePrintDataRespVO;
@ -12,16 +10,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.financialpayment.FinancialPaymentDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAPetitionDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOAReimbursementDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOASealDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAPetitionMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOAReimbursementMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOASealMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.service.financialpayment.FinancialPaymentService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOAAttachmentService;
@ -36,26 +25,21 @@ import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程实例") // 流程实例通过流程定义创建的一次申请
@Tag(name = "管理后台 - 流程实例") // 流程实例通过流程定义创建的一次"申请"
@RestController
@RequestMapping("/bpm/process-instance")
@Validated
@ -78,6 +62,9 @@ public class BpmProcessInstanceController {
@Resource
private FinancialPaymentService financialPaymentService;
@Resource
private BpmOAAttachmentService bpmOAAttachmentService;
@GetMapping("/my-page")
@Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@ -210,69 +197,14 @@ public class BpmProcessInstanceController {
return success(processInstanceService.getOAReportPrintData(reqVO));
}
private final Map<String, List> entityTypeMap = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// 注册支持的实体类型
entityTypeMap.put("oa_reimbursement", new ArrayList<Class<?>>(Arrays.asList(BpmOAReimbursementDO.class, BpmOAReimbursementMapper.class)));
entityTypeMap.put("oa_seal", new ArrayList<Class<?>>(Arrays.asList(BpmOASealDO.class, BpmOASealMapper.class)));
entityTypeMap.put("oa_contract", new ArrayList<Class<?>>(Arrays.asList(BpmOASealDO.class, BpmOASealMapper.class)));
entityTypeMap.put("oa_leave", new ArrayList<Class<?>>(Arrays.asList(BpmOALeaveDO.class, BpmOALeaveMapper.class)));
entityTypeMap.put("oa_cash", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_overtime", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_regular", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_shiftjobs", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_second", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_evection", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_procure", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_procure_pay", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_imprest", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_incentive", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("work_task", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_entry", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_overtime", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_regular", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_incentive", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_salary", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_go_out", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_salary_adjustment", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_petition", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_business_card", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_hire", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_business_card", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_business_hospitality", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_finance", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_invoice", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_replacementCard", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_project", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_payment", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_expenses", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_loan", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_receipt", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
entityTypeMap.put("oa_refund", new ArrayList<Class<?>>(Arrays.asList(BpmOAPetitionDO.class, BpmOAPetitionMapper.class)));
}
@Resource
private BpmOAAttachmentService bpmOAAttachmentService;
@PostMapping("/uploadAttachment")
@Operation(summary = "更新流程附件")
@DataPermission(enable = false)
public CommonResult<?> uploadAttachment(@Valid @RequestBody BpmOAAttachmentReqVO reqVO) {
// 解析实体类型
List<Class<?>> entityClazzs = resolveEntityType(reqVO.getProcessType());
// 调用通用服务
// 通过服务层解析实体类型
List<Class<?>> entityClazzs = bpmOAAttachmentService.resolveEntityType(reqVO.getProcessType());
// 调用服务层更新附件
bpmOAAttachmentService.updateAttachment(entityClazzs, reqVO.getProcessBusinessId(), reqVO.getFileItems());
return success("附件更新成功");
}
private List<Class<?>> resolveEntityType(String typeName) {
List<Class<?>> entityClazzs = entityTypeMap.get(typeName);
if (entityClazzs == null) {
// 添加日志记录以便更好地调试
log.error("不支持的流程类型: {}", typeName);
throw new ServiceException(500, "不支持的流程类型: " + typeName);
}
return entityClazzs;
}
}

View File

@ -4,15 +4,23 @@ import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import java.util.List;
/**
* BPM OA 附件服务接口
*/
public interface BpmOAAttachmentService {
/**
* 更新业务实体的附件信息
* @param entityClazzs 业务实体
* @param entityClazzs 业务实体类列表
* @param entityId 业务ID
* @param fileItems 附件数据
*/
void updateAttachment(List<Class<?>> entityClazzs, Long entityId, List<UploadUserFile> fileItems);
/**
* 通过流程代码解析实体类型
* @param processCode 流程标识码
* @return 实体类和Mapper类的列表
*/
List<Class<?>> resolveEntityType(String processCode);
}

View File

@ -1,47 +1,98 @@
package cn.iocoder.yudao.module.bpm.service.oa;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.UploadUserFile;
import cn.iocoder.yudao.module.bpm.dal.dataobject.processmapping.BpmProcessMappingConfigDO;
import cn.iocoder.yudao.module.bpm.framework.core.util.ReflectionInvoker;
import cn.iocoder.yudao.module.bpm.service.processmapping.ProcessMappingConfigService;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import liquibase.pro.packaged.T;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.persistence.entity.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_MAPPING_CONFIG_NOT_EXISTS;
@Service
@Validated
@Slf4j
public class BpmOAAttachmentServiceImpl implements BpmOAAttachmentService {
@Resource
private ProcessMappingConfigService processMappingConfigService;
/**
* 更新业务实体的附件信息
* @param entityClazzs 业务实体
* @param entityClazzs 业务实体类列表
* @param entityId 业务ID
* @param fileItems 附件数据
*/
@Override
public void updateAttachment(List<Class<?>> entityClazzs, Long entityId, List<UploadUserFile> fileItems) {
try {
// 1. 获取实体, 赋值实体类
Class doClass = entityClazzs.get(0) ;
// 1. 获取实体类并创建实例
Class<?> doClass = entityClazzs.get(0);
Object entity = doClass.getDeclaredConstructor().newInstance();
Method setIdMethod = doClass.getMethod("setId", Long.class) ;
// 2. 使用反射设置实体属性
Method setIdMethod = doClass.getMethod("setId", Long.class);
setIdMethod.invoke(entity, entityId);
Method setFileItems = doClass.getMethod("setFileItems", List.class) ;
Method setFileItems = doClass.getMethod("setFileItems", List.class);
setFileItems.invoke(entity, fileItems);
//获取MyBatist的操作类实体
Class mapperClass = entityClazzs.get(1) ;
BaseMapper mapper = (BaseMapper) SpringUtil.getBean(mapperClass);
mapper.updateById(entity);
// 3. 获取Mapper实例并调用updateById方法
Class<?> mapperClass = entityClazzs.get(1);
Object mapperBean = SpringUtil.getBean(mapperClass);
// 4. 使用ReflectionInvoker调用updateById方法
ReflectionInvoker.invokeMapperMethod(mapperBean, "updateById", Object.class, entity);
log.debug("[updateAttachment][附件更新成功] entityClass={}, entityId={}, fileCount={}",
doClass.getSimpleName(), entityId, fileItems != null ? fileItems.size() : 0);
} catch (Exception e) {
log.error(e.getMessage());
throw new ServiceException(500, "系统异常,请联系管理员");
log.error("[updateAttachment][附件更新失败] entityId={}, error={}", entityId, e.getMessage(), e);
throw new ServiceException(500, "附件更新失败,请联系管理员");
}
}
/**
* 通过流程代码动态解析实体类型
* @param processCode 流程标识码
* @return 实体类和Mapper类的列表
*/
@Override
public List<Class<?>> resolveEntityType(String processCode) {
try {
// 1. 获取流程映射配置
BpmProcessMappingConfigDO config = processMappingConfigService.getProcessMappingConfigByCode(processCode);
if (config == null) {
log.error("[resolveEntityType][流程映射配置不存在] processCode={}", processCode);
throw exception(PROCESS_MAPPING_CONFIG_NOT_EXISTS);
}
// 2. 动态加载实体类和Mapper类
Class<?> entityClass = ClassUtil.loadClass(config.getEntityClass());
Class<?> mapperClass = ClassUtil.loadClass(config.getMapperClass());
// 3. 验证Mapper类是否可获取Bean实例
SpringUtil.getBean(mapperClass);
log.debug("[resolveEntityType][成功解析流程类型] processCode={}, entityClass={}, mapperClass={}",
processCode, config.getEntityClass(), config.getMapperClass());
return Arrays.asList(entityClass, mapperClass);
} catch (Exception e) {
log.error("[resolveEntityType][解析流程类型失败] processCode={}", processCode, e);
throw new ServiceException(500, String.format("不支持的流程类型: %s, 错误信息: %s", processCode, e.getMessage()));
}
}
}