package com.ruoyi.approve.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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.vo.ApprovalInstanceVo;
import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
import com.ruoyi.approve.mapper.FinReimbursementMapper;
import com.ruoyi.approve.pojo.*;
import com.ruoyi.approve.service.*;
import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper;
import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
import com.ruoyi.common.enums.*;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.project.system.service.ISysNoticeService;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.quality.utils.QualityInspectHelper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.mapper.SalesQuotationMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.staff.mapper.HolidayApplicationMapper;
import com.ruoyi.staff.pojo.HolidayApplication;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
*
* 审批实例服务实现类
*
*
* @since 2026-05-18 03:27:46
*/
@Service
@RequiredArgsConstructor
public class ApprovalInstanceServiceImpl extends ServiceImpl implements ApprovalInstanceService {
private static final String ENTERPRISE_NEWS_STATUS_PUBLISHED = "PUBLISHED";
private static final String ENTERPRISE_NEWS_STATUS_REJECTED = "REJECTED";
private final ApprovalInstanceMapper approvalInstanceMapper;
private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils;
private final ApprovalInstanceNodeService approvalInstanceNodeService;
private final ApprovalTaskService approvalTaskService;
private final ApprovalRecordService approvalRecordService;
private final ApprovalTemplateNodeService approvalTemplateNodeService;
private final FinReimbursementMapper finReimbursementMapper;
private final FileUtil fileUtil;
private final ISysNoticeService sysNoticeService;
private final PurchaseLedgerMapper purchaseLedgerMapper;
private final SalesLedgerProductMapper salesLedgerProductMapper;
private final StockUtils stockUtils;
private final SalesQuotationMapper salesQuotationMapper;
private final ShippingInfoMapper shippingInfoMapper;
private final QualityInspectHelper qualityInspectHelper;
private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper;
private final SysUserMapper sysUserMapper;
private final SysUserDeptMapper sysUserDeptMapper;
private final SysDeptMapper sysDeptMapper;
private final HolidayApplicationMapper holidayApplicationMapper;
private final EnterpriseNewsMapper enterpriseNewsMapper;
private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
@Override
public R listPage(Page page, ApprovalInstanceDto approvalInstanceDto) {
IPage approvalInstanceVoIPage = approvalInstanceMapper.listPage(page, approvalInstanceDto);
List records = approvalInstanceVoIPage.getRecords();
if (records == null || records.isEmpty()) {
return R.ok(approvalInstanceVoIPage);
}
records.forEach(vo -> {
vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType()));
});
Long currentUserId = SecurityUtils.getUserId();
List instanceIds = records.stream()
.map(ApprovalInstanceVo::getId)
.filter(id -> id != null)
.distinct()
.collect(Collectors.toList());
if (!instanceIds.isEmpty()) {
Map> recordMap = approvalRecordService.list(
Wrappers.lambdaQuery()
.in(ApprovalRecord::getInstanceId, instanceIds)
.eq(ApprovalRecord::getDeleted, 0)
).stream().collect(Collectors.groupingBy(ApprovalRecord::getInstanceId));
Map> taskMap = approvalTaskService.list(
Wrappers.lambdaQuery()
.in(ApprovalTask::getInstanceId, instanceIds)
.eq(ApprovalTask::getDeleted, 0)
).stream().collect(Collectors.groupingBy(ApprovalTask::getInstanceId));
for (ApprovalInstanceVo vo : records) {
vo.setIsApprove(approveProcessConfigNodeUtils.isCurrentApprover(vo.getId(), currentUserId));
vo.setRecords(recordMap.getOrDefault(vo.getId(), new ArrayList<>()));
vo.setTasks(taskMap.getOrDefault(vo.getId(), new ArrayList<>()));
vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.APPROVAL_INSTANCE, vo.getId()));
}
}
return R.ok(approvalInstanceVoIPage);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean add(ApprovalInstanceDto approvalInstanceDto) {
String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no");
approvalInstanceDto.setInstanceNo(instanceNo);
approvalInstanceDto.setStatus("PENDING");
approvalInstanceDto.setCurrentLevel(1);
boolean saved = this.save(approvalInstanceDto);
if (!saved) {
return false;
}
approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto);
fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean update(ApprovalInstanceDto approvalInstanceDto) {
if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
return false;
}
// 判断是否有正在进行的审批任务,有则不允许修改
long pendingTaskCount = approvalTaskService.count(
Wrappers.lambdaQuery()
.eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId())
.eq(ApprovalTask::getTaskStatus, "PENDING")
.eq(ApprovalTask::getDeleted, 0)
);
if (pendingTaskCount > 0) {
throw new ServiceException("该审批单有正在进行的审批任务,不允许修改");
}
boolean updated = this.updateById(approvalInstanceDto);
if (!updated) {
return false;
}
fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean delete(List ids) {
if (ids == null || ids.isEmpty()) {
return false;
}
fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids);
int instanceRows = approvalInstanceMapper.update(
null,
Wrappers.lambdaUpdate()
.in(ApprovalInstance::getId, ids)
.eq(ApprovalInstance::getDeleted, 0)
.set(ApprovalInstance::getDeleted, (byte) 1)
);
LambdaUpdateWrapper nodeUpdateWrapper = Wrappers.lambdaUpdate();
nodeUpdateWrapper.in(ApprovalInstanceNode::getInstanceId, ids)
.eq(ApprovalInstanceNode::getDeleted, 0)
.set(ApprovalInstanceNode::getDeleted, (byte) 1);
approvalInstanceNodeService.update(nodeUpdateWrapper);
LambdaUpdateWrapper taskUpdateWrapper = Wrappers.lambdaUpdate();
taskUpdateWrapper.in(ApprovalTask::getInstanceId, ids)
.eq(ApprovalTask::getDeleted, 0)
.set(ApprovalTask::getDeleted, (byte) 1);
approvalTaskService.update(taskUpdateWrapper);
LambdaUpdateWrapper recordUpdateWrapper = Wrappers.lambdaUpdate();
recordUpdateWrapper.in(ApprovalRecord::getInstanceId, ids)
.eq(ApprovalRecord::getDeleted, 0)
.set(ApprovalRecord::getDeleted, (byte) 1);
approvalRecordService.update(recordUpdateWrapper);
return instanceRows > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public R approve(ApprovalInstanceDto approvalInstanceDto) {
if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
return R.fail("审批实例 ID 不能为空");
}
String approveAction = normalizeApproveAction(approvalInstanceDto.getApproveAction());
if (approveAction == null) {
return R.fail("审批动作只支持 APPROVED 或 REJECTED");
}
ApprovalInstance instance = getPendingApprovalInstance(approvalInstanceDto.getId());
if (instance == null) {
return R.fail("审批实例不存在");
}
ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(instance.getId());
if (currentNode == null) {
return R.fail("当前没有待处理的审批节点");
}
Long currentUserId = SecurityUtils.getUserId();
ApprovalTask currentTask = approveProcessConfigNodeUtils.getCurrentUserTask(instance.getId(), currentUserId);
if (currentTask == null) {
return R.fail("当前用户没有可审批任务");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
String operatorName = loginUser.getUser() != null ? loginUser.getUser().getNickName() : SecurityUtils.getUsername();
LocalDateTime now = LocalDateTime.now();
if (!updateCurrentTask(approvalInstanceDto, approveAction, currentTask, now)) {
return R.fail("当前任务已被处理,请刷新后重试");
}
saveApprovalRecord(
instance.getId(),
currentNode.getId(),
currentTask.getId(),
currentUserId,
operatorName,
approveAction,
approvalInstanceDto.getApproveComment()
);
//审批拒绝的处理
if ("REJECTED".equals(approveAction)) {
return rejectCurrentNode(instance, currentNode, now);
}
if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
return R.ok("审批成功,等待其他审批人处理");
}
return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now);
}
private String normalizeApproveAction(String approveAction) {
if (!StringUtils.hasText(approveAction)) {
return null;
}
String normalizedAction = approveAction.trim().toUpperCase(Locale.ROOT);
return "APPROVED".equals(normalizedAction) || "REJECTED".equals(normalizedAction)
? normalizedAction
: null;
}
private ApprovalInstance getPendingApprovalInstance(Long instanceId) {
return this.getOne(
new LambdaQueryWrapper()
.eq(ApprovalInstance::getId, instanceId)
.eq(ApprovalInstance::getDeleted, 0)
.last("LIMIT 1")
);
}
private boolean updateCurrentTask(ApprovalInstanceDto approvalInstanceDto,
String approveAction,
ApprovalTask currentTask,
LocalDateTime now) {
// 仅允许待审批任务被成功处理一次,避免并发下重复审批成功。
return approvalTaskService.update(
Wrappers.lambdaUpdate()
.eq(ApprovalTask::getId, currentTask.getId())
.eq(ApprovalTask::getTaskStatus, "PENDING")
.eq(ApprovalTask::getDeleted, 0)
.set(ApprovalTask::getTaskStatus, approveAction)
.set(ApprovalTask::getComment, approvalInstanceDto.getApproveComment())
.set(ApprovalTask::getApproveTime, now)
.set(ApprovalTask::getIsRead, (byte) 1)
);
}
private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
return R.ok("当前节点已处理完成");
}
closePendingTasks(instance.getId(), currentNode.getId());
instance.setStatus("REJECTED");
instance.setFinishTime(now);
this.updateById(instance);
// 驳回对应的企业新闻, 差旅报销
if (instance.getBusinessType().equals(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())) {
enterpriseNewsMapper.update(
new LambdaUpdateWrapper()
.eq(EnterpriseNews::getId, instance.getBusinessId())
.set(EnterpriseNews::getStatus, "REJECTED")
);
}else if (instance.getBusinessType().equals(TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode())||instance.getBusinessType().equals(TypeEnums.EXPENSE_APPROVAL.getCode())) {
finReimbursementMapper.update(
new LambdaUpdateWrapper()
.eq(FinReimbursement::getId, instance.getBusinessId())
.set(FinReimbursement::getBillStatus, "REJECTED")
);
}
return R.ok("审批已驳回");
}
private R approveAndMoveNext(ApprovalInstance instance,
ApprovalInstanceNode currentNode,
ApprovalInstanceDto approvalInstanceDto,
LocalDateTime now) {
if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) {
return R.ok("当前节点已处理完成");
}
closePendingTasks(instance.getId(), currentNode.getId());
int nextLevel = currentNode.getLevelNo() + 1;
ApprovalInstanceNode nextInstanceNode = approvalInstanceNodeService.getOne(
new LambdaQueryWrapper()
.eq(ApprovalInstanceNode::getInstanceId, instance.getId())
.eq(ApprovalInstanceNode::getLevelNo, nextLevel)
.eq(ApprovalInstanceNode::getDeleted, 0)
.orderByAsc(ApprovalInstanceNode::getId)
.last("LIMIT 1")
);
if (nextInstanceNode != null) {
if (!activateNextInstanceNode(nextInstanceNode.getId(), now)) {
return R.ok("下一审批节点已被激活,请刷新后重试");
}
instance.setCurrentLevel(nextLevel);
instance.setStatus("PENDING");
this.updateById(instance);
List nextTasks = approvalTaskService.list(
Wrappers.lambdaQuery()
.eq(ApprovalTask::getInstanceId, instance.getId())
.eq(ApprovalTask::getNodeId, nextInstanceNode.getId())
.eq(ApprovalTask::getTaskStatus, "PENDING")
.eq(ApprovalTask::getDeleted, 0)
);
sendApproveNotice(instance, nextTasks);
return R.ok("审批成功,已流转到下一节点");
}
ApprovalTemplateNode nextTemplateNode = approvalTemplateNodeService.getOne(
new LambdaQueryWrapper()
.eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
.eq(ApprovalTemplateNode::getLevelNo, nextLevel)
.orderByAsc(ApprovalTemplateNode::getId)
.last("LIMIT 1")
);
if (nextTemplateNode == null) {
instance.setStatus("APPROVED");
instance.setFinishTime(now);
this.updateById(instance);
handleBusinessAfterApprovalFinished(instance);
return R.ok("审批已完成");
}
instance.setCurrentLevel(nextLevel);
instance.setStatus("PENDING");
this.updateById(instance);
approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false);
sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
return R.ok("审批成功,已流转到下一节点");
}
private boolean activateNextInstanceNode(Long nodeId, LocalDateTime now) {
return approvalInstanceNodeService.update(
Wrappers.lambdaUpdate()
.eq(ApprovalInstanceNode::getId, nodeId)
.eq(ApprovalInstanceNode::getStatus, "WAITING")
.eq(ApprovalInstanceNode::getDeleted, 0)
.set(ApprovalInstanceNode::getStatus, "PENDING")
.set(ApprovalInstanceNode::getStartTime, now)
);
}
private boolean updateCurrentNodeStatus(Long nodeId, String targetStatus, LocalDateTime now) {
// 仅允许一个请求将当前节点从待处理推进到目标状态,避免重复流转。
return approvalInstanceNodeService.update(
Wrappers.lambdaUpdate()
.eq(ApprovalInstanceNode::getId, nodeId)
.eq(ApprovalInstanceNode::getStatus, "PENDING")
.eq(ApprovalInstanceNode::getDeleted, 0)
.set(ApprovalInstanceNode::getStatus, targetStatus)
.set(ApprovalInstanceNode::getFinishTime, now)
);
}
private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
String status = instance.getStatus();
Long businessType = instance.getBusinessType();
if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
handlePurchaseApprovalFinished(instance, status);
return;
}
if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(businessType)) {
handleSalesQuotationApprovalFinished(instance, status);
return;
}
if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(businessType)) {
handleShippingApprovalFinished(instance, status);
return;
}
if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)
|| TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
handleReimbursementApprovalFinished(instance, status);
return;
}
// 驳回对应的企业新闻、加班申请、请假申请可以重新再提交
if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType)
|| TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) {
handleHolidayApplicationApprovalFinished(instance, status);
return;
}
if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
handleNewsApprovalFinished(instance, status);
}
}
private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
if (instance == null || instance.getBusinessId() == null) {
return;
}
FinReimbursement reimbursement = new FinReimbursement();
reimbursement.setId(instance.getBusinessId());
if ("APPROVED".equals(status)) {
reimbursement.setBillStatus("APPROVED");
reimbursement.setApprovedTime(instance.getFinishTime());
} else if ("REJECTED".equals(status)) {
reimbursement.setBillStatus("REJECTED");
} else if ("PENDING".equals(status)) {
reimbursement.setBillStatus("IN_APPROVAL");
} else {
return;
}
finReimbursementMapper.updateById(reimbursement);
}
private void handleNewsApprovalFinished(ApprovalInstance instance, String status) {
if (instance == null || instance.getBusinessId() == null) {
return;
}
EnterpriseNews enterpriseNews = new EnterpriseNews();
enterpriseNews.setId(instance.getBusinessId());
if ("APPROVED".equals(status)) {
enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_PUBLISHED);
enterpriseNewsMapper.updateById(enterpriseNews);
sendEnterpriseNewsNotice(instance.getBusinessId());
return;
}
if ("REJECTED".equals(status)) {
enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_REJECTED);
enterpriseNewsMapper.updateById(enterpriseNews);
}
}
private void handleHolidayApplicationApprovalFinished(ApprovalInstance instance, String status) {
if (instance == null || instance.getBusinessId() == null) {
return;
}
HolidayApplication holidayApplication = new HolidayApplication();
holidayApplication.setId(instance.getBusinessId());
if ("APPROVED".equals(status)) {
holidayApplication.setStatus("APPROVED");
} else if ("REJECTED".equals(status)) {
holidayApplication.setStatus("REJECTED");
} else if ("PENDING".equals(status)) {
holidayApplication.setStatus("PENDING");
} else {
return;
}
holidayApplicationMapper.updateById(holidayApplication);
}
private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) {
PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(
new LambdaQueryWrapper()
.eq(PurchaseLedger::getPurchaseContractNumber, instance.getTitle())
.last("limit 1")
);
if (purchaseLedger == null) {
return;
}
if ("APPROVED".equals(status)) {
purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode());
List salesLedgerProducts = salesLedgerProductMapper.selectList(
new QueryWrapper().lambda()
.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
.eq(SalesLedgerProduct::getType, 2)
);
for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
if (salesLedgerProduct.getIsChecked()) {
qualityInspectHelper.addQualityInspect(purchaseLedger, salesLedgerProduct);
} else {
stockUtils.addStockWithBatchNo(
salesLedgerProduct.getProductModelId(),
salesLedgerProduct.getQuantity(),
StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
purchaseLedger.getId(),
purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId()
);
}
}
} else if ("REJECTED".equals(status)) {
purchaseLedger.setApprovalStatus(ApprovalStatusEnum.REJECTED.getCode());
} else if ("PENDING".equals(status)) {
purchaseLedger.setApprovalStatus(ApprovalStatusEnum.IN_PROGRESS.getCode());
}
purchaseLedgerMapper.updateById(purchaseLedger);
}
private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) {
SalesQuotation salesQuote = salesQuotationMapper.selectOne(
new LambdaQueryWrapper()
.eq(SalesQuotation::getQuotationNo, instance.getTitle())
.last("limit 1")
);
if (salesQuote == null) {
return;
}
if ("APPROVED".equals(status)) {
salesQuote.setStatus(SalesQuotationStatusEnum.APPROVED.getCode());
} else if ("REJECTED".equals(status)) {
salesQuote.setStatus(SalesQuotationStatusEnum.REJECTED.getCode());
} else if ("PENDING".equals(status)) {
salesQuote.setStatus(SalesQuotationStatusEnum.IN_PROGRESS.getCode());
}
salesQuotationMapper.updateById(salesQuote);
}
private void handleShippingApprovalFinished(ApprovalInstance instance, String status) {
ShippingInfo shippingInfo = shippingInfoMapper.selectOne(
new LambdaQueryWrapper()
.eq(ShippingInfo::getShippingNo, instance.getTitle())
.orderByDesc(ShippingInfo::getCreateTime)
.last("limit 1")
);
if (shippingInfo == null) {
return;
}
if ("APPROVED".equals(status)) {
shippingInfo.setStatus(ShippingStatusEnum.APPROVED.getCode());
shippingInfo.setShippingDate(new Date());
stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
} else if ("REJECTED".equals(status)) {
stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
shippingInfo.setStatus(ShippingStatusEnum.REJECTED.getCode());
} else if ("PENDING".equals(status)) {
shippingInfo.setStatus(ShippingStatusEnum.IN_PROGRESS.getCode());
}
shippingInfoMapper.updateById(shippingInfo);
}
private List createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) {
List approvers = approvalTemplateNodeApproverMapper.selectList(
new LambdaQueryWrapper()
.eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId())
.eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId())
.eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
.orderByAsc(ApprovalTemplateNodeApprover::getSortNo)
);
if (approvers == null || approvers.isEmpty()) {
throw new RuntimeException("下一审批节点未配置审批人");
}
ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
instanceNode.setInstanceId(instance.getId());
instanceNode.setLevelNo(templateNode.getLevelNo());
instanceNode.setApproveType(templateNode.getApproveType());
instanceNode.setStatus("PENDING");
instanceNode.setStartTime(LocalDateTime.now());
instanceNode.setDeleted((byte) 0);
approvalInstanceNodeService.save(instanceNode);
List taskList = new ArrayList<>(approvers.size());
for (ApprovalTemplateNodeApprover approver : approvers) {
ApprovalTask task = new ApprovalTask();
task.setInstanceId(instance.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);
taskList.add(task);
}
approvalTaskService.saveBatch(taskList);
return taskList;
}
private void sendApproveNotice(ApprovalInstance instance, List tasks) {
if (instance == null || tasks == null || tasks.isEmpty()) {
return;
}
List approverIds = tasks.stream()
.map(ApprovalTask::getApproverId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
if (approverIds.isEmpty()) {
return;
}
String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "审批提醒";
String message = "审批单号 " + instance.getInstanceNo() + " 需要您审批";
String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId();
sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
}
private void sendEnterpriseNewsNotice(Long newsId) {
EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId);
if (enterpriseNews == null) {
return;
}
List userIds = getEnterpriseNewsNoticeUserIds(enterpriseNews);
if (userIds == null || userIds.isEmpty()) {
return;
}
String title = "企业新闻";
String message = "您有新的企业新闻《" + enterpriseNews.getTitle() + "》请及时查阅";
String jumpPath = "/officeProcessAutomation/EnterpriseNews?id=" + newsId;
sysNoticeService.simpleNoticeByUser(title, message, userIds, jumpPath);
}
private List getEnterpriseNewsNoticeUserIds(EnterpriseNews enterpriseNews) {
if (enterpriseNews == null || !org.springframework.util.StringUtils.hasText(enterpriseNews.getReadScope())) {
return Collections.emptyList();
}
String readScope = enterpriseNews.getReadScope().trim();
if ("all".equals(readScope)) {
return sysUserMapper.selectList(new LambdaQueryWrapper()
.select(SysUser::getUserId)
.eq(SysUser::getDelFlag, "0"))
.stream()
.map(SysUser::getUserId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
}
if ("dept".equals(readScope)) {
List deptIds = enterpriseNewsScopeDeptMapper.selectList(
new LambdaQueryWrapper()
.eq(EnterpriseNewsScopeDept::getNewsId, enterpriseNews.getId()))
.stream()
.map(EnterpriseNewsScopeDept::getDeptId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
if (deptIds.isEmpty()) {
return Collections.emptyList();
}
return sysUserDeptMapper.selectDistinctUserIdsByDeptIds(collectDeptIdsWithChildren(deptIds));
}
if ("custom".equals(readScope)) {
return enterpriseNewsScopeUserMapper.selectList(
new LambdaQueryWrapper()
.eq(EnterpriseNewsScopeUser::getNewsId, enterpriseNews.getId()))
.stream()
.map(EnterpriseNewsScopeUser::getUserId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
}
return Collections.emptyList();
}
private List collectDeptIdsWithChildren(List deptIds) {
Set allDeptIds = new LinkedHashSet<>();
for (Long deptId : deptIds) {
if (deptId == null) {
continue;
}
allDeptIds.add(deptId);
List children = sysDeptMapper.selectChildrenDeptById(deptId);
if (children != null && !children.isEmpty()) {
for (SysDept child : children) {
if (child != null && child.getDeptId() != null) {
allDeptIds.add(child.getDeptId());
}
}
}
}
return new ArrayList<>(allDeptIds);
}
private void closePendingTasks(Long instanceId, Long nodeId) {
LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate();
updateWrapper.eq(ApprovalTask::getInstanceId, instanceId)
.eq(ApprovalTask::getNodeId, nodeId)
.eq(ApprovalTask::getTaskStatus, "PENDING")
.eq(ApprovalTask::getDeleted, 0)
.set(ApprovalTask::getDeleted, (byte) 1);
approvalTaskService.update(updateWrapper);
}
private void saveApprovalRecord(Long instanceId,
Long nodeId,
Long taskId,
Long operatorId,
String operatorName,
String action,
String comment) {
ApprovalRecord record = new ApprovalRecord();
record.setInstanceId(instanceId);
record.setNodeId(nodeId);
record.setTaskId(taskId);
record.setOperatorId(operatorId);
record.setOperatorName(operatorName);
record.setAction(action);
record.setComment(comment);
record.setDeleted((byte) 0);
approvalRecordService.save(record);
}
}