|
@@ -1,7 +1,7 @@
|
|
|
package com.zksy.base.service.impl;
|
|
package com.zksy.base.service.impl;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
-import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.zksy.base.domain.EquipmentBase;
|
|
import com.zksy.base.domain.EquipmentBase;
|
|
@@ -9,33 +9,86 @@ import com.zksy.base.domain.HazardHiddenAccount;
|
|
|
import com.zksy.base.domain.WorkOrder;
|
|
import com.zksy.base.domain.WorkOrder;
|
|
|
import com.zksy.base.domain.WorkOrderLog;
|
|
import com.zksy.base.domain.WorkOrderLog;
|
|
|
import com.zksy.base.domain.vo.WorkOrderPushVO;
|
|
import com.zksy.base.domain.vo.WorkOrderPushVO;
|
|
|
|
|
+import com.zksy.base.enums.WorkOrderDelayApplyStatusEnum;
|
|
|
|
|
+import com.zksy.base.enums.WorkOrderLevelEnum;
|
|
|
|
|
+import com.zksy.base.enums.WorkOrderLogOperTypeEnum;
|
|
|
|
|
+import com.zksy.base.enums.WorkOrderStatusEnum;
|
|
|
import com.zksy.base.mapper.EquipmentBaseMapper;
|
|
import com.zksy.base.mapper.EquipmentBaseMapper;
|
|
|
import com.zksy.base.mapper.HazardHiddenAccountMapper;
|
|
import com.zksy.base.mapper.HazardHiddenAccountMapper;
|
|
|
import com.zksy.base.mapper.WorkOrderLogMapper;
|
|
import com.zksy.base.mapper.WorkOrderLogMapper;
|
|
|
import com.zksy.base.mapper.WorkOrderMapper;
|
|
import com.zksy.base.mapper.WorkOrderMapper;
|
|
|
import com.zksy.base.service.BusinessWorkOrderService;
|
|
import com.zksy.base.service.BusinessWorkOrderService;
|
|
|
|
|
+import com.zksy.common.core.domain.TreeSelect;
|
|
|
import com.zksy.common.core.domain.entity.SysUser;
|
|
import com.zksy.common.core.domain.entity.SysUser;
|
|
|
|
|
+import com.zksy.common.core.domain.model.LoginUser;
|
|
|
|
|
+import com.zksy.common.constant.UserConstants;
|
|
|
import com.zksy.common.exception.ServiceException;
|
|
import com.zksy.common.exception.ServiceException;
|
|
|
|
|
+import com.zksy.common.utils.DateUtils;
|
|
|
|
|
+import com.zksy.common.utils.SecurityUtils;
|
|
|
|
|
+import com.zksy.common.utils.StringUtils;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderDelayApplyDTO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderDelayAuditDTO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderDetailVO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderDispatchDTO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderOperateDTO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderPageDTO;
|
|
|
|
|
+import com.zksy.ops.dto.WorkOrderVerifyDTO;
|
|
|
|
|
+import com.zksy.ops.support.OpsAssignScopeSupport;
|
|
|
import com.zksy.system.service.ISysUserService;
|
|
import com.zksy.system.service.ISysUserService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
|
|
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
|
|
+import java.util.Collections;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
-import java.util.ArrayList;
|
|
|
|
|
|
|
+import java.util.Locale;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+import java.util.concurrent.ThreadLocalRandom;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 业务工单管理 Service 实现
|
|
|
|
|
|
|
+ * 运维工单 Service 实现(ops 工单流程)
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>状态流转:0待派单 →(派单) 1待接单 →(核查) 2已接单 →(处置) 3处理中
|
|
|
|
|
+ * →(提交验收) 4待验收 →(验收通过) 5已结案;延期审核通过后进入 6延期中;任意前置状态可 7已取消。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author zksy
|
|
|
*/
|
|
*/
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Service
|
|
@Service
|
|
|
public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements BusinessWorkOrderService {
|
|
public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder> implements BusinessWorkOrderService {
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 工单编号前缀
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final String ORDER_NO_PREFIX = "WO";
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 工单编号最大生成重试次数(并发唯一性兜底)
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final int ORDER_NO_MAX_RETRY = 5;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 工单编号随机数上限(4位)
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final int ORDER_NO_RANDOM_BOUND = 10000;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 框架数据范围参数名,与 DataScopeAspect.DATA_SCOPE 保持一致。
|
|
|
|
|
+ * zksy-system 模块不依赖 zksy-framework(DataScopeAspect 所在模块),故此处使用字面量。
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final String DATA_SCOPE = "dataScope";
|
|
|
|
|
+
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private WorkOrderLogMapper workOrderLogMapper;
|
|
private WorkOrderLogMapper workOrderLogMapper;
|
|
|
|
|
|
|
@@ -48,336 +101,422 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private ISysUserService sysUserService;
|
|
private ISysUserService sysUserService;
|
|
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private OpsAssignScopeSupport assignScopeSupport;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
- public IPage<WorkOrder> selectWorkOrderList(Integer orderType, Integer orderLevel, String orderStatus, String deviceCode, String keyword, String equipmentType, Integer pageNum, Integer pageSize) {
|
|
|
|
|
|
|
+ public Page<WorkOrder> findByPage(WorkOrderPageDTO dto) {
|
|
|
|
|
+ if (dto == null) {
|
|
|
|
|
+ dto = new WorkOrderPageDTO();
|
|
|
|
|
+ }
|
|
|
|
|
+ long pageNum = (dto.getPageNum() == null || dto.getPageNum() < 1) ? 1L : dto.getPageNum();
|
|
|
|
|
+ long pageSize = (dto.getPageSize() == null || dto.getPageSize() < 1) ? 10L : dto.getPageSize();
|
|
|
Page<WorkOrder> page = new Page<>(pageNum, pageSize);
|
|
Page<WorkOrder> page = new Page<>(pageNum, pageSize);
|
|
|
|
|
+
|
|
|
|
|
+ if (assignScopeSupport.currentUserId() == null) {
|
|
|
|
|
+ // 未获取到登录用户时返回空结果,避免越权查看
|
|
|
|
|
+ return new Page<>(pageNum, pageSize);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<WorkOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
- // 按设备类型过滤工单:gas-燃气(父类别ID=3),drainage-排水(typeId为drainage的顶级分类),water-供水;为空查询全部设备工单
|
|
|
|
|
- if (StringUtils.isNotEmpty(equipmentType)) {
|
|
|
|
|
- if ("gas".equals(equipmentType)) {
|
|
|
|
|
- wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.parent_type_id = '3'");
|
|
|
|
|
- } else if ("drainage".equals(equipmentType)) {
|
|
|
|
|
- // 排水:typeId为drainage的顶级分类及其全部子类型
|
|
|
|
|
- wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.id IN (SELECT id FROM app_user.equipment_type WHERE type_id = 'drainage' AND parent_type_id = '0' " + "UNION SELECT id FROM app_user.equipment_type WHERE parent_type_id = (SELECT id FROM app_user.equipment_type WHERE type_id = 'drainage' AND parent_type_id = '0'))");
|
|
|
|
|
- } else if ("water".equals(equipmentType)) {
|
|
|
|
|
- // 供水:顶级类型名称为“供水”的分类及其全部子类型
|
|
|
|
|
- wrapper.inSql(WorkOrder::getDeviceId, "SELECT eb.equipment_id FROM app_user.equipment_base eb " + "INNER JOIN app_user.equipment_type et ON eb.equipment_type_id = et.id " + "WHERE et.id IN (SELECT id FROM app_user.equipment_type WHERE type_name = '供水' AND parent_type_id = '0' " + "UNION SELECT id FROM app_user.equipment_type WHERE parent_type_id = (SELECT id FROM app_user.equipment_type WHERE type_name = '供水' AND parent_type_id = '0'))");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ String dataScope = dataScopeSql(dto);
|
|
|
|
|
+ if (StringUtils.isNotEmpty(dataScope)) {
|
|
|
|
|
+ wrapper.apply(dataScope);
|
|
|
}
|
|
}
|
|
|
- if (orderType != null) wrapper.eq(WorkOrder::getOrderType, orderType);
|
|
|
|
|
- if (orderLevel != null) wrapper.eq(WorkOrder::getOrderLevel, orderLevel);
|
|
|
|
|
- // orderStatus 支持逗号分隔多值,如 1,2,3
|
|
|
|
|
- if (StringUtils.isNotEmpty(orderStatus)) {
|
|
|
|
|
- if (orderStatus.contains(",")) {
|
|
|
|
|
- List<Integer> statusList = Arrays.stream(orderStatus.split(","))
|
|
|
|
|
- .filter(StringUtils::isNumeric)
|
|
|
|
|
- .map(Integer::valueOf)
|
|
|
|
|
- .collect(Collectors.toList());
|
|
|
|
|
- if (!statusList.isEmpty()) {
|
|
|
|
|
- wrapper.in(WorkOrder::getOrderStatus, statusList);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- wrapper.eq(WorkOrder::getOrderStatus, Integer.valueOf(orderStatus));
|
|
|
|
|
|
|
+ wrapper.eq(dto.getOrderLevel() != null, WorkOrder::getOrderLevel, dto.getOrderLevel()).eq(dto.getDelayApplyStatus() != null, WorkOrder::getDelayApplyStatus, dto.getDelayApplyStatus()).eq(StringUtils.isNotEmpty(dto.getDeviceCode()), WorkOrder::getDeviceCode, dto.getDeviceCode()).ge(StringUtils.isNotEmpty(dto.getFaultTimeStart()), WorkOrder::getFaultTime, DateUtils.parseDate(dto.getFaultTimeStart())).le(StringUtils.isNotEmpty(dto.getFaultTimeEnd()), WorkOrder::getFaultTime, DateUtils.parseDate(dto.getFaultTimeEnd()));
|
|
|
|
|
+ if (StringUtils.isNotEmpty(dto.getOrderStatus())) {
|
|
|
|
|
+ List<Integer> statusList = parseStatusList(dto.getOrderStatus());
|
|
|
|
|
+ if (!statusList.isEmpty()) {
|
|
|
|
|
+ wrapper.in(WorkOrder::getOrderStatus, statusList);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if (StringUtils.isNotEmpty(deviceCode)) wrapper.eq(WorkOrder::getDeviceCode, deviceCode);
|
|
|
|
|
- if (StringUtils.isNotEmpty(keyword)) {
|
|
|
|
|
|
|
+ if (StringUtils.isNotEmpty(dto.getKeyword())) {
|
|
|
|
|
+ String keyword = dto.getKeyword().trim();
|
|
|
wrapper.and(w -> w.like(WorkOrder::getOrderNo, keyword).or().like(WorkOrder::getDeviceCode, keyword).or().like(WorkOrder::getOrderDesc, keyword));
|
|
wrapper.and(w -> w.like(WorkOrder::getOrderNo, keyword).or().like(WorkOrder::getDeviceCode, keyword).or().like(WorkOrder::getOrderDesc, keyword));
|
|
|
}
|
|
}
|
|
|
- wrapper.orderByAsc(WorkOrder::getOrderLevel, WorkOrder::getOrderStatus);
|
|
|
|
|
- wrapper.orderByDesc(WorkOrder::getCreateTime);
|
|
|
|
|
- return this.page(page, wrapper);
|
|
|
|
|
|
|
+ applyDeviceTypeScope(wrapper, dto.getDeviceTypeParentId());
|
|
|
|
|
+ wrapper.orderByAsc(WorkOrder::getOrderLevel).orderByAsc(WorkOrder::getOrderStatus).orderByDesc(WorkOrder::getFaultTime).orderByDesc(WorkOrder::getCreateTime);
|
|
|
|
|
+
|
|
|
|
|
+ Page<WorkOrder> result = this.page(page, wrapper);
|
|
|
|
|
+ fillUserName(result.getRecords());
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public WorkOrder getWorkOrderDetail(Long orderId) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- return order;
|
|
|
|
|
|
|
+ public WorkOrderDetailVO getDetailById(Long orderId) {
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ fillUserName(Collections.singletonList(order));
|
|
|
|
|
+ WorkOrderDetailVO vo = new WorkOrderDetailVO();
|
|
|
|
|
+ vo.setOrder(order);
|
|
|
|
|
+ vo.setLogs(getWorkOrderLogs(orderId));
|
|
|
|
|
+ return vo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public List<WorkOrderLog> getWorkOrderLogs(Long orderId) {
|
|
public List<WorkOrderLog> getWorkOrderLogs(Long orderId) {
|
|
|
- LambdaQueryWrapper<WorkOrderLog> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
- wrapper.eq(WorkOrderLog::getOrderId, orderId);
|
|
|
|
|
- wrapper.orderByDesc(WorkOrderLog::getOperTime);
|
|
|
|
|
- return workOrderLogMapper.selectList(wrapper);
|
|
|
|
|
|
|
+ if (orderId == null) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ List<WorkOrderLog> logs = workOrderLogMapper.selectList(new LambdaQueryWrapper<WorkOrderLog>().eq(WorkOrderLog::getOrderId, orderId).orderByDesc(WorkOrderLog::getOperTime));
|
|
|
|
|
+ if (!CollectionUtils.isEmpty(logs)) {
|
|
|
|
|
+ for (WorkOrderLog item : logs) {
|
|
|
|
|
+ item.setOperTypeDesc(WorkOrderLogOperTypeEnum.getDescByCode(item.getOperType()));
|
|
|
|
|
+ item.setAttachList(splitAttach(item.getAttachUrl()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return logs;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ==================== 新增 ====================
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean dispatchOrder(Long orderId, Long handlerId, String remarks, Long operUserId, String operUserName) {
|
|
|
|
|
- validateDispatchHandler(handlerId);
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (order.getOrderStatus() != 1) throw new ServiceException("当前工单状态不允许派单");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(2);
|
|
|
|
|
- entity.setDispatchUser(operUserId);
|
|
|
|
|
- entity.setDispatchTime(new Date());
|
|
|
|
|
- entity.setReceiveUser(handlerId.toString());
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ public Long addWorkOrder(WorkOrder workOrder) {
|
|
|
|
|
+ if (workOrder == null || StringUtils.isEmpty(workOrder.getOrderDesc())) {
|
|
|
|
|
+ throw new ServiceException("故障描述不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ checkDeviceOrderConflict(workOrder.getDeviceId());
|
|
|
|
|
+ workOrder.setOrderId(null);
|
|
|
|
|
+ workOrder.setOrderNo(generateOrderNo());
|
|
|
|
|
+ workOrder.setOrderLevel(WorkOrderLevelEnum.getByCode(workOrder.getOrderLevel()).getCode());
|
|
|
|
|
+ if (StringUtils.isNotEmpty(workOrder.getDeviceId()) && StringUtils.isEmpty(workOrder.getDeviceCode())) {
|
|
|
|
|
+ workOrder.setDeviceCode(resolveDeviceCode(workOrder.getDeviceId()));
|
|
|
|
|
+ }
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ if (workOrder.getFaultTime() == null) {
|
|
|
|
|
+ workOrder.setFaultTime(now);
|
|
|
|
|
+ }
|
|
|
|
|
+ workOrder.setOrderStatus(WorkOrderStatusEnum.PENDING_DISPATCH.getCode());
|
|
|
|
|
+ workOrder.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.NONE.getCode());
|
|
|
|
|
+ // 数据按部门隔离:默认归属创建人所在部门(框架 DataScopeAspect 依据该字段过滤)
|
|
|
|
|
+ if (workOrder.getDeptId() == null) {
|
|
|
|
|
+ workOrder.setDeptId(assignScopeSupport.currentDeptId());
|
|
|
|
|
+ }
|
|
|
|
|
+ workOrder.setCreateTime(now);
|
|
|
|
|
+ workOrder.setUpdateTime(now);
|
|
|
|
|
+ this.save(workOrder);
|
|
|
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(2);
|
|
|
|
|
- logEntry.setOperDesc("派单给处理人ID:" + handlerId + ",备注:" + (remarks != null ? remarks : ""));
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ writeLog(workOrder.getOrderId(), WorkOrderLogOperTypeEnum.CREATE, buildDesc("故障描述", workOrder.getOrderDesc()), null);
|
|
|
|
|
+ log.info("手动创建工单成功,工单ID={},编号={}", workOrder.getOrderId(), workOrder.getOrderNo());
|
|
|
|
|
+ return workOrder.getOrderId();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean batchDispatchOrder(Long orderId, List<Long> handlerIds, String remarks, Long operUserId, String operUserName) {
|
|
|
|
|
- if (handlerIds == null || handlerIds.isEmpty()) {
|
|
|
|
|
- throw new ServiceException("派单人员不能为空");
|
|
|
|
|
|
|
+ public Long createWorkOrderFromHazard(String hazardId, String hazardNo, String hazardDesc, String equipmentId, String hazardLevel, String hazardImages, Date planFinishTime) {
|
|
|
|
|
+ if (StringUtils.isEmpty(equipmentId)) {
|
|
|
|
|
+ throw new ServiceException("隐患关联设备不能为空");
|
|
|
}
|
|
}
|
|
|
- handlerIds.forEach(this::validateDispatchHandler);
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (order.getOrderStatus() != 1) throw new ServiceException("当前工单状态不允许派单");
|
|
|
|
|
-
|
|
|
|
|
- // 更新工单状态(只更新一次)
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(2);
|
|
|
|
|
- entity.setDispatchUser(operUserId);
|
|
|
|
|
- entity.setDispatchTime(new Date());
|
|
|
|
|
- // 多个处理人用逗号分隔存储
|
|
|
|
|
- String receiveUserStr = handlerIds.stream()
|
|
|
|
|
- .map(String::valueOf)
|
|
|
|
|
- .collect(Collectors.joining(","));
|
|
|
|
|
- entity.setReceiveUser(receiveUserStr);
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
-
|
|
|
|
|
- // 为每个 handler 写入操作日志
|
|
|
|
|
- for (Long handlerId : handlerIds) {
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(2);
|
|
|
|
|
- logEntry.setOperDesc("派单给处理人ID:" + handlerId + ",备注:" + (remarks != null ? remarks : ""));
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ checkDeviceOrderConflict(equipmentId);
|
|
|
|
|
+
|
|
|
|
|
+ WorkOrder workOrder = new WorkOrder();
|
|
|
|
|
+ workOrder.setOrderNo(generateOrderNo());
|
|
|
|
|
+ workOrder.setHazardId(hazardId);
|
|
|
|
|
+ workOrder.setHazardNo(hazardNo);
|
|
|
|
|
+ workOrder.setDeviceId(equipmentId);
|
|
|
|
|
+ workOrder.setDeviceCode(resolveDeviceCode(equipmentId));
|
|
|
|
|
+ workOrder.setOrderLevel(mapHazardLevelToOrderLevel(hazardLevel).getCode());
|
|
|
|
|
+ workOrder.setOrderDesc(hazardDesc);
|
|
|
|
|
+ // 数据按部门隔离:默认归属创建人所在部门(框架 DataScopeAspect 依据该字段过滤)
|
|
|
|
|
+ workOrder.setDeptId(assignScopeSupport.currentDeptId());
|
|
|
|
|
+ workOrder.setOrderStatus(WorkOrderStatusEnum.PENDING_DISPATCH.getCode());
|
|
|
|
|
+ workOrder.setDelayApplyStatus(WorkOrderDelayApplyStatusEnum.NONE.getCode());
|
|
|
|
|
+ workOrder.setPlanFinishTime(planFinishTime);
|
|
|
|
|
+ if (StringUtils.isNotEmpty(hazardImages)) {
|
|
|
|
|
+ workOrder.setMaintenanceBeforeImg(hazardImages);
|
|
|
}
|
|
}
|
|
|
- return true;
|
|
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ workOrder.setFaultTime(now);
|
|
|
|
|
+ workOrder.setCreateTime(now);
|
|
|
|
|
+ workOrder.setUpdateTime(now);
|
|
|
|
|
+ this.save(workOrder);
|
|
|
|
|
+
|
|
|
|
|
+ writeLog(workOrder.getOrderId(), WorkOrderLogOperTypeEnum.CREATE, buildDesc("由隐患[" + hazardNo + "]自动创建工单", hazardDesc), null);
|
|
|
|
|
+ log.info("从隐患[{}]创建工单成功,工单ID={}", hazardId, workOrder.getOrderId());
|
|
|
|
|
+ return workOrder.getOrderId();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void validateDispatchHandler(Long handlerId) {
|
|
|
|
|
|
|
+ // ==================== 流程操作 ====================
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public boolean dispatch(WorkOrderDispatchDTO dto) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ Long handlerId = dto.getHandlerId();
|
|
|
if (handlerId == null) {
|
|
if (handlerId == null) {
|
|
|
- throw new ServiceException("派单人员不能为空");
|
|
|
|
|
|
|
+ throw new ServiceException("责任人不能为空");
|
|
|
}
|
|
}
|
|
|
- if (sysUserService == null) {
|
|
|
|
|
- throw new ServiceException("责任人校验服务不可用");
|
|
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ if (!Objects.equals(WorkOrderStatusEnum.PENDING_DISPATCH.getCode(), order.getOrderStatus())) {
|
|
|
|
|
+ throw new ServiceException("仅待派单工单允许派单");
|
|
|
}
|
|
}
|
|
|
- SysUser user = sysUserService.selectUserById(handlerId);
|
|
|
|
|
- if (user == null || !"0".equals(String.valueOf(user.getStatus()))
|
|
|
|
|
- || !"0".equals(String.valueOf(user.getDelFlag()))) {
|
|
|
|
|
- throw new ServiceException("接单责任人不存在或已停用");
|
|
|
|
|
|
|
+ assignScopeSupport.validateAssignUserInScope(handlerId);
|
|
|
|
|
+ SysUser handler = sysUserService.selectUserById(handlerId);
|
|
|
|
|
+ if (handler == null || !UserConstants.NORMAL.equals(String.valueOf(handler.getStatus())) || !UserConstants.NORMAL.equals(String.valueOf(handler.getDelFlag()))) {
|
|
|
|
|
+ throw new ServiceException("责任人不存在或已停用");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ Long operUserId = currentUserId();
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_ACCEPT.getCode()).set(WorkOrder::getReceiveUser, handlerId).set(WorkOrder::getDispatchUser, operUserId).set(WorkOrder::getDispatchTime, now)
|
|
|
|
|
+ // 数据归属部门跟随派单指定的责任人
|
|
|
|
|
+ .set(handler.getDeptId() != null, WorkOrder::getDeptId, handler.getDeptId()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_DISPATCH.getCode()));
|
|
|
|
|
+
|
|
|
|
|
+ String logContent = "派单给责任人:" + userLabel(handler) + (StringUtils.isEmpty(dto.getRemarks()) ? "" : ",备注:" + dto.getRemarks());
|
|
|
|
|
+ writeLog(orderId, WorkOrderLogOperTypeEnum.DISPATCH, logContent, null);
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean verifyOrder(Long orderId, String handleResult, String solution, Long operUserId, String operUserName) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (!Arrays.asList(2, 3).contains(order.getOrderStatus())) throw new ServiceException("当前工单状态不允许核查");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(3);
|
|
|
|
|
- entity.setRepairResult(solution);
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ public boolean accept(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ WorkOrder order = loadForOperate(dto, "核查接单");
|
|
|
|
|
+ if (!Objects.equals(WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), order.getOrderStatus())) {
|
|
|
|
|
+ throw new ServiceException("仅待接单工单允许核查接单");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 核查接单必须上传现场情况附件
|
|
|
|
|
+ if (StringUtils.isEmpty(attach(dto))) {
|
|
|
|
|
+ throw new ServiceException("核查接单必须上传现场情况附件");
|
|
|
|
|
+ }
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.ACCEPTED.getCode())
|
|
|
|
|
+ // 维修前图片固定来源于核查接单上传的现场情况附件
|
|
|
|
|
+ .set(WorkOrder::getMaintenanceBeforeImg, attach(dto)).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_ACCEPT.getCode());
|
|
|
|
|
+ this.update(uw);
|
|
|
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(3);
|
|
|
|
|
- logEntry.setOperDesc("核查结果:" + handleResult + ",说明:" + solution);
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.ACCEPT, buildDesc("", content(dto)), attach(dto));
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean processOrder(Long orderId, String handleResult, String solution, String maintenanceBeforeImg, Long operUserId, String operUserName) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (!Arrays.asList(3, 4).contains(order.getOrderStatus())) throw new ServiceException("当前工单状态不允许处理");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(4);
|
|
|
|
|
- entity.setRepairResult(solution);
|
|
|
|
|
- if (StringUtils.isNotBlank(maintenanceBeforeImg)) {
|
|
|
|
|
- entity.setMaintenanceBeforeImg(maintenanceBeforeImg);
|
|
|
|
|
|
|
+ public boolean process(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ WorkOrder order = loadForOperate(dto, "处置");
|
|
|
|
|
+ Integer status = order.getOrderStatus();
|
|
|
|
|
+ if (!WorkOrderStatusEnum.canProcess(status)) {
|
|
|
|
|
+ throw new ServiceException("当前工单状态不允许处置");
|
|
|
}
|
|
}
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ // 已接单 → 处理中;处理中/延期中 保持原状态,支持多次处置
|
|
|
|
|
+ Integer targetStatus = Objects.equals(WorkOrderStatusEnum.ACCEPTED.getCode(), status) ? WorkOrderStatusEnum.PROCESSING.getCode() : status;
|
|
|
|
|
+ LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, targetStatus).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, status);
|
|
|
|
|
+ this.update(uw);
|
|
|
|
|
+
|
|
|
|
|
+ syncHazardStatus(order, "整改中", content(dto));
|
|
|
|
|
+ writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.PROCESS, buildDesc("", content(dto)), attach(dto));
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 工单处理中 → 同步隐患状态为"整改中"
|
|
|
|
|
- syncHazardStatus(order, "整改中", solution);
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public boolean submitVerify(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ WorkOrder order = loadForOperate(dto, "提交验收");
|
|
|
|
|
+ Integer status = order.getOrderStatus();
|
|
|
|
|
+ if (!WorkOrderStatusEnum.canSubmitVerify(status)) {
|
|
|
|
|
+ throw new ServiceException("仅处理中/延期中工单允许提交验收");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 提交验收必须上传维修后的情况附件
|
|
|
|
|
+ if (StringUtils.isEmpty(attach(dto))) {
|
|
|
|
|
+ throw new ServiceException("提交验收必须上传维修后的情况附件");
|
|
|
|
|
+ }
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ // 提交验收:附件已在上方强制必填,直接写入工单维修后图片
|
|
|
|
|
+ LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_VERIFY.getCode()).set(WorkOrder::getMaintenanceAfterImg, attach(dto)).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, order.getOrderId()).eq(WorkOrder::getOrderStatus, status);
|
|
|
|
|
+ this.update(uw);
|
|
|
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(4);
|
|
|
|
|
- logEntry.setOperDesc("处理结果:" + handleResult + ",说明:" + solution);
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ writeLog(order.getOrderId(), WorkOrderLogOperTypeEnum.SUBMIT_VERIFY, buildDesc("", content(dto)), attach(dto));
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean completeOrder(Long orderId, String result, String maintenanceAfterImg, Long operUserId, String operUserName) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (order.getOrderStatus() != 4)
|
|
|
|
|
- throw new ServiceException("当前工单状态不允许提交验收");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(5);
|
|
|
|
|
- entity.setRepairResult(result);
|
|
|
|
|
- if (StringUtils.isNotBlank(maintenanceAfterImg)) {
|
|
|
|
|
- entity.setMaintenanceAfterImg(maintenanceAfterImg);
|
|
|
|
|
|
|
+ public boolean verify(WorkOrderVerifyDTO dto) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ Boolean passed = dto == null ? null : dto.getPassed();
|
|
|
|
|
+ if (passed == null) {
|
|
|
|
|
+ throw new ServiceException("验收结果不能为空");
|
|
|
}
|
|
}
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ if (!Objects.equals(WorkOrderStatusEnum.PENDING_VERIFY.getCode(), order.getOrderStatus())) {
|
|
|
|
|
+ throw new ServiceException("仅待验收工单允许验收");
|
|
|
|
|
+ }
|
|
|
|
|
+ Long operUserId = currentUserId();
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ WorkOrderStatusEnum targetStatus = passed ? WorkOrderStatusEnum.CLOSED : WorkOrderStatusEnum.PROCESSING;
|
|
|
|
|
+ LambdaUpdateWrapper<WorkOrder> uw = new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, targetStatus.getCode()).set(WorkOrder::getVerifyUser, operUserId).set(WorkOrder::getVerifyTime, now).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PENDING_VERIFY.getCode());
|
|
|
|
|
+ if (passed) {
|
|
|
|
|
+ uw.set(WorkOrder::getRealFinishTime, now);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isNotEmpty(dto.getContent())) {
|
|
|
|
|
+ uw.set(WorkOrder::getRepairResult, dto.getContent());
|
|
|
|
|
+ }
|
|
|
|
|
+ this.update(uw);
|
|
|
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(5);
|
|
|
|
|
- logEntry.setOperDesc("提交验收,结果:" + result);
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ if (passed) {
|
|
|
|
|
+ syncHazardStatus(order, "已整改", dto.getContent());
|
|
|
|
|
+ }
|
|
|
|
|
+ writeLog(orderId, passed ? WorkOrderLogOperTypeEnum.VERIFY_PASS : WorkOrderLogOperTypeEnum.VERIFY_REJECT, buildDesc("验收意见", dto.getContent()), dto.getAttachUrl());
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public Long createWorkOrderFromHazard(String hazardId, String hazardNo, String hazardDesc, String equipmentId, String hazardLevel, String hazardImages, Date planFinishTime) {
|
|
|
|
|
- // 同一设备存在进行中工单(状态非 6 已结案/7 已驳回)时禁止重复建单
|
|
|
|
|
- checkDeviceOrderConflict(equipmentId, 1, null);
|
|
|
|
|
-
|
|
|
|
|
- // 隐患等级映射到工单优先级:0-重大→紧急(1) 1-较大→一般(2) 2-一般→一般(2) 3-轻微→低(3)
|
|
|
|
|
- Integer orderLevel;
|
|
|
|
|
- if ("0".equals(hazardLevel)) {
|
|
|
|
|
- orderLevel = 1;
|
|
|
|
|
- } else if ("3".equals(hazardLevel)) {
|
|
|
|
|
- orderLevel = 3;
|
|
|
|
|
- } else {
|
|
|
|
|
- orderLevel = 2;
|
|
|
|
|
|
|
+ public boolean cancel(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ if (!WorkOrderStatusEnum.canCancel(order.getOrderStatus())) {
|
|
|
|
|
+ throw new ServiceException("当前工单状态不允许取消");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isEmpty(content(dto))) {
|
|
|
|
|
+ throw new ServiceException("请填写取消原因");
|
|
|
}
|
|
}
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getOrderStatus, WorkOrderStatusEnum.CANCELLED.getCode()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, order.getOrderStatus()));
|
|
|
|
|
|
|
|
- WorkOrder workOrder = new WorkOrder();
|
|
|
|
|
- workOrder.setOrderNo(generateOrderNo());
|
|
|
|
|
- workOrder.setHazardId(hazardId);
|
|
|
|
|
- workOrder.setHazardNo(hazardNo);
|
|
|
|
|
- workOrder.setDeviceId(equipmentId);
|
|
|
|
|
- // 查询设备信息,填充设备编码
|
|
|
|
|
- if (StringUtils.isNotEmpty(equipmentId)) {
|
|
|
|
|
- EquipmentBase equipment = equipmentBaseMapper.selectById(equipmentId);
|
|
|
|
|
- if (equipment != null) {
|
|
|
|
|
- workOrder.setDeviceCode(equipment.getEquipmentCode());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ writeLog(orderId, WorkOrderLogOperTypeEnum.CANCEL, buildDesc("原因", content(dto)), attach(dto));
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public boolean applyDelay(WorkOrderDelayApplyDTO dto) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ Long currentUserId = currentUserId();
|
|
|
|
|
+ requireResponsible(order, currentUserId, "申请延期");
|
|
|
|
|
+ if (!Objects.equals(WorkOrderStatusEnum.PROCESSING.getCode(), order.getOrderStatus())) {
|
|
|
|
|
+ throw new ServiceException("仅处理中工单允许申请延期");
|
|
|
}
|
|
}
|
|
|
- workOrder.setOrderType(1); // 默认故障维修
|
|
|
|
|
- workOrder.setOrderLevel(orderLevel);
|
|
|
|
|
- workOrder.setOrderDesc(hazardDesc);
|
|
|
|
|
- workOrder.setOrderStatus(1); // 待派单
|
|
|
|
|
- // 隐患图片作为维修前图片(如有)
|
|
|
|
|
- if (StringUtils.isNotEmpty(hazardImages)) {
|
|
|
|
|
- workOrder.setMaintenanceBeforeImg(hazardImages);
|
|
|
|
|
|
|
+ if (!Objects.equals(WorkOrderDelayApplyStatusEnum.NONE.getCode(), order.getDelayApplyStatus())) {
|
|
|
|
|
+ throw new ServiceException("该工单已提交过延期申请,请勿重复提交");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.isEmpty(dto.getDelayedReason())) {
|
|
|
|
|
+ throw new ServiceException("延期原因不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ Date delayedDeadline = DateUtils.parseDate(dto.getDelayedDeadline());
|
|
|
|
|
+ if (delayedDeadline == null) {
|
|
|
|
|
+ throw new ServiceException("延期截止时间格式不正确");
|
|
|
}
|
|
}
|
|
|
- workOrder.setPlanFinishTime(planFinishTime);
|
|
|
|
|
- workOrder.setCreateTime(new Date());
|
|
|
|
|
- this.save(workOrder);
|
|
|
|
|
|
|
|
|
|
- // 写入操作日志
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(workOrder.getOrderId());
|
|
|
|
|
- logEntry.setOperType(1);
|
|
|
|
|
- logEntry.setOperDesc("由隐患[" + hazardId + "]自动创建工单");
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()).set(WorkOrder::getDelayedDeadline, delayedDeadline).set(WorkOrder::getDelayedReason, dto.getDelayedReason()).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getOrderStatus, WorkOrderStatusEnum.PROCESSING.getCode()).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.NONE.getCode()));
|
|
|
|
|
|
|
|
- log.info("从隐患[{}]创建工单成功,工单ID={}", hazardId, workOrder.getOrderId());
|
|
|
|
|
- return workOrder.getOrderId();
|
|
|
|
|
|
|
+ writeLog(orderId, WorkOrderLogOperTypeEnum.DELAY_APPLY, "申请延期至 " + dto.getDelayedDeadline() + ",原因:" + dto.getDelayedReason(), dto.getAttachUrl());
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public Long createWorkOrder(WorkOrder workOrder) {
|
|
|
|
|
- // 同一设备存在进行中工单(状态非 6 已结案/7 已驳回)时禁止重复建单
|
|
|
|
|
- if (workOrder.getOrderType() == null) {
|
|
|
|
|
- workOrder.setOrderType(1);
|
|
|
|
|
|
|
+ public boolean auditDelay(WorkOrderDelayAuditDTO dto) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ Boolean approved = dto.getApproved();
|
|
|
|
|
+ if (approved == null) {
|
|
|
|
|
+ throw new ServiceException("延期审核结果不能为空");
|
|
|
}
|
|
}
|
|
|
- checkDeviceOrderConflict(workOrder.getDeviceId(), workOrder.getOrderType(), workOrder.getAlarmId());
|
|
|
|
|
- if (workOrder.getOrderLevel() == null) {
|
|
|
|
|
- workOrder.setOrderLevel(2);
|
|
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ if (!Objects.equals(WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode(), order.getDelayApplyStatus())) {
|
|
|
|
|
+ throw new ServiceException("该工单没有待审核的延期申请");
|
|
|
}
|
|
}
|
|
|
- workOrder.setOrderNo(generateOrderNo());
|
|
|
|
|
- // 设备ID存在但未填设备编码时自动补全
|
|
|
|
|
- if (StringUtils.isNotEmpty(workOrder.getDeviceId()) && StringUtils.isEmpty(workOrder.getDeviceCode())) {
|
|
|
|
|
- EquipmentBase equipment = equipmentBaseMapper.selectById(workOrder.getDeviceId());
|
|
|
|
|
- if (equipment != null) {
|
|
|
|
|
- workOrder.setDeviceCode(equipment.getEquipmentCode());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ WorkOrderDelayApplyStatusEnum applyStatus = approved ? WorkOrderDelayApplyStatusEnum.APPROVED : WorkOrderDelayApplyStatusEnum.REJECTED;
|
|
|
|
|
+ Integer orderStatus = approved ? WorkOrderStatusEnum.DELAYING.getCode() : order.getOrderStatus();
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ this.update(new LambdaUpdateWrapper<WorkOrder>().set(WorkOrder::getDelayApplyStatus, applyStatus.getCode()).set(WorkOrder::getOrderStatus, orderStatus).set(WorkOrder::getUpdateTime, now).eq(WorkOrder::getOrderId, orderId).eq(WorkOrder::getDelayApplyStatus, WorkOrderDelayApplyStatusEnum.PENDING_AUDIT.getCode()));
|
|
|
|
|
+
|
|
|
|
|
+ writeLog(orderId, approved ? WorkOrderLogOperTypeEnum.DELAY_APPROVE : WorkOrderLogOperTypeEnum.DELAY_REJECT, buildDesc("审核意见", dto.getAuditOpinion()), null);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public boolean deleteWithLogs(Long orderId) {
|
|
|
|
|
+ if (orderId == null) {
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
- workOrder.setOrderStatus(1); // 待派单
|
|
|
|
|
- workOrder.setCreateTime(new Date());
|
|
|
|
|
- this.save(workOrder);
|
|
|
|
|
|
|
+ // 先清理该工单的操作日志,避免 work_order_log 残留脏数据
|
|
|
|
|
+ workOrderLogMapper.delete(new LambdaQueryWrapper<WorkOrderLog>().eq(WorkOrderLog::getOrderId, orderId));
|
|
|
|
|
+ return this.removeById(orderId);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 写入操作日志
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(workOrder.getOrderId());
|
|
|
|
|
- logEntry.setOperType(1);
|
|
|
|
|
- logEntry.setOperDesc("手动创建工单");
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
|
|
+ private String dataScopeSql(WorkOrderPageDTO dto) {
|
|
|
|
|
+ Map<String, Object> params = dto.getParams();
|
|
|
|
|
+ if (params == null || params.isEmpty()) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ Object dataScope = params.get(DATA_SCOPE);
|
|
|
|
|
+ if (dataScope == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ String sql = String.valueOf(dataScope).trim();
|
|
|
|
|
+ if (sql.length() == 0) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ String upper = sql.toUpperCase(Locale.ROOT);
|
|
|
|
|
+ if (upper.startsWith("AND ")) {
|
|
|
|
|
+ sql = sql.substring(4).trim();
|
|
|
|
|
+ } else if (upper.startsWith("OR ")) {
|
|
|
|
|
+ sql = sql.substring(3).trim();
|
|
|
|
|
+ }
|
|
|
|
|
+ return sql.length() == 0 ? null : sql;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- log.info("手动创建工单成功,工单ID={},类型={}", workOrder.getOrderId(), workOrder.getOrderType());
|
|
|
|
|
- return workOrder.getOrderId();
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按设备父类别收窄工单查询范围(业务口径过滤)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>过滤链路:work_order.device_id → equipment_base.equipment_id
|
|
|
|
|
+ * → equipment_base.equipment_type_id → equipment_type.id → equipment_type.parent_type_id。
|
|
|
|
|
+ * 采用 EXISTS 子查询而非 JOIN,避免设备表一对多时放大分页结果集,也不影响已有的
|
|
|
|
|
+ * 数据权限片段(dataScope)与 MyBatis-Plus 分页计数。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>parentTypeId 通过 MyBatis-Plus 的 apply 占位符 {0} 注入,由框架做 SQL 注入检查,不拼接原始入参。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param wrapper 工单查询条件
|
|
|
|
|
+ * @param parentTypeId 设备父类别ID,为空表示不限制
|
|
|
|
|
+ */
|
|
|
|
|
+ private void applyDeviceTypeScope(LambdaQueryWrapper<WorkOrder> wrapper, String parentTypeId) {
|
|
|
|
|
+ if (StringUtils.isEmpty(parentTypeId)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ wrapper.apply("EXISTS (SELECT 1 FROM app_user.equipment_base eb "
|
|
|
|
|
+ + "INNER JOIN app_user.equipment_type et ON et.id = eb.equipment_type_id "
|
|
|
|
|
+ + "WHERE eb.equipment_id = app_user.work_order.device_id "
|
|
|
|
|
+ + "AND et.parent_type_id = {0})",
|
|
|
|
|
+ parentTypeId.trim());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<TreeSelect> assignableDeptTree() {
|
|
|
|
|
+ return assignScopeSupport.assignableDeptTree();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<SysUser> assignableUsers(Long deptId) {
|
|
|
|
|
+ return assignScopeSupport.assignableUsers(deptId);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public List<WorkOrderPushVO> selectPendingOrOverdueWorkOrders(Date now) {
|
|
public List<WorkOrderPushVO> selectPendingOrOverdueWorkOrders(Date now) {
|
|
|
Date currentTime = now != null ? now : new Date();
|
|
Date currentTime = now != null ? now : new Date();
|
|
|
List<WorkOrder> orders = baseMapper.selectPendingOrOverdue(currentTime);
|
|
List<WorkOrder> orders = baseMapper.selectPendingOrOverdue(currentTime);
|
|
|
-
|
|
|
|
|
List<WorkOrderPushVO> result = new ArrayList<>();
|
|
List<WorkOrderPushVO> result = new ArrayList<>();
|
|
|
if (orders == null || orders.isEmpty()) {
|
|
if (orders == null || orders.isEmpty()) {
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
for (WorkOrder order : orders) {
|
|
for (WorkOrder order : orders) {
|
|
|
Integer status = order.getOrderStatus();
|
|
Integer status = order.getOrderStatus();
|
|
|
- Date effectiveDeadline = order.getDelayedDeadline() != null
|
|
|
|
|
- ? order.getDelayedDeadline() : order.getPlanFinishTime();
|
|
|
|
|
- boolean pending = Integer.valueOf(1).equals(status) || Integer.valueOf(2).equals(status);
|
|
|
|
|
|
|
+ Date effectiveDeadline = order.getDelayedDeadline() != null ? order.getDelayedDeadline() : order.getPlanFinishTime();
|
|
|
|
|
+ boolean pending = Objects.equals(WorkOrderStatusEnum.PENDING_DISPATCH.getCode(), status) || Objects.equals(WorkOrderStatusEnum.PENDING_ACCEPT.getCode(), status);
|
|
|
boolean overdue = effectiveDeadline != null && effectiveDeadline.before(currentTime);
|
|
boolean overdue = effectiveDeadline != null && effectiveDeadline.before(currentTime);
|
|
|
if (!pending && !overdue) {
|
|
if (!pending && !overdue) {
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
WorkOrderPushVO item = new WorkOrderPushVO();
|
|
WorkOrderPushVO item = new WorkOrderPushVO();
|
|
|
item.setOrderId(order.getOrderId());
|
|
item.setOrderId(order.getOrderId());
|
|
|
item.setOrderNo(order.getOrderNo());
|
|
item.setOrderNo(order.getOrderNo());
|
|
|
item.setOrderStatus(status);
|
|
item.setOrderStatus(status);
|
|
|
|
|
+ item.setOrderStatusDesc(WorkOrderStatusEnum.getDescByCode(status));
|
|
|
item.setOrderLevel(order.getOrderLevel());
|
|
item.setOrderLevel(order.getOrderLevel());
|
|
|
item.setDeptId(order.getDeptId());
|
|
item.setDeptId(order.getDeptId());
|
|
|
- item.setReceiveUser(order.getReceiveUser());
|
|
|
|
|
|
|
+ item.setReceiveUser(order.getReceiveUser() == null ? null : String.valueOf(order.getReceiveUser()));
|
|
|
item.setDeviceId(order.getDeviceId());
|
|
item.setDeviceId(order.getDeviceId());
|
|
|
item.setDeviceCode(order.getDeviceCode());
|
|
item.setDeviceCode(order.getDeviceCode());
|
|
|
item.setOrderDesc(order.getOrderDesc());
|
|
item.setOrderDesc(order.getOrderDesc());
|
|
@@ -385,11 +524,13 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
|
|
|
item.setDelayedDeadline(order.getDelayedDeadline());
|
|
item.setDelayedDeadline(order.getDelayedDeadline());
|
|
|
item.setEffectiveDeadline(effectiveDeadline);
|
|
item.setEffectiveDeadline(effectiveDeadline);
|
|
|
item.setOverdue(overdue);
|
|
item.setOverdue(overdue);
|
|
|
- item.setOverdueType(overdue
|
|
|
|
|
- ? order.getDelayedDeadline() != null ? "DELAYED_DEADLINE" : "PLAN_FINISH_TIME"
|
|
|
|
|
- : "PENDING");
|
|
|
|
|
- item.setOverdueMinutes(overdue
|
|
|
|
|
- ? Math.max(0L, (currentTime.getTime() - effectiveDeadline.getTime()) / 60000L) : 0L);
|
|
|
|
|
|
|
+ if (overdue) {
|
|
|
|
|
+ item.setOverdueType(order.getDelayedDeadline() != null ? "DELAYED_DEADLINE" : "PLAN_FINISH_TIME");
|
|
|
|
|
+ item.setOverdueMinutes(Math.max(0L, (currentTime.getTime() - effectiveDeadline.getTime()) / 60000L));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ item.setOverdueType("PENDING");
|
|
|
|
|
+ item.setOverdueMinutes(0L);
|
|
|
|
|
+ }
|
|
|
item.setCreateTime(order.getCreateTime());
|
|
item.setCreateTime(order.getCreateTime());
|
|
|
item.setUpdateTime(order.getUpdateTime());
|
|
item.setUpdateTime(order.getUpdateTime());
|
|
|
result.add(item);
|
|
result.add(item);
|
|
@@ -397,129 +538,126 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // ==================== 私有方法 ====================
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * 校验同一业务对象是否已经存在进行中的同类工单。
|
|
|
|
|
- * 带报警身份的工单按设备、工单类型和报警记录去重;没有报警身份的
|
|
|
|
|
- * 手工工单只与同设备的另一条未关联报警工单冲突,避免挡住不同报警原因。
|
|
|
|
|
|
|
+ * 加载工单并校验操作人是责任人本人(超级管理员放行)
|
|
|
*/
|
|
*/
|
|
|
- private void checkDeviceOrderConflict(String deviceId, Integer orderType, String alarmId) {
|
|
|
|
|
- if (StringUtils.isEmpty(deviceId)) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper<WorkOrder> query = this.lambdaQuery()
|
|
|
|
|
- .eq(WorkOrder::getDeviceId, deviceId)
|
|
|
|
|
- .notIn(WorkOrder::getOrderStatus, 6, 7);
|
|
|
|
|
- if (orderType != null) {
|
|
|
|
|
- query.eq(WorkOrder::getOrderType, orderType);
|
|
|
|
|
- }
|
|
|
|
|
- if (StringUtils.isNotBlank(alarmId)) {
|
|
|
|
|
- query.eq(WorkOrder::getAlarmId, alarmId);
|
|
|
|
|
- } else {
|
|
|
|
|
- query.isNull(WorkOrder::getAlarmId);
|
|
|
|
|
- }
|
|
|
|
|
- Long count = query.count();
|
|
|
|
|
- if (count > 0) {
|
|
|
|
|
- throw new ServiceException("该设备已有处理中的工单,需结案或驳回后才能创建新工单");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private WorkOrder loadForOperate(WorkOrderOperateDTO dto, String actionName) {
|
|
|
|
|
+ Long orderId = requireOrderId(dto == null ? null : dto.getOrderId());
|
|
|
|
|
+ WorkOrder order = getOrderById(orderId);
|
|
|
|
|
+ requireResponsible(order, currentUserId(), actionName);
|
|
|
|
|
+ return order;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 生成工单编号:WO-yyyyMMddHHmmssSSS
|
|
|
|
|
|
|
+ * 校验操作人是工单责任人本人,超级管理员放行
|
|
|
*/
|
|
*/
|
|
|
- private String generateOrderNo() {
|
|
|
|
|
- return "WO-" + new java.text.SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
|
|
|
|
|
|
|
+ private void requireResponsible(WorkOrder order, Long operUserId, String actionName) {
|
|
|
|
|
+ if (operUserId == null) {
|
|
|
|
|
+ throw new ServiceException("未获取到登录用户,无法" + actionName);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (SecurityUtils.isAdmin(operUserId)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getReceiveUser() == null || !Objects.equals(order.getReceiveUser(), operUserId)) {
|
|
|
|
|
+ throw new ServiceException("仅工单责任人可" + actionName);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public boolean cancelOrder(Long orderId, Long operUserId, String operUserName) {
|
|
|
|
|
|
|
+ private WorkOrder getOrderById(Long orderId) {
|
|
|
|
|
+ if (orderId == null) {
|
|
|
|
|
+ throw new ServiceException("工单ID不能为空");
|
|
|
|
|
+ }
|
|
|
WorkOrder order = this.getById(orderId);
|
|
WorkOrder order = this.getById(orderId);
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (order.getOrderStatus() == 6) throw new ServiceException("已结案工单不可取消");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(7);
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
-
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(7);
|
|
|
|
|
- logEntry.setOperDesc("取消工单");
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ if (order == null) {
|
|
|
|
|
+ throw new ServiceException("工单不存在或已被删除");
|
|
|
|
|
+ }
|
|
|
|
|
+ return order;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public boolean rejectOrder(Long orderId, String reason, Long operUserId, String operUserName) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- // 仅待派单/待接单可驳回,已流转的工单请使用取消
|
|
|
|
|
- if (!Arrays.asList(1, 2).contains(order.getOrderStatus())) throw new ServiceException("当前工单状态不允许驳回");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(7);
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ private Long requireOrderId(Long orderId) {
|
|
|
|
|
+ if (orderId == null) {
|
|
|
|
|
+ throw new ServiceException("工单ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ return orderId;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 写入工单操作日志(操作人取当前登录用户)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void writeLog(Long orderId, WorkOrderLogOperTypeEnum operType, String operDesc, String attachUrl) {
|
|
|
WorkOrderLog logEntry = new WorkOrderLog();
|
|
WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
logEntry.setOrderId(orderId);
|
|
logEntry.setOrderId(orderId);
|
|
|
- logEntry.setOperType(7);
|
|
|
|
|
- logEntry.setOperDesc("驳回工单,原因:" + (reason != null ? reason : ""));
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
|
|
+ logEntry.setOperType(operType.getCode());
|
|
|
|
|
+ logEntry.setOperDesc(operDesc);
|
|
|
|
|
+ logEntry.setAttachUrl(attachUrl);
|
|
|
|
|
+ logEntry.setOperUserId(currentUserId());
|
|
|
|
|
+ logEntry.setOperUserName(currentUserName());
|
|
|
logEntry.setOperTime(new Date());
|
|
logEntry.setOperTime(new Date());
|
|
|
workOrderLogMapper.insert(logEntry);
|
|
workOrderLogMapper.insert(logEntry);
|
|
|
- return true;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
- public boolean closeOrder(Long orderId, String result, Long operUserId, String operUserName) {
|
|
|
|
|
- WorkOrder order = this.getById(orderId);
|
|
|
|
|
- if (order == null) throw new ServiceException("工单不存在");
|
|
|
|
|
- if (order.getOrderStatus() != 5) throw new ServiceException("当前工单状态不允许验收通过");
|
|
|
|
|
-
|
|
|
|
|
- WorkOrder entity = new WorkOrder();
|
|
|
|
|
- entity.setOrderId(orderId);
|
|
|
|
|
- entity.setOrderStatus(6);
|
|
|
|
|
- entity.setRealFinishTime(new Date());
|
|
|
|
|
- this.updateById(entity);
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 校验同一设备是否已存在未办结工单
|
|
|
|
|
+ */
|
|
|
|
|
+ private void checkDeviceOrderConflict(String deviceId) {
|
|
|
|
|
+ if (StringUtils.isEmpty(deviceId)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long count = this.lambdaQuery().eq(WorkOrder::getDeviceId, deviceId).in(WorkOrder::getOrderStatus, WorkOrderStatusEnum.activeCodes()).count();
|
|
|
|
|
+ if (count != null && count > 0) {
|
|
|
|
|
+ throw new ServiceException("该设备已有处理中的工单,需结案或取消后才能创建新工单");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 工单已结案 → 同步隐患状态为"已整改"
|
|
|
|
|
- syncHazardStatus(order, "已整改", result);
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成全局唯一工单编号:WO + 14位时间戳 + 4位随机数
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateOrderNo() {
|
|
|
|
|
+ String pattern = "yyyyMMddHHmmss";
|
|
|
|
|
+ for (int i = 0; i < ORDER_NO_MAX_RETRY; i++) {
|
|
|
|
|
+ String no = ORDER_NO_PREFIX + new SimpleDateFormat(pattern).format(new Date()) + String.format("%04d", ThreadLocalRandom.current().nextInt(ORDER_NO_RANDOM_BOUND));
|
|
|
|
|
+ Long count = this.count(new LambdaQueryWrapper<WorkOrder>().eq(WorkOrder::getOrderNo, no));
|
|
|
|
|
+ if (count == null || count == 0) {
|
|
|
|
|
+ return no;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ throw new ServiceException("工单编号生成失败,请重试");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 设备报警状态由实时监测链路维护;工单结案不直接清除报警,避免覆盖仍存在的实时告警。
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 隐患等级映射工单优先级:0-重大→紧急 3-轻微→低 其余→一般
|
|
|
|
|
+ */
|
|
|
|
|
+ private WorkOrderLevelEnum mapHazardLevelToOrderLevel(String hazardLevel) {
|
|
|
|
|
+ if ("0".equals(hazardLevel)) {
|
|
|
|
|
+ return WorkOrderLevelEnum.URGENT;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("3".equals(hazardLevel)) {
|
|
|
|
|
+ return WorkOrderLevelEnum.LOW;
|
|
|
|
|
+ }
|
|
|
|
|
+ return WorkOrderLevelEnum.NORMAL;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- WorkOrderLog logEntry = new WorkOrderLog();
|
|
|
|
|
- logEntry.setOrderId(orderId);
|
|
|
|
|
- logEntry.setOperType(6);
|
|
|
|
|
- logEntry.setOperDesc("验收通过,结果:" + result);
|
|
|
|
|
- logEntry.setOperUserId(operUserId);
|
|
|
|
|
- logEntry.setOperUserName(operUserName);
|
|
|
|
|
- logEntry.setOperTime(new Date());
|
|
|
|
|
- workOrderLogMapper.insert(logEntry);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ private String resolveDeviceCode(String deviceId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ EquipmentBase equipment = equipmentBaseMapper.selectById(deviceId);
|
|
|
|
|
+ return equipment == null ? null : equipment.getEquipmentCode();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("查询设备编码失败,deviceId={}", deviceId, e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 同步隐患状态:根据工单处理结果更新关联隐患记录的状态和整改反馈
|
|
* 同步隐患状态:根据工单处理结果更新关联隐患记录的状态和整改反馈
|
|
|
- * @param order 工单对象
|
|
|
|
|
- * @param hazardStatus 隐患状态:整改中/已整改
|
|
|
|
|
- * @param rectifyFeedback 整改反馈(说明/方案)
|
|
|
|
|
*/
|
|
*/
|
|
|
private void syncHazardStatus(WorkOrder order, String hazardStatus, String rectifyFeedback) {
|
|
private void syncHazardStatus(WorkOrder order, String hazardStatus, String rectifyFeedback) {
|
|
|
if (StringUtils.isEmpty(order.getHazardNo())) {
|
|
if (StringUtils.isEmpty(order.getHazardNo())) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
try {
|
|
try {
|
|
|
- LambdaQueryWrapper<HazardHiddenAccount> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
- wrapper.eq(HazardHiddenAccount::getHazardNo, order.getHazardNo());
|
|
|
|
|
- HazardHiddenAccount hazard = hazardHiddenAccountMapper.selectOne(wrapper);
|
|
|
|
|
|
|
+ HazardHiddenAccount hazard = hazardHiddenAccountMapper.selectOne(new LambdaQueryWrapper<HazardHiddenAccount>().eq(HazardHiddenAccount::getHazardNo, order.getHazardNo()));
|
|
|
if (hazard != null) {
|
|
if (hazard != null) {
|
|
|
HazardHiddenAccount update = new HazardHiddenAccount();
|
|
HazardHiddenAccount update = new HazardHiddenAccount();
|
|
|
update.setHazardId(hazard.getHazardId());
|
|
update.setHazardId(hazard.getHazardId());
|
|
@@ -528,12 +666,124 @@ public class BusinessWorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, W
|
|
|
update.setRectifyFeedback(rectifyFeedback);
|
|
update.setRectifyFeedback(rectifyFeedback);
|
|
|
}
|
|
}
|
|
|
hazardHiddenAccountMapper.updateById(update);
|
|
hazardHiddenAccountMapper.updateById(update);
|
|
|
- log.info("工单[{}]关联隐患[{}]状态同步为:{}, 整改反馈:{}",
|
|
|
|
|
- order.getOrderNo(), order.getHazardNo(), hazardStatus, rectifyFeedback);
|
|
|
|
|
|
|
+ log.info("工单[{}]关联隐患[{}]状态同步为:{}", order.getOrderNo(), order.getHazardNo(), hazardStatus);
|
|
|
}
|
|
}
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("工单[{}]同步隐患状态失败", order.getOrderNo(), e);
|
|
log.error("工单[{}]同步隐患状态失败", order.getOrderNo(), e);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 批量回填责任人/派单人姓名,避免逐条查库
|
|
|
|
|
+ */
|
|
|
|
|
+ private void fillUserName(List<WorkOrder> records) {
|
|
|
|
|
+ if (CollectionUtils.isEmpty(records)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, String> userMap = loadUserMap(records);
|
|
|
|
|
+ for (WorkOrder record : records) {
|
|
|
|
|
+ if (record.getReceiveUser() != null) {
|
|
|
|
|
+ record.setReceiveUserName(userMap.getOrDefault(record.getReceiveUser(), String.valueOf(record.getReceiveUser())));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (record.getDispatchUser() != null) {
|
|
|
|
|
+ record.setDispatchUserName(userMap.getOrDefault(record.getDispatchUser(), String.valueOf(record.getDispatchUser())));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按本页实际涉及的用户ID批量查询并构建「用户ID → 姓名」映射。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>此前是全量拉取用户表(selectAllEnabledUser)再建 Map,用户量大时每次分页查询都要扫描全表。
|
|
|
|
|
+ * 实际只用到当页记录的 receiveUser / dispatchUser,故改为按 ID 精确查询。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param records 当前页工单记录
|
|
|
|
|
+ * @return 用户ID → 姓名的映射;查询失败时返回空 Map(降级为展示ID,不影响主流程)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<Long, String> loadUserMap(List<WorkOrder> records) {
|
|
|
|
|
+ Set<Long> userIds = new HashSet<>();
|
|
|
|
|
+ for (WorkOrder record : records) {
|
|
|
|
|
+ if (record.getReceiveUser() != null) {
|
|
|
|
|
+ userIds.add(record.getReceiveUser());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (record.getDispatchUser() != null) {
|
|
|
|
|
+ userIds.add(record.getDispatchUser());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (userIds.isEmpty()) {
|
|
|
|
|
+ return new HashMap<>(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<SysUser> users = sysUserService.selectEnabledUsersByIds(userIds);
|
|
|
|
|
+ if (CollectionUtils.isEmpty(users)) {
|
|
|
|
|
+ return new HashMap<>(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, String> userMap = new HashMap<>(users.size() * 2);
|
|
|
|
|
+ for (SysUser user : users) {
|
|
|
|
|
+ if (user.getUserId() != null) {
|
|
|
|
|
+ userMap.put(user.getUserId(), userLabel(user));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return userMap;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("加载工单责任人用户映射失败", e);
|
|
|
|
|
+ return new HashMap<>(0);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String userLabel(SysUser user) {
|
|
|
|
|
+ if (user == null) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ return StringUtils.isNotEmpty(user.getNickName()) ? user.getNickName() : user.getUserName();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Long currentUserId() {
|
|
|
|
|
+ return assignScopeSupport.currentUserId();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取当前登录用户姓名:用户昵称(nickName)优先,缺省时回退到登录账号。
|
|
|
|
|
+ * 工单操作日志展示姓名比账号更直观,与 WaterSupplyAlarmServiceImpl 保持一致。
|
|
|
|
|
+ */
|
|
|
|
|
+ private String currentUserName() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
|
|
|
+ if (loginUser == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (loginUser.getUser() != null && StringUtils.isNotEmpty(loginUser.getUser().getNickName())) {
|
|
|
|
|
+ return loginUser.getUser().getNickName();
|
|
|
|
|
+ }
|
|
|
|
|
+ return loginUser.getUsername();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("获取当前登录用户姓名失败", e);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Integer> parseStatusList(String orderStatus) {
|
|
|
|
|
+ return Arrays.stream(orderStatus.split(",")).map(String::trim).filter(StringUtils::isNumeric).map(Integer::valueOf).filter(WorkOrderStatusEnum::isValid).collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String content(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ return dto == null ? null : dto.getContent();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String attach(WorkOrderOperateDTO dto) {
|
|
|
|
|
+ return dto == null ? null : dto.getAttachUrl();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String buildDesc(String prefix, String detail) {
|
|
|
|
|
+ boolean prefixEmpty = StringUtils.isEmpty(prefix);
|
|
|
|
|
+ boolean detailEmpty = StringUtils.isEmpty(detail);
|
|
|
|
|
+ return prefixEmpty ? detail : (detailEmpty ? prefix : prefix + ":" + detail);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<String> splitAttach(String attachUrl) {
|
|
|
|
|
+ if (StringUtils.isEmpty(attachUrl)) {
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ return Arrays.stream(attachUrl.split(",")).map(String::trim).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|