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)));
}
}