package cn.iocoder.yudao.module.vehicle.service.application; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationPageReqVO; import cn.iocoder.yudao.module.vehicle.controller.admin.application.vo.VehicleApplicationSaveReqVO; import cn.iocoder.yudao.module.vehicle.dal.dataobject.application.VehicleApplicationDO; import cn.iocoder.yudao.module.vehicle.dal.mysql.application.VehicleApplicationMapper; import cn.iocoder.yudao.module.vehicle.dal.redis.VehicleNoRedisDAO; import cn.iocoder.yudao.module.vehicle.enums.VehicleApplicationStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.repository.ProcessDefinition; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.vehicle.enums.ErrorCodeConstants.*; /** * 用车申请 Service 实现类 */ @Service @Validated @Slf4j public class VehicleApplicationServiceImpl implements VehicleApplicationService { /** 用车审批 BPM 分类编码 */ public static final String VEHICLE_APPROVE_CATEGORY_CODE = "vehicle_application_approve"; @Resource private VehicleApplicationMapper applicationMapper; @Resource private VehicleNoRedisDAO noRedisDAO; @Resource private BpmProcessInstanceApi processInstanceApi; @Resource private BpmCategoryService bpmCategoryService; @Resource private BpmProcessDefinitionService bpmProcessDefinitionService; @Resource private AdminUserApi adminUserApi; @Override @Transactional(rollbackFor = Exception.class) public Long createApplication(VehicleApplicationSaveReqVO reqVO) { validateUseTime(reqVO); Long userId = getLoginUserId(); AdminUserRespDTO user = userId != null ? adminUserApi.getUser(userId) : null; VehicleApplicationDO application = BeanUtils.toBean(reqVO, VehicleApplicationDO.class, in -> in .setNo(noRedisDAO.generate(VehicleNoRedisDAO.APPLICATION_NO_PREFIX)) .setApplicantId(userId) .setApplicantName(user != null ? user.getNickname() : "未知用户") .setStatus(VehicleApplicationStatusEnum.DRAFT.getStatus())); applicationMapper.insert(application); return application.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateApplication(VehicleApplicationSaveReqVO reqVO) { VehicleApplicationDO old = validateApplicationExists(reqVO.getId()); if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(old.getStatus()) && !VehicleApplicationStatusEnum.REJECTED.getStatus().equals(old.getStatus())) { throw exception(VEHICLE_APPLICATION_UPDATE_FAIL, old.getNo(), VehicleApplicationStatusEnum.nameOf(old.getStatus())); } validateUseTime(reqVO); VehicleApplicationDO updateObj = BeanUtils.toBean(reqVO, VehicleApplicationDO.class); applicationMapper.updateById(updateObj); } @Override @Transactional(rollbackFor = Exception.class) public void deleteApplication(Long id) { VehicleApplicationDO application = validateApplicationExists(id); if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(application.getStatus())) { throw exception(VEHICLE_APPLICATION_DELETE_FAIL, application.getNo(), VehicleApplicationStatusEnum.nameOf(application.getStatus())); } applicationMapper.deleteById(id); } @Override public VehicleApplicationDO getApplication(Long id) { return applicationMapper.selectById(id); } @Override public PageResult getApplicationPage(VehicleApplicationPageReqVO reqVO) { return applicationMapper.selectPage(reqVO); } @Override @Transactional(rollbackFor = Exception.class) public void submitApplication(Long id, String processDefinitionKey) { VehicleApplicationDO application = validateApplicationExists(id); if (!VehicleApplicationStatusEnum.DRAFT.getStatus().equals(application.getStatus()) && !VehicleApplicationStatusEnum.REJECTED.getStatus().equals(application.getStatus())) { throw exception(VEHICLE_APPLICATION_SUBMIT_FAIL, application.getNo(), VehicleApplicationStatusEnum.nameOf(application.getStatus())); } String processInstanceId = processInstanceApi.createProcessInstance(getLoginUserId(), new BpmProcessInstanceCreateReqDTO() .setProcessDefinitionKey(processDefinitionKey) .setBusinessKey(String.valueOf(id))); applicationMapper.updateById(new VehicleApplicationDO() .setId(id) .setStatus(VehicleApplicationStatusEnum.APPROVING.getStatus()) .setProcessInstanceId(processInstanceId)); } @Override @Transactional(rollbackFor = Exception.class) public void updateApplicationAuditStatus(Long id, Integer bpmResult) { VehicleApplicationDO application = validateApplicationExists(id); if (!VehicleApplicationStatusEnum.APPROVING.getStatus().equals(application.getStatus())) { log.warn("[updateApplicationAuditStatus] 用车申请({}) 不处于审批中状态", id); return; } Integer newStatus = convertBpmResultToStatus(bpmResult); if (newStatus == null) { return; } VehicleApplicationDO updateObj = new VehicleApplicationDO().setId(id).setStatus(newStatus); if (VehicleApplicationStatusEnum.APPROVED.getStatus().equals(newStatus)) { updateObj.setApproveTime(LocalDateTime.now()); } applicationMapper.updateById(updateObj); } @Override public List> getApproveProcessDefinitionList() { validateVehicleApproveCategory(); List definitionInfoList = bpmProcessDefinitionService.getProcessDefinitionInfoListByCategory(VEHICLE_APPROVE_CATEGORY_CODE); if (CollUtil.isEmpty(definitionInfoList)) { return Collections.emptyList(); } Map latestVersionMap = new HashMap<>(); for (BpmProcessDefinitionInfoDO info : definitionInfoList) { ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId()); if (pd == null || pd.isSuspended()) { continue; } ProcessDefinition existing = latestVersionMap.get(pd.getKey()); if (existing == null || pd.getVersion() > existing.getVersion()) { latestVersionMap.put(pd.getKey(), pd); } } List> result = new ArrayList<>(); for (ProcessDefinition pd : latestVersionMap.values()) { Map item = new HashMap<>(); item.put("id", pd.getId()); item.put("key", pd.getKey()); item.put("name", pd.getName()); result.add(item); } return result; } private void validateVehicleApproveCategory() { List categories = bpmCategoryService.getCategoryListByCode( Collections.singletonList(VEHICLE_APPROVE_CATEGORY_CODE)); if (CollUtil.isEmpty(categories)) { throw exception(VEHICLE_APPLICATION_BPM_CATEGORY_NOT_EXISTS); } } private void validateUseTime(VehicleApplicationSaveReqVO reqVO) { if (reqVO.getUseStartTime() != null && reqVO.getUseEndTime() != null && !reqVO.getUseEndTime().isAfter(reqVO.getUseStartTime())) { throw exception(VEHICLE_APPLICATION_TIME_INVALID); } } private VehicleApplicationDO validateApplicationExists(Long id) { VehicleApplicationDO application = id != null ? applicationMapper.selectById(id) : null; if (application == null) { throw exception(VEHICLE_APPLICATION_NOT_EXISTS); } return application; } private Integer convertBpmResultToStatus(Integer bpmResult) { if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) { return VehicleApplicationStatusEnum.APPROVED.getStatus(); } if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) { return VehicleApplicationStatusEnum.REJECTED.getStatus(); } if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) { return VehicleApplicationStatusEnum.DRAFT.getStatus(); } return null; } }