From 6391a4902d5b873ddd7d5034e19ba434d312a431 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 14 Jan 2026 18:16:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(dms):=20=E6=B7=BB=E5=8A=A0=E6=95=85?= =?UTF-8?q?=E9=9A=9C=E6=8A=A5=E4=BF=AE=E5=B7=A5=E5=8D=95=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=A4=87=E4=BB=BD=E5=AE=9E=E7=8E=B0,=E4=BD=BF=E7=94=A8startCom?= =?UTF-8?q?pleteTask=EF=BC=88=E4=BB=8Ehwbm=E7=9A=84=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=ACplus=E6=A1=86=E6=9E=B6=E4=B8=AD=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=EF=BC=8C=E6=9C=AA=E7=BB=8F=E8=BF=87=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了完整的故障报修工单业务逻辑,包括查询、新增、审批、维修等核心功能 - 集成了工作流服务,支持工单的流程化审批和状态推进 - 添加了设备信息、零部件更换记录、文件附件等关联数据处理 - 实现了PDA端兼容的工单创建和状态管理功能 - 添加了故障实例活动跟踪和维修过程记录功能 - 集成了远程文件服务,支持工单相关的图片上传和管理 --- ...msBillsFaultInstanceServiceImplBackup.java | 917 ++++++++++++++++++ 1 file changed, 917 insertions(+) create mode 100644 ruoyi-modules/hwmom-dms/src/main/java/org/dromara/dms/service/impl/DmsBillsFaultInstanceServiceImplBackup.java diff --git a/ruoyi-modules/hwmom-dms/src/main/java/org/dromara/dms/service/impl/DmsBillsFaultInstanceServiceImplBackup.java b/ruoyi-modules/hwmom-dms/src/main/java/org/dromara/dms/service/impl/DmsBillsFaultInstanceServiceImplBackup.java new file mode 100644 index 00000000..d6d1864d --- /dev/null +++ b/ruoyi-modules/hwmom-dms/src/main/java/org/dromara/dms/service/impl/DmsBillsFaultInstanceServiceImplBackup.java @@ -0,0 +1,917 @@ +package org.dromara.dms.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import io.seata.spring.annotation.GlobalTransactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.dromara.common.core.constant.DmsConstants; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.uuid.Seq; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.dms.domain.*; +import org.dromara.dms.domain.bo.DmsBillsFaultInstanceBo; +import org.dromara.dms.domain.bo.DmsBillsFaultInstanceScanBo; +import org.dromara.dms.domain.bo.DmsFaultInstanceActivityBo; +import org.dromara.dms.domain.bo.DmsInstanceFileBo; +import org.dromara.dms.domain.mobile.DeviceBeen; +import org.dromara.dms.domain.vo.DmsBillsFaultInstanceVo; +import org.dromara.dms.domain.vo.DmsFaultComponentsPartsVo; +import org.dromara.dms.domain.vo.DmsFaultInstanceActivityVo; +import org.dromara.dms.domain.vo.DmsInstanceFileVo; +import org.dromara.dms.mapper.DmsBillsFaultInstanceMapper; +import org.dromara.dms.mapper.DmsFaultComponentsPartsMapper; +import org.dromara.dms.mapper.DmsFaultInstanceActivityMapper; +import org.dromara.dms.mapper.DmsInstanceFileMapper; +import org.dromara.dms.service.IDmsBillsFaultInstanceService; +import org.dromara.dms.service.IDmsFaultInstanceActivityService; +import org.dromara.dms.service.IDmsInstanceFileService; +import org.dromara.resource.api.RemoteFileService; +import org.dromara.resource.api.domain.RemoteFile; +import org.dromara.workflow.api.RemoteWorkflowService; +import org.dromara.workflow.api.domain.RemoteCompleteTask; +import org.dromara.workflow.api.domain.RemoteStartProcess; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; + +/** + * 故障报修工单Service业务层处理 + * 备份:调用startCompleteTask而不是异步推动,但是未经过测试 + * + * @author zch + * @date 2025-04-07 + */ +@RequiredArgsConstructor +@Service +@Slf4j +public class DmsBillsFaultInstanceServiceImplBackup implements IDmsBillsFaultInstanceService { + + private final DmsBillsFaultInstanceMapper baseMapper; + + + private final DmsInstanceFileMapper dmsInstanceFileMapper; + + private final IDmsInstanceFileService DmsInstanceFileService; + + @DubboReference(timeout = 30000, retries = 2) + private final RemoteWorkflowService remoteWorkflowService; + + private final DmsFaultInstanceActivityMapper dmsFaultInstanceActivityMapper; + + private final IDmsFaultInstanceActivityService dmsFaultInstanceActivityService; + + @DubboReference(timeout = 15000, retries = 1) + private final RemoteFileService remoteFileService; + + private final DmsFaultComponentsPartsMapper dmsFaultComponentsPartsMapper; + + /** + * 查询故障报修工单 + * 包含维修零件列表,前端只需调用此接口即可获取完整工单信息 + * + * @param repairInstanceId 主键 + * @return 故障报修工单(含零件列表) + */ + @Override + public DmsBillsFaultInstanceVo queryById(Long repairInstanceId){ + DmsBillsFaultInstanceVo vo = baseMapper.selectVoById(repairInstanceId); + if (vo != null) { + // 查询维修零件列表(不使用分页,避免SQL Server OFFSET语法问题) + List partsList = dmsFaultComponentsPartsMapper.selectList( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(DmsFaultComponentsParts::getRepairInstanceId, repairInstanceId) + ); + vo.setParts(partsList); + } + return vo; + } + + /** + * 分页查询故障报修工单列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 故障报修工单分页列表 + */ + @Override + public TableDataInfo queryPageList(DmsBillsFaultInstanceBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的故障报修工单列表 + * + * @param bo 查询条件 + * @return 故障报修工单列表 + */ + @Override + public List queryList(DmsBillsFaultInstanceBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public Long count(DmsBillsFaultInstanceBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(DmsBillsFaultInstance.class) + .eq(bo.getRepairInstanceId() != null, DmsBillsFaultInstance::getRepairInstanceId, bo.getRepairInstanceId()) + .eq(StringUtils.isNotBlank(bo.getBillsFaultCode()), DmsBillsFaultInstance::getBillsFaultCode, bo.getBillsFaultCode()) + .eq(StringUtils.isNotBlank(bo.getInstanceType()), DmsBillsFaultInstance::getInstanceType, bo.getInstanceType()) + .eq(StringUtils.isNotBlank(bo.getFaultSourceType()), DmsBillsFaultInstance::getFaultSourceType, bo.getFaultSourceType()) + .eq(bo.getFaultSourceId() != null, DmsBillsFaultInstance::getFaultSourceId, bo.getFaultSourceId()) + .eq(bo.getWfDefinitionId() != null, DmsBillsFaultInstance::getWfDefinitionId, bo.getWfDefinitionId()) + .eq(bo.getMachineId() != null, DmsBillsFaultInstance::getMachineId, bo.getMachineId()) + .eq(StringUtils.isNotBlank(bo.getApplyUser()), DmsBillsFaultInstance::getApplyUser, bo.getApplyUser()) + .eq(bo.getApplyTime() != null, DmsBillsFaultInstance::getApplyTime, bo.getApplyTime()) + .eq(bo.getRealBeginTime() != null, DmsBillsFaultInstance::getRealBeginTime, bo.getRealBeginTime()) + .eq(bo.getRealEndTime() != null, DmsBillsFaultInstance::getRealEndTime, bo.getRealEndTime()) + .eq(bo.getRequireEndTime() != null, DmsBillsFaultInstance::getRequireEndTime, bo.getRequireEndTime()) + .eq(StringUtils.isNotBlank(bo.getBillsStatus()), DmsBillsFaultInstance::getBillsStatus, bo.getBillsStatus()) + .eq(StringUtils.isNotBlank(bo.getApproveStatus()), DmsBillsFaultInstance::getApproveStatus, bo.getApproveStatus()) + .eq(StringUtils.isNotBlank(bo.getRepairConfirm()), DmsBillsFaultInstance::getRepairConfirm, bo.getRepairConfirm()) + .eq(StringUtils.isNotBlank(bo.getConfirmUser()), DmsBillsFaultInstance::getConfirmUser, bo.getConfirmUser()) + .eq(StringUtils.isNotBlank(bo.getActiveFlag()), DmsBillsFaultInstance::getActiveFlag, bo.getActiveFlag()); + return baseMapper.selectCount(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(DmsBillsFaultInstanceBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(DmsBillsFaultInstance.class) + .selectAll(DmsBillsFaultInstance.class) + + //关联查询设备信息 + .select(DmsBaseMachineInfo::getMachineName) + .select(DmsBaseMachineInfo::getMachineCode) + .select(DmsBaseMachineInfo::getMachineLocation) + .select(DmsBaseMachineInfo::getMachineSpec) + .leftJoin(DmsBaseMachineInfo.class, DmsBaseMachineInfo::getMachineId, DmsBaseShutReason::getMachineId) + + //FIXME:关联查询设备信息 + .select(DeviceBeen::getDeviceId) + .select(DeviceBeen::getDeviceCode) + .select(DeviceBeen::getDeviceName) + .select(DeviceBeen::getDeviceLocation) + .select(DeviceBeen::getDeviceTypeId) + .select(DeviceBeen::getDeviceSpec) + .select(DeviceBeen::getDeviceStatus) + .leftJoin(DeviceBeen.class, DeviceBeen::getDeviceId, DmsBillsFaultInstance::getMachineId) + + //关联查询故障报修工单活动信息 + + //关联查询零部件更换记录 + .select(DmsFaultComponentsParts::getPartName) + .select(DmsFaultComponentsParts::getPartSpecifications) + .select(DmsFaultComponentsParts::getAmount) + .leftJoin(DmsFaultComponentsParts.class, DmsFaultComponentsParts::getRepairInstanceId, DmsBillsFaultInstance::getRepairInstanceId) + + .select(DmsInstanceFile::getFilePath) + .leftJoin(DmsInstanceFile.class, DmsInstanceFile::getTargetId, DmsBillsFaultInstance::getRepairInstanceId) + + .eq(bo.getRepairInstanceId() != null, DmsBillsFaultInstance::getRepairInstanceId, bo.getRepairInstanceId()) + .eq(StringUtils.isNotBlank(bo.getBillsFaultCode()), DmsBillsFaultInstance::getBillsFaultCode, bo.getBillsFaultCode()) + .eq(StringUtils.isNotBlank(bo.getInstanceType()), DmsBillsFaultInstance::getInstanceType, bo.getInstanceType()) + .eq(StringUtils.isNotBlank(bo.getFaultSourceType()), DmsBillsFaultInstance::getFaultSourceType, bo.getFaultSourceType()) + .eq(bo.getFaultSourceId() != null, DmsBillsFaultInstance::getFaultSourceId, bo.getFaultSourceId()) + .eq(bo.getWfDefinitionId() != null, DmsBillsFaultInstance::getWfDefinitionId, bo.getWfDefinitionId()) + .eq(bo.getMachineId() != null, DmsBillsFaultInstance::getMachineId, bo.getMachineId()) + .eq(StringUtils.isNotBlank(bo.getApplyUser()), DmsBillsFaultInstance::getApplyUser, bo.getApplyUser()) + .eq(bo.getApplyTime() != null, DmsBillsFaultInstance::getApplyTime, bo.getApplyTime()) + .eq(bo.getRealBeginTime() != null, DmsBillsFaultInstance::getRealBeginTime, bo.getRealBeginTime()) + .eq(bo.getRealEndTime() != null, DmsBillsFaultInstance::getRealEndTime, bo.getRealEndTime()) + .eq(bo.getRequireEndTime() != null, DmsBillsFaultInstance::getRequireEndTime, bo.getRequireEndTime()) + .eq(StringUtils.isNotBlank(bo.getBillsStatus()), DmsBillsFaultInstance::getBillsStatus, bo.getBillsStatus()) + .eq(StringUtils.isNotBlank(bo.getApproveStatus()), DmsBillsFaultInstance::getApproveStatus, bo.getApproveStatus()) + .eq(StringUtils.isNotBlank(bo.getRepairConfirm()), DmsBillsFaultInstance::getRepairConfirm, bo.getRepairConfirm()) + .eq(StringUtils.isNotBlank(bo.getConfirmUser()), DmsBillsFaultInstance::getConfirmUser, bo.getConfirmUser()) + .eq(StringUtils.isNotBlank(bo.getActiveFlag()), DmsBillsFaultInstance::getActiveFlag, bo.getActiveFlag()) + .orderByDesc(DmsBillsFaultInstance::getCreateTime); + return lqw; + } + + /** + * 新增故障报修工单 + * + * @param bo 故障报修工单 + * @return 是否新增成功 + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public Boolean insertByBo(DmsBillsFaultInstanceBo bo) { + bo.setMachineId((long) bo.getDeviceId()); + + // 【关键修复】处理PDA端outsrcId到outsourcingId的转换 + if (bo.getOutsrcId() != null && bo.getOutsrcId() > 0) { + bo.setOutsourcingId(Long.valueOf(bo.getOutsrcId())); + } + + DmsBillsFaultInstance add = MapstructUtils.convert(bo, DmsBillsFaultInstance.class); + validEntityBeforeSave(add); + // 获取当前日期时间 + Date curretnDate = new Date(); + add.setApplyTime(curretnDate); + add.setApplyUser(LoginHelper.getUsername()); + add.setBillsFaultCode(Seq.getId(Seq.dmsBillsFaultInstanceSeqType, Seq.dmsBillsFaultInstanceSeqCode)); + // 设置报修状态为待维修 + add.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_TO_REPAIR); + // 设置审批状态为待审批(按照PDA标准) + add.setApproveStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_PENDING); + // 设置实例类型为手动录入 + add.setInstanceType(DmsConstants.DMS_BILLS_FAULT_INSTANCE_TYPE_MAUAL); + // 【修复】工单创建时确认状态应该为null,不设置任何值 + add.setRepairConfirm(null); + + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setRepairInstanceId(add.getRepairInstanceId()); + } + + // 【方案三】工作流相关:使用 startCompleteTask 一次性完成启动+推进 + RemoteStartProcess remoteStartProcess = new RemoteStartProcess(); + remoteStartProcess.setFlowCode(DmsConstants.DMS_BILLS_FAULT_INSTANCE_WF_CODE); + remoteStartProcess.setBusinessId(add.getRepairInstanceId().toString()); + + // 设置流程变量(后端发起必须忽略权限检查) + Map variables = new HashMap<>(); + variables.put("businessId", add.getRepairInstanceId().toString()); + variables.put("initiator", LoginHelper.getUsername()); + variables.put("ignore", true); // 后端发起必须忽略权限 + remoteStartProcess.setVariables(variables); + + // 调用 startCompleteTask 一次完成启动+推进(内部使用独立事务,避免死锁) + boolean workflowResult = remoteWorkflowService.startCompleteTask(remoteStartProcess); + if (!workflowResult) { + throw new ServiceException("工作流启动失败"); + } + + // 获取流程实例ID(用于后续关联) + Long processInstanceId = remoteWorkflowService.getInstanceIdByBusinessId(add.getRepairInstanceId().toString()); + + // 更新工单的工作流实例ID + add.setWfDefinitionId(processInstanceId); + baseMapper.updateById(add); + + log.info("工作流启动并推进成功,repairInstanceId: {}, processInstanceId: {}", + add.getRepairInstanceId(), processInstanceId); + + // 【重要修改】创建故障实例活动对象 - 只有PDA创建工单时才生成 + DmsFaultInstanceActivity dmsFaultInstanceActivity = new DmsFaultInstanceActivity(); + // 设置维修实例ID + dmsFaultInstanceActivity.setRepairInstanceId(add.getRepairInstanceId()); + // 设置流程活动ID + dmsFaultInstanceActivity.setWfNodeId(processInstanceId); + // 设置故障类型 + dmsFaultInstanceActivity.setFaultType(add.getFaultType()); + // 设置故障描述 + dmsFaultInstanceActivity.setFaultDescription(add.getFaultDescription()); + // 设置设计操作 + dmsFaultInstanceActivity.setDesignOperations(add.getDesignOperations()); + // 设置设备ID + dmsFaultInstanceActivity.setMachineId(add.getMachineId()); + // 设置维修类型 + dmsFaultInstanceActivity.setRepairType(add.getRepairType()); + // 【关键修复】如果外部资源ID不为空,则设置外部资源ID + if (bo.getOutsourcingId() != null) { + dmsFaultInstanceActivity.setOutsourcingId(bo.getOutsourcingId()); + } + // 设置处理状态为待审批 + dmsFaultInstanceActivity.setProcessHandleStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_ACTIVITY_HANDLE_STATUS_TO_PICK); + // 设置流程步骤顺序 + dmsFaultInstanceActivity.setProcessStepOrder(1l); + // 插入故障实例活动记录 + dmsFaultInstanceActivityMapper.insert(dmsFaultInstanceActivity); + //存储图片 + // 获取文件URL列表 + List fileUrls = bo.getFileUrls(); + // 批量插入实例文件记录 + batchInsertInstanceFiles(fileUrls, dmsFaultInstanceActivity.getInstanceActivityId()); + return flag; + } + + /** + * 审批故障报修工单 + * + * @param repairInstanceId 工单ID + * @param approveStatus 审批状态(2-审批成功,3-审批失败) + * @param message 审批意见 + * @return 是否审批成功 + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public Boolean approveWorkOrder(Long repairInstanceId, String approveStatus, String message) { + // 查询工单信息 + DmsBillsFaultInstance workOrder = baseMapper.selectById(repairInstanceId); + if (workOrder == null) { + throw new ServiceException("工单不存在"); + } + + // 检查当前状态是否可以审批(必须是待审批状态) + if (!DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_PENDING.equals(workOrder.getApproveStatus())) { + throw new ServiceException("工单当前状态不允许审批"); + } + + // 更新审批状态和工单状态 + workOrder.setApproveStatus(approveStatus); + + if (DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_APPROVED.equals(approveStatus)) { + // 审批通过:保持待维修状态 + workOrder.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_TO_REPAIR); + } else if (DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_REJECTED.equals(approveStatus)) { + // 审批拒绝:设置为维修完成状态(终止工单) + workOrder.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_FINISH); + } + + // 先更新工单状态,确保业务数据一致性 + boolean updateResult = baseMapper.updateById(workOrder) > 0; + + if (!updateResult) { + throw new ServiceException("工单状态更新失败"); + } + + // 业务状态更新成功后,再推进工作流 + if (workOrder.getWfDefinitionId() != null) { + try { + // 获取当前任务ID + Long currentTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId(workOrder.getWfDefinitionId()); + if (currentTaskId != null) { + RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask(); + remoteCompleteTask.setTaskId(currentTaskId); + remoteCompleteTask.setMessage(message != null ? message : "审批完成"); + + // 设置消息类型 + List messageTypes = new ArrayList<>(); + messageTypes.add("1"); // 站内信 + remoteCompleteTask.setMessageType(messageTypes); + + // 设置消息通知内容 + if (DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_APPROVED.equals(approveStatus)) { + remoteCompleteTask.setNotice("故障报修工单审批通过,请及时处理,工单编号:" + workOrder.getBillsFaultCode()); + } else { + remoteCompleteTask.setNotice("故障报修工单审批被拒绝,工单编号:" + workOrder.getBillsFaultCode()); + } + + // 设置工作流变量 - approveStatus用于互斥网关判断 + Map variables = new HashMap<>(); + variables.put("approveStatus", approveStatus); + variables.put("businessId", repairInstanceId.toString()); + + remoteCompleteTask.setVariables(variables); + + // 推进工作流 + boolean workflowResult = remoteWorkflowService.completeTask(remoteCompleteTask); + if (!workflowResult) { + log.warn("工作流推进失败,但业务状态已更新成功,repairInstanceId: {}, approveStatus: {}", + repairInstanceId, approveStatus); + // 工作流推进失败不影响业务操作的成功 + } else { + log.info("审批工单成功,repairInstanceId: {}, approveStatus: {}", repairInstanceId, approveStatus); + } + } else { + log.warn("未找到当前活跃任务,跳过工作流推进,repairInstanceId: {}", repairInstanceId); + } + } catch (Exception e) { + log.error("工作流推进异常,repairInstanceId: {}, approveStatus: {}", repairInstanceId, approveStatus, e); + // 工作流推进失败不影响业务操作的成功,但需要记录日志 + } + } + + return updateResult; + } + + /** + * 开始维修 + * + * @param dmsFaultInstanceActivity 报修工单实例节点 + * @return 结果 + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public int startRepair(DmsFaultInstanceActivityBo dmsFaultInstanceActivity) { + // 根据维修实例ID查询故障实例信息 + DmsBillsFaultInstance dmsBillsFaultInstance = baseMapper.selectById(dmsFaultInstanceActivity.getRepairInstanceId()); + if (dmsBillsFaultInstance == null) { + throw new ServiceException("工单不存在"); + } + + // 检查审批状态(按照PDA标准,只有状态为"2"审批成功才可以开始维修) + if (!DmsConstants.DMS_BILLS_FAULT_INSTANCE_APPROVE_STATUS_APPROVED.equals(dmsBillsFaultInstance.getApproveStatus())) { + throw new ServiceException("工单未审批通过,无法开始维修"); + } + + // 检查工单状态 + String dmsBillsStatus = dmsBillsFaultInstance.getBillsStatus(); + if (!dmsBillsStatus.equals(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_TO_REPAIR)) { + throw new ServiceException("工单状态不允许开始维修"); + } + + // 获取当前日期和登录用户名 + Date currentDate = new Date(); + String userName = LoginHelper.getLoginUser() == null ? LoginHelper.getUsername() : LoginHelper.getLoginUser().getNickname(); + + // 设置工单状态为维修中 + dmsBillsFaultInstance.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_REPAIRING); + // 设置实际开始时间 + dmsBillsFaultInstance.setRealBeginTime(currentDate); + + // 推进工作流 + if (dmsBillsFaultInstance.getWfDefinitionId() != null) { + Long currentTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId(dmsBillsFaultInstance.getWfDefinitionId()); + if (currentTaskId != null) { + RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask(); + remoteCompleteTask.setTaskId(currentTaskId); + remoteCompleteTask.setMessage("开始维修"); + List messageTypes = new ArrayList<>(); + messageTypes.add("1"); // 站内信 + remoteCompleteTask.setMessageType(messageTypes); + remoteCompleteTask.setNotice("故障报修工单已开始维修,工单编号:" + dmsBillsFaultInstance.getBillsFaultCode()); + boolean b = remoteWorkflowService.completeTask(remoteCompleteTask); + if (!b) { + throw new ServiceException("工作流推进失败"); + } + } + } + + // 更新工单状态 + baseMapper.updateById(dmsBillsFaultInstance); + + // 设置维修实例活动记录 + dmsFaultInstanceActivity.setRepairInstanceId(dmsBillsFaultInstance.getRepairInstanceId()); + dmsFaultInstanceActivity.setMachineId(dmsBillsFaultInstance.getMachineId()); + dmsFaultInstanceActivity.setProcessHandleStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_ACTIVITY_HANDLE_STATUS_EXECUTING); + dmsFaultInstanceActivity.setProcessStepOrder(2L); + dmsFaultInstanceActivity.setStartTime(currentDate); + dmsFaultInstanceActivity.setHandleUserId(LoginHelper.getUserId()); + dmsFaultInstanceActivity.setHandleBy(userName); + dmsFaultInstanceActivity.setHandleTime(currentDate); + + // 【调试日志】打印传入的活动ID,以确认问题根源 + log.info("调用startRepair时,从控制器传入的instanceActivityId为: {}", dmsFaultInstanceActivity.getInstanceActivityId()); + + // 【关键修复】在插入新的活动记录前,必须将ID设置为空。 + // 这是因为传入的dmsFaultInstanceActivity对象可能包含了由PDA端getBillsFaultInstance4Repair接口返回的 + // 第一个活动节点(process_step_order=1)的ID。如果不清空,MyBatis会尝试使用这个旧ID插入新记录, + // 如果instance_activity_id不是主键或唯一键,就会导致数据重复;如果是,则会抛出主键冲突异常。 + dmsFaultInstanceActivity.setInstanceActivityId(null); + // 插入新的故障实例活动记录 + boolean result = dmsFaultInstanceActivityService.insertByBo(dmsFaultInstanceActivity); + + return result ? 1 : 0; + } + + /** + * 完成维修 + * + * @param dmsFaultInstanceActivity 报修工单实例节点 + * @return 结果 + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public int completeRepair(DmsFaultInstanceActivityBo dmsFaultInstanceActivity, List files) { + // 更新 dms_fault_instance_activity 表 + DmsFaultInstanceActivity activity = new DmsFaultInstanceActivity(); + activity.setInstanceActivityId(dmsFaultInstanceActivity.getInstanceActivityId()); + activity.setProcessHandleStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_ACTIVITY_HANDLE_STATUS_FINISH); + activity.setEndTime(new Date()); + activity.setRepairer(LoginHelper.getLoginUser() == null ? LoginHelper.getUsername() : LoginHelper.getLoginUser().getNickname()); + dmsFaultInstanceActivityMapper.updateById(activity); + + // 批量保存处理的零部件 + List parts = dmsFaultInstanceActivity.getParts(); + if (parts != null && !parts.isEmpty()) { + parts.forEach(part -> { + part.setRepairInstanceId(dmsFaultInstanceActivity.getRepairInstanceId()); + dmsFaultComponentsPartsMapper.insert(part); + }); + } + + // 处理文件上传 + if (files != null && !files.isEmpty()) { + List fileList = new ArrayList<>(); + for (MultipartFile file : files) { + try { + RemoteFile remoteFile = remoteFileService.upload(file.getName(), file.getOriginalFilename(), file.getContentType(), file.getBytes()); + DmsInstanceFile instanceFile = new DmsInstanceFile(); + instanceFile.setTargetId(dmsFaultInstanceActivity.getInstanceActivityId()); + instanceFile.setFilePath(remoteFile.getUrl()); + instanceFile.setTargetType("1"); // 维修完成图片 + fileList.add(instanceFile); + dmsInstanceFileMapper.insert(instanceFile); + } catch (Exception e) { + throw new ServiceException("文件上传失败"); + } + } + } + + // 更新 dms_bills_fault_instance 表 + DmsBillsFaultInstance instance = baseMapper.selectById(dmsFaultInstanceActivity.getRepairInstanceId()); + if (instance == null) { + throw new ServiceException("工单不存在: " + dmsFaultInstanceActivity.getRepairInstanceId()); + } + instance.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_FINISH); + instance.setRealEndTime(new Date()); + instance.setRepairConfirm("0"); // 设置为待确认状态 + baseMapper.updateById(instance); + + // 推进工作流 + if (instance.getWfDefinitionId() != null) { + Long currentTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId(instance.getWfDefinitionId()); + if (currentTaskId != null) { + RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask(); + remoteCompleteTask.setTaskId(currentTaskId); + remoteCompleteTask.setMessage("完成维修"); + List messageTypes = new ArrayList<>(); + messageTypes.add("1"); // 站内信 + remoteCompleteTask.setMessageType(messageTypes); + remoteCompleteTask.setNotice("故障报修工单维修完成,待主管确认,工单编号:" + instance.getBillsFaultCode()); + boolean b = remoteWorkflowService.completeTask(remoteCompleteTask); + if (!b) { + throw new ServiceException("工作流推进失败"); + } + } + } + + return 1; + } + + /** + * 主管确认维修结果 + * + * @param repairInstanceId 工单ID + * @param confirmResult 确认结果(1-确认通过,2-确认不通过) + * @param confirmUser 确认人 + * @return 是否确认成功 + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public Boolean confirmRepairResult(Long repairInstanceId, String confirmResult, String confirmUser) { + // 查询工单信息 + DmsBillsFaultInstance workOrder = baseMapper.selectById(repairInstanceId); + if (workOrder == null) { + throw new ServiceException("工单不存在"); + } + + // 检查当前状态是否可以确认 + if (!DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_FINISH.equals(workOrder.getBillsStatus())) { + throw new ServiceException("工单尚未完成维修,无法确认"); + } + + if (!"0".equals(workOrder.getRepairConfirm())) { + throw new ServiceException("工单已经确认过了"); + } + + // 根据确认结果设置不同的业务状态 + if ("1".equals(confirmResult)) { + // 确认通过:完成工单 + workOrder.setRepairConfirm(confirmResult); + workOrder.setConfirmUser(confirmUser != null ? confirmUser : LoginHelper.getUsername()); + workOrder.setConfirmTime(new Date()); + } else { + // 确认不通过:返回到维修环节,重置相关状态 + workOrder.setRepairConfirm(confirmResult); + workOrder.setConfirmUser(confirmUser != null ? confirmUser : LoginHelper.getUsername()); + workOrder.setConfirmTime(new Date()); + // 重置工单状态为待维修,让维修人员重新维修 + workOrder.setBillsStatus(DmsConstants.DMS_BILLS_FAULT_INSTANCE_STATUS_TO_REPAIR); + // 清空实际结束时间,因为需要重新维修 + workOrder.setRealEndTime(null); + } + + // 推进工作流:业务状态和工作流分支变量分离处理 + if (workOrder.getWfDefinitionId() != null) { + Long currentTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId(workOrder.getWfDefinitionId()); + if (currentTaskId != null) { + RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask(); + remoteCompleteTask.setTaskId(currentTaskId); + + // 设置工作流变量 - 用于互斥网关判断分支走向 + Map variables = new HashMap<>(); + if ("1".equals(confirmResult)) { + // 确认通过:工作流分支变量=0(走确认通过分支,结束流程) + remoteCompleteTask.setMessage("主管确认通过,工单完成"); + variables.put("repairConfirm", "0"); + } else { + // 确认不通过:工作流分支变量=1(走确认不通过分支,返回维修环节) + remoteCompleteTask.setMessage("主管确认不通过,返回维修环节"); + variables.put("repairConfirm", "1"); + } + + variables.put("businessId", repairInstanceId.toString()); + remoteCompleteTask.setVariables(variables); + + List messageTypes = new ArrayList<>(); + messageTypes.add("1"); // 站内信 + remoteCompleteTask.setMessageType(messageTypes); + + // 设置消息通知内容 + if ("1".equals(confirmResult)) { + remoteCompleteTask.setNotice("故障报修工单已完成,工单编号:" + workOrder.getBillsFaultCode()); + } else { + remoteCompleteTask.setNotice("故障报修工单确认不通过,需重新维修,工单编号:" + workOrder.getBillsFaultCode()); + } + + boolean workflowResult = remoteWorkflowService.completeTask(remoteCompleteTask); + if (!workflowResult) { + log.warn("工作流推进失败,但业务状态已更新成功,repairInstanceId: {}, confirmResult: {}", + repairInstanceId, confirmResult); + } else { + log.info("主管确认完成,工作流推进成功,repairInstanceId: {}, confirmResult: {}", + repairInstanceId, confirmResult); + } + } + } + + return baseMapper.updateById(workOrder) > 0; + } + + /** + * 修改故障报修工单 + * + * @param bo 故障报修工单 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(DmsBillsFaultInstanceBo bo) { + DmsBillsFaultInstance update = MapstructUtils.convert(bo, DmsBillsFaultInstance.class); + + + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(DmsBillsFaultInstance entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除故障报修工单信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 更新故障报修工单的工作流定义ID + * @param repairInstanceId 故障报修工单的ID + * @param wfDefinitionId 工作流定义的ID + * @return 如果更新成功返回true,否则返回false + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateWfDefinitionIdById(Long repairInstanceId, Long wfDefinitionId) { + if(StringUtils.isNull(repairInstanceId)){ + throw new ServiceException("参数为空"); + } + DmsBillsFaultInstance entity = baseMapper.selectById(repairInstanceId); + if (entity == null) { + throw new ServiceException("记录不存在"); + } + entity.setWfDefinitionId(wfDefinitionId); + entity.setRealBeginTime(new Date()); + return baseMapper.updateById(entity) > 0; + } + + + @Override + public DmsFaultInstanceActivity selectFaults(Long repairInstanceId) { + List list = baseMapper.selectFaults(repairInstanceId); + List parts = baseMapper.selectParts(repairInstanceId); + DmsFaultInstanceActivity activity = new DmsFaultInstanceActivity(); + BeanUtils.copyProperties(list.get(0),activity); +// activity.setCheckedFault(list.get(0).getCheckedFault()); + activity.setParts(parts); + return activity; + } + + /** + * 查询报修工单 + * + * @param repairInstanceId 报修工单主键 + * @return 报修工单 + */ + @Override + public DmsBillsFaultInstanceVo selectDmsBillsFaultInstanceByRepairInstanceId(Long repairInstanceId) { + try { + // 根据repairInstanceId查询故障实例及其关联的设备和第一条活动记录 + DmsBillsFaultInstanceVo dmsBillsFaultInstance = baseMapper.selectFaultInstanceJoinFirstAndDeviceById(repairInstanceId); + + if (dmsBillsFaultInstance == null) { + throw new ServiceException("工单不存在,repairInstanceId: " + repairInstanceId); + } + + // 【调试日志】输出查询结果,确认outsourcingId是否正确获取 + log.info("查询工单详情,repairInstanceId: {}, outsourcingId: {}, instanceActivityId: {}", + repairInstanceId, dmsBillsFaultInstance.getOutsourcingId(), dmsBillsFaultInstance.getInstanceActivityId()); + + // 【关键修复】查询零部件信息,确保PDA端兼容性 + List partsVoList = baseMapper.selectParts(repairInstanceId); + if (partsVoList == null) { + partsVoList = new ArrayList<>(); // 确保不为null + } + // 转换为实体对象并设置到工单中 + List partsList = MapstructUtils.convert(partsVoList, DmsFaultComponentsParts.class); + dmsBillsFaultInstance.setParts(partsList); + + // 创建实例文件查询对象 + DmsInstanceFileBo queryInstanceFile = new DmsInstanceFileBo(); + // 设置目标类型为故障实例活动 + queryInstanceFile.setTargetType(DmsConstants.DMS_INSTANCE_FILE_TARGET_TYPE_FAULT_INSTANCE_ACTIVITY); + // 设置目标ID为故障实例的活动ID + queryInstanceFile.setTargetId(dmsBillsFaultInstance.getInstanceActivityId()); + // 查询实例文件列表 + List dmsInstanceFiles = DmsInstanceFileService.queryList(queryInstanceFile); + + // 遍历实例文件列表FIXME + for (DmsInstanceFileVo dmsInstanceFile : dmsInstanceFiles){ + // 获取文件路径 + String filePath = dmsInstanceFile.getFilePath(); + // 如果文件路径不包含"9005" + if(!filePath.contains("9005")){ + // 如果文件路径包含"https",将其替换为"http" + if(filePath.contains("https")){ + filePath.replaceFirst("https","http"); + } + // 找到"/statics"的索引位置 + int index= filePath.indexOf("/statics"); + // 修改文件路径为新的地址 + filePath = "http://118.25.106.99:9005"+filePath.substring(index); + // 设置新的URL和文件路径 + dmsInstanceFile.setFilePath(filePath); + }else{ + // 如果文件路径包含"9005",直接设置URL为文件路径 + dmsInstanceFile.setFilePath(dmsInstanceFile.getFilePath()); + } + } + List InstanceFiles = MapstructUtils.convert(dmsInstanceFiles, DmsInstanceFile.class); + // 将修改后的实例文件列表设置到故障实例中 + dmsBillsFaultInstance.setDmsInstanceFiles(InstanceFiles); + + // 返回故障实例对象 + return dmsBillsFaultInstance; + + } catch (Exception e) { + log.error("查询工单详情异常,repairInstanceId: {}", repairInstanceId, e); + + // 如果是查询返回多条记录的异常,尝试备用查询方案 + if (e.getMessage() != null && e.getMessage().contains("Expected one result")) { + log.warn("检测到查询返回多条记录异常,尝试备用查询方案,repairInstanceId: {}", repairInstanceId); + + // 备用方案:查询工单基本信息和最新的活动记录 + DmsBillsFaultInstanceVo workOrder = baseMapper.selectVoById(repairInstanceId); + if (workOrder != null) { + // 查询最新的活动记录 + MPJLambdaWrapper wrapper = JoinWrappers.lambda(DmsFaultInstanceActivity.class) + .eq(DmsFaultInstanceActivity::getRepairInstanceId, repairInstanceId) + .orderByDesc(DmsFaultInstanceActivity::getProcessStepOrder) + .orderByDesc(DmsFaultInstanceActivity::getInstanceActivityId) + .last("LIMIT 1"); + DmsFaultInstanceActivity latestActivity = dmsFaultInstanceActivityMapper.selectOne(wrapper); + + if (latestActivity != null) { + // 设置活动相关信息 + workOrder.setInstanceActivityId(latestActivity.getInstanceActivityId()); + workOrder.setFaultType(latestActivity.getFaultType()); + workOrder.setFaultDescription(latestActivity.getFaultDescription()); + workOrder.setDesignOperations(latestActivity.getDesignOperations()); + workOrder.setRepairType(latestActivity.getRepairType()); + workOrder.setOutsourcingId(latestActivity.getOutsourcingId()); + workOrder.setProcessHandleResolution(latestActivity.getProcessHandleResolution()); + } + + log.info("备用查询方案成功,repairInstanceId: {}, instanceActivityId: {}", + repairInstanceId, workOrder.getInstanceActivityId()); + return workOrder; + } + } + + throw new ServiceException("查询工单详情失败:" + e.getMessage()); + } + } + + + + public void batchInsertInstanceFiles(List fileUrls, Long instanceActivityId) { + // 判断文件URL列表是否不为空且非空 + if (fileUrls != null && !fileUrls.isEmpty()) { + // 创建一个DmsInstanceFile对象的列表 + List dmsInstanceFiles = new ArrayList<>(); + // 遍历文件URL列表 + for (String fileUrl : fileUrls) { + // 创建一个新的DmsInstanceFile对象 + DmsInstanceFile dmsInstanceFile = new DmsInstanceFile(); + // 设置目标ID为实例活动ID + dmsInstanceFile.setTargetId(instanceActivityId); + // 设置目标类型为故障实例活动 + dmsInstanceFile.setTargetType(DmsConstants.DMS_INSTANCE_FILE_TARGET_TYPE_FAULT_INSTANCE_ACTIVITY); + // 设置文件路径为当前URL + dmsInstanceFile.setFilePath(fileUrl); + // 将DmsInstanceFile对象添加到列表中 + dmsInstanceFiles.add(dmsInstanceFile); + } + // 批量插入DmsInstanceFile对象到数据库 + dmsInstanceFileMapper.batchDmsInstanceFile(dmsInstanceFiles); + } + } + + + /** + * 修改报修工单实例节点 + * + * @param dmsFaultInstanceActivity 报修工单实例节点 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateDmsFaultInstanceActivity(DmsFaultInstanceActivityBo dmsFaultInstanceActivity) { + // 获取文件URL列表 + List fileUrls = dmsFaultInstanceActivity.getFileUrls(); + // 获取实例ID + Long activityId = dmsFaultInstanceActivity.getInstanceActivityId(); + // 根据实例ID查询对应的维修实例ID + Long repairInstanceId = dmsFaultInstanceActivityMapper.selectRepairInstanceId(activityId); + // 创建新的DmsBillsFaultInstance对象 + DmsBillsFaultInstance dmsBillsFaultInstance = new DmsBillsFaultInstance(); + // 设置外包ID + dmsBillsFaultInstance.setOutsourcingId(dmsFaultInstanceActivity.getOutsourcingId()); + // 设置故障来源类型 + dmsBillsFaultInstance.setFaultSourceType(dmsFaultInstanceActivity.getFaultSourceType()); + // 设置故障类型 + dmsBillsFaultInstance.setFaultType(dmsFaultInstanceActivity.getFaultType()); + // 设置维修工单ID(主键) + dmsBillsFaultInstance.setRepairInstanceId(repairInstanceId); + validEntityBeforeSave(dmsBillsFaultInstance); + // 根据ID更新DmsBillsFaultInstance记录 + int i = baseMapper.updateById(dmsBillsFaultInstance); + + // 根据目标ID删除dms_instance_file表记录 + int rows = dmsFaultInstanceActivityMapper.deleteByTargetId(activityId); + // 批量插入实例文件 + batchInsertInstanceFiles(fileUrls,activityId); + // 根据ID更新记录并返回影响行数 + DmsFaultInstanceActivity faultInstanceActivity = MapstructUtils.convert(dmsFaultInstanceActivity, DmsFaultInstanceActivity.class); + return dmsFaultInstanceActivityMapper.updateById(faultInstanceActivity); + } + + /** + * 扫描设备编码验证,验证通过后进行维修 + * + * @param dmsBillsFaultInstanceScanVo + */ + @Override + public Long scanDevice4Repair(DmsBillsFaultInstanceScanBo dmsBillsFaultInstanceScan) { + Long count = baseMapper.selectCountByRepairInstanceIdAndDeviceCode( + dmsBillsFaultInstanceScan.getRepairInstanceId(), dmsBillsFaultInstanceScan.getDeviceCode()); + if (count <= 0) { + throw new ServiceException("扫描验证失败"); + }else { + return count; + } + } + + public void batchInsertInstanceAfterFiles(List fileUrls, Long instanceActivityId) { + // 判断文件URL列表是否不为空且非空集合 + if (fileUrls != null && !fileUrls.isEmpty()) { + // 创建一个DmsInstanceFile对象的列表,用于存储文件信息 + List dmsInstanceFiles = new ArrayList<>(); + // 遍历文件URL列表 + for (String fileUrl : fileUrls) { + // 创建一个新的DmsInstanceFile对象 + DmsInstanceFile dmsInstanceFile = new DmsInstanceFile(); + // 设置目标ID为传入的实例活动ID + dmsInstanceFile.setTargetId(instanceActivityId); + // 设置目标类型为"2",表示关联类型为维修后 + dmsInstanceFile.setTargetType("2");//关联类型(1维修前,2维修后) + // 设置文件路径为当前遍历到的文件URL + dmsInstanceFile.setFilePath(fileUrl); + // 将DmsInstanceFile对象添加到列表中 + dmsInstanceFiles.add(dmsInstanceFile); + } + // 调用数据映射器批量插入文件信息 + dmsInstanceFileMapper.batchDmsInstanceFile(dmsInstanceFiles); + } + } + + +}