package com.ruoyi.device.service.impl; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.ruoyi.common.constant.MenuJumpPathConstants; import com.ruoyi.common.core.domain.Result; import com.ruoyi.common.core.domain.entity.InformationNotification; import com.ruoyi.common.core.domain.entity.User; import com.ruoyi.common.utils.QueryWrappers; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.WxCpUtils; import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto; import com.ruoyi.device.dto.DeviceMaintenancePlanDto; import com.ruoyi.device.mapper.DeviceMaintenanceAttachmentMapper; import com.ruoyi.device.mapper.DeviceMaintenanceMapper; import com.ruoyi.device.mapper.DeviceMaintenancePlanDetailsMapper; import com.ruoyi.device.mapper.DeviceMaintenancePlanMapper; import com.ruoyi.device.mapper.DeviceMapper; import com.ruoyi.device.pojo.Device; import com.ruoyi.device.pojo.DeviceMaintenance; import com.ruoyi.device.pojo.DeviceMaintenanceAttachment; import com.ruoyi.device.pojo.DeviceMaintenancePlan; import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails; import com.ruoyi.device.service.DeviceMaintenancePlanDetailsService; import com.ruoyi.device.service.DeviceMaintenancePlanService; import com.ruoyi.framework.exception.ErrorException; import com.ruoyi.inspect.util.HackLoopTableRenderPolicy; import com.ruoyi.inspect.util.UserUtils; import com.ruoyi.system.mapper.UserMapper; import com.ruoyi.system.service.InformationNotificationService; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** *

* 设备保养计划表 服务实现类 *

