package com.ruoyi.device.service.impl; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; 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.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.DeviceExaminePlanDetailsDto; import com.ruoyi.device.dto.DeviceExaminePlanDto; import com.ruoyi.device.excel.upload.DeviceExaminePlanUpload; import com.ruoyi.device.mapper.DeviceExaminePlanDetailsMapper; import com.ruoyi.device.mapper.DeviceExaminePlanMapper; import com.ruoyi.device.mapper.DeviceMapper; import com.ruoyi.device.pojo.Device; import com.ruoyi.device.pojo.DeviceExaminePlan; import com.ruoyi.device.pojo.DeviceExaminePlanDetails; import com.ruoyi.device.service.DeviceExaminePlanDetailsService; import com.ruoyi.device.service.DeviceExaminePlanService; 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.springframework.beans.BeanUtils; 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.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.util.ArrayList; import java.util.HashMap; import java.util.List; /** *

* 设备核查计划主表 服务实现类 *

* * @author 江苏鵷雏网络科技有限公司 * @since 2024-12-16 07:14:04 */ @Service @Transactional(rollbackFor = Exception.class) public class DeviceExaminePlanServiceImpl extends ServiceImpl implements DeviceExaminePlanService { @Resource private DeviceExaminePlanDetailsMapper deviceExaminePlanDetailsMapper; @Resource private DeviceExaminePlanDetailsService deviceExaminePlanDetailsService; @Resource private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Resource private InformationNotificationService informationNotificationService; @Resource private DeviceMapper deviceMapper; @Resource private UserMapper userMapper; /** * 新增设备核查计划 * @param examinePlanDto * @return */ @Override public boolean addDeviceExaminePlan(DeviceExaminePlanDto examinePlanDto) { // 当前登录用户 Integer userId = SecurityUtils.getUserId().intValue(); // 文件名称 examinePlanDto.setWriteUserId(userId); examinePlanDto.setWriteTime(LocalDateTime.now()); baseMapper.insert(examinePlanDto); // 添加详情 if (CollectionUtils.isNotEmpty(examinePlanDto.getExaminePlanDetailsList())) { for (DeviceExaminePlanDetails deviceExaminePlanDetails : examinePlanDto.getExaminePlanDetailsList()) { deviceExaminePlanDetails.setPlanId(examinePlanDto.getPlanId()); } deviceExaminePlanDetailsService.saveBatch(examinePlanDto.getExaminePlanDetailsList()); } return true; } /** * 导入设备核查计划 * @param file * @return */ @Override public boolean importDeviceExaminePlan(MultipartFile file) { // 当前登录用户 Integer userId = SecurityUtils.getUserId().intValue(); // 文件名称 String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf(".")); DeviceExaminePlan examinePlan = new DeviceExaminePlan(); examinePlan.setPlanName(fileName); examinePlan.setWriteUserId(userId); examinePlan.setWriteTime(LocalDateTime.now()); baseMapper.insert(examinePlan); List examinePlanDetails = new ArrayList<>(); // 导入附件内容 try { // excel解析 EasyExcel.read(file.getInputStream(), DeviceExaminePlanUpload.class, new AnalysisEventListener() { @Override public void invoke(DeviceExaminePlanUpload detailsUpload, AnalysisContext analysisContext) { // 判断是否为空 if (StringUtils.isNotBlank(detailsUpload.getDeviceName()) && StringUtils.isNotBlank(detailsUpload.getDeviceNumber())) { // 对象复制 DeviceExaminePlanDetails planDetails = new DeviceExaminePlanDetails(); BeanUtils.copyProperties(detailsUpload, planDetails); planDetails.setPlanId(examinePlan.getPlanId()); examinePlanDetails.add(planDetails); } } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }).sheet().doRead(); for (DeviceExaminePlanDetails examinePlanDetail : examinePlanDetails) { Device device = deviceMapper.selectOne(Wrappers.lambdaQuery() .eq(Device::getManagementNumber, examinePlanDetail.getDeviceNumber().trim())); if (device == null) { throw new ErrorException("设备编号" + examinePlanDetail.getDeviceNumber() + "未查询到设备, 请重新导入"); } User user = userMapper.selectOne(Wrappers.lambdaQuery() .eq(User::getName, examinePlanDetail.getCheckChargerUser())); if (user == null) { throw new ErrorException("设备编号" + examinePlanDetail.getDeviceNumber() + "未查询到核查人"); } examinePlanDetail.setDeviceId(device.getId()); examinePlanDetail.setCheckChargerUserId(user.getId()); } deviceExaminePlanDetailsService.saveBatch(examinePlanDetails); } catch (IOException e) { throw new RuntimeException(e); } return true; } /** * 提交批准 * @param deviceExaminePlan * @return */ @Override public boolean submitRatifyDeviceExaminePlan(DeviceExaminePlan deviceExaminePlan) { baseMapper.update(null, Wrappers.lambdaUpdate() .eq(DeviceExaminePlan::getPlanId, deviceExaminePlan.getPlanId()) .set(DeviceExaminePlan::getRatifyUserId, deviceExaminePlan.getRatifyUserId()) .set(DeviceExaminePlan::getRatifyRemark, null) .set(DeviceExaminePlan::getRatifyStatus, null) .set(DeviceExaminePlan::getRatifyTime, null) ); DeviceExaminePlan examinePlan = baseMapper.selectById(deviceExaminePlan.getPlanId()); // 发送消息 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(examinePlan.getPlanName() + "的设备核查计划待批准"); info.setSenderId(userId); // 接收人 info.setConsigneeId(examinePlan.getRatifyUserId()); info.setJumpPath(MenuJumpPathConstants.DEVICE); informationNotificationService.addInformationNotification(info); // 发送企业微信通知 threadPoolTaskExecutor.execute(() -> { // 查询接收人 User personnel = userMapper.selectById(examinePlan.getRatifyUserId()); String message = ""; message += "CNAS设备核查计划批准通知"; message += "\n请去资源管理-设备一层目录-设备核查计划填写"; message += "\n" + examinePlan.getPlanName() + "的校准核查划待批准"; //发送企业微信消息通知 try { WxCpUtils.inform(personnel.getAccount(), message, null); } catch (Exception e) { throw new RuntimeException(e); } }); return false; } /** * 设备核查计划批准 * @param deviceExaminePlan * @return */ @Override public boolean ratifyDeviceExaminePlan(DeviceExaminePlan deviceExaminePlan) { // 当前登录用户 baseMapper.update(null, Wrappers.lambdaUpdate() .eq(DeviceExaminePlan::getPlanId, deviceExaminePlan.getPlanId()) .set(DeviceExaminePlan::getRatifyRemark, deviceExaminePlan.getRatifyRemark()) .set(DeviceExaminePlan::getRatifyStatus, deviceExaminePlan.getRatifyStatus()) .set(DeviceExaminePlan::getRatifyTime, LocalDateTime.now()) ); return true; } /** * 设备核查计划列表 * @param page * @param deviceExaminePlan * @return */ @Override public IPage pageDeviceExaminePlan(Page page, DeviceExaminePlan deviceExaminePlan) { return baseMapper.deviceExaminePlanDetailsMapper(page, QueryWrappers.queryWrappers(deviceExaminePlan)); } /** * 设备核查计划详情列表 * @param page * @param deviceExaminePlanDetails * @return */ @Override public IPage pageDeviceExaminePlanDetail(Page page, DeviceExaminePlanDetails deviceExaminePlanDetails) { if (deviceExaminePlanDetails.getPlanId() == null) { return new Page(); } return deviceExaminePlanDetailsMapper.pageDeviceExaminePlanDetail(page, QueryWrappers.queryWrappers(deviceExaminePlanDetails)); } /** * 导出设备核查计划 * @param deviceExaminePlanId 设备核查计划id * @param response 响应体 */ @Override public void exportDeviceExaminePlanDetail(Integer deviceExaminePlanId, HttpServletResponse response) { // 查询设备核查计划 DeviceExaminePlanDto deviceExaminePlanDto = baseMapper.selectExamineExaminePlanDto(deviceExaminePlanId); // 查询设备核查计划详情 List deviceExaminePlanDetailsList = deviceExaminePlanDetailsMapper.selectList(Wrappers.lambdaQuery().eq(DeviceExaminePlanDetails::getPlanId, deviceExaminePlanId) .orderByAsc(DeviceExaminePlanDetails::getCheckTime)); // 设置序号 ArrayList deviceExaminePlanDetailsDtoList = new ArrayList<>(); deviceExaminePlanDetailsList.forEach(deviceExamineRecordContrastDetail -> { DeviceExaminePlanDetailsDto deviceExaminePlanDetailsDto = new DeviceExaminePlanDetailsDto(); BeanUtils.copyProperties(deviceExamineRecordContrastDetail, deviceExaminePlanDetailsDto); deviceExaminePlanDetailsDto.setIndex(deviceExaminePlanDetailsList.indexOf(deviceExamineRecordContrastDetail) + 1); deviceExaminePlanDetailsDtoList.add(deviceExaminePlanDetailsDto); }); // 获取路径 InputStream inputStream = this.getClass().getResourceAsStream("/static/word/examine-plan-detail.docx"); Configure configure = Configure.builder() .bind("deviceExaminePlanDetailsDtoList", new HackLoopTableRenderPolicy()) .build(); XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render( new HashMap() {{ put("deviceExaminePlan", deviceExaminePlanDto); put("deviceExaminePlanDetailsDtoList", deviceExaminePlanDetailsDtoList); //获取核查人的签名地址 put("writeUserUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceExaminePlanDto.getWriteUserId())); //获取审查人的签名地址 put("reviewUserUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceExaminePlanDto.getRatifyUserId())); }}); 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("导出失败"); } } @Override public boolean updateDeviceExaminePlan(DeviceExaminePlanDto examinePlanDto) { // 当前登录用户 Integer userId = SecurityUtils.getUserId().intValue(); // 文件名称 examinePlanDto.setWriteUserId(userId); examinePlanDto.setWriteTime(LocalDateTime.now()); baseMapper.updateById(examinePlanDto); deviceExaminePlanDetailsService.remove(Wrappers.lambdaQuery() .eq(DeviceExaminePlanDetails::getPlanId, examinePlanDto.getPlanId())); // 添加详情 if (CollectionUtils.isNotEmpty(examinePlanDto.getExaminePlanDetailsList())) { for (DeviceExaminePlanDetails deviceExaminePlanDetails : examinePlanDto.getExaminePlanDetailsList()) { deviceExaminePlanDetails.setPlanId(examinePlanDto.getPlanId()); } deviceExaminePlanDetailsService.saveBatch(examinePlanDto.getExaminePlanDetailsList()); } return true; } @Override public DeviceExaminePlanDto getDeviceExaminePlan(Integer planId) { DeviceExaminePlan examinePlan = baseMapper.selectById(planId); DeviceExaminePlanDto deviceExaminePlanDto = new DeviceExaminePlanDto(); BeanUtils.copyProperties(examinePlan, deviceExaminePlanDto); // 查询详情 List list = deviceExaminePlanDetailsService.list(Wrappers.lambdaQuery() .eq(DeviceExaminePlanDetails::getPlanId, planId) .orderByAsc(DeviceExaminePlanDetails::getCheckTime)); deviceExaminePlanDto.setExaminePlanDetailsList(list); return deviceExaminePlanDto; } /** * 按年份查询设备核查计划(不存在则自动创建),并自动补全已到点的设备明细 */ @Override public DeviceExaminePlanDto getOrCreatePlanByYear(String planYear) { if (StringUtils.isBlank(planYear)) { planYear = String.valueOf(LocalDate.now().getYear()); } DeviceExaminePlan plan = getOrCreatePlanEntity(planYear); ensureExamineDetails(plan); DeviceExaminePlanDto planDto = new DeviceExaminePlanDto(); BeanUtils.copyProperties(plan, planDto); DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); planDto.setWriteTimeStr(plan.getWriteTime() == null ? null : plan.getWriteTime().format(dateFormatter)); planDto.setRatifyTimeStr(plan.getRatifyTime() == null ? null : plan.getRatifyTime().format(dateFormatter)); if (plan.getWriteUserId() != null) { User writeUser = userMapper.selectById(plan.getWriteUserId()); if (writeUser != null) { planDto.setWriteName(writeUser.getName()); } } if (plan.getRatifyUserId() != null) { User ratifyUser = userMapper.selectById(plan.getRatifyUserId()); if (ratifyUser != null) { planDto.setRatifyName(ratifyUser.getName()); } } return planDto; } /** * 设备档案变更后,把设备同步到对应年份的设备核查计划明细 */ @Override public void syncDeviceExamineDetail(Device device) { LocalDate remind = calcExamineRemindDate(device); if (remind == null || remind.isAfter(LocalDate.now())) { return; } DeviceExaminePlan plan = getOrCreatePlanEntity(String.valueOf(remind.getYear())); fillExamineDetailIfAbsent(plan, device); } /** * 复核通过后,把设备档案的最近/下次核查日期滚动到本轮 */ @Override public void updateDeviceExamineDate(Integer planDetailsId) { DeviceExaminePlanDetails detail = deviceExaminePlanDetailsMapper.selectById(planDetailsId); if (detail == null || detail.getDeviceId() == null) { return; } Device device = deviceMapper.selectById(detail.getDeviceId()); if (device == null) { return; } LocalDate checkDate = parseCheckDate(detail.getCheckTime()); if (checkDate.isAfter(LocalDate.now())) { checkDate = LocalDate.now(); } LocalDateTime checkDateTime = checkDate.atStartOfDay(); // 只允许向前滚动,避免重复复核旧记录时把日期改回去 if (device.getLastExamineDate() != null && !checkDateTime.isAfter(device.getLastExamineDate())) { return; } device.setLastExamineDate(checkDateTime); if (StringUtils.isNotBlank(device.getExamineCycle())) { try { device.setNextExamineDate(checkDateTime.plusMonths(Long.parseLong(device.getExamineCycle().trim()))); } catch (NumberFormatException ignored) { } } deviceMapper.updateById(device); } /** * 查询年份对应的计划,不存在则创建 */ private DeviceExaminePlan getOrCreatePlanEntity(String planYear) { DeviceExaminePlan plan = baseMapper.selectOne(Wrappers.lambdaQuery() .eq(DeviceExaminePlan::getPlanYear, planYear) .orderByDesc(DeviceExaminePlan::getPlanId) .last("limit 1")); if (plan == null) { plan = new DeviceExaminePlan(); plan.setPlanName(planYear + "年度设备核查计划"); plan.setPlanYear(planYear); plan.setWriteUserId(SecurityUtils.getUserId().intValue()); plan.setWriteTime(LocalDateTime.now()); baseMapper.insert(plan); } return plan; } /** * 扫描所有设备,把已到点(下次核查日期前1个月起,含已过期)的设备补进本年度计划 */ private void ensureExamineDetails(DeviceExaminePlan plan) { List devices = deviceMapper.selectList(null); for (Device device : devices) { fillExamineDetailIfAbsent(plan, device); } } /** * 设备提醒起点:下次核查日期前1个月(没填下次日期时用最近日期+核查周期兜底) */ private LocalDate calcExamineRemindDate(Device device) { LocalDateTime base = device.getNextExamineDate(); if (base == null && device.getLastExamineDate() != null && StringUtils.isNotBlank(device.getExamineCycle())) { try { base = device.getLastExamineDate().plusMonths(Long.parseLong(device.getExamineCycle().trim())); } catch (NumberFormatException ignored) { } } return base == null ? null : base.toLocalDate().minusMonths(1); } /** * 已到点且计划中尚无该设备本轮明细时自动补一条 */ private void fillExamineDetailIfAbsent(DeviceExaminePlan plan, Device device) { LocalDate remind = calcExamineRemindDate(device); if (remind == null || remind.isAfter(LocalDate.now())) { return; } if (plan.getPlanYear() != null && String.valueOf(remind.getYear()).compareTo(plan.getPlanYear()) > 0) { return; } String checkTime = remind.format(DateTimeFormatter.ofPattern("yyyy-MM")); Long exist = deviceExaminePlanDetailsMapper.selectCount(Wrappers.lambdaQuery() .eq(DeviceExaminePlanDetails::getPlanId, plan.getPlanId()) .eq(DeviceExaminePlanDetails::getDeviceId, device.getId()) .likeRight(DeviceExaminePlanDetails::getCheckTime, checkTime)); if (exist != null && exist > 0) { return; } DeviceExaminePlanDetails detail = new DeviceExaminePlanDetails(); detail.setPlanId(plan.getPlanId()); detail.setDeviceId(device.getId()); String deviceName = device.getDeviceName() == null ? "" : device.getDeviceName(); if (StringUtils.isNotBlank(device.getSpecificationModel())) { deviceName = deviceName + " " + device.getSpecificationModel(); } detail.setDeviceName(deviceName); detail.setDeviceNumber(device.getManagementNumber()); detail.setCheckTime(checkTime); deviceExaminePlanDetailsMapper.insert(detail); } /** * 计划明细的核查时间可能是 yyyy-MM 或 yyyy-MM-dd,统一取月份首日 */ private LocalDate parseCheckDate(String checkTime) { if (StringUtils.isNotBlank(checkTime)) { String normalized = checkTime.trim().replace('.', '-').replace('/', '-'); if (normalized.length() >= 7) { try { return YearMonth.parse(normalized.substring(0, 7)).atDay(1); } catch (Exception ignored) { } } } return LocalDate.now(); } }