吴羡
5 小时以前 2f07589b54d56085af77b50d580890cd4130b444
设备模块后端业务规则、计划接口与日期联动

- 设备停用/启用:审批完成(关闭)后回写设备档案状态(报废→3、启用→0、停用→2),并校验当前责任人与已关闭单据
- 设备故障维修/事故/报废/外部申请/借用/使用授权:接口层增加当前环节责任人校验,非责任人不可推进
- 设备校准/核查/保养计划:新增按年份自动创建计划并补全到点明细、明细分页/修改/删除、同步设备日期及提醒日期计算等接口
- 设备校准/保养完成后自动滚动设备档案的最近/下次日期;校准记录明细保存时清除维护表主键避免覆盖历史
- 设备事故报告单导出:修正主任签名取数
- 清理设备服务中的无效数采代码
- 修复 LambdaUtils 在隐藏类 canonicalName 为 null 时的缓存异常
- 用户查询:当前用户无所属部门时 selectUserCondition 返回全部有效用户
- 标准物质:查询返回剩余数量并按验收日期倒序,修正筛选字段前缀
已添加1个文件
已修改36个文件
1259 ■■■■■ 文件已修改
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceExaminePlanController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMetricRecordController.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceStateController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceExaminePlanDetailsDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenancePlanDetailsMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/DeviceExaminePlanService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/DeviceStateService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceAccidentReportServiceImpl.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceBorrowServiceImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceBreakdownMaintenanceServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExaminePlanServiceImpl.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExamineRecordContrastServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExamineRecordServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExternalApplyServiceImpl.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceImpowerServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceScrappedServiceImpl.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceServiceImpl.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceStateServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/util/DeviceFlowPermissionUtils.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/resources/mapper/DeviceExaminePlanDetailsMapper.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/resources/mapper/DeviceMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-require/src/main/java/com/ruoyi/require/pojo/FeStandardSubstance.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-require/src/main/resources/mapper/FeStandardSubstanceMapper.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/numgen/LambdaUtils.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/UserMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.device.dto.DeviceCalibrationPlanDetailDto;
import com.ruoyi.device.dto.DeviceCalibrationPlanDto;
import com.ruoyi.device.pojo.DeviceCalibrationPlan;
import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
@@ -118,12 +119,22 @@
    }
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡æ ¡å‡†è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @return
     */
    @Operation(summary = "按年份查询设备校准计划(自动创建并补全到点明细)")
    @GetMapping("/getPlanByYear")
    public Result<DeviceCalibrationPlanDto> getPlanByYear(String planYear) {
        return Result.success(deviceCalibrationPlanService.getOrCreatePlanByYear(planYear));
    }
    /**
     * è®¾å¤‡æ ¡å‡†è®¡åˆ’详情列表
     * @return
     */
    @Operation(summary = "设备校准计划详情列表")
    @GetMapping("/pageDeviceCalibrationPlanDetail")
    public Result<IPage<DeviceCalibrationPlanDetail>> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
    public Result<IPage<DeviceCalibrationPlanDetailDto>> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
        return Result.success(deviceCalibrationPlanService.pageDeviceCalibrationPlanDetail(page, deviceCalibrationPlanDetails));
    }
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceExaminePlanController.java
@@ -129,6 +129,16 @@
    }
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡æ ¸æŸ¥è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @return
     */
    @Operation(summary = "按年份查询设备核查计划(自动创建并补全到点明细)")
    @GetMapping("/getPlanByYear")
    public Result<DeviceExaminePlanDto> getPlanByYear(String planYear) {
        return Result.success(deviceExaminePlanService.getOrCreatePlanByYear(planYear));
    }
    /**
     * è®¾å¤‡æ ¸æŸ¥è®¡åˆ’详情列表
     * @return
     */
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
@@ -5,6 +5,7 @@
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.device.dto.DeviceMaintenanceDto;
import com.ruoyi.device.pojo.DeviceMaintenance;
import com.ruoyi.device.service.DeviceMaintenancePlanService;
import com.ruoyi.device.service.DeviceMaintenanceService;
import com.ruoyi.system.mapper.UserMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -27,6 +28,9 @@
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DeviceMaintenancePlanService deviceMaintenancePlanService;
    /**
@@ -55,7 +59,12 @@
        User user = userMapper.selectById(deviceMaintenance.getMaintenanceUserId());
        deviceMaintenance.setMaintenanceUserName(user.getName());
        return Result.success(deviceMaintenanceService.saveOrUpdate(deviceMaintenance));
        boolean saved = deviceMaintenanceService.saveOrUpdate(deviceMaintenance);
        // ä¿å…»å®ŒæˆåŽï¼ŒæŠŠè®¾å¤‡æ¡£æ¡ˆçš„æœ€è¿‘/下次保养日期滚动到本轮
        if (saved && deviceMaintenance.getMaintenanceDate() != null) {
            deviceMaintenancePlanService.updateDeviceMaintenanceDate(deviceMaintenance.getDeviceId(), deviceMaintenance.getMaintenanceDate());
        }
        return Result.success(saved);
    }
    /**
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java
@@ -3,8 +3,11 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
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 io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -29,6 +32,9 @@
public class DeviceMaintenancePlanController {
    @Resource
    private DeviceMaintenancePlanService deviceMaintenancePlanService;
    @Resource
    private DeviceMaintenancePlanDetailsService deviceMaintenancePlanDetailsService;
    /**
     * åˆ†é¡µæŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’
@@ -80,6 +86,42 @@
    }
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     */
    @Operation(summary = "按年份查询设备保养计划(自动创建并补全到点明细)")
    @GetMapping("/getPlanByYear")
    public Result<DeviceMaintenancePlanDto> getPlanByYear(String planYear) {
        return Result.success(deviceMaintenancePlanService.getOrCreatePlanByYear(planYear));
    }
    /**
     * è®¾å¤‡ä¿å…»è®¡åˆ’详情列表(含到点标红)
     */
    @Operation(summary = "设备保养计划详情列表")
    @GetMapping("/pageMaintenancePlanDetail")
    public Result<IPage<DeviceMaintenancePlanDetailsDto>> pageMaintenancePlanDetail(Page page, Integer planId, String deviceName, String deviceNumber) {
        return Result.success(deviceMaintenancePlanService.pageMaintenancePlanDetail(page, planId, deviceName, deviceNumber));
    }
    /**
     * ä¿®æ”¹è®¾å¤‡ä¿å…»è®¡åˆ’详情
     */
    @Operation(summary = "修改设备保养计划详情")
    @PostMapping("/updateMaintenancePlanDetail")
    public Result updateMaintenancePlanDetail(@RequestBody DeviceMaintenancePlanDetails deviceMaintenancePlanDetails) {
        return Result.success(deviceMaintenancePlanDetailsService.updateById(deviceMaintenancePlanDetails));
    }
    /**
     * åˆ é™¤è®¾å¤‡ä¿å…»è®¡åˆ’详情
     */
    @Operation(summary = "删除设备保养计划详情")
    @DeleteMapping("/delMaintenancePlanDetail")
    public Result delMaintenancePlanDetail(Integer maintenancePlanDetailId) {
        return Result.success(deviceMaintenancePlanDetailsService.removeById(maintenancePlanDetailId));
    }
    /**
     * å¯¼å‡ºè®¾å¤‡ä¿å…»è®¡åˆ’
     */
    @Operation(summary = "导出设备保养计划")
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMetricRecordController.java
@@ -2,12 +2,15 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.common.numgen.NumberGenerator;
import com.ruoyi.common.utils.FileSaveUtil;
import com.ruoyi.device.dto.DeviceMetricRecordDto;
import com.ruoyi.device.mapper.DeviceMapper;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceMetricRecord;
import com.ruoyi.device.pojo.DeviceMetricsCopy;
import com.ruoyi.device.service.DeviceMetricRecordService;
@@ -18,6 +21,8 @@
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Locale;
@@ -43,6 +48,9 @@
    @Autowired
    private NumberGenerator<DeviceMetricRecord> numberGenerator;
    @Autowired
    private DeviceMapper deviceMapper;
    @Operation(summary = "设备校准分页查询")
    @GetMapping("/deviceMetricRecordPage")
    public Result deviceMetricRecordPage(@RequestParam("deviceId") Integer deviceId, Page page, @RequestParam("type") String type) {
@@ -62,12 +70,51 @@
        }
        deviceMetricRecordService.saveOrUpdate(deviceMetricRecordDto);
        if (CollectionUtils.isNotEmpty(deviceMetricRecordDto.getDeviceMetricsCopyList())) {
            deviceMetricRecordDto.getDeviceMetricsCopyList().forEach(i -> i.setDeviceMetricsId(deviceMetricRecordDto.getId()));
            // æ ¡å‡†æ¡ç›®æ¥è‡ªæ ¡å‡†é¡¹ç›®ç»´æŠ¤è¡¨ï¼Œä¸»é”®ä¸èƒ½å¸¦è¿›è®°å½•明细表,否则会覆盖历史记录的条目
            deviceMetricRecordDto.getDeviceMetricsCopyList().forEach(i -> {
                i.setId(null);
                i.setDeviceMetricsId(deviceMetricRecordDto.getId());
            });
            deviceMetricsCopyService.saveOrUpdateBatch(deviceMetricRecordDto.getDeviceMetricsCopyList());
        }
        // æ ¡å‡†å®ŒæˆåŽï¼ŒæŠŠè®¾å¤‡æ¡£æ¡ˆçš„æœ€è¿‘/下次校准日期滚动到新周期
        if ("calibrate".equals(deviceMetricRecordDto.getType()) && deviceMetricRecordDto.getDeviceId() != null
                && deviceMetricRecordDto.getCalibrationDate() != null) {
            updateDeviceCalibrationDate(deviceMetricRecordDto);
        }
        return Result.success();
    }
    /**
     * æ»šåŠ¨è®¾å¤‡æ¡£æ¡ˆçš„æœ€è¿‘/下次校准日期:仅当本次校准日期比档案中的最近校准日期更新时生效
     */
    private void updateDeviceCalibrationDate(DeviceMetricRecordDto dto) {
        Device device = deviceMapper.selectById(dto.getDeviceId());
        if (device == null) {
            return;
        }
        LocalDateTime calibrationDate = LocalDateTime.ofInstant(dto.getCalibrationDate().toInstant(), ZoneId.systemDefault());
        if (device.getLastCalibrationDate() != null && !calibrationDate.isAfter(device.getLastCalibrationDate())) {
            return;
        }
        device.setLastCalibrationDate(calibrationDate);
        LocalDateTime next = null;
        if (StringUtils.isNotBlank(device.getCalibrationDate())) {
            try {
                next = calibrationDate.plusMonths(Long.parseLong(device.getCalibrationDate().trim()));
            } catch (NumberFormatException ignored) {
            }
        }
        if (next == null && dto.getNextCalibrationDate() != null) {
            next = LocalDateTime.ofInstant(dto.getNextCalibrationDate().toInstant(), ZoneId.systemDefault());
        }
        if (next != null) {
            device.setNextCalibrationDate(next);
            device.setActivationDate(next);
        }
        deviceMapper.updateById(device);
    }
    @Operation(summary = "设备校准 åˆ é™¤")
    @DeleteMapping("/deleteDeviceMetricRecord")
    public Result deleteDeviceMetricRecord(@RequestParam("id") Integer id) {
cnas-device/src/main/java/com/ruoyi/device/controller/DeviceStateController.java
@@ -49,7 +49,7 @@
            String processNumber = numberGenerator.generateNumberWithPrefix(3, "DG-TC-23FM " + month + "-" + year + month, DeviceState::getProcessNumber);
            deviceState.setProcessNumber(processNumber);
        }
        deviceStateService.saveOrUpdate(deviceState);
        deviceStateService.saveDeviceState(deviceState);
        return Result.success();
    }
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java
@@ -18,4 +18,6 @@
    private String lastDateStr;
    @Schema(description = "本年计划校准时间Str")
    private String planDateStr;
    @Schema(description = "是否到点需校准标红(1是)")
    private Integer redFlag;
}
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceExaminePlanDetailsDto.java
@@ -20,4 +20,7 @@
    @Schema(description = "对比状态, 0: æœªå¼€å§‹, 1:待批准, 2:通过, 3:不通过")
    private Integer recordContrastStatus;
    @Schema(description = "到点标红, 1:需核查")
    private Integer redFlag;
}
cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java
@@ -29,4 +29,7 @@
    @Schema(description = "归属实验室")
    private String storagePoint;
    @Schema(description = "到点标红, 1:需保养")
    private Integer redFlag;
}
cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.device.dto.DeviceCalibrationPlanDetailDto;
import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
import org.apache.ibatis.annotations.Param;
@@ -23,5 +24,5 @@
     * @param ew
     * @return
     */
    IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, @Param("ew") QueryWrapper<DeviceCalibrationPlanDetail> ew);
    IPage<DeviceCalibrationPlanDetailDto> pageDeviceCalibrationPlanDetail(Page page, @Param("ew") QueryWrapper<DeviceCalibrationPlanDetail> ew);
}
cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenancePlanDetailsMapper.java
@@ -1,6 +1,8 @@
package com.ruoyi.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
import org.apache.ibatis.annotations.Param;
@@ -22,4 +24,12 @@
     * @param maintenancePlanId è®¾å¤‡ä¿å…»è®¡åˆ’id
     */
    List<DeviceMaintenancePlanDetailsDto> deviceInspectionRecordDetailsList(@Param("maintenancePlanId") Integer maintenancePlanId);
    /**
     * åˆ†é¡µæŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’详情(含设备信息与到点标红)
     */
    IPage<DeviceMaintenancePlanDetailsDto> pageMaintenancePlanDetail(Page page,
                                                                     @Param("planId") Integer planId,
                                                                     @Param("deviceName") String deviceName,
                                                                     @Param("deviceNumber") String deviceNumber);
}
cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java
@@ -3,7 +3,9 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.device.dto.DeviceCalibrationPlanDetailDto;
import com.ruoyi.device.dto.DeviceCalibrationPlanDto;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceCalibrationPlan;
import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
import org.springframework.web.multipart.MultipartFile;
@@ -62,7 +64,20 @@
     * @param DeviceCalibrationPlanDetails
     * @return
     */
    IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails);
    IPage<DeviceCalibrationPlanDetailDto> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails);
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡æ ¡å‡†è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @param planYear å¹´ä»½
     * @return
     */
    DeviceCalibrationPlanDto getOrCreatePlanByYear(String planYear);
    /**
     * è®¾å¤‡æ¡£æ¡ˆå˜æ›´åŽï¼ŒæŠŠè®¾å¤‡åŒæ­¥åˆ°å¯¹åº”年份的设备校准计划明细(仅当已进入提醒期/已过期时生成)
     * @param device è®¾å¤‡
     */
    void syncDeviceCalibrationDetail(Device device);
    /**
cnas-device/src/main/java/com/ruoyi/device/service/DeviceExaminePlanService.java
@@ -5,6 +5,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.device.dto.DeviceExaminePlanDetailsDto;
import com.ruoyi.device.dto.DeviceExaminePlanDto;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceExaminePlan;
import com.ruoyi.device.pojo.DeviceExaminePlanDetails;
import org.springframework.web.multipart.MultipartFile;
@@ -86,4 +87,23 @@
     * @return
     */
    DeviceExaminePlanDto getDeviceExaminePlan(Integer planId);
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡æ ¸æŸ¥è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @param planYear å¹´ä»½
     * @return è®¡åˆ’主表信息
     */
    DeviceExaminePlanDto getOrCreatePlanByYear(String planYear);
    /**
     * è®¾å¤‡æ¡£æ¡ˆå˜æ›´åŽï¼ŒæŠŠè®¾å¤‡åŒæ­¥åˆ°å¯¹åº”年份的设备核查计划明细(仅当已进入提醒期/已过期时生成)
     * @param device è®¾å¤‡
     */
    void syncDeviceExamineDetail(Device device);
    /**
     * æ ¸æŸ¥è®°å½•复核通过后,滚动设备档案的最近/下次核查日期
     * @param planDetailsId è®¡åˆ’明细id
     */
    void updateDeviceExamineDate(Integer planDetailsId);
}
cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java
@@ -4,10 +4,13 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceMaintenancePlan;
import jakarta.servlet.http.HttpServletResponse;
import java.time.LocalDate;
/**
 * <p>
@@ -76,4 +79,28 @@
     */
    Result reviewMaintenancePlanStatus(DeviceMaintenancePlanDto deviceMaintenancePlanDto);
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @param planYear è®¡åˆ’年份
     * @return è®¾å¤‡ä¿å…»è®¡åˆ’
     */
    DeviceMaintenancePlanDto getOrCreatePlanByYear(String planYear);
    /**
     * è®¾å¤‡æ¡£æ¡ˆå˜æ›´åŽï¼ŒæŠŠå·²åˆ°ç‚¹çš„设备同步到对应年份的设备保养计划明细
     * @param device è®¾å¤‡
     */
    void syncDeviceMaintenanceDetail(Device device);
    /**
     * è®¾å¤‡ä¿å…»è®°å½•保存后,滚动设备档案的最近/下次保养日期
     * @param deviceId è®¾å¤‡id
     * @param maintenanceDate æœ¬æ¬¡ä¿å…»æ—¥æœŸ
     */
    void updateDeviceMaintenanceDate(Integer deviceId, LocalDate maintenanceDate);
    /**
     * åˆ†é¡µæŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’详情(含到点标红)
     */
    IPage<DeviceMaintenancePlanDetailsDto> pageMaintenancePlanDetail(Page page, Integer planId, String deviceName, String deviceNumber);
}
cnas-device/src/main/java/com/ruoyi/device/service/DeviceStateService.java
@@ -18,6 +18,11 @@
 */
