/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.sxkj.gd.workorder.service.impl; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.commons.lang3.StringUtils; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.utils.DateUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.sxkj.common.constant.WordOrderConstant; import org.sxkj.common.utils.OrderNumUtils; import org.springblade.core.tool.api.R; import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.utils.StringUtil; import org.sxkj.gd.workorder.entity.GdPatrolTaskEntity; import org.sxkj.gd.workorder.entity.GdTaskResultEntity; import org.sxkj.gd.workorder.entity.GdWorkOrderEntity; import org.sxkj.gd.workorder.entity.GdWorkOrderFlowEntity; import org.sxkj.gd.workorder.enums.PatrolTaskStatusEnum; import org.sxkj.gd.workorder.enums.WorkOrderStatusEnum; import org.sxkj.gd.workorder.enums.WorkOrderTypeEnum; import org.sxkj.gd.workorder.excel.GdPatrolTaskExcel; import org.sxkj.gd.workorder.mapper.GdPatrolTaskMapper; import org.sxkj.gd.workorder.param.GdPatrolTaskAuditParam; import org.sxkj.gd.workorder.param.GdPatrolTaskPageParam; import org.sxkj.gd.workorder.service.IGdPatrolTaskService; import org.sxkj.gd.workorder.service.IGdTaskResultService; import org.sxkj.gd.workorder.service.IGdWorkOrderFlowService; import org.sxkj.gd.workorder.utils.GdPatrolReportWordUtil; import org.sxkj.gd.workorder.vo.GdPatrolTaskVO; import org.sxkj.system.entity.Dept; import org.sxkj.system.entity.User; import org.sxkj.system.feign.ISysClient; import org.sxkj.system.feign.IUserClient; import java.io.File; import java.util.Date; import java.util.List; /** * 巡查任务表 服务实现类 * * @author lw * @since 2026-01-14 */ @Service public class GdPatrolTaskServiceImpl extends BaseServiceImpl implements IGdPatrolTaskService { @Autowired private IGdWorkOrderFlowService gdWorkOrderFlowService; // @Autowired // private IGdWorkOrderService gdWorkOrderService; @Autowired private ISysClient sysClient; @Autowired private IUserClient userClient; @Autowired private IGdTaskResultService gdTaskResultService; @Override public IPage selectGdPatrolTaskPage(IPage page, GdPatrolTaskPageParam gdPatrolTask, List deptIdList) { return page.setRecords(baseMapper.selectGdPatrolTaskPage(page, gdPatrolTask, deptIdList)); } @Override public List exportGdPatrolTask(Wrapper queryWrapper) { List gdPatrolTaskList = baseMapper.exportGdPatrolTask(queryWrapper); // gdPatrolTaskList.forEach(gdPatrolTask -> { // gdPatrolTask.setTypeName(DictCache.getValue(DictEnum.YES_NO, GdPatrolTask.getType())); //}); return gdPatrolTaskList; } @Override @Transactional(rollbackFor = Exception.class) public boolean savePatrolTask(List taskEntityList) { // 更新流程节点 for (GdPatrolTaskEntity gdPatrolTaskEntity : taskEntityList) { // 生成巡查任务编号 if (StringUtils.isBlank(gdPatrolTaskEntity.getTaskNo())) { // 使用时间戳生成唯一巡查任务编号 String timestamp = OrderNumUtils.initOrderNum(WordOrderConstant.PATROL_TASK_KEY); gdPatrolTaskEntity.setTaskNo(WordOrderConstant.PATROL_TASK_PREFIX + timestamp); } // 设置初始状态为待签收(对应流程图:等待接单) gdPatrolTaskEntity.setTaskStatus(PatrolTaskStatusEnum.PENDING_SIGNING.getValue()); // 保存巡逻任务 boolean save = save(gdPatrolTaskEntity); if (!save) { // 任务保存失败,抛出异常触发回滚 throw new RuntimeException("保存巡逻任务失败:" + gdPatrolTaskEntity.toString()); } // 保存流转记录(发布任务,等待接单) GdWorkOrderFlowEntity gdWorkOrderFlowEntity = new GdWorkOrderFlowEntity(); gdWorkOrderFlowEntity.setCreateUser(AuthUtil.getUserId()); gdWorkOrderFlowEntity.setWorkOrderId(gdPatrolTaskEntity.getId()); gdWorkOrderFlowEntity.setFlowName("任务发布"); gdWorkOrderFlowEntity.setFlowDesc(AuthUtil.getUserName() + " " + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " 发布任务,等待接单"); gdWorkOrderFlowEntity.setOperator(AuthUtil.getUserName()); gdWorkOrderFlowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); gdWorkOrderFlowEntity.setAreaCode(gdPatrolTaskEntity.getAreaCode()); gdWorkOrderFlowEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId())); boolean save1 = gdWorkOrderFlowService.save(gdWorkOrderFlowEntity); if (!save1) { // 流转记录保存失败,抛出异常触发回滚 throw new RuntimeException("保存流转记录失败:" + gdWorkOrderFlowEntity.toString()); } } // 所有任务和流转记录都保存成功 return true; } /** * 审核巡查任务 * * @param auditParam 审核参数,包含任务ID、审核状态和拒绝原因 * @return 是否审核成功 * @throws RuntimeException 当任务不存在、状态不允许转换或操作失败时抛出 */ @Override @Transactional(rollbackFor = Exception.class) public boolean auditPatrolTask(GdPatrolTaskAuditParam auditParam) { // 1. 获取巡查任务 GdPatrolTaskEntity task = getById(auditParam.getId()); if (task == null) { throw new RuntimeException("巡查任务不存在"); } // 2. 状态权限校验 PatrolTaskStatusEnum currentStatus = PatrolTaskStatusEnum.getByValue(task.getTaskStatus()); Integer auditStatus = auditParam.getAuditStatus(); if (currentStatus == null || !currentStatus.isAllowedTransition(auditStatus)) { throw new RuntimeException("当前任务状态无法进行该操作"); } // 1.同意签收、2.拒绝签收 、3.撤回任务 、4.同意审核、 5.拒绝审核、 6.验收通过、 7.验收拒绝 // 3. 确定新状态和流转名称 PatrolTaskStatusEnum newStatusEnum; switch (auditStatus) { case 1: // 接单 newStatusEnum = PatrolTaskStatusEnum.PENDING_AUDIT; break; case 2: // 拒接接单 newStatusEnum = PatrolTaskStatusEnum.REJECTED_SIGNING; break; case 3: // 撤回任务 newStatusEnum = PatrolTaskStatusEnum.WITHDRAWN; break; case 4: // 同意审核 newStatusEnum = PatrolTaskStatusEnum.AUDIT_PASSED; break; case 5: // 拒绝审核 newStatusEnum = PatrolTaskStatusEnum.AUDIT_REJECTED; break; case 6: // 验收通过 newStatusEnum = PatrolTaskStatusEnum.ACCEPTANCE_PASSED; break; case 7: // 验收拒绝 newStatusEnum = PatrolTaskStatusEnum.REJECTED_ACCEPTANCE; break; default: // 无效操作类型 return false; } // 4. 更新任务状态 Date now = new Date(); task.setTaskStatus(newStatusEnum.getValue()); task.setUpdateUser(AuthUtil.getUserId()); task.setUpdateTime(now); task.setRejectReason(auditParam.getRejectReason()); boolean updateResult = updateById(task); if (!updateResult) { throw new RuntimeException("更新任务状态失败"); } // 5. 保存流转记录 boolean b = saveWorkOrderFlow(task.getId(), newStatusEnum, auditParam.getRejectReason(), task.getAreaCode()); // 6. 判断是否审核成功,如果是,需要判断是否整个订单的子任务都审核成功了,除了被撤回和验收拒绝的 if (auditStatus == 4) { // 查询同一个工单下的所有子任务 List subTasks = list(Wrappers.lambdaQuery() .eq(GdPatrolTaskEntity::getWorkOrderId, task.getWorkOrderId())); // 检查是否所有子任务都审核成功(除了被撤回和验收拒绝的) boolean allSubTasksAudited = checkAllSubTasksStatus(subTasks, PatrolTaskStatusEnum.AUDIT_PASSED); // 示例:如果所有子任务都审核成功,可以更新工单状态为已完成 if (allSubTasksAudited) { // 执行工单状态更新逻辑 return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.EXECUTING_TO_BE_COMPLETED); } } // 7. 验收拒绝 if (auditStatus == 7) { return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.EXECUTING_TO_BE_COMPLETED); } // 8. 验收通过 if (auditStatus == 6) { // 查询同一个工单下的所有子任务 List subTasks = list(Wrappers.lambdaQuery() .eq(GdPatrolTaskEntity::getWorkOrderId, task.getWorkOrderId())); // 检查是否所有子任务都验收通过(除了被撤回和验收拒绝的) boolean allSubTasksAudited = checkAllSubTasksStatus(subTasks, PatrolTaskStatusEnum.ACCEPTANCE_PASSED); // 这里可以根据allSubTasksAudited的结果执行后续逻辑,比如更新工单状态等 if (allSubTasksAudited) { // 执行工单状态更新逻辑 return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.ACCEPTED_TO_BE_SETTLED); } } return b; } /** * 检查是否所有子任务都达到指定状态(除了被撤回和验收拒绝的) * * @param subTasks 子任务列表 * @param targetStatus 目标状态枚举 * @return 是否所有子任务都达到指定状态 */ private boolean checkAllSubTasksStatus(List subTasks, PatrolTaskStatusEnum targetStatus) { for (GdPatrolTaskEntity subTask : subTasks) { String taskStatus = subTask.getTaskStatus(); PatrolTaskStatusEnum statusEnum = PatrolTaskStatusEnum.getByValue(taskStatus); // 如果任务状态不是目标状态,且不是被撤回和验收拒绝的,则返回false if (statusEnum != targetStatus && statusEnum != PatrolTaskStatusEnum.WITHDRAWN && statusEnum != PatrolTaskStatusEnum.REJECTED_ACCEPTANCE) { return false; } } return true; } /** * 更新工单状态并保存流转记录 * * @param workOrderId 工单ID * @param newStatus 新的工单状态枚举 * @return 是否更新成功 */ private boolean updateWorkOrderStatusAndFlow(Long workOrderId, WorkOrderStatusEnum newStatus) { // 1. 更新工单状态 GdWorkOrderEntity gdWorkOrderEntity = new GdWorkOrderEntity(); gdWorkOrderEntity.setId(workOrderId); gdWorkOrderEntity.setWorkOrderStatus(newStatus.getCode()); Integer updateResult = baseMapper.updateWorkOrderById(gdWorkOrderEntity); if (updateResult <= 0) { return false; } // 2. 保存流转记录 GdWorkOrderFlowEntity flowEntity = new GdWorkOrderFlowEntity(); flowEntity.setCreateUser(AuthUtil.getUserId()); flowEntity.setWorkOrderId(workOrderId); flowEntity.setFlowName(newStatus.getDesc()); flowEntity.setFlowDesc(String.format("%s %s 更新了任务信息,状态转换为:%s", AuthUtil.getUserName(), DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"), newStatus.getDesc())); flowEntity.setOperator(AuthUtil.getUserName()); flowEntity.setType(WorkOrderTypeEnum.WORK_ORDER_TASK.getValue()); flowEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId())); return gdWorkOrderFlowService.save(flowEntity); } @Override @Transactional(rollbackFor = Exception.class) public boolean republishPatrolTask(GdPatrolTaskEntity taskEntity) { if (taskEntity == null || taskEntity.getId() == null) { throw new RuntimeException("任务信息或ID不能为空"); } // 1. 验证任务是否存在 GdPatrolTaskEntity existingTask = getById(taskEntity.getId()); taskEntity.setId(existingTask.getId()); if (existingTask == null) { throw new RuntimeException("巡查任务不存在"); } // 2. 先确定枚举状态:根据当前状态确定新的枚举状态 PatrolTaskStatusEnum targetStatus; String currentStatus = existingTask.getTaskStatus(); // 1.发布中_拒绝接单 、 4.响应中_审核驳回 switch (currentStatus) { case "1": // 发布中_拒绝接单 → 发布中_接单中 targetStatus = PatrolTaskStatusEnum.PENDING_SIGNING; break; case "4": // 响应中_审核驳回 → 响应中_待审核 targetStatus = PatrolTaskStatusEnum.PENDING_AUDIT; break; default: // 其他状态,保持原有状态 targetStatus = PatrolTaskStatusEnum.getByValue(currentStatus); // 如果无法获取枚举,使用待签收作为默认状态 if (targetStatus == null) { targetStatus = PatrolTaskStatusEnum.PENDING_SIGNING; } break; } // 3. 设置任务状态:使用枚举设置任务状态 taskEntity.setTaskStatus(targetStatus.getValue()); // 4. 更新任务信息:合并传入的任务信息 taskEntity.setUpdateUser(AuthUtil.getUserId()); taskEntity.setUpdateTime(new Date()); // 5. 更新数据库 boolean updateResult = updateById(taskEntity); if (!updateResult) { throw new RuntimeException("更新任务信息失败"); } // 6. 保存流转记录:使用枚举设置flowName GdWorkOrderFlowEntity flowEntity = new GdWorkOrderFlowEntity(); flowEntity.setCreateUser(AuthUtil.getUserId()); flowEntity.setWorkOrderId(taskEntity.getId()); flowEntity.setFlowName(targetStatus.getDesc()); flowEntity.setFlowDesc(AuthUtil.getUserName() + " " + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " 更新了任务信息,状态转换为:" + targetStatus.getDesc()); flowEntity.setOperator(AuthUtil.getUserName()); flowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); flowEntity.setAreaCode(existingTask.getAreaCode()); flowEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId())); return gdWorkOrderFlowService.save(flowEntity); } @Override public File exportPatrolReport(Long patrolTaskId) { if (patrolTaskId == null) { throw new RuntimeException("巡查任务主键不能为空"); } GdPatrolTaskEntity taskEntity = getById(patrolTaskId); if (taskEntity == null) { throw new RuntimeException("巡查任务不存在"); } List resultList = gdTaskResultService.list(Wrappers.lambdaQuery() .eq(GdTaskResultEntity::getPatrolTaskId, patrolTaskId) .eq(GdTaskResultEntity::getIsDeleted, BladeConstant.DB_NOT_DELETED)); String creatorName = getUserName(taskEntity.getCreateUser()); String deptName = getDeptName(taskEntity.getCreateDept()); try { return GdPatrolReportWordUtil.generateReportFile(taskEntity, resultList, creatorName, deptName); } catch (Exception e) { throw new RuntimeException("生成巡查报告失败", e); } } /** * 检查拒绝原因是否必填 * * @param newStatus 新状态 * @param rejectReason 拒绝原因 * @throws RuntimeException 当需要拒绝原因但未提供时抛出 */ // private void checkRejectReason(PatrolTaskStatusEnum newStatus, String rejectReason) { // // 拒绝类状态需要填写拒绝原因 // if ((newStatus == PatrolTaskStatusEnum.REJECTED_SIGNING || // newStatus == PatrolTaskStatusEnum.AUDIT_REJECTED || // newStatus == PatrolTaskStatusEnum.REJECTED_ACCEPTANCE) && // (rejectReason == null || rejectReason.trim().isEmpty())) { // throw new RuntimeException("拒绝操作时必须填写拒绝原因"); // } // } /** * 保存工单流转记录 * * @param workOrderId 工单ID * @param newStatus 新状态 * @param rejectReason 拒绝原因 * @param areaCode 区域编码 * @return 是否保存成功 * @throws RuntimeException 当保存失败时抛出 */ private boolean saveWorkOrderFlow(Long workOrderId, PatrolTaskStatusEnum newStatus, String rejectReason, String areaCode) { GdWorkOrderFlowEntity flowEntity = new GdWorkOrderFlowEntity(); flowEntity.setCreateUser(AuthUtil.getUserId()); flowEntity.setWorkOrderId(workOrderId); flowEntity.setFlowName(newStatus.getDesc()); // 构建流转描述 String flowDesc = buildFlowDesc(newStatus, rejectReason); flowEntity.setFlowDesc(flowDesc); flowEntity.setOperator(AuthUtil.getUserName()); flowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); flowEntity.setAreaCode(areaCode); flowEntity.setCreateDept(Long.valueOf(AuthUtil.getDeptId())); boolean saveResult = gdWorkOrderFlowService.save(flowEntity); if (!saveResult) { throw new RuntimeException("保存流转记录失败"); } return true; } /** * 构建流转描述 * * @param newStatus 新状态 * @param rejectReason 拒绝原因 * @return 流转描述 */ private String buildFlowDesc(PatrolTaskStatusEnum newStatus, String rejectReason) { String baseDesc = String.format("%s %s ", AuthUtil.getUserName(), DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")); // 拒绝类状态需要添加拒绝原因 if (newStatus == PatrolTaskStatusEnum.REJECTED_SIGNING || newStatus == PatrolTaskStatusEnum.AUDIT_REJECTED || newStatus == PatrolTaskStatusEnum.REJECTED_ACCEPTANCE) { return String.format("%s%s:%s", baseDesc, newStatus.getDesc(), rejectReason); } // 其他状态直接使用枚举描述 return baseDesc + newStatus.getDesc(); } private String getDeptName(Long deptId) { if (deptId == null) { return "/"; } R deptResult = sysClient.getDept(deptId); Dept dept = deptResult != null ? deptResult.getData() : null; if (dept == null || StringUtil.isBlank(dept.getDeptName())) { return "/"; } return dept.getDeptName(); } private String getUserName(Long userId) { if (userId == null) { return "/"; } R userResult = userClient.userInfoById(userId); User user = userResult != null ? userResult.getData() : null; if (user == null) { return "/"; } String realName = user.getRealName(); if (StringUtil.isBlank(realName)) { realName = user.getName(); } return StringUtil.isBlank(realName) ? "/" : realName; } }