| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeApproverDto; |
| | | import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto; |
| | | import com.ruoyi.approve.bean.dto.FinReimbursementDto; |
| | | import com.ruoyi.approve.bean.vo.FinReimbursementVo; |
| | | import com.ruoyi.approve.mapper.ApprovalInstanceMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementDetailMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementMapper; |
| | | import com.ruoyi.approve.mapper.FinReimbursementTravelMapper; |
| | | import com.ruoyi.approve.pojo.*; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.approve.service.*; |
| | | import com.ruoyi.common.enums.TypeEnums; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.project.system.service.ISysNoticeService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ¥éå主表 æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-21 09:56:15 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class FinReimbursementServiceImpl extends ServiceImpl<FinReimbursementMapper, FinReimbursement> implements FinReimbursementService { |
| | | |
| | | private static final String BILL_STATUS_DRAFT = "DRAFT"; |
| | | private static final String BILL_STATUS_IN_APPROVAL = "IN_APPROVAL"; |
| | | private static final String NODE_STATUS_WAITING = "WAITING"; |
| | | |
| | | private final ApprovalInstanceMapper approvalInstanceMapper; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final ApprovalInstanceNodeService approvalInstanceNodeService; |
| | | private final ApprovalTaskService approvalTaskService; |
| | | private final ApprovalRecordService approvalRecordService; |
| | | private final FinReimbursementMapper finReimbursementMapper; |
| | | private final FinReimbursementTravelMapper finReimbursementTravelMapper; |
| | | private final FinReimbursementDetailMapper finReimbursementDetailMapper; |
| | | private final FileUtil fileUtil; |
| | | private final ISysNoticeService sysNoticeService; |
| | | @Override |
| | | public IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page) { |
| | | IPage<FinReimbursementVo> finReimbursementVoIPage = finReimbursementMapper.listPage(finReimbursementDto, page); |
| | | finReimbursementVoIPage.getRecords().forEach(vo -> { |
| | | vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT, vo.getId())); |
| | | }); |
| | | |
| | | return finReimbursementVoIPage; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean add(FinReimbursementDto finReimbursementDto) { |
| | | String billStatus = validateAddParam(finReimbursementDto); |
| | | |
| | | // çææ¥éåå· |
| | | String billNo = OrderUtils.countTodayByCreateTime(finReimbursementMapper, "BXD", "bill_no"); |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | BigDecimal totalAmount = details.stream() |
| | | .map(FinReimbursementDetail::getAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | FinReimbursement reimbursement = buildReimbursement(finReimbursementDto, billNo, totalAmount, billStatus); |
| | | // ä¿åæ¥éå主表 |
| | | boolean saved = this.save(reimbursement); |
| | | if (!saved || reimbursement.getId() == null) { |
| | | throw new ServiceException("æ°å¢æ¥éå失败"); |
| | | } |
| | | Long reimbursementId = reimbursement.getId(); |
| | | |
| | | // ä¿åå·®æ
æ¥éæ©å±ä¿¡æ¯ï¼æ¥éç±»å为差æ
æ¥éæ¶ï¼ |
| | | FinReimbursementTravel travel = finReimbursementDto.getTravel(); |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType())) { |
| | | travel.setReimbursementId(reimbursementId); |
| | | int travelRows = finReimbursementTravelMapper.insert(travel); |
| | | if (travelRows != 1) { |
| | | throw new ServiceException("æ°å¢å·®æ
æ¥éæ©å±ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | // ä¿åæ¥éåæç» |
| | | for (int i = 0; i < details.size(); i++) { |
| | | FinReimbursementDetail detail = details.get(i); |
| | | detail.setId(null); |
| | | detail.setReimbursementId(reimbursementId); |
| | | detail.setRowNo(i + 1); |
| | | int detailRows = finReimbursementDetailMapper.insert(detail); |
| | | if (detailRows != 1) { |
| | | throw new ServiceException("æ°å¢æ¥éåæç»å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | startApproval(reimbursement, finReimbursementDto); |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs()); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean update(FinReimbursementDto finReimbursementDto) { |
| | | String billStatus = validateUpdateParam(finReimbursementDto); |
| | | |
| | | Long reimbursementId = finReimbursementDto.getId(); |
| | | FinReimbursement existing = finReimbursementMapper.selectById(reimbursementId); |
| | | if (existing == null) { |
| | | throw new ServiceException("æ¥éåä¸åå¨"); |
| | | } |
| | | |
| | | // è®¡ç®æç»æ±æ»éé¢ |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | BigDecimal totalAmount = details.stream() |
| | | .map(FinReimbursementDetail::getAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | // æ´æ°ä¸»è¡¨ |
| | | FinReimbursement reimbursement = buildReimbursement( |
| | | finReimbursementDto, |
| | | existing.getBillNo(), |
| | | totalAmount, |
| | | billStatus |
| | | ); |
| | | reimbursement.setId(reimbursementId); |
| | | int mainRows = finReimbursementMapper.updateById(reimbursement); |
| | | if (mainRows != 1) { |
| | | throw new ServiceException("æ´æ°æ¥éå主表失败"); |
| | | } |
| | | |
| | | // æ¥è¯¢æ°æ®åºä¸å·²æçæç» |
| | | List<FinReimbursementDetail> existingDetails = finReimbursementDetailMapper.selectList( |
| | | new LambdaQueryWrapper<FinReimbursementDetail>() |
| | | .eq(FinReimbursementDetail::getReimbursementId, reimbursementId)); |
| | | Set<Long> existingDetailIds = existingDetails.stream() |
| | | .map(FinReimbursementDetail::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | // æ°æç»ä¸æIDç â æ´æ°ï¼æ IDç â æ°å¢ |
| | | Set<Long> submittedDetailIds = new HashSet<>(); |
| | | for (int i = 0; i < details.size(); i++) { |
| | | FinReimbursementDetail detail = details.get(i); |
| | | detail.setReimbursementId(reimbursementId); |
| | | detail.setRowNo(i + 1); |
| | | if (detail.getId() != null && existingDetailIds.contains(detail.getId())) { |
| | | finReimbursementDetailMapper.updateById(detail); |
| | | submittedDetailIds.add(detail.getId()); |
| | | } else { |
| | | detail.setId(null); |
| | | finReimbursementDetailMapper.insert(detail); |
| | | } |
| | | } |
| | | |
| | | // æ°æ®åºä¸å·²æä½æ°æç»ä¸æ²¡æç â å é¤ |
| | | for (Long existingId : existingDetailIds) { |
| | | if (!submittedDetailIds.contains(existingId)) { |
| | | finReimbursementDetailMapper.deleteById(existingId); |
| | | } |
| | | } |
| | | |
| | | // å·®æ
æ©å±ï¼æåæ´æ°ï¼æ åæ°å¢ |
| | | FinReimbursementTravel existingTravel = finReimbursementTravelMapper.selectOne( |
| | | new LambdaQueryWrapper<FinReimbursementTravel>() |
| | | .eq(FinReimbursementTravel::getReimbursementId, reimbursementId) |
| | | .last("LIMIT 1")); |
| | | FinReimbursementTravel travel = finReimbursementDto.getTravel(); |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && travel != null) { |
| | | travel.setReimbursementId(reimbursementId); |
| | | if (existingTravel != null) { |
| | | travel.setId(existingTravel.getId()); |
| | | finReimbursementTravelMapper.updateById(travel); |
| | | } else { |
| | | travel.setId(null); |
| | | finReimbursementTravelMapper.insert(travel); |
| | | } |
| | | } |
| | | |
| | | resetApprovalFlow(existing, reimbursementId); |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | reimbursement.setApprovalInstanceId(null); |
| | | startApproval(reimbursement, finReimbursementDto); |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public FinReimbursementVo detail(Long id) { |
| | | if (id == null ) { |
| | | throw new ServiceException("æ¥éåIDä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | FinReimbursement reimbursement = finReimbursementMapper.selectById(id); |
| | | if (reimbursement == null) { |
| | | throw new ServiceException("æ¥éåä¸åå¨"); |
| | | } |
| | | |
| | | FinReimbursementVo vo = new FinReimbursementVo(); |
| | | vo.setId(reimbursement.getId()); |
| | | vo.setBillNo(reimbursement.getBillNo()); |
| | | vo.setReimbursementType(reimbursement.getReimbursementType()); |
| | | vo.setExpenseType(reimbursement.getExpenseType()); |
| | | vo.setApplicantId(reimbursement.getApplicantId()); |
| | | vo.setApplicantCode(reimbursement.getApplicantCode()); |
| | | vo.setApplicantName(reimbursement.getApplicantName()); |
| | | vo.setApplicantDeptId(reimbursement.getApplicantDeptId()); |
| | | vo.setApplicantDeptName(reimbursement.getApplicantDeptName()); |
| | | vo.setReason(reimbursement.getReason()); |
| | | vo.setApplyAmount(reimbursement.getApplyAmount()); |
| | | vo.setDetailTotalAmount(reimbursement.getDetailTotalAmount()); |
| | | vo.setPayeeName(reimbursement.getPayeeName()); |
| | | vo.setPayeeAccount(reimbursement.getPayeeAccount()); |
| | | vo.setPayeeBank(reimbursement.getPayeeBank()); |
| | | vo.setApprovalInstanceId(reimbursement.getApprovalInstanceId()); |
| | | vo.setApproveProcessId(reimbursement.getApproveProcessId()); |
| | | vo.setBillStatus(reimbursement.getBillStatus()); |
| | | vo.setApprovedTime(reimbursement.getApprovedTime()); |
| | | vo.setPaidTime(reimbursement.getPaidTime()); |
| | | vo.setAccountExpenseId(reimbursement.getAccountExpenseId()); |
| | | vo.setRemark(reimbursement.getRemark()); |
| | | vo.setTenantId(reimbursement.getTenantId()); |
| | | vo.setCreateUser(reimbursement.getCreateUser()); |
| | | vo.setCreateTime(reimbursement.getCreateTime()); |
| | | vo.setUpdateUser(reimbursement.getUpdateUser()); |
| | | vo.setUpdateTime(reimbursement.getUpdateTime()); |
| | | vo.setDeptId(reimbursement.getDeptId()); |
| | | vo.setDeleted(reimbursement.getDeleted()); |
| | | |
| | | vo.setDetails(finReimbursementDetailMapper.selectList( |
| | | new LambdaQueryWrapper<FinReimbursementDetail>() |
| | | .eq(FinReimbursementDetail::getReimbursementId, reimbursement.getId()) |
| | | .orderByAsc(FinReimbursementDetail::getRowNo) |
| | | )); |
| | | |
| | | if (isTravelReimbursement(reimbursement.getReimbursementType())) { |
| | | vo.setTravel(finReimbursementTravelMapper.selectOne( |
| | | new LambdaQueryWrapper<FinReimbursementTravel>() |
| | | .eq(FinReimbursementTravel::getReimbursementId, reimbursement.getId()) |
| | | .last("LIMIT 1") |
| | | )); |
| | | } |
| | | vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT, reimbursement.getId())); |
| | | //审æ¹è®°å½è¿å |
| | | vo.setTasks(approvalTaskService.list(new LambdaQueryWrapper<ApprovalTask>().eq(ApprovalTask::getInstanceId, reimbursement.getApprovalInstanceId()))); |
| | | vo.setRecords(approvalRecordService.list(new LambdaQueryWrapper<ApprovalRecord>().eq(ApprovalRecord::getInstanceId, reimbursement.getApprovalInstanceId()))); |
| | | return vo; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean delete(List<Long> ids) { |
| | | fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, ids); |
| | | //å
å é¤æç» |
| | | finReimbursementDetailMapper.delete(new LambdaQueryWrapper<FinReimbursementDetail>().in(FinReimbursementDetail::getReimbursementId, ids)); |
| | | //å é¤å·®æ
|
| | | finReimbursementTravelMapper.delete(new LambdaQueryWrapper<FinReimbursementTravel>().in(FinReimbursementTravel::getReimbursementId, ids)); |
| | | //å é¤ä¸»è¡¨ |
| | | int rows = finReimbursementMapper.delete(new LambdaQueryWrapper<FinReimbursement>().in(FinReimbursement::getId, ids)); |
| | | return rows == ids.size(); |
| | | } |
| | | |
| | | private String validateUpdateParam(FinReimbursementDto finReimbursementDto) { |
| | | if (finReimbursementDto == null || finReimbursementDto.getId() == null) { |
| | | throw new ServiceException("æ¥éåIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (finReimbursementDto.getReimbursementType() == null) { |
| | | throw new ServiceException("æ¥éç±»åä¸è½ä¸ºç©º"); |
| | | } |
| | | String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus()); |
| | | if (billStatus == null) { |
| | | throw new ServiceException("åæ®ç¶æåªæ¯æ DRAFT æ IN_APPROVAL"); |
| | | } |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | validateApprovalNodes(finReimbursementDto.getNodes()); |
| | | } |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | if (details == null || details.isEmpty()) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (FinReimbursementDetail detail : details) { |
| | | if (detail == null) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("æ¥éåæç»éé¢å¿
须大äº0"); |
| | | } |
| | | } |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) { |
| | | throw new ServiceException("å·®æ
æ¥éå¿
须填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) { |
| | | throw new ServiceException("éå·®æ
æ¥éä¸å
许填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | return billStatus; |
| | | } |
| | | |
| | | private String validateAddParam(FinReimbursementDto finReimbursementDto) { |
| | | if (finReimbursementDto == null) { |
| | | throw new ServiceException("æ¥éåæ°æ®ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (finReimbursementDto.getReimbursementType() == null) { |
| | | throw new ServiceException("æ¥éç±»åä¸è½ä¸ºç©º"); |
| | | } |
| | | String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus()); |
| | | if (billStatus == null) { |
| | | throw new ServiceException("åæ®ç¶æåªæ¯æ DRAFT æ IN_APPROVAL"); |
| | | } |
| | | if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) { |
| | | validateApprovalNodes(finReimbursementDto.getNodes()); |
| | | } |
| | | List<FinReimbursementDetail> details = finReimbursementDto.getDetails(); |
| | | if (details == null || details.isEmpty()) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (FinReimbursementDetail detail : details) { |
| | | if (detail == null) { |
| | | throw new ServiceException("æ¥éåæç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("æ¥éåæç»éé¢å¿
须大äº0"); |
| | | } |
| | | } |
| | | if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) { |
| | | throw new ServiceException("å·®æ
æ¥éå¿
须填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) { |
| | | throw new ServiceException("éå·®æ
æ¥éä¸å
许填åå·®æ
æ©å±ä¿¡æ¯"); |
| | | } |
| | | return billStatus; |
| | | } |
| | | |
| | | private FinReimbursement buildReimbursement(FinReimbursementDto finReimbursementDto, String billNo, BigDecimal totalAmount, String billStatus) { |
| | | FinReimbursement reimbursement = new FinReimbursement(); |
| | | reimbursement.setId(null); |
| | | reimbursement.setBillNo(billNo); |
| | | reimbursement.setReimbursementType(finReimbursementDto.getReimbursementType()); |
| | | reimbursement.setExpenseType(finReimbursementDto.getExpenseType()); |
| | | reimbursement.setApplicantId(finReimbursementDto.getApplicantId()); |
| | | reimbursement.setApplicantCode(finReimbursementDto.getApplicantCode()); |
| | | reimbursement.setApplicantName(finReimbursementDto.getApplicantName()); |
| | | reimbursement.setApplicantDeptId(finReimbursementDto.getApplicantDeptId()); |
| | | reimbursement.setApplicantDeptName(finReimbursementDto.getApplicantDeptName()); |
| | | reimbursement.setReason(finReimbursementDto.getReason()); |
| | | reimbursement.setApplyAmount(finReimbursementDto.getApplyAmount()); |
| | | reimbursement.setDetailTotalAmount(totalAmount); |
| | | reimbursement.setPayeeName(finReimbursementDto.getPayeeName()); |
| | | reimbursement.setPayeeAccount(finReimbursementDto.getPayeeAccount()); |
| | | reimbursement.setPayeeBank(finReimbursementDto.getPayeeBank()); |
| | | reimbursement.setRemark(finReimbursementDto.getRemark()); |
| | | reimbursement.setTenantId(finReimbursementDto.getTenantId()); |
| | | reimbursement.setApproveProcessId(null); |
| | | reimbursement.setBillStatus(billStatus); |
| | | return reimbursement; |
| | | } |
| | | |
| | | private void startApproval(FinReimbursement reimbursement, FinReimbursementDto finReimbursementDto) { |
| | | Long businessType = resolveBusinessType(finReimbursementDto.getReimbursementType()); |
| | | ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto(); |
| | | approvalInstanceDto.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no")); |
| | | approvalInstanceDto.setBusinessId(reimbursement.getId()); |
| | | approvalInstanceDto.setTemplateId(null); |
| | | approvalInstanceDto.setTemplateName(TypeEnums.getLabelByValue(businessType) + "审æ¹"); |
| | | approvalInstanceDto.setBusinessType(businessType); |
| | | approvalInstanceDto.setTitle("æ¥éåå·ï¼" + reimbursement.getBillNo()); |
| | | approvalInstanceDto.setApplicantId(reimbursement.getApplicantId() != null ? reimbursement.getApplicantId() : SecurityUtils.getUserId()); |
| | | approvalInstanceDto.setApplicantName(reimbursement.getApplicantName() != null ? reimbursement.getApplicantName() : SecurityUtils.getLoginUser().getNickName()); |
| | | approvalInstanceDto.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceDto.setStatus("PENDING"); |
| | | approvalInstanceDto.setCurrentLevel(1); |
| | | |
| | | boolean approvalSaved = approvalInstanceService.save(approvalInstanceDto); |
| | | if (!approvalSaved || approvalInstanceDto.getId() == null) { |
| | | throw new ServiceException("å起审æ¹å¤±è´¥"); |
| | | } |
| | | List<ApprovalTask> firstTasks = createApprovalNodes(approvalInstanceDto, finReimbursementDto.getNodes()); |
| | | sendApproveNotice(approvalInstanceDto, firstTasks); |
| | | |
| | | FinReimbursement update = new FinReimbursement(); |
| | | update.setId(reimbursement.getId()); |
| | | update.setApprovalInstanceId(approvalInstanceDto.getId()); |
| | | update.setBillStatus(BILL_STATUS_IN_APPROVAL); |
| | | int rows = finReimbursementMapper.updateById(update); |
| | | if (rows != 1) { |
| | | throw new ServiceException("å填审æ¹å®ä¾å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | private List<ApprovalTask> createApprovalNodes(ApprovalInstanceDto approvalInstanceDto, List<ApprovalTemplateNodeDto> nodes) { |
| | | List<ApprovalTask> firstTasks = Collections.emptyList(); |
| | | for (int i = 0; i < nodes.size(); i++) { |
| | | ApprovalTemplateNodeDto nodeDto = nodes.get(i); |
| | | ApprovalInstanceNode instanceNode = new ApprovalInstanceNode(); |
| | | instanceNode.setInstanceId(approvalInstanceDto.getId()); |
| | | instanceNode.setLevelNo(nodeDto.getLevelNo()); |
| | | instanceNode.setApproveType(nodeDto.getApproveType()); |
| | | instanceNode.setStatus(i == 0 ? "PENDING" : NODE_STATUS_WAITING); |
| | | instanceNode.setStartTime(i == 0 ? LocalDateTime.now() : null); |
| | | instanceNode.setDeleted((byte) 0); |
| | | approvalInstanceNodeService.save(instanceNode); |
| | | |
| | | List<ApprovalTask> tasks = nodeDto.getApprovers().stream().map(approver -> { |
| | | ApprovalTask task = new ApprovalTask(); |
| | | task.setInstanceId(approvalInstanceDto.getId()); |
| | | task.setNodeId(instanceNode.getId()); |
| | | task.setLevelNo(instanceNode.getLevelNo()); |
| | | task.setApproverId(approver.getApproverId()); |
| | | task.setApproverName(approver.getApproverName()); |
| | | task.setTaskStatus("PENDING"); |
| | | task.setIsRead((byte) 0); |
| | | task.setDeleted((byte) 0); |
| | | return task; |
| | | }).collect(Collectors.toList()); |
| | | approvalTaskService.saveBatch(tasks); |
| | | |
| | | if (i == 0) { |
| | | firstTasks = tasks; |
| | | ApprovalRecord record = new ApprovalRecord(); |
| | | record.setInstanceId(approvalInstanceDto.getId()); |
| | | record.setNodeId(instanceNode.getId()); |
| | | record.setOperatorId(approvalInstanceDto.getApplicantId()); |
| | | record.setOperatorName(approvalInstanceDto.getApplicantName()); |
| | | record.setAction("SUBMIT"); |
| | | record.setComment("å起审æ¹"); |
| | | record.setDeleted((byte) 0); |
| | | approvalRecordService.save(record); |
| | | } |
| | | } |
| | | return firstTasks; |
| | | } |
| | | |
| | | private void validateApprovalNodes(List<ApprovalTemplateNodeDto> nodes) { |
| | | if (nodes == null || nodes.isEmpty()) { |
| | | throw new ServiceException("æäº¤å®¡æ¹æ¶å®¡æ¹èç¹ä¸è½ä¸ºç©º"); |
| | | } |
| | | for (int i = 0; i < nodes.size(); i++) { |
| | | ApprovalTemplateNodeDto node = nodes.get(i); |
| | | if (node == null) { |
| | | throw new ServiceException("审æ¹èç¹ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (node.getLevelNo() == null) { |
| | | node.setLevelNo(i + 1); |
| | | } |
| | | if (!StringUtils.hasText(node.getApproveType())) { |
| | | throw new ServiceException("审æ¹èç¹å®¡æ¹æ¹å¼ä¸è½ä¸ºç©º"); |
| | | } |
| | | List<ApprovalTemplateNodeApproverDto> approvers = node.getApprovers(); |
| | | if (approvers == null || approvers.isEmpty()) { |
| | | throw new ServiceException("审æ¹èç¹å®¡æ¹äººä¸è½ä¸ºç©º"); |
| | | } |
| | | for (ApprovalTemplateNodeApproverDto approver : approvers) { |
| | | if (approver == null || approver.getApproverId() == null) { |
| | | throw new ServiceException("审æ¹äººä¸è½ä¸ºç©º"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void sendApproveNotice(ApprovalInstanceDto instance, List<ApprovalTask> tasks) { |
| | | if (instance == null || tasks == null || tasks.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> approverIds = tasks.stream() |
| | | .map(ApprovalTask::getApproverId) |
| | | .filter(id -> id != null && id > 0) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (approverIds.isEmpty()) { |
| | | return; |
| | | } |
| | | String title = "æ¥é审æ¹"; |
| | | String message = "审æ¹åå· " + instance.getInstanceNo() + " éè¦æ¨å®¡æ¹"; |
| | | String jumpPath = "/approvalInstance?id=" + instance.getId(); |
| | | sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath); |
| | | } |
| | | |
| | | private void resetApprovalFlow(FinReimbursement existing, Long reimbursementId) { |
| | | if (existing == null || existing.getApprovalInstanceId() == null) { |
| | | return; |
| | | } |
| | | Long approvalInstanceId = existing.getApprovalInstanceId(); |
| | | if (!"REJECTED".equals(existing.getBillStatus())) { |
| | | approvalInstanceService.delete(Collections.singletonList(approvalInstanceId)); |
| | | } |
| | | clearApprovalBinding(reimbursementId); |
| | | } |
| | | |
| | | private void clearApprovalBinding(Long reimbursementId) { |
| | | int rows = finReimbursementMapper.update( |
| | | null, |
| | | Wrappers.<FinReimbursement>lambdaUpdate() |
| | | .eq(FinReimbursement::getId, reimbursementId) |
| | | .set(FinReimbursement::getApprovalInstanceId, null) |
| | | ); |
| | | if (rows != 1) { |
| | | throw new ServiceException("éç½®å®¡æ¹æµç¨å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | private Long resolveBusinessType(Byte reimbursementType) { |
| | | return isTravelReimbursement(reimbursementType) |
| | | ? TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode() |
| | | : TypeEnums.EXPENSE_APPROVAL.getCode(); |
| | | } |
| | | |
| | | private String normalizeBillStatus(String billStatus) { |
| | | if (billStatus == null) { |
| | | return BILL_STATUS_DRAFT; |
| | | } |
| | | String normalized = billStatus.trim().toUpperCase(); |
| | | if (BILL_STATUS_DRAFT.equals(normalized) || BILL_STATUS_IN_APPROVAL.equals(normalized)) { |
| | | return normalized; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isTravelReimbursement(Byte reimbursementType) { |
| | | return Byte.valueOf((byte) 1).equals(reimbursementType); |
| | | } |
| | | } |