public interface DeviceStateService extends IService<DeviceState> {
    /**
     * ä¿å­˜åœç”¨/启用流程,流程结束(关闭)时同步设备档案状态
     */
    boolean saveDeviceState(DeviceState deviceState);
    IPage<DeviceStateDto> getDeviceStatePage(Integer deviceId, Page page, String processNumber);
    /**
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceAccidentReportServiceImpl.java
@@ -6,6 +6,7 @@
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.dto.DeviceAccidentReportDto;
@@ -14,6 +15,7 @@
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceAccidentReport;
import com.ruoyi.device.service.DeviceAccidentReportService;
import com.ruoyi.device.util.DeviceFlowPermissionUtils;
import com.ruoyi.inspect.util.UserUtils;
import com.ruoyi.system.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
@@ -65,6 +67,7 @@
    public boolean addDeviceAccidentReport(DeviceAccidentReport deviceAccidentReport) {
        DeviceAccidentReport accidentReport = new DeviceAccidentReport();
        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
        checkFlowPermission(deviceAccidentReport, user);
        switch (deviceAccidentReport.getFlowType()) {
            case 0:
                // æŠ¥å‘Š
@@ -136,6 +139,43 @@
        return true;
    }
    private void checkFlowPermission(DeviceAccidentReport deviceAccidentReport, User user) {
        Integer flowType = deviceAccidentReport.getFlowType();
        Integer accidentReportId = deviceAccidentReport.getAccidentReportId();
        if (flowType != null && flowType > 0) {
            DeviceAccidentReport existing = baseMapper.selectById(accidentReportId);
            if (existing == null) {
                throw new ServiceException("单据不存在");
            }
            DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
            Integer expectedUserId;
            switch (flowType) {
                case 1:
                    expectedUserId = existing.getAssessorUserId();
                    break;
                case 2:
                    expectedUserId = existing.getDepartmentHeadUserId();
                    break;
                case 3:
                    expectedUserId = existing.getTechnicalDirectorUserId();
                    break;
                case 4:
                    expectedUserId = existing.getDirectorHeadUserId();
                    break;
                default:
                    expectedUserId = null;
                    break;
            }
            DeviceFlowPermissionUtils.checkResponsible(expectedUserId, user.getId());
        } else if (accidentReportId != null) {
            DeviceAccidentReport existing = baseMapper.selectById(accidentReportId);
            if (existing != null) {
                DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
                DeviceFlowPermissionUtils.checkResponsible(existing.getReportUserId(), user.getId());
            }
        }
    }
    /**
     * å¯¼å‡ºè®¾å¤‡äº‹æ•…报告
     * @param accidentReportId è®¾å¤‡äº‹æ•…报告id
@@ -167,7 +207,7 @@
                    // æŠ€æœ¯è´Ÿè´£äººç­¾å
                    put("technicalDirectorUserUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceAccidentReport.getTechnicalDirectorUserId()));
                    // ä¸»ä»»ç­¾å
                    put("directorHeadUserUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceAccidentReport.getDepartmentHeadUserId()));
                    put("directorHeadUserUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceAccidentReport.getDirectorHeadUserId()));
                }});
        try {
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceBorrowServiceImpl.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.numgen.NumberGenerator;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
@@ -75,6 +76,21 @@
            deviceLogMapper.insert(deviceLog);
        } else {
            DeviceBorrow borrow = deviceBorrowMapper.selectById(deviceBorrow.getId());
            if (borrow == null) {
                throw new ServiceException("单据不存在");
            }
            if ("关闭".equals(borrow.getNowState())) {
                throw new ServiceException("该流程已结束,不能再操作");
            }
            String expectedUser = null;
            if ("归还".equals(borrow.getNowState())) {
                expectedUser = borrow.getNowUser();
            } else if ("借出".equals(borrow.getNowState())) {
                expectedUser = borrow.getSubmitOperationUser();
            }
            if (expectedUser != null && !expectedUser.isBlank() && !expectedUser.equals(user.getName())) {
                throw new ServiceException("您不是当前环节责任人,无权操作");
            }
            deviceBorrowMapper.updateById(deviceBorrow);
            //通过或者驳回增加流程跟踪
            if (deviceBorrow.getNowState().equals("关闭")) {
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceBreakdownMaintenanceServiceImpl.java
@@ -6,6 +6,7 @@
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.dto.DeviceBreakdownMaintenance;
@@ -13,6 +14,7 @@
import com.ruoyi.device.mapper.DeviceMapper;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.service.DeviceBreakdownMaintenanceService;
import com.ruoyi.device.util.DeviceFlowPermissionUtils;
import com.ruoyi.inspect.util.UserUtils;
import com.ruoyi.system.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
@@ -63,6 +65,7 @@
        DeviceBreakdownMaintenance breakdownMaintenance = new DeviceBreakdownMaintenance();
        //当前登录用户
        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
        checkFlowPermission(deviceBreakdownMaintenance, user);
        switch (deviceBreakdownMaintenance.getFlowType()) {
            case 0:
@@ -72,11 +75,6 @@
                breakdownMaintenance.setApplicantUserId(user.getId());
                breakdownMaintenance.setApplicantUser(user.getName());
                breakdownMaintenance.setRepairDate(deviceBreakdownMaintenance.getRepairDate());
                // å¤„理人信息
                User departmentHeadUser = userMapper.selectById(deviceBreakdownMaintenance.getDepartmentHeadUserId());
                breakdownMaintenance.setApplicantUserId(departmentHeadUser.getId());
                breakdownMaintenance.setApplicantUser(departmentHeadUser.getName());
                baseMapper.insert(breakdownMaintenance);
                break;
@@ -103,6 +101,38 @@
        return true;
    }
    private void checkFlowPermission(DeviceBreakdownMaintenance deviceBreakdownMaintenance, User user) {
        Integer flowType = deviceBreakdownMaintenance.getFlowType();
        Integer maintenanceId = deviceBreakdownMaintenance.getMaintenanceId();
        if (flowType != null && flowType > 0) {
            DeviceBreakdownMaintenance existing = baseMapper.selectById(maintenanceId);
            if (existing == null) {
                throw new ServiceException("单据不存在");
            }
            DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
            Integer expectedUserId;
            switch (flowType) {
                case 1:
                    expectedUserId = existing.getDepartmentHeadUserId();
                    break;
                case 2:
                    // ç»´ä¿®è®°å½•环节未单独指定责任人,由申请人填写维修结果
                    expectedUserId = existing.getApplicantUserId();
                    break;
                default:
                    expectedUserId = null;
                    break;
            }
            DeviceFlowPermissionUtils.checkResponsible(expectedUserId, user.getId());
        } else if (maintenanceId != null) {
            DeviceBreakdownMaintenance existing = baseMapper.selectById(maintenanceId);
            if (existing != null) {
                DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
                DeviceFlowPermissionUtils.checkResponsible(existing.getApplicantUserId(), user.getId());
            }
        }
    }
    /**
     * å¯¼å‡ºè®¾å¤‡æ•…障维修
     * @param maintenanceId è®¾å¤‡æ•…障维修id
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java
@@ -22,6 +22,8 @@
import com.ruoyi.device.excel.upload.DeviceCalibrationPlanDetailUpload;
import com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper;
import com.ruoyi.device.mapper.DeviceCalibrationPlanMapper;
import com.ruoyi.device.mapper.DeviceMapper;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceCalibrationPlan;
import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
import com.ruoyi.device.service.DeviceCalibrationPlanDetailService;
@@ -71,6 +73,8 @@
    private InformationNotificationService informationNotificationService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DeviceMapper deviceMapper;
    /**
@@ -238,14 +242,133 @@
     * @return
     */
    @Override
    public IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
    public IPage<DeviceCalibrationPlanDetailDto> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
        if (deviceCalibrationPlanDetails.getPlanId() == null) {
            return new Page();
            return new Page<>();
        }
        return deviceCalibrationPlanDetailMapper.pageDeviceCalibrationPlanDetail(page, QueryWrappers.queryWrappers(deviceCalibrationPlanDetails));
    }
    /**
     * æŒ‰å¹´ä»½æŸ¥è¯¢è®¾å¤‡æ ¡å‡†è®¡åˆ’(不存在则自动创建),并自动补全已到点的设备明细
     * @param planYear å¹´ä»½
     * @return
     */
    @Override
    public DeviceCalibrationPlanDto getOrCreatePlanByYear(String planYear) {
        if (StringUtils.isBlank(planYear)) {
            planYear = String.valueOf(LocalDate.now().getYear());
        }
        DeviceCalibrationPlan plan = getOrCreatePlanEntity(planYear);
        ensureCalibrationDetails(plan);
        DeviceCalibrationPlanDto planDto = new DeviceCalibrationPlanDto();
        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;
    }
    /**
     * è®¾å¤‡æ¡£æ¡ˆå˜æ›´åŽï¼ŒæŠŠè®¾å¤‡åŒæ­¥åˆ°å¯¹åº”年份的设备校准计划明细(仅当已进入提醒期/已过期时生成)
     * @param device è®¾å¤‡
     */
    @Override
    public void syncDeviceCalibrationDetail(Device device) {
        LocalDate remind = calcCalibrationRemindDate(device);
        if (remind == null || remind.isAfter(LocalDate.now())) {
            return;
        }
        DeviceCalibrationPlan plan = getOrCreatePlanEntity(String.valueOf(remind.getYear()));
        fillCalibrationDetailIfAbsent(plan, device);
    }
    /**
     * æŸ¥è¯¢å¹´ä»½å¯¹åº”的计划,不存在则创建
     */
    private DeviceCalibrationPlan getOrCreatePlanEntity(String planYear) {
        DeviceCalibrationPlan plan = baseMapper.selectOne(Wrappers.<DeviceCalibrationPlan>lambdaQuery()
                .eq(DeviceCalibrationPlan::getPlanYear, planYear)
                .orderByDesc(DeviceCalibrationPlan::getPlanId)
                .last("limit 1"));
        if (plan == null) {
            plan = new DeviceCalibrationPlan();
            plan.setPlanName(planYear + "年度设备校准计划");
            plan.setPlanYear(planYear);
            plan.setWriteUserId(SecurityUtils.getUserId().intValue());
            plan.setWriteTime(LocalDateTime.now());
            baseMapper.insert(plan);
        }
        return plan;
    }
    /**
     * æ‰«ææ‰€æœ‰è®¾å¤‡ï¼ŒæŠŠå·²åˆ°ç‚¹ï¼ˆä¸‹æ¬¡æ ¡å‡†æ—¥æœŸå‰1个月起,含已过期)的设备补进本年度计划
     */
    private void ensureCalibrationDetails(DeviceCalibrationPlan plan) {
        List<Device> devices = deviceMapper.selectList(null);
        for (Device device : devices) {
            fillCalibrationDetailIfAbsent(plan, device);
        }
    }
    /**
     * è®¾å¤‡çš„æé†’起点日期:下次校准日期前1个月(取不到下次日期时用校准有效期前1个月兜底)
     */
    private LocalDate calcCalibrationRemindDate(Device device) {
        LocalDateTime base = device.getNextCalibrationDate() != null ? device.getNextCalibrationDate() : device.getActivationDate();
        return base == null ? null : base.toLocalDate().minusMonths(1);
    }
    /**
     * å·²åˆ°ç‚¹ä¸”计划中尚无该设备本轮明细时,自动补一条
     */
    private void fillCalibrationDetailIfAbsent(DeviceCalibrationPlan plan, Device device) {
        LocalDate remind = calcCalibrationRemindDate(device);
        // æœªåˆ°ç‚¹ä¸ç”Ÿæˆï¼›æœªæ¥å¹´ä»½çš„æé†’不补进当前计划
        if (remind == null || remind.isAfter(LocalDate.now())) {
            return;
        }
        if (plan.getPlanYear() != null && String.valueOf(remind.getYear()).compareTo(plan.getPlanYear()) > 0) {
            return;
        }
        Long exist = deviceCalibrationPlanDetailMapper.selectCount(Wrappers.<DeviceCalibrationPlanDetail>lambdaQuery()
                .eq(DeviceCalibrationPlanDetail::getPlanId, plan.getPlanId())
                .eq(DeviceCalibrationPlanDetail::getDeviceId, device.getId())
                .eq(DeviceCalibrationPlanDetail::getPlanDate, remind));
        if (exist != null && exist > 0) {
            return;
        }
        DeviceCalibrationPlanDetail detail = new DeviceCalibrationPlanDetail();
        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.setDeviceAmount("1");
        detail.setDeviceNumber(device.getManagementNumber());
        detail.setVerificationUnit(device.getCalibrationServices());
        detail.setVerificationCycles(device.getCalibrationDate());
        detail.setLastDate(device.getLastCalibrationDate() == null ? null : device.getLastCalibrationDate().toLocalDate());
        detail.setPlanDate(remind);
        deviceCalibrationPlanDetailMapper.insert(detail);
    }
    /**
     * å¯¼å‡ºè®¾å¤‡æ ¡å‡†è®¡åˆ’
     * @param deviceCalibrationPlanId
     * @param response
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExaminePlanServiceImpl.java
@@ -45,7 +45,10 @@
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;
@@ -346,4 +349,170 @@
        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.<DeviceExaminePlan>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<Device> 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.<DeviceExaminePlanDetails>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();
    }
}
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExamineRecordContrastServiceImpl.java
@@ -15,6 +15,7 @@
import com.ruoyi.device.pojo.DeviceExaminePlanDetails;
import com.ruoyi.device.pojo.DeviceExamineRecordContrast;
import com.ruoyi.device.pojo.DeviceExamineRecordContrastDetails;
import com.ruoyi.device.service.DeviceExaminePlanService;
import com.ruoyi.device.service.DeviceExamineRecordContrastDetailsService;
import com.ruoyi.device.service.DeviceExamineRecordContrastService;
import com.ruoyi.framework.exception.ErrorException;
@@ -49,6 +50,8 @@
    @Resource
    private DeviceExamineRecordContrastDetailsService deviceExamineRecordContrastDetailsService;
    @Resource
    private DeviceExaminePlanService deviceExaminePlanService;
    @Resource
    private DeviceExaminePlanDetailsMapper deviceExaminePlanDetailsMapper;
    @Resource
@@ -135,6 +138,10 @@
        }
        this.update(wrapper);
        // å®¡æ ¸é€šè¿‡åŽï¼Œè®¾å¤‡æ¡£æ¡ˆçš„æœ€è¿‘/下次核查日期进入下一轮
        if (dto.getReviewStatus() != null && dto.getReviewStatus() == 1) {
            deviceExaminePlanService.updateDeviceExamineDate(dto.getPlanDetailsId());
        }
        return true;
    }
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExamineRecordServiceImpl.java
@@ -20,6 +20,7 @@
import com.ruoyi.device.pojo.DeviceExaminePlanDetails;
import com.ruoyi.device.pojo.DeviceExamineRecord;
import com.ruoyi.device.pojo.DeviceExamineRecordDetail;
import com.ruoyi.device.service.DeviceExaminePlanService;
import com.ruoyi.device.service.DeviceExamineRecordDetailService;
import com.ruoyi.device.service.DeviceExamineRecordService;
import com.ruoyi.framework.exception.ErrorException;
@@ -53,6 +54,8 @@
    @Resource
    private DeviceExamineRecordDetailService deviceExamineRecordDetailService;
    @Resource
    private DeviceExaminePlanService deviceExaminePlanService;
    @Resource
    private DeviceExaminePlanDetailsMapper deviceExaminePlanDetailsMapper;
    @Resource
@@ -186,6 +189,10 @@
        }
        this.update(wrapper);
        // å¤æ ¸é€šè¿‡åŽï¼Œè®¾å¤‡æ¡£æ¡ˆçš„æœ€è¿‘/下次核查日期进入下一轮
        if (dto.getReviewStatus() != null && dto.getReviewStatus() == 1) {
            deviceExaminePlanService.updateDeviceExamineDate(dto.getPlanDetailsId());
        }
        return true;
    }
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceExternalApplyServiceImpl.java
@@ -6,11 +6,13 @@
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.mapper.DeviceExternalApplyMapper;
import com.ruoyi.device.pojo.DeviceExternalApply;
import com.ruoyi.device.service.DeviceExternalApplyService;
import com.ruoyi.device.util.DeviceFlowPermissionUtils;
import com.ruoyi.inspect.util.UserUtils;
import com.ruoyi.system.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
@@ -59,6 +61,7 @@
        DeviceExternalApply apply = new DeviceExternalApply();
        // å½“前登录用户信息和部门
        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
        checkFlowPermission(deviceExternalApply, user);
        switch (deviceExternalApply.getFlowType()) {
            case 0:
                BeanUtils.copyProperties(deviceExternalApply, apply);
@@ -67,11 +70,6 @@
                apply.setApplicantUserId(user.getId());
                apply.setApplicantUser(user.getName());
                apply.setApplicantDate(LocalDate.now());
                // å¤„理人信息
                User departmentHeadUser = userMapper.selectById(deviceExternalApply.getDepartmentHeadUserId());
                apply.setApplicantUserId(departmentHeadUser.getId());
                apply.setApplicantUser(departmentHeadUser.getName());
                baseMapper.insert(apply);
                break;
@@ -115,6 +113,40 @@
        return true;
    }
    private void checkFlowPermission(DeviceExternalApply deviceExternalApply, User user) {
        Integer flowType = deviceExternalApply.getFlowType();
        Integer externalApplyId = deviceExternalApply.getExternalApplyId();
        if (flowType != null && flowType > 0) {
            DeviceExternalApply existing = baseMapper.selectById(externalApplyId);
            if (existing == null) {
                throw new ServiceException("单据不存在");
            }
            DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
            Integer expectedUserId;
            switch (flowType) {
                case 1:
                    expectedUserId = existing.getDepartmentHeadUserId();
                    break;
                case 2:
                    expectedUserId = existing.getMeteringRoomUserId();
                    break;
                case 3:
                    expectedUserId = existing.getApproverUserId();
                    break;
                default:
                    expectedUserId = null;
                    break;
            }
            DeviceFlowPermissionUtils.checkResponsible(expectedUserId, user.getId());
        } else if (externalApplyId != null) {
            DeviceExternalApply existing = baseMapper.selectById(externalApplyId);
            if (existing != null) {
                DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
                DeviceFlowPermissionUtils.checkResponsible(existing.getApplicantUserId(), user.getId());
            }
        }
    }
    /**
     * å¯¼å‡ºåˆ©ç”¨å¤–部设备申请
     *
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceImpowerServiceImpl.java
@@ -10,6 +10,7 @@
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.exception.ServiceException;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.WxCpUtils;
@@ -233,6 +234,17 @@
     */
    @Override
    public Result reviewImpowerStatus(DeviceImpowerDto deviceImpowerDto) {
        DeviceImpower existing = baseMapper.selectById(deviceImpowerDto.getImpowerId());
        if (existing == null) {
            throw new ServiceException("单据不存在");
        }
        if (existing.getStatus() != null && existing.getStatus() == 1) {
            throw new ServiceException("该单据已审核完成,不能再操作");
        }
        if (existing.getAuditId() != null
                && !existing.getAuditId().equals(SecurityUtils.getUserId().intValue())) {
            throw new ServiceException("您不是当前审核人,无权操作");
        }
        LambdaUpdateWrapper<DeviceImpower> wrapper = Wrappers.<DeviceImpower>lambdaUpdate()
                .eq(DeviceImpower::getImpowerId, deviceImpowerDto.getImpowerId())
                .set(DeviceImpower::getAuditDate, LocalDateTime.now())
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java
@@ -2,6 +2,7 @@
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;
@@ -18,6 +19,8 @@
import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
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.DeviceMaintenancePlan;
import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
import com.ruoyi.device.service.DeviceMaintenancePlanDetailsService;
@@ -30,13 +33,16 @@
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
@@ -50,6 +56,7 @@
 * @since 2024-12-16 06:10:52
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class DeviceMaintenancePlanServiceImpl extends ServiceImpl<DeviceMaintenancePlanMapper, DeviceMaintenancePlan> implements DeviceMaintenancePlanService {
    @Resource
@@ -62,6 +69,8 @@
    private InformationNotificationService informationNotificationService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DeviceMapper deviceMapper;
    /**
     * åˆ†é¡µæŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’
@@ -288,5 +297,157 @@
        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 void syncDeviceMaintenanceDetail(Device device) {
        LocalDate remind = calcMaintenanceRemindDate(device);
        if (remind == null || remind.isAfter(LocalDate.now())) {
            return;
        }
        DeviceMaintenancePlan plan = getOrCreatePlanEntity(String.valueOf(remind.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);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢è®¾å¤‡ä¿å…»è®¡åˆ’详情(含到点标红)
     */
    @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;
    }
    /**
     * æ‰«ææ‰€æœ‰è®¾å¤‡ï¼ŒæŠŠå·²åˆ°ç‚¹ï¼ˆä¸‹æ¬¡ä¿å…»æ—¥æœŸå‰1个月起,含已过期)的设备补进本年度计划
     */
    private void ensureMaintenanceDetails(DeviceMaintenancePlan plan) {
        List<Device> devices = deviceMapper.selectList(null);
        for (Device device : devices) {
            fillMaintenanceDetailIfAbsent(plan, device);
        }
    }
    /**
     * è®¾å¤‡æé†’起点:下次保养日期前1个月(没填下次日期时用最近日期+保养周期兜底)
     */
    private LocalDate calcMaintenanceRemindDate(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().minusMonths(1);
    }
    /**
     * å·²åˆ°ç‚¹ä¸”计划中尚无该设备本轮明细时自动补一条
     */
    private void fillMaintenanceDetailIfAbsent(DeviceMaintenancePlan plan, Device device) {
        LocalDate remind = calcMaintenanceRemindDate(device);
        if (remind == null || remind.isAfter(LocalDate.now())) {
            return;
        }
        if (plan.getPlanYear() != null && String.valueOf(remind.getYear()).compareTo(plan.getPlanYear()) > 0) {
            return;
        }
        Long exist = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
                .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId())
                .eq(DeviceMaintenancePlanDetails::getDeviceId, device.getId())
                .eq(DeviceMaintenancePlanDetails::getMaintenanceDate, remind));
        if (exist != null && exist > 0) {
            return;
        }
        DeviceMaintenancePlanDetails detail = new DeviceMaintenancePlanDetails();
        detail.setMaintenancePlanId(plan.getMaintenancePlanId());
        detail.setDeviceId(device.getId());
        detail.setMaintenanceIntervals(device.getMaintenanceCycle());
        detail.setMaintenanceDate(remind);
        deviceMaintenancePlanDetailsMapper.insert(detail);
    }
}
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceScrappedServiceImpl.java
@@ -7,6 +7,7 @@
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.dto.DeviceScrappedDto;
@@ -15,6 +16,7 @@
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceScrapped;
import com.ruoyi.device.service.DeviceScrappedService;
import com.ruoyi.device.util.DeviceFlowPermissionUtils;
import com.ruoyi.inspect.util.UserUtils;
import com.ruoyi.system.mapper.UserMapper;
import org.springframework.beans.BeanUtils;
@@ -66,6 +68,7 @@
        DeviceScrapped scrapped = new DeviceScrapped();
        // å½“前登录用户信息和部门
        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
        checkFlowPermission(deviceScrapped, user);
        switch (deviceScrapped.getFlowType()) {
            case 0:
                BeanUtils.copyProperties(deviceScrapped, scrapped);
@@ -74,11 +77,6 @@
                scrapped.setApplicantUserId(user.getId());
                scrapped.setApplicantUser(user.getName());
                scrapped.setApplicantDate(LocalDate.now());
                // å¤„理人信息
                User departmentHeadUser = userMapper.selectById(deviceScrapped.getDepartmentHeadUserId());
                scrapped.setApplicantUserId(departmentHeadUser.getId());
                scrapped.setApplicantUser(departmentHeadUser.getName());
                baseMapper.insert(scrapped);
                break;
@@ -121,6 +119,40 @@
        return true;
    }
    private void checkFlowPermission(DeviceScrapped deviceScrapped, User user) {
        Integer flowType = deviceScrapped.getFlowType();
        Integer scrappedId = deviceScrapped.getScrappedId();
        if (flowType != null && flowType > 0) {
            DeviceScrapped existing = baseMapper.selectById(scrappedId);
            if (existing == null) {
                throw new ServiceException("单据不存在");
            }
            DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
            Integer expectedUserId;
            switch (flowType) {
                case 1:
                    expectedUserId = existing.getDepartmentHeadUserId();
                    break;
                case 2:
                    expectedUserId = existing.getMeteringRoomUserId();
                    break;
                case 3:
                    expectedUserId = existing.getApproverUserId();
                    break;
                default:
                    expectedUserId = null;
                    break;
            }
            DeviceFlowPermissionUtils.checkResponsible(expectedUserId, user.getId());
        } else if (scrappedId != null) {
            DeviceScrapped existing = baseMapper.selectById(scrappedId);
            if (existing != null) {
                DeviceFlowPermissionUtils.checkFinished(existing.getIsFinish());
                DeviceFlowPermissionUtils.checkResponsible(existing.getApplicantUserId(), user.getId());
            }
        }
    }
    /**
     * å¯¼å‡ºè®¾å¤‡æŠ¥åºŸç”³è¯·
     *
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceServiceImpl.java
@@ -25,15 +25,14 @@
import com.ruoyi.device.mapper.CollectBridgeMapper;
import com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper;
import com.ruoyi.device.mapper.DeviceCalibrationPlanMapper;
import com.ruoyi.device.mapper.DeviceExaminePlanDetailsMapper;
import com.ruoyi.device.mapper.DeviceExaminePlanMapper;
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.mapper.DeviceMetricRecordMapper;
import com.ruoyi.device.pojo.*;
import com.ruoyi.device.service.DataConfigService;
import com.ruoyi.device.service.DeviceCalibrationPlanService;
import com.ruoyi.device.service.DeviceExaminePlanService;
import com.ruoyi.device.service.DeviceMaintenancePlanService;
import com.ruoyi.device.service.DeviceService;
import com.ruoyi.device.service.DeviceDocumentsService;
import com.ruoyi.device.utils.DataAcquisition;
@@ -89,17 +88,11 @@
    private CollectBridgeMapper collectBridgeMapper;
    private DeviceCalibrationPlanMapper deviceCalibrationPlanMapper;
    private DeviceCalibrationPlanService deviceCalibrationPlanService;
    private DeviceCalibrationPlanDetailMapper deviceCalibrationPlanDetailMapper;
    private DeviceExaminePlanService deviceExaminePlanService;
    private DeviceExaminePlanMapper deviceExaminePlanMapper;
    private DeviceExaminePlanDetailsMapper deviceExaminePlanDetailsMapper;
    private DeviceMaintenancePlanMapper deviceMaintenancePlanMapper;
    private DeviceMaintenancePlanDetailsMapper deviceMaintenancePlanDetailsMapper;
    private DeviceMaintenancePlanService deviceMaintenancePlanService;
    @Override
    public IPage<DeviceDto> selectDeviceParameter(Page page, DeviceDto itemParameter, Boolean laboratoryNameIsNull) {
@@ -121,153 +114,22 @@
    @Override
    public int upDeviceParameter(Device itemParameter) {
        int rows = deviceMapper.updateById(itemParameter);
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†æ ¡å‡†æœ‰æ•ˆæœŸå’Œæ ¡å‡†å‘¨æœŸåŽï¼Œè‡ªåŠ¨åŒæ­¥åˆ°è®¾å¤‡æ ¡å‡†è®¡åˆ’
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†æ ¡å‡†ä¿¡æ¯åŽï¼Œåˆ°ç‚¹çš„自动同步到设备校准计划
        Device device = deviceMapper.selectById(itemParameter.getId());
        if (device != null && device.getActivationDate() != null && StringUtils.isNotBlank(device.getCalibrationDate())) {
            syncCalibrationPlanDetail(device);
        if (device != null && (device.getNextCalibrationDate() != null || device.getActivationDate() != null)) {
            deviceCalibrationPlanService.syncDeviceCalibrationDetail(device);
        }
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†æ ¸æŸ¥ä¿¡æ¯åŽï¼Œè‡ªåŠ¨åŒæ­¥åˆ°è®¾å¤‡æ ¸æŸ¥è®¡åˆ’
        if (device != null && device.getNextExamineDate() != null && StringUtils.isNotBlank(device.getExamineCycle())) {
            syncExaminePlanDetail(device);
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†æ ¸æŸ¥ä¿¡æ¯åŽï¼Œåˆ°ç‚¹çš„自动同步到设备核查计划
        if (device != null && (device.getNextExamineDate() != null
                || (device.getLastExamineDate() != null && StringUtils.isNotBlank(device.getExamineCycle())))) {
            deviceExaminePlanService.syncDeviceExamineDetail(device);
        }
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†ä¿å…»ä¿¡æ¯åŽï¼Œè‡ªåŠ¨åŒæ­¥åˆ°è®¾å¤‡ä¿å…»è®¡åˆ’
        if (device != null && device.getNextMaintenanceDate() != null && StringUtils.isNotBlank(device.getMaintenanceCycle())) {
            syncMaintenancePlanDetail(device);
        // æ¡£æ¡ˆä¿®è®¢å¡«å†™äº†ä¿å…»ä¿¡æ¯åŽï¼Œåˆ°ç‚¹çš„自动同步到设备保养计划
        if (device != null && (device.getNextMaintenanceDate() != null
                || (device.getLastMaintenanceDate() != null && StringUtils.isNotBlank(device.getMaintenanceCycle())))) {
            deviceMaintenancePlanService.syncDeviceMaintenanceDetail(device);
        }
        return rows;
    }
    /**
     * æŠŠè®¾å¤‡åŒæ­¥åˆ°ä»Šå¹´çš„设备校准计划明细:本年计划校准时间 = æ ¡å‡†æœ‰æ•ˆæœŸå‰ä¸€ä¸ªæœˆ
     */
    private void syncCalibrationPlanDetail(Device device) {
        String planYear = String.valueOf(LocalDate.now().getYear());
        DeviceCalibrationPlan plan = deviceCalibrationPlanMapper.selectOne(Wrappers.<DeviceCalibrationPlan>lambdaQuery()
                .eq(DeviceCalibrationPlan::getPlanYear, planYear)
                .and(w -> w.isNull(DeviceCalibrationPlan::getRatifyStatus).or().ne(DeviceCalibrationPlan::getRatifyStatus, 1))
                .orderByDesc(DeviceCalibrationPlan::getPlanId)
                .last("limit 1"));
        if (plan == null) {
            plan = new DeviceCalibrationPlan();
            plan.setPlanName(planYear + "年度设备校准计划");
            plan.setPlanYear(planYear);
            plan.setWriteUserId(SecurityUtils.getUserId().intValue());
            plan.setWriteTime(LocalDateTime.now());
            deviceCalibrationPlanMapper.insert(plan);
        }
        DeviceCalibrationPlanDetail detail = deviceCalibrationPlanDetailMapper.selectOne(Wrappers.<DeviceCalibrationPlanDetail>lambdaQuery()
                .eq(DeviceCalibrationPlanDetail::getPlanId, plan.getPlanId())
                .eq(DeviceCalibrationPlanDetail::getDeviceId, device.getId())
                .last("limit 1"));
        boolean isInsert = detail == null;
        if (isInsert) {
            detail = new DeviceCalibrationPlanDetail();
            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.setDeviceAmount("1");
        detail.setDeviceNumber(device.getManagementNumber());
        detail.setVerificationUnit(device.getCalibrationServices());
        detail.setVerificationCycles(device.getCalibrationDate());
        detail.setLastDate(device.getLastCalibrationDate() == null ? null : device.getLastCalibrationDate().toLocalDate());
        detail.setPlanDate(device.getActivationDate().toLocalDate().minusMonths(1));
        if (isInsert) {
            deviceCalibrationPlanDetailMapper.insert(detail);
        } else {
            deviceCalibrationPlanDetailMapper.updateById(detail);
        }
    }
    /**
     * æŠŠè®¾å¤‡åŒæ­¥åˆ°ä»Šå¹´çš„设备核查计划明细:核查时间 = ä¸‹æ¬¡æ ¸æŸ¥æ—¥æœŸå‰ä¸€ä¸ªæœˆ
     */
    private void syncExaminePlanDetail(Device device) {
        String planYear = String.valueOf(LocalDate.now().getYear());
        DeviceExaminePlan plan = deviceExaminePlanMapper.selectOne(Wrappers.<DeviceExaminePlan>lambdaQuery()
                .eq(DeviceExaminePlan::getPlanYear, planYear)
                .and(w -> w.isNull(DeviceExaminePlan::getRatifyStatus).or().ne(DeviceExaminePlan::getRatifyStatus, 1))
                .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());
            deviceExaminePlanMapper.insert(plan);
        }
        DeviceExaminePlanDetails detail = deviceExaminePlanDetailsMapper.selectOne(Wrappers.<DeviceExaminePlanDetails>lambdaQuery()
                .eq(DeviceExaminePlanDetails::getPlanId, plan.getPlanId())
                .eq(DeviceExaminePlanDetails::getDeviceId, device.getId())
                .last("limit 1"));
        boolean isInsert = detail == null;
        if (isInsert) {
            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(device.getNextExamineDate().toLocalDate().minusMonths(1).toString());
        if (isInsert) {
            deviceExaminePlanDetailsMapper.insert(detail);
        } else {
            deviceExaminePlanDetailsMapper.updateById(detail);
        }
    }
    /**
     * æŠŠè®¾å¤‡åŒæ­¥åˆ°ä»Šå¹´çš„设备保养计划明细:计划保养时间 = ä¸‹æ¬¡ä¿å…»æ—¥æœŸå‰ä¸€ä¸ªæœˆ
     */
    private void syncMaintenancePlanDetail(Device device) {
        String planYear = String.valueOf(LocalDate.now().getYear());
        DeviceMaintenancePlan plan = deviceMaintenancePlanMapper.selectOne(Wrappers.<DeviceMaintenancePlan>lambdaQuery()
                .eq(DeviceMaintenancePlan::getPlanYear, planYear)
                .and(w -> w.isNull(DeviceMaintenancePlan::getStatus).or().ne(DeviceMaintenancePlan::getStatus, 1))
                .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);
            if (user != null) {
                plan.setCompiler(user.getName());
            }
            plan.setDatePreparation(LocalDateTime.now());
            deviceMaintenancePlanMapper.insert(plan);
        }
        DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectOne(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
                .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId())
                .eq(DeviceMaintenancePlanDetails::getDeviceId, device.getId())
                .last("limit 1"));
        boolean isInsert = detail == null;
        if (isInsert) {
            detail = new DeviceMaintenancePlanDetails();
            detail.setMaintenancePlanId(plan.getMaintenancePlanId());
            detail.setDeviceId(device.getId());
        }
        detail.setMaintenanceIntervals(device.getMaintenanceCycle());
        detail.setMaintenanceDate(device.getNextMaintenanceDate().toLocalDate().minusMonths(1));
        if (isInsert) {
            deviceMaintenancePlanDetailsMapper.insert(detail);
        } else {
            deviceMaintenancePlanDetailsMapper.updateById(detail);
        }
    }
    @Override
cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceStateServiceImpl.java
@@ -9,12 +9,19 @@
import com.deepoove.poi.config.Configure;
import com.ruoyi.basic.mapper.LaboratoryMapper;
import com.ruoyi.basic.pojo.Laboratory;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.device.dto.DeviceStateDto;
import com.ruoyi.device.mapper.DeviceMapper;
import com.ruoyi.device.mapper.DeviceStateMapper;
import com.ruoyi.device.pojo.Device;
import com.ruoyi.device.pojo.DeviceState;
import com.ruoyi.device.service.DeviceStateService;
import com.ruoyi.inspect.util.UserUtils;
import com.ruoyi.system.mapper.UserMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
@@ -37,6 +44,59 @@
    @Resource
    private LaboratoryMapper laboratoryMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private DeviceMapper deviceMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveDeviceState(DeviceState deviceState) {
        User user = userMapper.selectById(SecurityUtils.getUserId().intValue());
        if (deviceState.getStateId() != null) {
            DeviceState existing = baseMapper.selectById(deviceState.getStateId());
            if (existing == null) {
                throw new ServiceException("单据不存在");
            }
            if ("关闭".equals(existing.getCurrentState())) {
                throw new ServiceException("该流程已结束,不能再操作");
            }
            if (StringUtils.isNotBlank(existing.getCurrentResponsible())
                    && !existing.getCurrentResponsible().equals(user.getName())) {
                throw new ServiceException("您不是当前环节责任人,无权操作");
            }
        }
        boolean saved = saveOrUpdate(deviceState);
        if (saved && "关闭".equals(deviceState.getCurrentState())) {
            updateDeviceStatus(deviceState);
        }
        return saved;
    }
    /**
     * æµç¨‹æœ€ç»ˆæ‰¹å‡†åŽï¼ŒæŠŠåœç”¨/启用结果同步到设备档案状态
     */
    private void updateDeviceStatus(DeviceState deviceState) {
        if (deviceState.getDeviceId() == null) {
            return;
        }
        Device device = deviceMapper.selectById(deviceState.getDeviceId());
        if (device == null) {
            return;
        }
        Integer status = null;
        if ("0scrap".equals(deviceState.getDeviceStatus())) {
            status = 3;
        } else if ("1startUsing".equals(deviceState.getDeviceStatus())) {
            status = 0;
        } else if ("2stopUsing".equals(deviceState.getDeviceStatus())) {
            status = 2;
        }
        if (status != null) {
            device.setDeviceStatus(status);
            deviceMapper.updateById(device);
        }
    }
    @Override
    public IPage<DeviceStateDto> getDeviceStatePage(Integer deviceId, Page page, String processNumber) {
@@ -86,7 +146,7 @@
        try {
            response.setContentType("application/msword");
            String fileName = URLEncoder.encode(
                    "设备维护保养记录", "UTF-8");
                    "设备停用启用记录", "UTF-8");
            response.setHeader("Content-disposition",
                    "attachment;filename=" + fileName + ".docx");
            OutputStream os = response.getOutputStream();
cnas-device/src/main/java/com/ruoyi/device/util/DeviceFlowPermissionUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.device.util;
import com.ruoyi.common.exception.ServiceException;
/**
 * è®¾å¤‡æµç¨‹æƒé™æ ¡éªŒ
 */
public final class DeviceFlowPermissionUtils {
    private DeviceFlowPermissionUtils() {
    }
    /**
     * æ ¡éªŒå½“前用户是否为指定环节责任人,历史单据未指定责任人时放行
     */
    public static void checkResponsible(Integer expectedUserId, Integer currentUserId) {
        if (expectedUserId != null && !expectedUserId.equals(currentUserId)) {
            throw new ServiceException("您不是当前环节责任人,无权操作");
        }
    }
    /**
     * æµç¨‹ç»“束后禁止再次操作
     */
    public static void checkFinished(Integer isFinish) {
        if (isFinish != null && isFinish == 1) {
            throw new ServiceException("该单据已审批完成,不能再操作");
        }
    }
}
cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml
@@ -3,11 +3,16 @@
<mapper namespace="com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper">
    <!-- è®¾å¤‡æ ¡å‡†è®¡åˆ’详情列表 -->
    <select id="pageDeviceCalibrationPlanDetail" resultType="com.ruoyi.device.pojo.DeviceCalibrationPlanDetail">
    <select id="pageDeviceCalibrationPlanDetail" resultType="com.ruoyi.device.dto.DeviceCalibrationPlanDetailDto">
        select * from (
        select *
        from device_calibration_plan_detail
        order by plan_date asc
        select t.*,
        case when d.next_calibration_date is not null
        and d.next_calibration_date &lt;= date_add(curdate(), interval 1 month)
        and (d.last_calibration_date is null or d.last_calibration_date &lt; t.plan_date)
        then 1 else 0 end as red_flag
        from device_calibration_plan_detail t
        left join device d on d.id = t.device_id
        order by t.plan_date asc
        ) a
        <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
            ${ew.customSqlSegment}
cnas-device/src/main/resources/mapper/DeviceExaminePlanDetailsMapper.xml
@@ -33,11 +33,16 @@
        WHEN dd.review_status = 0 THEN 3
        WHEN dd.review_status = 1 THEN 2
        WHEN dd.record_contrast_id IS NOT NULL THEN 1
        ELSE 0 END record_contrast_status
        ELSE 0 END record_contrast_status,
        CASE WHEN dev.next_examine_date IS NOT NULL
            AND dev.next_examine_date &lt;= date_add(curdate(), interval 1 month)
            AND (dev.last_examine_date IS NULL OR dev.last_examine_date &lt; str_to_date(concat(left(d.check_time, 7), '-01'), '%Y-%m-%d'))
            THEN 1 ELSE 0 END red_flag
        from device_examine_plan_details d
        left join user u on u.id = d.check_charger_user_id
        left join device_examine_record dc on d.plan_details_id = dc.plan_details_id
        left join device_examine_record_contrast dd on dd.plan_details_id = dc.plan_details_id
        left join device_examine_record_contrast dd on dd.plan_details_id = d.plan_details_id
        left join device dev on dev.id = d.device_id
        order by check_time asc
        ) a
        <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml
