| | |
| | | |
| | | 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.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; |
| | | |
| | | /** |
| | |
| | | * @since 2024-12-16 06:10:52 |
| | | */ |
| | | @Service |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public class DeviceMaintenancePlanServiceImpl extends ServiceImpl<DeviceMaintenancePlanMapper, DeviceMaintenancePlan> implements DeviceMaintenancePlanService { |
| | | |
| | | @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; |
| | | |
| | | /** |
| | | * 分页查询设备保养计划 |
| | |
| | | } |
| | | this.saveOrUpdate(deviceMaintenancePlanDto); |
| | | |
| | | // 删除原本的详情 |
| | | // 删除原本的详情;明细重建后仍保留原明细id的附件不清理,id丢失的才清理 |
| | | List<DeviceMaintenancePlanDetails> oldDetails = deviceMaintenancePlanDetailsService.list( |
| | | Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery() |
| | | .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); |
| | | deviceMaintenancePlanDetailsService.remove(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); |
| | | List<Integer> lostDetailIds = oldDetails.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId) |
| | | .collect(Collectors.toList()); |
| | | // 详情赋值并保存 |
| | | List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetails = deviceMaintenancePlanDto.getDeviceMaintenancePlanDetails(); |
| | | if (CollectionUtils.isNotEmpty(deviceMaintenancePlanDetails)) { // 详情不为空 |
| | | List<Integer> keptDetailIds = deviceMaintenancePlanDetails.stream() |
| | | .map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId) |
| | | .filter(id -> id != null) |
| | | .collect(Collectors.toList()); |
| | | lostDetailIds.removeAll(keptDetailIds); |
| | | List<DeviceMaintenancePlanDetails> collect = deviceMaintenancePlanDetails.stream().map(deviceMaintenancePlanDetail -> { // 遍历详情 |
| | | deviceMaintenancePlanDetail.setDeviceId(deviceMaintenancePlanDto.getDeviceId()); // 设备ID |
| | | deviceMaintenancePlanDetail.setMaintenancePlanId(deviceMaintenancePlanDto.getMaintenancePlanId()); // 保养计划ID |
| | | DeviceMaintenancePlanDetails planDetails = new DeviceMaintenancePlanDetails(); |
| | | BeanUtils.copyProperties(deviceMaintenancePlanDetail, planDetails); |
| | |
| | | }).collect(Collectors.toList()); |
| | | deviceMaintenancePlanDetailsService.saveBatch(collect); |
| | | } |
| | | removeAttachmentsByDetailIds(lostDetailIds); |
| | | return Result.success(); |
| | | } |
| | | |
| | |
| | | */ |
| | | @Override |
| | | public Result deleteMaintenancePlan(DeviceMaintenancePlanDto deviceMaintenancePlanDto) { |
| | | List<DeviceMaintenancePlanDetails> details = deviceMaintenancePlanDetailsService.list( |
| | | Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery() |
| | | .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); |
| | | this.removeById(deviceMaintenancePlanDto); |
| | | deviceMaintenancePlanDetailsService.remove(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId())); |
| | | removeAttachmentsByDetailIds(details.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId).collect(Collectors.toList())); |
| | | return Result.success(); |
| | | } |
| | | |
| | |
| | | // 查询设备保养计划 |
| | | DeviceMaintenancePlanDto deviceMaintenancePlan = baseMapper.selectMaintenancePlanById(maintenancePlanId); |
| | | |
| | | // 查询设备保养计划详情 |
| | | List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetailsDtoList = deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId); |
| | | // 查询设备保养计划详情,按“设备 + 保养月份”兜底去重 |
| | | List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetailsDtoList = dedupeMaintenanceDetails( |
| | | deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId)); |
| | | // 设置序号 |
| | | deviceMaintenancePlanDetailsDtoList.forEach(deviceInspectionRecordDetails -> { |
| | | deviceInspectionRecordDetails.setIndex(deviceMaintenancePlanDetailsDtoList.indexOf(deviceInspectionRecordDetails) + 1); |
| | | }); |
| | | 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(); |
| | | XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render( |
| | | new HashMap<String, Object>() {{ |
| | | put("deviceMaintenancePlan", deviceMaintenancePlan); |
| | | put("deviceMaintenancePlanDetailsDtoList", deviceMaintenancePlanDetailsDtoList); |
| | | // 编制人签名地址 |
| | | put("compilerUrl", UserUtils.getFinalUserSignatureUrl(deviceMaintenancePlan.getCompilerId())); |
| | | // 审核人签名地址 |
| | | put("auditUrl", UserUtils.getFinalUserSignatureUrl(deviceMaintenancePlan.getAuditId())); |
| | | }}); |
| | | Map<String, Object> 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"); |
| | |
| | | // 查询详情 |
| | | DeviceMaintenancePlanDto deviceMaintenancePlanDto = new DeviceMaintenancePlanDto(); |
| | | BeanUtils.copyProperties(deviceMaintenancePlan, deviceMaintenancePlanDto); |
| | | deviceMaintenancePlanDto.setDeviceMaintenancePlanDetails(deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId)); |
| | | deviceMaintenancePlanDto.setDeviceMaintenancePlanDetails(dedupeMaintenanceDetails( |
| | | deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId))); |
| | | return Result.success(deviceMaintenancePlanDto); |
| | | } |
| | | |
| | |
| | | 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<String> getMaintenancePlanYears() { |
| | | // 历史数据自愈:先让已审核的明细按审核完成年份归位,再清理空年份计划 |
| | | reclassifyReviewedDetails(); |
| | | List<DeviceMaintenancePlan> plans = baseMapper.selectList(null); |
| | | for (DeviceMaintenancePlan plan : plans) { |
| | | Long detailCount = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.<DeviceMaintenancePlanDetails>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<DeviceMaintenance> records = deviceMaintenanceMapper.selectList(Wrappers.<DeviceMaintenance>lambdaQuery() |
| | | .eq(DeviceMaintenance::getReviewStatus, 1) |
| | | .isNotNull(DeviceMaintenance::getMaintenancePlanDetailId) |
| | | .isNotNull(DeviceMaintenance::getReviewTime)); |
| | | if (CollectionUtils.isEmpty(records)) { |
| | | return; |
| | | } |
| | | List<Integer> detailIds = records.stream().map(DeviceMaintenance::getMaintenancePlanDetailId) |
| | | .distinct().collect(Collectors.toList()); |
| | | Map<Integer, DeviceMaintenancePlanDetails> detailMap = deviceMaintenancePlanDetailsMapper.selectBatchIds(detailIds) |
| | | .stream().collect(Collectors.toMap(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId, item -> item, (a, b) -> a)); |
| | | Map<Integer, String> 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<DeviceMaintenancePlanDetailsDto> 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.<DeviceMaintenancePlan>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<Device> 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.<DeviceMaintenancePlanDetails>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<DeviceMaintenancePlanDetails> details = deviceMaintenancePlanDetailsMapper.selectList( |
| | | Wrappers.<DeviceMaintenancePlanDetails>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.<DeviceMaintenance>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.<DeviceMaintenance>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<DeviceMaintenance> records = deviceMaintenanceMapper.selectList(Wrappers.<DeviceMaintenance>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<Integer> recordIds = records.stream().map(DeviceMaintenance::getId).collect(Collectors.toList()); |
| | | deviceMaintenanceMapper.deleteBatchIds(recordIds); |
| | | } |
| | | removeAttachmentsByDetailIds(List.of(maintenancePlanDetailId)); |
| | | return deviceMaintenancePlanDetailsMapper.deleteById(maintenancePlanDetailId) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public List<DeviceMaintenanceAttachment> selectAttachments(Integer maintenancePlanDetailId) { |
| | | if (maintenancePlanDetailId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return deviceMaintenanceAttachmentMapper.selectList(Wrappers.<DeviceMaintenanceAttachment>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<Integer> maintenancePlanDetailIds) { |
| | | if (CollectionUtils.isEmpty(maintenancePlanDetailIds)) { |
| | | return; |
| | | } |
| | | deviceMaintenanceAttachmentMapper.delete(Wrappers.<DeviceMaintenanceAttachment>lambdaQuery() |
| | | .in(DeviceMaintenanceAttachment::getMaintenancePlanDetailId, maintenancePlanDetailIds)); |
| | | } |
| | | |
| | | /** |
| | | * 同一计划、同一设备只保留一条;SQL 已按“已有保养记录优先、审核通过优先、最新优先”排序,这里取首条 |
| | | */ |
| | | private List<DeviceMaintenancePlanDetailsDto> dedupeMaintenanceDetails(List<DeviceMaintenancePlanDetailsDto> details) { |
| | | if (CollectionUtils.isEmpty(details)) { |
| | | return details; |
| | | } |
| | | Map<String, DeviceMaintenancePlanDetailsDto> 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))); |
| | | } |
| | | |
| | | } |