/* * 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.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springblade.core.mp.base.BaseServiceImpl; import org.springblade.core.oss.model.BladeFile; import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.api.R; import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.utils.DateUtil; import org.springblade.core.tool.utils.FileUtil; import org.springblade.core.tool.utils.StringUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.sxkj.common.constant.WordOrderConstant; import org.sxkj.common.utils.OrderNumUtils; import org.sxkj.gd.common.GdMultipartFileUtil; import org.sxkj.gd.workorder.dto.GdXingtuFlyTaskSaveDTO; 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.mapper.GdWorkOrderMapper; import org.sxkj.gd.workorder.param.GdPatrolTaskAuditParam; import org.sxkj.gd.workorder.param.GdPatrolTaskPageParam; import org.sxkj.gd.workorder.param.GdPatrolTaskPendingAcceptanceQueryParam; import org.sxkj.gd.workorder.service.IGdManageDeviceService; 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.GdPatrolTaskSimpleVO; import org.sxkj.gd.workorder.vo.GdPatrolTaskVO; import org.sxkj.gd.xingtu.JianXingtuApiService; import org.sxkj.resource.entity.Attach; import org.sxkj.resource.feign.IAttachClient; import org.sxkj.system.cache.SysCache; 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.io.IOException; import java.util.*; import java.util.stream.Collectors; /** * 巡查任务表 服务实现类 * * @author lw * @since 2026-01-14 */ @Slf4j @Service public class GdPatrolTaskServiceImpl extends BaseServiceImpl implements IGdPatrolTaskService { @Autowired private IGdWorkOrderFlowService gdWorkOrderFlowService; @Autowired private ISysClient sysClient; @Autowired private IUserClient userClient; @Autowired private IAttachClient attachClient; @Autowired private IGdTaskResultService gdTaskResultService; @Autowired private JianXingtuApiService jianXingtuApiService; @Autowired private IGdManageDeviceService gdManageDeviceService; @Autowired private GdWorkOrderMapper gdWorkOrderMapper; @Override public IPage selectGdPatrolTaskPage(IPage page, GdPatrolTaskPageParam gdPatrolTask) { List deptList = new ArrayList<>(); if (!AuthUtil.isAdministrator() || !AuthUtil.isAdmin()) { deptList = SysCache.getDeptChildIds(Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : AuthUtil.getDeptId())); } gdPatrolTask.setDeptList(deptList); List gdPatrolTaskVOS = baseMapper.selectGdPatrolTaskPage(page, gdPatrolTask); return page.setRecords(gdPatrolTaskVOS); } @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) { // 参数校验:防止空指针 if (taskEntityList == null || taskEntityList.isEmpty()) { throw new RuntimeException("任务列表不能为空"); } // 获取第一个任务的工单ID Long workOrderId = taskEntityList.get(0).getWorkOrderId(); if (workOrderId == null) { throw new RuntimeException("工单ID不能为空"); } // 查询工单信息 GdWorkOrderEntity gdWorkOrderEntity = gdWorkOrderMapper.selectById(workOrderId); if (gdWorkOrderEntity == null) { throw new RuntimeException("工单不存在,工单ID: " + workOrderId); } // 更新流程节点 for (GdPatrolTaskEntity gdPatrolTaskEntity : taskEntityList) { if (gdPatrolTaskEntity == null) { log.warn("跳过空任务对象"); continue; } // 设置航线ID gdPatrolTaskEntity.setAirlineId(gdPatrolTaskEntity.getPatrolRouteUrl()); // 生成巡查任务编号 if (StringUtils.isBlank(gdPatrolTaskEntity.getTaskNo())) { // 使用时间戳生成唯一巡查任务编号 String timestamp = OrderNumUtils.initOrderNum(WordOrderConstant.PATROL_TASK_KEY); gdPatrolTaskEntity.setTaskNo(WordOrderConstant.PATROL_TASK_PREFIX + timestamp); } // 从工单实体中获取区域编码,防止空指针 String areaCode = gdWorkOrderEntity.getAreaCode(); gdPatrolTaskEntity.setAreaCode(areaCode != null ? areaCode : ""); // 设置初始状态为待签收(对应流程图:等待接单) 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() == null ? 0L : AuthUtil.getUserId()); gdWorkOrderFlowEntity.setWorkOrderId(gdPatrolTaskEntity.getId()); gdWorkOrderFlowEntity.setFlowName(PatrolTaskStatusEnum.PENDING_SIGNING.getStageDesc()); gdWorkOrderFlowEntity.setFlowDesc(PatrolTaskStatusEnum.PENDING_SIGNING.getStageDesc()); gdWorkOrderFlowEntity.setOperator(StringUtil.isBlank(AuthUtil.getNickName()) ? "系统" : AuthUtil.getNickName()); gdWorkOrderFlowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); // 使用局部变量areaCode,防止空指针 gdWorkOrderFlowEntity.setAreaCode(areaCode != null ? areaCode : ""); gdWorkOrderFlowEntity.setCreateDept(Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : 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() == null ? 0L : 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) { // 将审核后的任务信息推送到星图 R pushResult = pushXingtuFlyTask(auditParam.getId()); // 检查推送结果,如果失败则抛出异常回滚事务 if (pushResult != null && !pushResult.isSuccess()) { String errorMsg = pushResult.getMsg(); log.error("推送星图飞行任务失败: {}", errorMsg); throw new RuntimeException(errorMsg + ",请更改执行时间重新提交。"); } // 查询同一个工单下的所有子任务 List subTasks = list(Wrappers.lambdaQuery() .eq(GdPatrolTaskEntity::getWorkOrderId, task.getWorkOrderId())); // 检查是否所有子任务都审核成功(除了被撤回和验收拒绝的) boolean allSubTasksAudited = isAllSubTasksAudited(subTasks, task, PatrolTaskStatusEnum.PENDING_AUDIT); if (!allSubTasksAudited) { // 执行工单状态更新逻辑 return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.EXECUTING_TO_BE_COMPLETED); } } if (auditStatus == 3) { // 撤回任务操作:查询同一个工单下的所有子任务 List subTasks = list(Wrappers.lambdaQuery() .eq(GdPatrolTaskEntity::getWorkOrderId, task.getWorkOrderId())); // 检查是否所有子任务都已撤回 boolean allSubTasksWithdrawn = subTasks.stream() .allMatch(item -> item.getTaskStatus().equals(PatrolTaskStatusEnum.WITHDRAWN.getValue())); // 如果所有子任务都已撤回,则将工单状态更新为"接单响应_已取消" if (allSubTasksWithdrawn) { return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.RESPONDING_CANCELED); } } // 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 = isAllSubTasksAudited(subTasks, task, PatrolTaskStatusEnum.ACCEPTANCE_PASSED); // 这里可以根据allSubTasksAudited的结果执行后续逻辑,比如更新工单状态等 if (!allSubTasksAudited) { // 1.验收通过,生成报告 asyncExportPatrolReport(task.getId()); // 2.执行工单状态更新逻辑 return updateWorkOrderStatusAndFlow(task.getWorkOrderId(), WorkOrderStatusEnum.ACCEPTED_TO_BE_SETTLED); } } return b; } /** * @param subTasks * @param task * @param pendingAudit * @return */ private static boolean isAllSubTasksAudited(List subTasks, GdPatrolTaskEntity task, PatrolTaskStatusEnum pendingAudit) { List validStatusList = subTasks.stream() // 过滤掉当前任务本身 .filter(subTask -> !subTask.getId().equals(task.getId())) .map(GdPatrolTaskEntity::getTaskStatus) .filter(Objects::nonNull) // 过滤掉撤回或验收拒绝的任务 .filter(status -> !status.equals(PatrolTaskStatusEnum.WITHDRAWN.getValue()) && !status.equals(PatrolTaskStatusEnum.REJECTED_ACCEPTANCE.getValue())) .collect(Collectors.toList()); // 如果过滤后没有有效任务,直接返回false if (validStatusList.isEmpty()) { return false; } // 判断是否有小于待审核的任务 boolean allSubTasksAudited = validStatusList.stream() .anyMatch(status -> status.compareTo(pendingAudit.getValue()) < 0); return allSubTasksAudited; } /** * 更新工单状态并保存流转记录 * * @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() == null ? 0L : AuthUtil.getUserId()); flowEntity.setWorkOrderId(workOrderId); flowEntity.setFlowName(newStatus.getStageDesc()); flowEntity.setFlowDesc(newStatus.getDesc()); flowEntity.setOperator(StringUtil.isBlank(AuthUtil.getNickName()) ? "系统" : AuthUtil.getNickName()); flowEntity.setType(WorkOrderTypeEnum.WORK_ORDER_TASK.getValue()); flowEntity.setFlowStatus(newStatus.getMaxCode()); flowEntity.setWorkStatus(newStatus.getCode()); flowEntity.setCreateDept(Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : 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; } } // 3. 设置任务状态:使用枚举设置任务状态 taskEntity.setTaskStatus(targetStatus.getValue()); // 4. 更新任务信息:合并传入的任务信息 taskEntity.setUpdateUser(AuthUtil.getUserId() == null ? 0L : 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() == null ? 0L : AuthUtil.getUserId()); flowEntity.setWorkOrderId(taskEntity.getId()); flowEntity.setFlowName(targetStatus.getDesc()); flowEntity.setFlowDesc((StringUtil.isBlank(AuthUtil.getNickName()) ? "系统" : AuthUtil.getNickName()) + " " + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss") + " 更新了任务信息,状态转换为:" + targetStatus.getDesc()); flowEntity.setOperator(StringUtil.isBlank(AuthUtil.getNickName()) ? "系统" : AuthUtil.getNickName()); flowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); flowEntity.setAreaCode(existingTask.getAreaCode()); flowEntity.setCreateDept(Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : AuthUtil.getDeptId())); return gdWorkOrderFlowService.save(flowEntity); } @Override public boolean 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 { File reportFile = GdPatrolReportWordUtil.generateReportFile(taskEntity, resultList, creatorName, deptName); return saveAttachFile(reportFile, taskEntity); } catch (Exception e) { throw new RuntimeException("生成巡查报告失败", e); } } /** * 异步生成巡查报告 * * @param patrolTaskId 巡查任务ID */ @Async public void asyncExportPatrolReport(Long patrolTaskId) { try { exportPatrolReport(patrolTaskId); } catch (Exception e) { // 记录异步执行错误日志 log.error("异步生成巡查报告失败,任务ID: {}"); } } /** * 检查拒绝原因是否必填 * * @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() == null ? 0L : AuthUtil.getUserId()); flowEntity.setWorkOrderId(workOrderId); flowEntity.setFlowName(newStatus.getStageDesc()); flowEntity.setFlowDesc(newStatus.getStageDesc()); flowEntity.setOperator(StringUtil.isBlank(AuthUtil.getNickName()) ? "系统" : AuthUtil.getNickName()); flowEntity.setType(WorkOrderTypeEnum.PATROL_TASK.getValue()); flowEntity.setAreaCode(areaCode); flowEntity.setCreateDept(Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : AuthUtil.getDeptId())); boolean saveResult = gdWorkOrderFlowService.save(flowEntity); if (!saveResult) { throw new RuntimeException("保存流转记录失败"); } return true; } /** * 根据部门ID获取部门名称 * * @param deptId 部门ID * @return 部门名称,缺失时返回"/" */ 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(); } /** * 根据用户ID获取用户姓名 * * @param userId 用户ID * @return 用户姓名,缺失时返回"/" */ 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; } /** * 上传巡查报告并保存附件信息 * * @param reportFile 报告文件 * @param taskEntity 巡查任务 * @return 是否保存成功 */ private boolean saveAttachFile(File reportFile, GdPatrolTaskEntity taskEntity) throws IOException { if (reportFile == null || !reportFile.exists()) { throw new RuntimeException("巡查报告文件不存在"); } String desiredName = buildReportAttachName(taskEntity, reportFile.getName()); BladeFile bladeFile = attachClient.putFile( GdMultipartFileUtil.fromFile(reportFile, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), desiredName ); if (bladeFile == null || StringUtil.isBlank(bladeFile.getLink())) { throw new RuntimeException("附件上传失败"); } // Integer resultType = parseResultType(taskEntity != null ? taskEntity.getPatrolTaskType() : null); Attach attach = buildAttachInfo(taskEntity, reportFile, bladeFile, taskEntity.getPatrolTaskType(), desiredName); Boolean saved = attachClient.saveAttachInfo(attach); if (!Boolean.TRUE.equals(saved)) { log.error("附件信息保存失败,报告文件:" + desiredName); return false; } return true; } /** * 构建附件元数据 * * @param taskEntity 巡查任务 * @param reportFile 报告文件 * @param bladeFile 文件上传结果 * @param resultType 结果类型 * @param desiredName 期望文件名 * @return 附件信息 */ private Attach buildAttachInfo(GdPatrolTaskEntity taskEntity, File reportFile, BladeFile bladeFile, List> resultType, String desiredName) { Attach attach = new Attach(); attach.setDomainUrl(bladeFile.getDomain()); attach.setLink(bladeFile.getLink()); attach.setName(bladeFile.getName()); attach.setOriginalName(bladeFile.getOriginalName()); attach.setAttachSize(reportFile.length()); attach.setExtension(FileUtil.getFileExtension(reportFile.getName())); attach.setResultType(resultType); attach.setPatrolTaskId(String.valueOf(taskEntity != null ? taskEntity.getId() : null)); Long createUser = taskEntity != null ? taskEntity.getCreateUser() : null; Long updateUser = taskEntity != null ? taskEntity.getUpdateUser() : null; Long createDept = taskEntity != null ? taskEntity.getCreateDept() : null; if (createUser == null) { createUser = AuthUtil.getUserId() == null ? 0L : AuthUtil.getUserId(); } if (updateUser == null) { updateUser = createUser; } if (createDept == null) { createDept = Long.valueOf(StringUtil.isBlank(AuthUtil.getDeptId()) ? "0" : AuthUtil.getDeptId()); } Date now = new Date(); attach.setCreateUser(createUser); attach.setUpdateUser(updateUser); attach.setCreateDept(createDept); attach.setCreateTime(now); attach.setUpdateTime(now); return attach; } /** * 生成报告附件名称 * * @param taskEntity 巡查任务 * @param fallbackName 兜底文件名 * @return 附件名称 */ private String buildReportAttachName(GdPatrolTaskEntity taskEntity, String fallbackName) { String taskNo = taskEntity != null ? taskEntity.getTaskNo() : null; if (StringUtil.isBlank(taskNo)) { return fallbackName; } return taskNo + ".docx"; } /** * 解析巡查任务类型为结果类型 * * @param patrolTaskType 巡查任务类型 * @return 结果类型 */ private Integer parseResultType(String patrolTaskType) { if (StringUtil.isBlank(patrolTaskType)) { return null; } try { Integer type = Integer.valueOf(patrolTaskType); if (type < 1 || type > 5) { return null; } return type; } catch (NumberFormatException ex) { return null; } } @Override public R pushXingtuFlyTask(Long patrolTaskId) { if (patrolTaskId == null) { return R.fail("巡查任务主键不能为空"); } GdPatrolTaskVO taskEntity = baseMapper.getPatrolTaskIdById(patrolTaskId); if (taskEntity == null) { return R.fail("巡查任务不存在"); } GdXingtuFlyTaskSaveDTO param = buildXingtuFlyTask(taskEntity); return jianXingtuApiService.saveFlyTask(param); } /** * 构建巡察任务推送的巡察任务信息 * * @param gdPatrolTaskVO * @return */ private GdXingtuFlyTaskSaveDTO buildXingtuFlyTask(GdPatrolTaskVO gdPatrolTaskVO) { GdXingtuFlyTaskSaveDTO param = new GdXingtuFlyTaskSaveDTO(); if (StringUtil.isNotBlank(gdPatrolTaskVO.getPatrolTaskName())) { param.setName(gdPatrolTaskVO.getPatrolTaskName()); } else if (StringUtil.isNotBlank(gdPatrolTaskVO.getTaskNo())) { param.setName(gdPatrolTaskVO.getTaskNo()); } // if (StringUtil.isNotBlank(gdPatrolTaskVO.getPatrolTaskType())) { // param.setInspectionType(gdPatrolTaskVO.getPatrolTaskType()); // } if ((gdPatrolTaskVO.getId() != null)) { param.setPatrolTaskId(String.valueOf(gdPatrolTaskVO.getId())); } param.setTimingType("单次执行"); if (StringUtil.isNotBlank(gdPatrolTaskVO.getAirlineId())) { param.setAirlineId(gdPatrolTaskVO.getAirlineId()); } if (gdPatrolTaskVO.getExecuteTime() != null) { param.setSingleTime(DateUtil.format(gdPatrolTaskVO.getExecuteTime(), "yyyy-MM-dd HH:mm:ss")); } if (StringUtil.isNotBlank(gdPatrolTaskVO.getAreaCode())) { // regionCode : "3600,3608,360802" String regionCode = convertAreaCodeToRegionCode(gdPatrolTaskVO.getAreaCode()); param.setRegionCode(regionCode); } if (StringUtil.isNotBlank(gdPatrolTaskVO.getAirportId())) { param.setAirportId(gdPatrolTaskVO.getAirportId()); } if (StringUtil.isNotBlank(gdPatrolTaskVO.getDeviceFlyerId())) { param.setDeviceFlyerId(gdPatrolTaskVO.getDeviceFlyerId()); } if (gdPatrolTaskVO.getAlgorithmIds() != null && !gdPatrolTaskVO.getAlgorithmIds().isEmpty()) { // 将 List 转换为逗号分隔的字符串 String algorithmIdStr = String.join(",", gdPatrolTaskVO.getAlgorithmIds()); param.setAlgorithmId(algorithmIdStr); } // 机构 param.setDeptId(gdPatrolTaskVO.getCreateDept()); // 巡检类型 param.setInspectionTypeArr(gdPatrolTaskVO.getPatrolTaskType()); // param.setInspectionType(gdPatrolTaskVO.getPatrolTaskType()); // 机巢名称 param.setAirportName(gdPatrolTaskVO.getDeviceName()); param.setStatus("待执行"); param.setTaskType("定时飞行"); return param; } @Override public GdPatrolTaskVO getPatrolTaskDetail(GdPatrolTaskEntity gdPatrolTaskEntity) { return baseMapper.getPatrolTaskDetail(gdPatrolTaskEntity); } @Override @Transactional(rollbackFor = Exception.class) // 添加事务注解,确保数据一致性 public boolean updatePatrolTaskById(GdPatrolTaskEntity auditParam) { // 1. 更新当前任务状态 boolean updateResult = updateById(auditParam); if (!updateResult) { log.warn("更新任务失败,任务ID: {}", auditParam.getId()); return false; } // 2. 获取当前任务实体(优先使用 auditParam 中的数据,避免重复查询) GdPatrolTaskEntity patrolTaskEntity = Optional.ofNullable(getById(auditParam.getId())) .orElseThrow(() -> new RuntimeException("任务不存在,任务ID: " + auditParam.getId())); // 3. 保存巡查工单流转(使用传入的实际状态) PatrolTaskStatusEnum statusEnum = PatrolTaskStatusEnum.getByValue(auditParam.getTaskStatus()); if (statusEnum != null) { saveWorkOrderFlow(patrolTaskEntity.getId(), statusEnum, null, patrolTaskEntity.getAreaCode()); } // 4. 查询同一个工单下的所有子任务 List subTasks = list(Wrappers.lambdaQuery() .eq(GdPatrolTaskEntity::getWorkOrderId, patrolTaskEntity.getWorkOrderId())); // 5. 判空处理,防止 NullPointerException if (CollectionUtils.isEmpty(subTasks)) { log.info("工单ID {} 下无子任务", patrolTaskEntity.getWorkOrderId()); return true; } // 6. 判断是否有小于待验收的工单(统一使用枚举值比较) boolean hasLessThanPendingAcceptance = subTasks.stream() .map(GdPatrolTaskEntity::getTaskStatus) .filter(Objects::nonNull) .anyMatch(status -> status.compareTo(PatrolTaskStatusEnum.PENDING_ACCEPTANCE.getValue()) < 0); // 7. 根据判断结果决定是否更新工单状态 if (!hasLessThanPendingAcceptance) { updateWorkOrderStatusAndFlow(patrolTaskEntity.getWorkOrderId(), WorkOrderStatusEnum.COMPLETED_TO_BE_INSPECTED); log.info("工单ID {} 下所有子任务均已达到待验收状态,更新工单状态为完成待验", patrolTaskEntity.getWorkOrderId()); } else { log.warn("工单ID {} 下存在未达待验收状态的子任务", patrolTaskEntity.getWorkOrderId()); } return updateResult; } @Override public List selectListByWorkOrderId(Long workOrderId) { return baseMapper.selectListByWorkOrderId(workOrderId); } /** * 查询待验收状态的巡查任务列表(对外接口) * * @param queryParam 查询参数,包含任务名称和时间区间 * @return 待验收状态的巡查任务列表 */ @Override public List selectPendingAcceptanceTasks(GdPatrolTaskPendingAcceptanceQueryParam queryParam) { // 1. 参数校验:防止空指针 if (queryParam == null) { queryParam = new GdPatrolTaskPendingAcceptanceQueryParam(); } // 2. 调用Mapper查询待验收状态的巡查任务 List pendingAcceptanceTasks = baseMapper.selectPendingAcceptanceTasks(queryParam); // 3. 返回查询结果 return pendingAcceptanceTasks; } /** * 将区域编码转换为多级行政区划编码格式 * * @param areaCode 区域编码(如:360802) * @return 多级行政区划编码,格式为 "省编码,市编码,区编码"(如:"3600,3608,360802") */ private String convertAreaCodeToRegionCode(String areaCode) { if (StringUtil.isBlank(areaCode)) { return null; } // 如果已经是逗号分隔的格式,直接返回 if (areaCode.contains(",")) { return areaCode; } // 去除空格并确保长度为6位 String cleanCode = areaCode.trim(); // 如果长度大于6位,只取前6位 if (cleanCode.length() > 6) { log.warn("区域编码长度超过6位,截取前6位: {}", cleanCode); cleanCode = cleanCode.substring(0, 6); } if (cleanCode.length() != 6) { log.warn("区域编码格式不正确,期望6位数字,实际为: {}", cleanCode); return cleanCode; } try { // 提取省、市、区三级编码 String provinceCode = cleanCode.substring(0, 2) + "00"; // 省级编码(如:3600) String cityCode = cleanCode.substring(0, 4); // 市级编码(如:3608) String districtCode = cleanCode; // 区级编码(如:360802) return provinceCode + "," + cityCode + "," + districtCode; } catch (Exception e) { log.error("区域编码转换失败: {}", e.getMessage()); return areaCode; } } }