@@ -17,4 +17,29 @@
        where dmpd.maintenance_plan_id = #{maintenancePlanId}
    </select>
    <!-- è®¾å¤‡ä¿å…»è®¡åˆ’详情列表(含到点标红) -->
    <select id="pageMaintenancePlanDetail" resultType="com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto">
        select
            dmpd.*,
            d.device_name,
            d.management_number device_number,
            d.specification_model,
            d.management_number,
            d.storage_point,
            case when d.next_maintenance_date is not null
                and d.next_maintenance_date &lt;= date_add(curdate(), interval 1 month)
                and (d.last_maintenance_date is null or d.last_maintenance_date &lt; dmpd.maintenance_date)
                then 1 else 0 end as red_flag
        from device_maintenance_plan_details dmpd
        left join device d on dmpd.device_id = d.id
        where dmpd.maintenance_plan_id = #{planId}
        <if test="deviceName != null and deviceName != ''">
            and d.device_name like concat('%', #{deviceName}, '%')
        </if>
        <if test="deviceNumber != null and deviceNumber != ''">
            and d.management_number like concat('%', #{deviceNumber}, '%')
        </if>
        order by dmpd.maintenance_date asc
    </select>
</mapper>
cnas-device/src/main/resources/mapper/DeviceMapper.xml
@@ -97,7 +97,7 @@
        cmr.calibration_date as last_calibration_date_two,
        cmr.next_calibration_date as next_calibration_date_two,
        GROUP_CONCAT(distinct inspection_item SEPARATOR ';') AS insProductItem,
        u.name ,
        u.name AS equipment_manager_user,
        sd.dept_name AS laboratory_name
        FROM
        device d
cnas-require/src/main/java/com/ruoyi/require/pojo/FeStandardSubstance.java
@@ -52,6 +52,10 @@
    @Schema(description = "数量")
    private Long quantity;
    @Schema(description = "剩余数量")
    @TableField(exist = false)
    private Long remainingQuantity;
    @Schema(description = "单位")
    private String unit;
cnas-require/src/main/resources/mapper/FeStandardSubstanceMapper.xml
@@ -24,33 +24,41 @@
        <result column="update_time" property="updateTime" />
    </resultMap>
    <select id="getPage" resultType="com.ruoyi.require.pojo.FeStandardSubstance">
        select * from cnas_fe_standard_substance
        select s.*,
               GREATEST(COALESCE(s.quantity, 0) - COALESCE((
                   SELECT SUM(CASE WHEN r.borrow_quantity IS NULL THEN COALESCE(s.quantity, 0)
                                   ELSE r.borrow_quantity END)
                   FROM cnas_fe_standard_substance_record r
                   WHERE r.substance_id = s.id AND COALESCE(r.status, '0') &lt;&gt; '1'
               ), 0), 0) AS remaining_quantity
        from cnas_fe_standard_substance s
        <where>
            <if test="ew.name != null and ew.name != ''">
                name like CONCAT('%', #{ew.name}, '%')
                s.name like CONCAT('%', #{ew.name}, '%')
            </if>
            <if test="ew.factoryManufacturer != null and ew.factoryManufacturer != ''">
                and factory_manufacturer like CONCAT('%', #{ew.factoryManufacturer}, '%')
                and s.factory_manufacturer like CONCAT('%', #{ew.factoryManufacturer}, '%')
            </if>
            <if test="ew.factoryNum != null and ew.factoryNum != ''">
                and factory_num like CONCAT('%', #{factoryNum}, '%')
                and s.factory_num like CONCAT('%', #{ew.factoryNum}, '%')
            </if>
            <if test="ew.manageNum != null and ew.manageNum != ''">
                and manage_num like CONCAT('%', #{ew.manageNum}, '%')
                and s.manage_num like CONCAT('%', #{ew.manageNum}, '%')
            </if>
            <if test="ew.model != null and ew.model != ''">
                and model like CONCAT('%', #{ew.model}, '%')
                and s.model like CONCAT('%', #{ew.model}, '%')
            </if>
            <if test="ew.effectiveDate != null and ew.effectiveDate != ''">
                and effective_date =  #{ew.effectiveDate}
                and s.effective_date =  #{ew.effectiveDate}
            </if>
            <if test="ew.fileNum != null and ew.fileNum != ''">
                and file_num like CONCAT('%', #{ew.fileNum}, '%')
                and s.file_num like CONCAT('%', #{ew.fileNum}, '%')
            </if>
            <if test="ew.position != null and ew.position != ''">
                and position like CONCAT('%', #{ew.position}, '%')
                and s.position like CONCAT('%', #{ew.position}, '%')
            </if>
        </where>
        ORDER BY s.acquisition_date DESC, s.id DESC
    </select>
</mapper>
ruoyi-common/src/main/java/com/ruoyi/common/numgen/LambdaUtils.java
@@ -58,12 +58,17 @@
     */
    public static <T> SerializedLambda resolve(SFunction<T, ?> func) {
        Class<?> clazz = func.getClass();
        String canonicalName = clazz.getCanonicalName();
        return Optional.ofNullable(FUNC_CACHE.get(canonicalName))
        // lambda/方法引用生成的是隐藏类,getCanonicalName() è¿”回 null,ConcurrentHashMap ä¸æŽ¥å— null key
        String cacheKey = clazz.getCanonicalName();
        if (cacheKey == null) {
            cacheKey = clazz.getName();
        }
        final String key = cacheKey;
        return Optional.ofNullable(FUNC_CACHE.get(key))
            .map(WeakReference::get)
            .orElseGet(() -> {
                SerializedLambda lambda = SerializedLambda.resolve(func);
                FUNC_CACHE.put(canonicalName, new WeakReference<>(lambda));
                FUNC_CACHE.put(key, new WeakReference<>(lambda));
                return lambda;
            });
    }
ruoyi-system/src/main/resources/mapper/system/UserMapper.xml
@@ -114,11 +114,24 @@
               name_en,
               status
        from user u
        where depart_lims_id is not null
          and depart_lims_id != ''
          and depart_lims_id = (select u2.depart_lims_id
                                from user u2
                                where u2.id = #{userId})
        where (
            (depart_lims_id is not null
             and depart_lims_id != ''
             and depart_lims_id = (select u2.depart_lims_id
                                   from user u2
                                   where u2.id = #{userId}))
            or
            (
                ((select u3.depart_lims_id
                  from user u3
                  where u3.id = #{userId}) is null
                 or (select u3.depart_lims_id
                     from user u3
                     where u3.id = #{userId}) = '')
                and u.status = '0'
                and u.del_flag = '0'
            )
        )
    </select>
</mapper>