* * @author 江苏鵷雏网络科技有限公司 * @since 2024-12-16 06:10:52 */ @Service @Transactional(rollbackFor = Exception.class) public class DeviceMaintenancePlanServiceImpl extends ServiceImpl implements DeviceMaintenancePlanService { @Resource private DeviceMaintenancePlanDetailsService deviceMaintenancePlanDetailsService; @Resource private DeviceMaintenancePlanDetailsMapper deviceMaintenancePlanDetailsMapper; @Resource private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Resource private InformationNotificationService informationNotificationService; @Resource private UserMapper userMapper; @Resource private DeviceMapper deviceMapper; @Resource private DeviceMaintenanceMapper deviceMaintenanceMapper; @Resource private DeviceMaintenanceAttachmentMapper deviceMaintenanceAttachmentMapper; @Value("${file.path}") private String filePath; @Value("${wordUrl}") private String wordUrl; /** * 分页查询设备保养计划 * * @param page * @return */ @Override public Result> selectDeviceMaintenancePlanByPage(Page page, DeviceMaintenancePlanDto deviceMaintenancePlanDto) { IPage iPage = baseMapper.selectDeviceParameterPage(page, QueryWrappers.queryWrappers(deviceMaintenancePlanDto)); return Result.success(iPage); } /** * 新增设备保养计划 * * @param deviceMaintenancePlanDto 设备保养计划 */ @Override public Result addMaintenancePlan(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { Integer userId = SecurityUtils.getUserId().intValue(); User user = userMapper.selectById(userId); deviceMaintenancePlanDto.setCompilerId(userId); deviceMaintenancePlanDto.setCompiler(user.getName()); deviceMaintenancePlanDto.setDatePreparation(LocalDateTime.now()); // 查询审核人id if (deviceMaintenancePlanDto.getAuditId() != null) { User auditUser = userMapper.selectById(deviceMaintenancePlanDto.getAuditId()); deviceMaintenancePlanDto.setAudit(auditUser.getName()); } this.saveOrUpdate(deviceMaintenancePlanDto); // 详情赋值并保存 List deviceMaintenancePlanDetails = deviceMaintenancePlanDto.getDeviceMaintenancePlanDetails(); if (CollectionUtils.isNotEmpty(deviceMaintenancePlanDetails)) { // 详情不为空 List collect = deviceMaintenancePlanDetails.stream().map(deviceMaintenancePlanDetail -> { // 遍历详情 deviceMaintenancePlanDetail.setDeviceId(deviceMaintenancePlanDetail.getDeviceId()); // 设备ID deviceMaintenancePlanDetail.setMaintenancePlanId(deviceMaintenancePlanDto.getMaintenancePlanId()); // 保养计划ID DeviceMaintenancePlanDetails planDetails = new DeviceMaintenancePlanDetails(); BeanUtils.copyProperties(deviceMaintenancePlanDetail, planDetails); return planDetails; }).collect(Collectors.toList()); deviceMaintenancePlanDetailsService.saveBatch(collect); } return Result.success(); } /** * 修改设备保养计划 * @param deviceMaintenancePlanDto 设备保养计划 */ @Override public Result updateMaintenancePlan(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { // 查询审核人id if (deviceMaintenancePlanDto.getAuditId() != null) { User auditUser = userMapper.selectById(deviceMaintenancePlanDto.getAuditId()); deviceMaintenancePlanDto.setAudit(auditUser.getName()); } this.saveOrUpdate(deviceMaintenancePlanDto); // 删除原本的详情;明细重建后仍保留原明细id的附件不清理,id丢失的才清理 List oldDetails = deviceMaintenancePlanDetailsService.list( Wrappers.lambdaQuery() .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); deviceMaintenancePlanDetailsService.remove(Wrappers.lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); List lostDetailIds = oldDetails.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId) .collect(Collectors.toList()); // 详情赋值并保存 List deviceMaintenancePlanDetails = deviceMaintenancePlanDto.getDeviceMaintenancePlanDetails(); if (CollectionUtils.isNotEmpty(deviceMaintenancePlanDetails)) { // 详情不为空 List keptDetailIds = deviceMaintenancePlanDetails.stream() .map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId) .filter(id -> id != null) .collect(Collectors.toList()); lostDetailIds.removeAll(keptDetailIds); List collect = deviceMaintenancePlanDetails.stream().map(deviceMaintenancePlanDetail -> { // 遍历详情 deviceMaintenancePlanDetail.setMaintenancePlanId(deviceMaintenancePlanDto.getMaintenancePlanId()); // 保养计划ID DeviceMaintenancePlanDetails planDetails = new DeviceMaintenancePlanDetails(); BeanUtils.copyProperties(deviceMaintenancePlanDetail, planDetails); return planDetails; }).collect(Collectors.toList()); deviceMaintenancePlanDetailsService.saveBatch(collect); } removeAttachmentsByDetailIds(lostDetailIds); return Result.success(); } /** * 删除设备保养计划 * * @param deviceMaintenancePlanDto 设备保养计划 */ @Override public Result deleteMaintenancePlan(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { List details = deviceMaintenancePlanDetailsService.list( Wrappers.lambdaQuery() .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); this.removeById(deviceMaintenancePlanDto); deviceMaintenancePlanDetailsService.remove(Wrappers.lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); removeAttachmentsByDetailIds(details.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId).collect(Collectors.toList())); return Result.success(); } /** * 导出设备保养计划 * * @param maintenancePlanId 设备保养计划id * @param response 响应 */ @Override public Result exportDeviceMaintenancePlanDto(Integer maintenancePlanId, HttpServletResponse response) { // 查询设备保养计划 DeviceMaintenancePlanDto deviceMaintenancePlan = baseMapper.selectMaintenancePlanById(maintenancePlanId); // 查询设备保养计划详情,按“设备 + 保养月份”兜底去重 List deviceMaintenancePlanDetailsDtoList = dedupeMaintenanceDetails( deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId)); // 设置序号 for (int i = 0; i < deviceMaintenancePlanDetailsDtoList.size(); i++) { deviceMaintenancePlanDetailsDtoList.get(i).setIndex(i + 1); } // 获取路径 InputStream inputStream = this.getClass().getResourceAsStream("/static/word/maintenance-plan.docx"); Configure configure = Configure.builder() .bind("deviceMaintenancePlanDetailsDtoList", new HackLoopTableRenderPolicy()) .build(); Map renderData = new HashMap<>(); renderData.put("deviceMaintenancePlan", deviceMaintenancePlan); renderData.put("deviceMaintenancePlanDetailsDtoList", deviceMaintenancePlanDetailsDtoList); // 编制人签名地址 renderData.put("compilerUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getCompilerId())); // 审核人签名地址 renderData.put("auditUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getAuditId())); XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(renderData); try { response.setContentType("application/msword"); String fileName = URLEncoder.encode( "设备保养计划表", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx"); OutputStream os = response.getOutputStream(); template.write(os); os.flush(); os.close(); inputStream.close(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("导出失败"); } return Result.success(); } /** * 查询设备保养计划详情 * * @param maintenancePlanId 设备保养计划id */ @Override public Result getMaintenancePlanDetail(Integer maintenancePlanId) { // 查询设备保养计划 DeviceMaintenancePlan deviceMaintenancePlan = baseMapper.selectById(maintenancePlanId); // 查询详情 DeviceMaintenancePlanDto deviceMaintenancePlanDto = new DeviceMaintenancePlanDto(); BeanUtils.copyProperties(deviceMaintenancePlan, deviceMaintenancePlanDto); deviceMaintenancePlanDto.setDeviceMaintenancePlanDetails(dedupeMaintenanceDetails( deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId))); return Result.success(deviceMaintenancePlanDto); } /** * 提交审核 * @param deviceMaintenancePlanDto * @return */ @Override public Result submitReviewMaintenancePlanStatus(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { User audit = userMapper.selectById(deviceMaintenancePlanDto.getAuditId()); this.update(Wrappers.lambdaUpdate() .eq(DeviceMaintenancePlan::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()) .set(DeviceMaintenancePlan::getAuditId, audit.getId()) .set(DeviceMaintenancePlan::getAudit, audit.getName()) .set(DeviceMaintenancePlan::getStatus, null) .set(DeviceMaintenancePlan::getAuditRemark, null) .set(DeviceMaintenancePlan::getAuditDate, null) ); DeviceMaintenancePlan deviceMaintenancePlan = baseMapper.selectById(deviceMaintenancePlanDto.getMaintenancePlanId()); // 发送消息 Integer userId = SecurityUtils.getUserId().intValue(); User user = userMapper.selectById(userId); // 消息发送 InformationNotification info = new InformationNotification(); // 发送人 info.setCreateUser(user.getName()); info.setMessageType("6"); info.setTheme("CNAS设备保养计划审核通知"); info.setContent(deviceMaintenancePlan.getPlanYear() + "年的设备保养计划待审核"); info.setSenderId(userId); // 接收人 info.setConsigneeId(audit.getId()); info.setJumpPath(MenuJumpPathConstants.DEVICE); informationNotificationService.addInformationNotification(info); // 发送企业微信通知 threadPoolTaskExecutor.execute(() -> { String message = ""; message += "CNAS设备保养计划核通知"; message += "\n请去资源管理-设备一层目录-设备保养计划填写"; message += "\n" + deviceMaintenancePlan.getPlanYear() + "年的设备保养计划待核"; //发送企业微信消息通知 try { WxCpUtils.inform(audit.getAccount(), message, null); } catch (Exception e) { throw new RuntimeException(e); } }); return Result.success(); } /** * 审核设备保养计划 * * @param deviceMaintenancePlanDto 设备保养计划 */ @Override public Result reviewMaintenancePlanStatus(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate() .eq(DeviceMaintenancePlan::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()) .set(DeviceMaintenancePlan::getStatus, deviceMaintenancePlanDto.getStatus()) .set(DeviceMaintenancePlan::getAuditRemark, deviceMaintenancePlanDto.getAuditRemark()) .set(DeviceMaintenancePlan::getAuditDate, LocalDateTime.now()); this.update(wrapper); // 更新 return Result.success(); } /** * 按年份查询设备保养计划(不存在则自动创建),并自动补全已到点的设备明细 */ @Override public DeviceMaintenancePlanDto getOrCreatePlanByYear(String planYear) { if (StringUtils.isBlank(planYear)) { planYear = String.valueOf(LocalDate.now().getYear()); } DeviceMaintenancePlan plan = getOrCreatePlanEntity(planYear); ensureMaintenanceDetails(plan); DeviceMaintenancePlanDto planDto = new DeviceMaintenancePlanDto(); BeanUtils.copyProperties(plan, planDto); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); planDto.setDatePreparationStr(plan.getDatePreparation() == null ? null : plan.getDatePreparation().format(formatter)); planDto.setAuditDateStr(plan.getAuditDate() == null ? null : plan.getAuditDate().format(formatter)); if (StringUtils.isBlank(planDto.getCompiler()) && plan.getCompilerId() != null) { User compiler = userMapper.selectById(plan.getCompilerId()); if (compiler != null) { planDto.setCompiler(compiler.getName()); } } if (StringUtils.isBlank(planDto.getAudit()) && plan.getAuditId() != null) { User audit = userMapper.selectById(plan.getAuditId()); if (audit != null) { planDto.setAudit(audit.getName()); } } return planDto; } /** * 保养计划年份列表:删除没有任何明细的空年份计划后,返回有明细的年份(倒序) */ @Override public List getMaintenancePlanYears() { // 历史数据自愈:先让已审核的明细按审核完成年份归位,再清理空年份计划 reclassifyReviewedDetails(); List plans = baseMapper.selectList(null); for (DeviceMaintenancePlan plan : plans) { Long detailCount = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.lambdaQuery() .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId())); if (detailCount == null || detailCount == 0) { removeById(plan.getMaintenancePlanId()); } } return baseMapper.selectPlanYears(); } /** * 设备档案变更后,立即把设备登记到下次保养日期所在年份的设备保养计划明细 */ @Override public void syncDeviceMaintenanceDetail(Device device) { LocalDate due = calcMaintenanceDueDate(device); if (due == null) { return; } DeviceMaintenancePlan plan = getOrCreatePlanEntity(String.valueOf(due.getYear())); fillMaintenanceDetailIfAbsent(plan, device); } /** * 审核通过后,按审核通过当天滚动设备档案的最近/下次保养日期 */ @Override public void updateDeviceMaintenanceDate(Integer deviceId, LocalDate maintenanceDate) { if (deviceId == null || maintenanceDate == null) { return; } Device device = deviceMapper.selectById(deviceId); if (device == null) { return; } LocalDateTime recordDate = maintenanceDate.atStartOfDay(); // 只允许向前滚动,避免补录旧记录时把日期改回去 if (device.getLastMaintenanceDate() != null && !recordDate.isAfter(device.getLastMaintenanceDate())) { return; } device.setLastMaintenanceDate(recordDate); if (StringUtils.isNotBlank(device.getMaintenanceCycle())) { try { device.setNextMaintenanceDate(recordDate.plusMonths(Long.parseLong(device.getMaintenanceCycle().trim()))); } catch (NumberFormatException ignored) { } } deviceMapper.updateById(device); // 本轮保养完成后立即登记下一轮,历史明细保持不动 syncDeviceMaintenanceDetail(device); } /** * 审核通过后,把计划明细归到审核完成年份的计划下;提前完成的明细不再留在原计划年份 */ @Override public void reclassifyMaintenanceDetailByReviewDate(Integer maintenancePlanDetailId, LocalDate reviewDate) { if (maintenancePlanDetailId == null || reviewDate == null) { return; } DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId); if (detail == null || detail.getMaintenancePlanId() == null) { return; } DeviceMaintenancePlan sourcePlan = baseMapper.selectById(detail.getMaintenancePlanId()); if (sourcePlan == null) { return; } String targetYear = String.valueOf(reviewDate.getYear()); if (StringUtils.isNotBlank(sourcePlan.getPlanYear()) && targetYear.equals(sourcePlan.getPlanYear().trim())) { return; } moveMaintenanceDetailToYear(detail, reviewDate); } /** * 把计划明细挪到审核完成年份的计划下,计划日期按实际完成日期记录 */ private void moveMaintenanceDetailToYear(DeviceMaintenancePlanDetails detail, LocalDate reviewDate) { DeviceMaintenancePlan targetPlan = getOrCreatePlanEntity(String.valueOf(reviewDate.getYear())); DeviceMaintenancePlanDetails update = new DeviceMaintenancePlanDetails(); update.setMaintenancePlanDetailId(detail.getMaintenancePlanDetailId()); update.setMaintenancePlanId(targetPlan.getMaintenancePlanId()); // 计划日期按实际完成日期记录,列表按完成年份排序,不再按原计划日期标红 update.setMaintenanceDate(reviewDate); deviceMaintenancePlanDetailsMapper.updateById(update); } /** * 历史数据自愈:已审核通过但计划年份与审核完成年份不一致的明细,按审核完成年份归位 */ private void reclassifyReviewedDetails() { List records = deviceMaintenanceMapper.selectList(Wrappers.lambdaQuery() .eq(DeviceMaintenance::getReviewStatus, 1) .isNotNull(DeviceMaintenance::getMaintenancePlanDetailId) .isNotNull(DeviceMaintenance::getReviewTime)); if (CollectionUtils.isEmpty(records)) { return; } List detailIds = records.stream().map(DeviceMaintenance::getMaintenancePlanDetailId) .distinct().collect(Collectors.toList()); Map detailMap = deviceMaintenancePlanDetailsMapper.selectBatchIds(detailIds) .stream().collect(Collectors.toMap(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId, item -> item, (a, b) -> a)); Map planYears = baseMapper.selectList(null).stream() .collect(Collectors.toMap(DeviceMaintenancePlan::getMaintenancePlanId, item -> item.getPlanYear() == null ? "" : item.getPlanYear().trim(), (a, b) -> a)); for (DeviceMaintenance record : records) { DeviceMaintenancePlanDetails detail = detailMap.get(record.getMaintenancePlanDetailId()); if (detail == null || detail.getMaintenancePlanId() == null) { continue; } String reviewYear = String.valueOf(record.getReviewTime().getYear()); if (reviewYear.equals(planYears.get(detail.getMaintenancePlanId()))) { continue; } moveMaintenanceDetailToYear(detail, record.getReviewTime().toLocalDate()); } } /** * 分页查询设备保养计划详情(含到点标红) */ @Override public IPage pageMaintenancePlanDetail(Page page, Integer planId, String deviceName, String deviceNumber) { if (planId == null) { return new Page<>(); } return deviceMaintenancePlanDetailsMapper.pageMaintenancePlanDetail(page, planId, deviceName, deviceNumber); } /** * 查询年份对应的计划,不存在则创建 */ private DeviceMaintenancePlan getOrCreatePlanEntity(String planYear) { DeviceMaintenancePlan plan = baseMapper.selectOne(Wrappers.lambdaQuery() .eq(DeviceMaintenancePlan::getPlanYear, planYear) .orderByDesc(DeviceMaintenancePlan::getMaintenancePlanId) .last("limit 1")); if (plan == null) { plan = new DeviceMaintenancePlan(); plan.setPlanName(planYear + "年度设备保养计划"); plan.setPlanYear(planYear); Integer userId = SecurityUtils.getUserId().intValue(); User user = userMapper.selectById(userId); plan.setCompilerId(userId); plan.setCompiler(user == null ? null : user.getName()); plan.setDatePreparation(LocalDateTime.now()); baseMapper.insert(plan); } return plan; } /** * 扫描所有设备,把应保养日期落在该计划年份的设备补进计划(当前年及以往年度包含逾期未保养的设备) */ private void ensureMaintenanceDetails(DeviceMaintenancePlan plan) { List devices = deviceMapper.selectList(null); for (Device device : devices) { fillMaintenanceDetailIfAbsent(plan, device); } } /** * 设备本轮应保养日期(没填下次日期时用最近日期+保养周期兜底) */ private LocalDate calcMaintenanceDueDate(Device device) { LocalDateTime base = device.getNextMaintenanceDate(); if (base == null && device.getLastMaintenanceDate() != null && StringUtils.isNotBlank(device.getMaintenanceCycle())) { try { base = device.getLastMaintenanceDate().plusMonths(Long.parseLong(device.getMaintenanceCycle().trim())); } catch (NumberFormatException ignored) { } } return base == null ? null : base.toLocalDate(); } /** * 计划中尚无该设备本轮明细时自动补一条;保养时间取应保养日期 */ private void fillMaintenanceDetailIfAbsent(DeviceMaintenancePlan plan, Device device) { LocalDate due = calcMaintenanceDueDate(device); if (due == null) { return; } if (StringUtils.isBlank(plan.getPlanYear())) { return; } int planYearValue; try { planYearValue = Integer.parseInt(plan.getPlanYear().trim()); } catch (NumberFormatException e) { return; } int dueYear = due.getYear(); // 未来年份的计划只登记应保养日期就在该年份的设备,避免把往年的旧日期补进未来的计划 if (planYearValue > LocalDate.now().getYear()) { if (dueYear != planYearValue) { return; } } else if (dueYear > planYearValue) { return; } // 同一设备同一应保养月份只保留一条,避免手改过行内日期后刷新重复生成 LocalDate monthStart = due.withDayOfMonth(1); Long exist = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.lambdaQuery() .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId()) .eq(DeviceMaintenancePlanDetails::getDeviceId, device.getId()) .ge(DeviceMaintenancePlanDetails::getMaintenanceDate, monthStart) .lt(DeviceMaintenancePlanDetails::getMaintenanceDate, monthStart.plusMonths(1))); if (exist != null && exist > 0) { return; } DeviceMaintenancePlanDetails detail = new DeviceMaintenancePlanDetails(); detail.setMaintenancePlanId(plan.getMaintenancePlanId()); detail.setDeviceId(device.getId()); detail.setMaintenanceIntervals(device.getMaintenanceCycle()); detail.setMaintenanceDate(due); deviceMaintenancePlanDetailsMapper.insert(detail); } /** * 为保养记录找一条可关联的计划明细;没有可用明细时在当前年度计划新增本轮明细 */ @Override public DeviceMaintenancePlanDetails resolveMaintenancePlanDetail(Integer deviceId, Integer maintenancePlanDetailId, LocalDate maintenanceDate) { if (maintenancePlanDetailId != null) { DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId); if (detail == null) { throw new ErrorException("保养计划明细不存在"); } return detail; } if (deviceId == null) { throw new ErrorException("缺少设备id"); } Device device = deviceMapper.selectById(deviceId); if (device == null) { throw new ErrorException("保养设备不存在"); } LocalDate planDate = maintenanceDate == null ? LocalDate.now() : maintenanceDate; DeviceMaintenancePlan plan = getOrCreatePlanEntity(String.valueOf(planDate.getYear())); List details = deviceMaintenancePlanDetailsMapper.selectList( Wrappers.lambdaQuery() .eq(DeviceMaintenancePlanDetails::getDeviceId, deviceId) .orderByAsc(DeviceMaintenancePlanDetails::getMaintenanceDate)); DeviceMaintenancePlanDetails nearest = null; long nearestDistance = Long.MAX_VALUE; for (DeviceMaintenancePlanDetails detail : details) { // 已有保养记录的明细代表已完成的一轮,后续记录永远不复用、不覆盖 Long recordCount = deviceMaintenanceMapper.selectCount(Wrappers.lambdaQuery() .eq(DeviceMaintenance::getMaintenancePlanDetailId, detail.getMaintenancePlanDetailId())); if (recordCount != null && recordCount > 0) { continue; } if (detail.getMaintenancePlanId() != null && detail.getMaintenancePlanId().equals(plan.getMaintenancePlanId())) { return detail; } long distance = monthDistance(detail.getMaintenanceDate(), planDate); if (distance < nearestDistance) { nearest = detail; nearestDistance = distance; } } if (nearest != null) { return nearest; } DeviceMaintenancePlanDetails detail = new DeviceMaintenancePlanDetails(); detail.setMaintenancePlanId(plan.getMaintenancePlanId()); detail.setDeviceId(device.getId()); detail.setMaintenanceIntervals(device.getMaintenanceCycle()); detail.setMaintenanceDate(planDate); deviceMaintenancePlanDetailsMapper.insert(detail); return detail; } /** * 修改设备保养计划详情(已审核的明细不允许修改) */ @Override public boolean updateMaintenancePlanDetail(DeviceMaintenancePlanDetails detail) { if (detail == null || detail.getMaintenancePlanDetailId() == null) { throw new ErrorException("缺少设备保养计划明细id"); } DeviceMaintenance record = deviceMaintenanceMapper.selectOne(Wrappers.lambdaQuery() .eq(DeviceMaintenance::getMaintenancePlanDetailId, detail.getMaintenancePlanDetailId()) .last("limit 1")); if (record != null && record.getReviewStatus() != null) { throw new ErrorException("该计划已审核,不允许修改"); } return deviceMaintenancePlanDetailsMapper.updateById(detail) > 0; } /** * 删除设备保养计划详情;已出审核结果的记录随历史留档,未审核的记录随明细一起删除 */ @Override public boolean removeMaintenancePlanDetail(Integer maintenancePlanDetailId) { if (maintenancePlanDetailId == null) { throw new ErrorException("缺少设备保养计划明细id"); } DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId); if (detail == null) { return false; } List records = deviceMaintenanceMapper.selectList(Wrappers.lambdaQuery() .eq(DeviceMaintenance::getMaintenancePlanDetailId, maintenancePlanDetailId)); // 审核不通过的记录允许连同明细一起删除;审核通过的记录为历史留档,不允许删除 boolean hasPassedRecord = records.stream().anyMatch(item -> item.getReviewStatus() != null && item.getReviewStatus() == 1); if (hasPassedRecord) { throw new ErrorException("该计划已存在审核通过的保养记录,为保留历史记录不允许删除"); } if (!records.isEmpty()) { List recordIds = records.stream().map(DeviceMaintenance::getId).collect(Collectors.toList()); deviceMaintenanceMapper.deleteBatchIds(recordIds); } removeAttachmentsByDetailIds(List.of(maintenancePlanDetailId)); return deviceMaintenancePlanDetailsMapper.deleteById(maintenancePlanDetailId) > 0; } @Override public List selectAttachments(Integer maintenancePlanDetailId) { if (maintenancePlanDetailId == null) { return new ArrayList<>(); } return deviceMaintenanceAttachmentMapper.selectList(Wrappers.lambdaQuery() .eq(DeviceMaintenanceAttachment::getMaintenancePlanDetailId, maintenancePlanDetailId) .orderByAsc(DeviceMaintenanceAttachment::getSort, DeviceMaintenanceAttachment::getId)); } @Override public DeviceMaintenanceAttachment uploadAttachment(Integer maintenancePlanDetailId, MultipartFile file) { if (maintenancePlanDetailId == null) { throw new ErrorException("缺少保养计划明细id"); } if (deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId) == null) { throw new ErrorException("保养计划明细不存在"); } if (file == null || file.isEmpty()) { throw new ErrorException("附件不能为空"); } // 系统全局上传上限为200MB,这里提前给出友好提示 if (file.getSize() > 200L * 1024 * 1024) { throw new ErrorException("附件不能超过200MB"); } String originalName = file.getOriginalFilename(); if (originalName == null || originalName.trim().isEmpty()) { originalName = "未命名文件"; } // 个别浏览器可能带上路径前缀,只保留文件名本身 originalName = originalName.replaceAll(".*[/\\\\]", ""); try { // 图片放图片目录(/img 静态路径),其余任意格式放 word 目录(/word 静态路径) boolean image = originalName.matches("(?i).+\\.(jpg|jpeg|png|gif)$"); File directory = new File(image ? filePath : wordUrl); if (!directory.exists()) { directory.mkdirs(); } String fileUrl = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmssSSS")) + "-" + originalName; file.transferTo(new File(directory, fileUrl)); DeviceMaintenanceAttachment attachment = new DeviceMaintenanceAttachment(); attachment.setMaintenancePlanDetailId(maintenancePlanDetailId); attachment.setFileName(originalName); attachment.setFileUrl(fileUrl); attachment.setSort(selectAttachments(maintenancePlanDetailId).size()); deviceMaintenanceAttachmentMapper.insert(attachment); return attachment; } catch (IOException e) { throw new ErrorException("附件上传失败"); } } @Override public boolean deleteAttachment(Long id) { return deviceMaintenanceAttachmentMapper.deleteById(id) > 0; } /** * 删除计划明细对应的附件记录 */ private void removeAttachmentsByDetailIds(List maintenancePlanDetailIds) { if (CollectionUtils.isEmpty(maintenancePlanDetailIds)) { return; } deviceMaintenanceAttachmentMapper.delete(Wrappers.lambdaQuery() .in(DeviceMaintenanceAttachment::getMaintenancePlanDetailId, maintenancePlanDetailIds)); } /** * 同一计划、同一设备只保留一条;SQL 已按“已有保养记录优先、审核通过优先、最新优先”排序,这里取首条 */ private List dedupeMaintenanceDetails(List details) { if (CollectionUtils.isEmpty(details)) { return details; } Map unique = new LinkedHashMap<>(); for (DeviceMaintenancePlanDetailsDto detail : details) { String key = detail.getDeviceId() != null ? String.valueOf(detail.getDeviceId()) : "name:" + detail.getDeviceName(); unique.putIfAbsent(key, detail); } return new ArrayList<>(unique.values()); } /** * 两个保养日期之间相差的月数 */ private long monthDistance(LocalDate from, LocalDate target) { if (from == null || target == null) { return Long.MAX_VALUE; } return Math.abs(ChronoUnit.MONTHS.between(YearMonth.from(from), YearMonth.from(target))); } }