package org.jeecg.modules.tms.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.apache.shiro.SecurityUtils; import org.flowable.engine.TaskService; import org.flowable.task.api.Task; import org.jeecg.common.api.vo.Result; import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.system.vo.LoginUser; import org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness; import org.jeecg.modules.flowable.apithird.business.service.IFlowMyBusinessService; import org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI; import org.jeecg.modules.flowable.apithird.service.FlowCommonService; import org.jeecg.modules.flowable.service.IFlowDefinitionService; import org.jeecg.modules.flowable.service.IFlowTaskService; import org.jeecg.modules.system.service.ISysBusinessCodeRuleService; import org.jeecg.modules.tms.entity.ToolLedger; import org.jeecg.modules.tms.entity.ToolLedgerDetail; import org.jeecg.modules.tms.entity.ToolsLossBound; import org.jeecg.modules.tms.entity.ToolsLossBoundDetail; import org.jeecg.modules.tms.entity.dto.LossBoundFlowDto; import org.jeecg.modules.tms.enums.OutBillStatus; import org.jeecg.modules.tms.enums.OutBoundStatusEnum; import org.jeecg.modules.tms.mapper.ToolsLossBoundDetailMapper; import org.jeecg.modules.tms.mapper.ToolsLossBoundMapper; import org.jeecg.modules.tms.service.IToolLedgerDetailService; import org.jeecg.modules.tms.service.IToolLedgerService; import org.jeecg.modules.tms.service.IToolsLossBoundDetailService; import org.jeecg.modules.tms.service.IToolsLossBoundService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.Serializable; import java.math.BigDecimal; import java.util.*; /** * @Description: 损耗单 * @Author: jeecg-boot * @Date: 2025-05-21 * @Version: V1.0 */ @Service("IToolsLossBoundService") public class ToolsLossBoundServiceImpl extends ServiceImpl implements IToolsLossBoundService, FlowCallBackServiceI { @Autowired private ToolsLossBoundMapper baseMapper; @Autowired private ToolsLossBoundDetailMapper baseDetailMapper; @Autowired private IToolsLossBoundDetailService toolsLossBoundDetailService; @Resource private FlowCommonService flowCommonService; @Resource private IFlowDefinitionService flowDefinitionService; @Autowired private IFlowTaskService flowTaskService; @Autowired private ISysBusinessCodeRuleService businessCodeRuleService; @Autowired private IFlowMyBusinessService flowMyBusinessService; @Autowired private TaskService taskService; @Autowired private IToolLedgerService toolLedgerService; @Autowired private IToolLedgerDetailService toolLedgerDetailService; @Override public void delMain(String id) { baseDetailMapper.deleteByMainId(id); baseMapper.deleteById(id); } @Override public void delBatchMain(Collection idList) { for (Serializable id : idList) { baseDetailMapper.deleteByMainId(id.toString()); baseMapper.deleteById(id); } } @Override public void addTotal(ToolsLossBound toolsLossBound) { LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); if (loginUser != null) { toolsLossBound.setHandler(loginUser.getId()); } toolsLossBound.setOrderCode(businessCodeRuleService.generateBusinessCodeSeq("LossBound")); toolsLossBound.setOrderStatus(OutBillStatus.DRAFT.getValue()); save(toolsLossBound); List detailList = toolsLossBound.getToolsLossBoundDetailList(); List newDetailList = new ArrayList<>(); if (CollectionUtils.isNotEmpty(detailList)) { detailList.forEach(item -> { item.setLossBoundId(toolsLossBound.getId()); newDetailList.add(item); }); toolsLossBoundDetailService.saveBatch(newDetailList); } } @Override public IPage queryPageList(Page page, Map parameterMap) { QueryWrapper queryWrapper = Wrappers.query(); String[] orderCode = parameterMap.get("orderCode"); if (orderCode != null && orderCode.length > 0) { queryWrapper.like("t.order_code", orderCode[0]); } String[] statuses = parameterMap.get("orderStatus"); if (statuses != null && statuses.length > 0) { queryWrapper.eq("t.order_status", statuses[0]); } return this.baseMapper.queryPageList(page, queryWrapper); } @Override public void editTotal(ToolsLossBound toolsLossBound) { //删除所有明细 toolsLossBoundDetailService.remove(new LambdaQueryWrapper().eq(ToolsLossBoundDetail::getLossBoundId, toolsLossBound.getId())); ToolsLossBound toolsLossBound1 = BeanUtil.copyProperties(toolsLossBound, ToolsLossBound.class); this.baseMapper.updateById(toolsLossBound1); List detailList = CollectionUtil.newArrayList(); toolsLossBound.getToolsLossBoundDetailList().forEach(item -> { item.setLossBoundId(toolsLossBound1.getId()); detailList.add(item); }); toolsLossBoundDetailService.saveBatch(detailList); } @Override public boolean submintOrder(String id) { ToolsLossBound toolsLossBound = this.getById(id); if (toolsLossBound == null) { return false; } else { toolsLossBound.setReviewer(toolsLossBound.getReviewer()); flowCommonService.initActBusiness("报损单号:" + toolsLossBound.getOrderCode() + ";报损人: " + toolsLossBound.getLosser() + ";进行报损", toolsLossBound.getId(), "IToolsStocktakingBoundService", "tools_Loss_Approval", null); Map variables = new HashMap<>(); variables.put("dataId", toolsLossBound.getId()); if (StrUtil.isEmpty(toolsLossBound.getReviewer())) { variables.put("organization", "新增工具报损单默认启动流程"); variables.put("comment", "新增工具报损单默认启动流程"); } else { variables.put("organization", toolsLossBound.getLossReason()); variables.put("comment", toolsLossBound.getLossReason()); } variables.put("proofreading", true); List usernames = new ArrayList<>(); usernames.add(toolsLossBound.getReviewer()); variables.put("NextAssignee", usernames); Result result = flowDefinitionService.startProcessInstanceByKey("tools_Loss_Approval", variables); if (result != null) { toolsLossBound.setLossTime(new Date()); toolsLossBound.setOrderStatus(OutBillStatus.SUBMITTED.getValue()); //保存工单 baseMapper.updateById(toolsLossBound); return result.isSuccess(); } return true; } } @Override public void approvalProcess(LossBoundFlowDto lossBoundFlowDto) { if (StrUtil.isBlank(lossBoundFlowDto.getTaskId()) || StrUtil.isBlank(lossBoundFlowDto.getDataId())) { throw new JeecgBootException("非法参数!"); } // 获取当前登录用户 LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal(); if (user == null || StrUtil.isBlank(user.getId())) { throw new JeecgBootException("账号不存在"); } //获取报损单信息 ToolsLossBound toolsLossBound = getById(lossBoundFlowDto.getDataId()); if (toolsLossBound == null) { throw new JeecgBootException("未找到对应的出库申请单!"); } //获取流程业务记录 FlowMyBusiness flowMyBusiness = getFlowMyBusiness(lossBoundFlowDto.getInstanceId()); if (flowMyBusiness == null) { throw new JeecgBootException("流程记录不存在"); } // 检查用户是否有权限操作任务 if (!isUserAuthorized(flowMyBusiness, user)) { throw new JeecgBootException("用户无权操作此任务"); } // 认领任务 if (!claimTask(flowMyBusiness.getTaskId(), user)) { throw new JeecgBootException("任务不存在、已完成或已被他人认领"); } //设置流程变量 setupProcessVariables(lossBoundFlowDto, toolsLossBound, user); //完成流程任务 Result result = flowTaskService.complete(lossBoundFlowDto); //根据任务完成结果更新申请单状态 if (result.isSuccess()) { toolsLossBound.setOrderStatus(lossBoundFlowDto.getStatus()); if (OutBillStatus.APPROVED.getValue().equals(lossBoundFlowDto.getStatus())) { toolsLossBound.setOrderStatus(OutBoundStatusEnum.NOT_OUTBOUND.getValue()); } /** * 更新审批时间、审批意见 更新库存扣减 库存总数 * 获取工具明细列表,遍历每个工具明细,更新对应的工具库存记录。 * 对于每个工具,获取对应的工具库存记录(ToolLedger),并更新总库存数量(totalCount)和损失数量(lossCount)。 * 确保在更新总库存数量之前,检查总库存数量是否大于或等于损失数量。如果不满足条件,抛出异常或进行其他处理。 * 最后,保存更新后的工具库存记录。 */ List detailList = toolsLossBound.getToolsLossBoundDetailList(); if (CollectionUtils.isEmpty(detailList)) { throw new JeecgBootException("报损明细为空,无法进行库存扣减"); } Map toolLossMap = new HashMap<>(); for (ToolsLossBoundDetail item : detailList) { if (item.getToolId() == null || item.getLossNumber() == null) { continue; // } toolLossMap.put(item.getToolId(), toolLossMap.getOrDefault(item.getToolId(), BigDecimal.ZERO).add(item.getLossNumber())); } /** * 遍历每个 toolId,更新对应的 ToolLedger */ for (Map.Entry entry : toolLossMap.entrySet()) { String toolId = entry.getKey(); BigDecimal totalLossNumber = entry.getValue(); ToolLedger toolLedger = toolLedgerService.getOne(new QueryWrapper().eq("id", toolId)); if (toolLedger == null) { throw new JeecgBootException("未找到对应的工具库存记录,toolId:" + toolId); } // 扣减总库存 if (toolLedger.getTotalCount() != null && toolLedger.getTotalCount().compareTo(totalLossNumber) >= 0) { toolLedger.setTotalCount(toolLedger.getTotalCount().subtract(totalLossNumber)); toolLedger.setLossCount(toolLedger.getLossCount().add(totalLossNumber)); } else { throw new JeecgBootException("库存不足,无法完成报损操作,toolId:" + toolId); } // 更新数据库中的 ToolLedger toolLedgerService.updateById(toolLedger); /** * 新增逻辑:更新台账明细表 ToolLedgerDetail 中的状态字段为 6 */ UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("tool_id", toolId) .set("status", 6); // 状态设置为 6 toolLedgerDetailService.update(updateWrapper); } toolsLossBound.setApprovalDate(new Date()); toolsLossBound.setApprovalOpinion(lossBoundFlowDto.getApprovalOpinion()); updateById(toolsLossBound); } } private void setupProcessVariables(LossBoundFlowDto lossBoundFlowDto, ToolsLossBound toolsLossBound, LoginUser user) { if (OutBillStatus.SUBMITTED.getValue().equals(toolsLossBound.getOrderStatus()) && user.getUsername().equals(toolsLossBound.getReviewer())) { Map values = new HashMap<>(); values.put("dataId", toolsLossBound.getId()); values.put("organization", lossBoundFlowDto.getApprovalOpinion()); values.put("comment", lossBoundFlowDto.getApprovalOpinion()); values.put("status", lossBoundFlowDto.getStatus()); values.put("NextAssignee", Collections.singletonList(toolsLossBound.getReviewer())); lossBoundFlowDto.setValues(values); } } private FlowMyBusiness getFlowMyBusiness(String instanceId) { List businessList = flowMyBusinessService.list(new LambdaQueryWrapper().eq(FlowMyBusiness::getProcessInstanceId, instanceId)); return businessList.isEmpty() ? null : businessList.get(0); } private boolean isUserAuthorized(FlowMyBusiness flowMyBusiness, LoginUser user) { List todoUsers = JSON.parseArray(flowMyBusiness.getTodoUsers(), String.class); return todoUsers != null && todoUsers.contains(user.getUsername()); } private boolean claimTask(String taskId, LoginUser user) { Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if (task == null) { return false; } if (task.getAssignee() != null && !task.getAssignee().equals(user.getUsername())) { return false; } taskService.claim(taskId, user.getUsername()); return true; } @Override public void afterFlowHandle(FlowMyBusiness business) { business.getTaskNameId();//接下来审批的节点 business.getValues();//前端传进来的参数 business.getActStatus(); } @Override public Object getBusinessDataById(String dataId) { return this.getById(dataId); } @Override public Map flowValuesOfTask(String taskNameId, Map values) { return null; } @Override public List flowCandidateUsernamesOfTask(String taskNameId, Map values) { Object object = values.get("NextAssignee"); return (List) object; } }