liding
2026-10-24 f8c024b1c1de318a4de485e048d1044cd6dcaa53
feat(search-new):  1.标准查新模块
2.印章使用记录
已添加13个文件
已修改15个文件
1101 ■■■■■ 文件已修改
cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodSearchNewController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/dto/ProcessMethodSearchNewBackupsDto.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/listener/StandardMethodSearchNewListener.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/mapper/ProcessMethodSearchNewHistoryMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/mapper/ProcessMethodSearchNewMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNew.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNewBackups.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNewHistory.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodSearchNewService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodSearchNewServiceImpl.java 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewBackupsMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewHistoryMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/static/excel/check-records.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/controller/SealUsageLedgerController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageCopiesUpdate.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageExportRequest.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageLedgerQuery.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/mapper/SealUsageLedgerMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/pojo/SealUsageLedger.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/service/SealUsageLedgerService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/SealUsageLedgerServiceImpl.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/util/SealUsageLedgerWordExporter.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/resources/mapper/SealUsageLedgerMapper.xml 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodSearchNewController.java
@@ -10,6 +10,7 @@
import com.ruoyi.process.pojo.ProcessMethodSearchNew;
import com.ruoyi.process.pojo.ProcessMethodSearchNewArchived;
import com.ruoyi.process.pojo.ProcessMethodSearchNewBackups;
import com.ruoyi.process.pojo.ProcessMethodSearchNewHistory;
import com.ruoyi.process.service.ProcessMethodSearchNewService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
@@ -76,10 +77,17 @@
        return Result.success(processMethodSearchNewService.pageMethodSearchNew(page, processMethodSearchNew));
    }
    @Operation(summary = "自动归档记录列表")
    @GetMapping("/pageAutoArchived")
    public Result<IPage<ProcessMethodSearchNew>> pageAutoArchived(ProcessMethodSearchNew processMethodSearchNew, Page page) {
        return Result.success(processMethodSearchNewService.pageAutoArchived(page, processMethodSearchNew));
    @Operation(summary = "查询历次标准查新记录")
    @GetMapping("/listSearchNewHistory")
    public Result<List<ProcessMethodSearchNewHistory>> listSearchNewHistory(Integer methodSearchNewId) {
        return Result.success(processMethodSearchNewService.listSearchNewHistory(methodSearchNewId));
    }
    @Operation(summary = "分页查询全部查新记录")
    @GetMapping("/pageSearchNewHistory")
    public Result<IPage<ProcessMethodSearchNewHistory>> pageSearchNewHistory(
            Page<ProcessMethodSearchNewHistory> page, String standardNo) {
        return Result.success(processMethodSearchNewService.pageSearchNewHistory(page, standardNo));
    }
    @Operation(summary = "标准查新导出")
cnas-process/src/main/java/com/ruoyi/process/dto/ProcessMethodSearchNewBackupsDto.java
@@ -32,29 +32,33 @@
    @Schema(description = "批准人日期")
    private String ratifyDate;
    @ExcelProperty(value = {"是否有更新标准", "是否有更新标准"}, index = 4)
    @Schema(description = "是否有更新标准")
    private String isNewStandardString;
    @ExcelProperty(value = {"查新日期", "查新日期"}, index = 4)
    private String searchNewDateString;
    @ExcelProperty(value = {"查新记录", "标准网"}, index = 7)
    @Schema(description = "标准网")
    private String standardNet;
    @ExcelProperty(value = {"查新结果", "查新结果"}, index = 5)
    private String searchNewResultString;
    @ExcelProperty(value = {"查新记录", "委托情报所"}, index = 8)
    @Schema(description = "委托情报所")
    private String informationOffices;
    @ExcelProperty(value = {"查新记录来源", "中国标准化委员会"}, index = 8)
    private String standardizationAdministration;
    @ExcelProperty(value = {"查新记录", "标准书店"}, index = 9)
    @Schema(description = "标准书店")
    private String standardBookstore;
    @ExcelProperty(value = {"查新记录来源", "国家市场监督管理总局"}, index = 9)
    private String marketRegulation;
    @ExcelProperty(value = {"查新记录", "其他"}, index = 10)
    @ExcelProperty(value = {"查新记录来源", "全国标准信息公共服务平台"}, index = 10)
    private String nationalStandardInfo;
    @ExcelProperty(value = {"查新记录来源", "国家标准全文公开系统"}, index = 11)
    private String nationalStandardFullText;
    @ExcelProperty(value = {"查新记录来源", "工标网"}, index = 12)
    private String industryStandard;
    @ExcelProperty(value = {"查新记录来源", "其他"}, index = 13)
    @Schema(description = "其他")
    private String other;
    @ExcelProperty(value = {"备注", "备注"}, index = 11)
    @Schema(description = "备注")
    private String remarkString;
    @ExcelProperty(value = {"其他来源名称", "其他来源名称"}, index = 14)
    private String otherWebsite;
    @ExcelProperty(value = {"序号", "序号"}, index = 0)
    @Schema(description = "序号")
cnas-process/src/main/java/com/ruoyi/process/listener/StandardMethodSearchNewListener.java
@@ -16,7 +16,7 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
/** æ ¹æ®æ ‡å‡†ç»´æŠ¤ä¿å­˜åŠ¨ä½œåŒæ­¥ç”Ÿæˆã€å½’æ¡£æ ‡å‡†æŸ¥æ–°è®°å½•ã€‚ */
/** æ ¹æ®æ ‡å‡†ç»´æŠ¤ä¿å­˜åŠ¨ä½œåŒæ­¥ç”Ÿæˆæˆ–æ›´æ–°çŽ°è¡Œæ ‡å‡†æŸ¥æ–°è®°å½•ã€‚ */
@Component
@RequiredArgsConstructor
public class StandardMethodSearchNewListener {
@@ -44,20 +44,24 @@
        ProcessMethodSearchNew current = searchNewMapper.selectOne(Wrappers.<ProcessMethodSearchNew>lambdaQuery()
                .eq(ProcessMethodSearchNew::getCurrentAutoStandardId, event.getStandardMethodId())
                .last("FOR UPDATE"));
        if (current == null && searchNewMapper.selectCount(Wrappers.<ProcessMethodSearchNew>lambdaQuery()
                .eq(ProcessMethodSearchNew::getStandardMethodId, event.getStandardMethodId())
                .eq(ProcessMethodSearchNew::getIsAutoArchived, 1)) > 0) {
            return;
        }
        if (!event.isEdit() && current != null) {
            return;
        }
        LocalDateTime now = LocalDateTime.now(CHINA_ZONE);
        if (current != null) {
            searchNewMapper.update(null, Wrappers.<ProcessMethodSearchNew>lambdaUpdate()
                    .eq(ProcessMethodSearchNew::getMethodSearchNewId, current.getMethodSearchNewId())
                    .set(ProcessMethodSearchNew::getIsNewStandard, 0)
                    .set(ProcessMethodSearchNew::getIsAutoArchived, 1)
                    .set(ProcessMethodSearchNew::getCurrentAutoStandardId, null)
                    .set(ProcessMethodSearchNew::getAutoArchivedTime, now));
                    .set(ProcessMethodSearchNew::getMethodName, event.getName())
                    .set(ProcessMethodSearchNew::getStandardNo, event.getCode()));
            return;
        }
        LocalDateTime now = LocalDateTime.now(CHINA_ZONE);
        ProcessMethodSearchNew created = new ProcessMethodSearchNew();
        created.setMethodName(event.getName());
        created.setStandardNo(event.getCode());
cnas-process/src/main/java/com/ruoyi/process/mapper/ProcessMethodSearchNewHistoryMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.process.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.process.pojo.ProcessMethodSearchNewHistory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ProcessMethodSearchNewHistoryMapper extends BaseMapper<ProcessMethodSearchNewHistory> {
    List<ProcessMethodSearchNewHistory> selectByMainId(@Param("methodSearchNewId") Integer methodSearchNewId);
    IPage<ProcessMethodSearchNewHistory> pageAll(@Param("page") Page<ProcessMethodSearchNewHistory> page,
                                                 @Param("standardNo") String standardNo);
}
cnas-process/src/main/java/com/ruoyi/process/mapper/ProcessMethodSearchNewMapper.java
@@ -28,8 +28,5 @@
     */
    IPage<ProcessMethodSearchNew> pageMethodSearchNew(@Param("page") Page page, @Param("ew") QueryWrapper<ProcessMethodSearchNewBackupsDto> processMethodSearchNewDtoQueryWrapper, @Param("beginDate") String beginDate, @Param("endDate") String endDate);
    IPage<ProcessMethodSearchNew> pageAutoArchived(@Param("page") Page page,
                                                    @Param("ew") QueryWrapper<ProcessMethodSearchNew> queryWrapper);
}
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNew.java
@@ -2,9 +2,11 @@
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
@@ -43,7 +45,7 @@
    @Schema(description = "新标准号")
    private String newStandardNo;
    @Schema(description = "查新记录来源, 0,标准网, 1委托情报, 2标准数, 3其他")
    @Schema(description = "查新来源:0-2为历史来源,3其他,4中国标准化委员会,5国家市场监督管理总局,6全国标准信息公共服务平台,7国家标准全文公开系统,8工标网")
    private Integer searchNewSource;
    @Schema(description = "查新时间")
@@ -56,10 +58,26 @@
    @Schema(description = "来源为其他时填写的具体网址或网站名称")
    private String searchNewWebsite;
    @Schema(description = "最近一次查新人ID")
    private Integer searchNewUserId;
    @Schema(description = "查新人")
    @TableField(exist = false)
    private String searchNewUserName;
    @Schema(description = "下次查新日期")
    @TableField(exist = false)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextSearchDate;
    @JsonIgnore
    @TableField("next_search_date_override")
    private LocalDate nextSearchDatePersisted;
    @Schema(description = "查新状态:未到期、今日到期、已逾期")
    @TableField(exist = false)
    private String searchDueStatus;
    @Schema(description = "备注,0作废, 1替换")
    private Integer remark;
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNewBackups.java
@@ -9,6 +9,7 @@
import lombok.Setter;
import java.time.LocalDateTime;
import java.time.LocalDate;
/**
 * <p>
@@ -49,17 +50,37 @@
    @Schema(description = "是否是新标准, 0否,1是")
    private Integer isNewStandard;
    @ExcelProperty(value = {"新标准名称", "新标准名称"}, index = 5)
    @ExcelProperty(value = {"新标准名称", "新标准名称"}, index = 6)
    @Schema(description = "新标准名称")
    private String newMethodName;
    @ExcelProperty(value = {"新标准号", "新标准号"}, index =6)
    @ExcelProperty(value = {"新标准号", "新标准号"}, index = 7)
    @Schema(description = "新标准号")
    private String newStandardNo;
    @Schema(description = "查新记录来源, 0,标准网, 1委托情报, 2标准数, 3其他")
    private Integer searchNewSource;
    @Schema(description = "查新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime searchNewTime;
    @Schema(description = "下次查新日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextSearchDate;
    @Schema(description = "查新结果")
    private String searchNewResult;
    @Schema(description = "其他来源网站名称")
    private String searchNewWebsite;
    @Schema(description = "查新人ID")
    private Integer searchNewUserId;
    @TableField(exist = false)
    private String searchNewUserName;
    @Schema(description = "备注,0作废, 1替换")
    private Integer remark;
cnas-process/src/main/java/com/ruoyi/process/pojo/ProcessMethodSearchNewHistory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.process.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@TableName("cnas_process_method_search_new_history")
@Schema(description = "标准历次查新记录")
public class ProcessMethodSearchNewHistory {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Integer methodSearchNewId;
    private Integer standardMethodId;
    private String methodName;
    private String standardNo;
    private String fileNo;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate searchNewDate;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate nextSearchDate;
    private String searchNewResult;
    private Integer searchNewSource;
    private String searchNewWebsite;
    private String newMethodName;
    private String newStandardNo;
    private Integer searchNewUserId;
    @TableField(exist = false)
    private String searchNewUserName;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}
cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodSearchNewService.java
@@ -8,6 +8,7 @@
import com.ruoyi.process.pojo.ProcessMethodSearchNew;
import com.ruoyi.process.pojo.ProcessMethodSearchNewArchived;
import com.ruoyi.process.pojo.ProcessMethodSearchNewBackups;
import com.ruoyi.process.pojo.ProcessMethodSearchNewHistory;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
@@ -44,7 +45,9 @@
     */
    IPage<ProcessMethodSearchNew> pageMethodSearchNew(Page page, ProcessMethodSearchNewBackupsDto processMethodSearchNew);
    IPage<ProcessMethodSearchNew> pageAutoArchived(Page page, ProcessMethodSearchNew processMethodSearchNew);
    List<ProcessMethodSearchNewHistory> listSearchNewHistory(Integer methodSearchNewId);
    IPage<ProcessMethodSearchNewHistory> pageSearchNewHistory(Page<ProcessMethodSearchNewHistory> page, String standardNo);
    /**
     * æ ‡å‡†æŸ¥æ–°å¯¼å‡º
cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodSearchNewServiceImpl.java
@@ -20,10 +20,13 @@
import com.ruoyi.process.dto.ProcessMethodSearchNewBackupsDto;
import com.ruoyi.process.mapper.ProcessMethodSearchNewArchivedMapper;
import com.ruoyi.process.mapper.ProcessMethodSearchNewBackupsMapper;
import com.ruoyi.process.mapper.ProcessMethodSearchNewFileSeqMapper;
import com.ruoyi.process.mapper.ProcessMethodSearchNewMapper;
import com.ruoyi.process.mapper.ProcessMethodSearchNewHistoryMapper;
import com.ruoyi.process.pojo.ProcessMethodSearchNew;
import com.ruoyi.process.pojo.ProcessMethodSearchNewArchived;
import com.ruoyi.process.pojo.ProcessMethodSearchNewBackups;
import com.ruoyi.process.pojo.ProcessMethodSearchNewHistory;
import com.ruoyi.process.service.ProcessMethodSearchNewBackupsService;
import com.ruoyi.process.service.ProcessMethodSearchNewService;
import org.springframework.beans.BeanUtils;
@@ -40,6 +43,8 @@
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@@ -55,12 +60,19 @@
@Transactional(rollbackFor = Exception.class)
public class ProcessMethodSearchNewServiceImpl extends ServiceImpl<ProcessMethodSearchNewMapper, ProcessMethodSearchNew> implements ProcessMethodSearchNewService {
    private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai");
    private static final DateTimeFormatter FILE_DATE = DateTimeFormatter.BASIC_ISO_DATE;
    @Resource
    private ProcessMethodSearchNewArchivedMapper processMethodSearchNewArchivedMapper;
    @Resource
    private ProcessMethodSearchNewBackupsService processMethodSearchNewBackupsService;
    @Resource
    private ProcessMethodSearchNewBackupsMapper processMethodSearchNewBackupsMapper;
    @Resource
    private ProcessMethodSearchNewHistoryMapper processMethodSearchNewHistoryMapper;
    @Resource
    private ProcessMethodSearchNewFileSeqMapper processMethodSearchNewFileSeqMapper;
    /**
     * æ–°å¢žæ ‡å‡†æŸ¥æ–°
@@ -71,18 +83,26 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addMethodSearchNew(List<ProcessMethodSearchNew> processMethodSearchNewList) {
        if (processMethodSearchNewList == null || processMethodSearchNewList.isEmpty()) {
            throw new ErrorException("请填写标准查新记录");
        }
        Integer userId = SecurityUtils.getUserId().intValue();
        for (ProcessMethodSearchNew processMethodSearchNew : processMethodSearchNewList) {
            validateSearchRecord(processMethodSearchNew);
            processMethodSearchNew.setIsNewStandard(1);
            processMethodSearchNew.setIsAutoArchived(0);
            processMethodSearchNew.setStandardMethodId(null);
            processMethodSearchNew.setCurrentAutoStandardId(null);
            processMethodSearchNew.setAutoArchivedTime(null);
            processMethodSearchNew.setSearchNewUserId(userId);
            processMethodSearchNew.setRemark(toLegacyRemark(processMethodSearchNew.getSearchNewResult()));
            if (!Integer.valueOf(3).equals(processMethodSearchNew.getSearchNewSource())) {
                processMethodSearchNew.setSearchNewWebsite(null);
            }
            this.save(processMethodSearchNew);
            insertHistory(processMethodSearchNew, userId);
            handleSearchResult(processMethodSearchNew);
        }
        // æ–°å¢ž
        this.saveBatch(processMethodSearchNewList);
        return true;
    }
@@ -93,24 +113,35 @@
            throw new ErrorException("标准查新记录不存在");
        }
        if (Integer.valueOf(1).equals(old.getIsAutoArchived())) {
            throw new ErrorException("自动归档记录不允许修改");
            throw new ErrorException("已归档标准不能继续查新");
        }
        return update(Wrappers.<ProcessMethodSearchNew>lambdaUpdate()
        processMethodSearchNew.setMethodName(old.getMethodName());
        processMethodSearchNew.setStandardNo(old.getStandardNo());
        processMethodSearchNew.setFileNo(old.getFileNo());
        processMethodSearchNew.setStandardMethodId(old.getStandardMethodId());
        validateSearchRecord(processMethodSearchNew);
        Integer userId = SecurityUtils.getUserId().intValue();
        boolean updated = update(Wrappers.<ProcessMethodSearchNew>lambdaUpdate()
                .eq(ProcessMethodSearchNew::getMethodSearchNewId, old.getMethodSearchNewId())
                .set(ProcessMethodSearchNew::getVerifyId, processMethodSearchNew.getVerifyId())
                .set(ProcessMethodSearchNew::getMethodName, processMethodSearchNew.getMethodName())
                .set(ProcessMethodSearchNew::getStandardNo, processMethodSearchNew.getStandardNo())
                .set(ProcessMethodSearchNew::getFileNo, processMethodSearchNew.getFileNo())
                .set(ProcessMethodSearchNew::getNewMethodName, processMethodSearchNew.getNewMethodName())
                .set(ProcessMethodSearchNew::getNewStandardNo, processMethodSearchNew.getNewStandardNo())
                .set(ProcessMethodSearchNew::getSearchNewSource, processMethodSearchNew.getSearchNewSource())
                .set(ProcessMethodSearchNew::getSearchNewTime, processMethodSearchNew.getSearchNewTime())
                .set(ProcessMethodSearchNew::getNextSearchDatePersisted,
                        processMethodSearchNew.getNextSearchDatePersisted())
                .set(ProcessMethodSearchNew::getSearchNewResult, processMethodSearchNew.getSearchNewResult())
                .set(ProcessMethodSearchNew::getSearchNewUserId, userId)
                .set(ProcessMethodSearchNew::getSearchNewWebsite,
                        Integer.valueOf(3).equals(processMethodSearchNew.getSearchNewSource())
                                ? processMethodSearchNew.getSearchNewWebsite() : null)
                .set(ProcessMethodSearchNew::getRemark, processMethodSearchNew.getRemark())
                .set(ProcessMethodSearchNew::getRemark, toLegacyRemark(processMethodSearchNew.getSearchNewResult()))
                .set(ProcessMethodSearchNew::getIsNewStandard, 1));
        if (updated) {
            ProcessMethodSearchNew latest = getById(old.getMethodSearchNewId());
            insertHistory(latest, userId);
            handleSearchResult(latest);
        }
        return updated;
    }
    @Override
@@ -123,7 +154,13 @@
            throw new ErrorException("标准查新记录不存在");
        }
        if (Integer.valueOf(1).equals(old.getIsAutoArchived())) {
            throw new ErrorException("自动归档记录不允许删除");
            throw new ErrorException("已归档标准不能删除");
        }
        Long historyCount = processMethodSearchNewHistoryMapper.selectCount(
                Wrappers.<ProcessMethodSearchNewHistory>lambdaQuery()
                        .eq(ProcessMethodSearchNewHistory::getMethodSearchNewId, methodSearchNewId));
        if (historyCount != null && historyCount > 0) {
            throw new ErrorException("该标准已有查新记录,不允许删除");
        }
        return removeById(methodSearchNewId);
    }
@@ -140,13 +177,152 @@
        String endDate = processMethodSearchNew.getEndDate();
        processMethodSearchNew.setBeginDate(null);
        processMethodSearchNew.setEndDate(null);
        return baseMapper.pageMethodSearchNew(page, QueryWrappers.queryWrappers(processMethodSearchNew), beginDate, endDate);
        IPage<ProcessMethodSearchNew> result = baseMapper.pageMethodSearchNew(
                page, QueryWrappers.queryWrappers(processMethodSearchNew), beginDate, endDate);
        LocalDate today = LocalDate.now(CHINA_ZONE);
        result.getRecords().forEach(item -> {
            LocalDate nextDate = item.getNextSearchDate();
            if (nextDate == null) {
                item.setSearchDueStatus("待首次查新");
            } else if (today.isAfter(nextDate)) {
                item.setSearchDueStatus("已逾期");
            } else if (today.isEqual(nextDate)) {
                item.setSearchDueStatus("今日到期");
            } else {
                item.setSearchDueStatus("未到期");
            }
        });
        return result;
    }
    @Override
    public IPage<ProcessMethodSearchNew> pageAutoArchived(Page page, ProcessMethodSearchNew processMethodSearchNew) {
        processMethodSearchNew.setIsAutoArchived(null);
        return baseMapper.pageAutoArchived(page, QueryWrappers.queryWrappers(processMethodSearchNew));
    public List<ProcessMethodSearchNewHistory> listSearchNewHistory(Integer methodSearchNewId) {
        if (methodSearchNewId == null) {
            throw new ErrorException("缺少标准查新记录id");
        }
        if (getById(methodSearchNewId) == null) {
            throw new ErrorException("标准查新记录不存在");
        }
        return processMethodSearchNewHistoryMapper.selectByMainId(methodSearchNewId);
    }
    @Override
    public IPage<ProcessMethodSearchNewHistory> pageSearchNewHistory(Page<ProcessMethodSearchNewHistory> page,
                                                                      String standardNo) {
        return processMethodSearchNewHistoryMapper.pageAll(page,
                StringUtils.isBlank(standardNo) ? null : standardNo.trim());
    }
    private void validateSearchRecord(ProcessMethodSearchNew row) {
        if (row == null) {
            throw new ErrorException("请填写标准查新记录");
        }
        if (StringUtils.isBlank(row.getMethodName()) || StringUtils.isBlank(row.getStandardNo())
                || StringUtils.isBlank(row.getFileNo())) {
            throw new ErrorException("请填写标准名称、标准号和文件编号");
        }
        if (row.getSearchNewTime() == null) {
            throw new ErrorException("请选择查新日期");
        }
        row.setSearchNewTime(row.getSearchNewTime().toLocalDate().atStartOfDay());
        if (!"现行有效".equals(row.getSearchNewResult())
                && !"已修订/有新版本替代".equals(row.getSearchNewResult())
                && !"废止(无替代)".equals(row.getSearchNewResult())
                && !"即将实施".equals(row.getSearchNewResult())) {
            throw new ErrorException("请选择有效的查新结果");
        }
        Integer source = row.getSearchNewSource();
        if (source == null || source < 3 || source > 8) {
            throw new ErrorException("请选择有效的查新记录来源");
        }
        if ("已修订/有新版本替代".equals(row.getSearchNewResult())
                && (StringUtils.isBlank(row.getNewMethodName()) || StringUtils.isBlank(row.getNewStandardNo()))) {
            throw new ErrorException("查新结果为已修订/有新版本替代时,请填写新标准名称和新标准号");
        }
        if (!"已修订/有新版本替代".equals(row.getSearchNewResult())) {
            row.setNewMethodName(null);
            row.setNewStandardNo(null);
        }
        if (!Integer.valueOf(3).equals(source)) {
            row.setSearchNewWebsite(null);
        }
        if ("已修订/有新版本替代".equals(row.getSearchNewResult())
                || "废止(无替代)".equals(row.getSearchNewResult())) {
            row.setNextSearchDate(null);
        } else if (row.getNextSearchDate() == null) {
            row.setNextSearchDate(row.getSearchNewTime().toLocalDate().plusMonths(3));
        }
        row.setNextSearchDatePersisted(row.getNextSearchDate());
    }
    private Integer toLegacyRemark(String result) {
        if ("废止(无替代)".equals(result)) return 0;
        if ("已修订/有新版本替代".equals(result)) return 1;
        return null;
    }
    private void insertHistory(ProcessMethodSearchNew row, Integer userId) {
        ProcessMethodSearchNewHistory history = new ProcessMethodSearchNewHistory();
        history.setMethodSearchNewId(row.getMethodSearchNewId());
        history.setStandardMethodId(row.getStandardMethodId());
        history.setMethodName(row.getMethodName());
        history.setStandardNo(row.getStandardNo());
        history.setFileNo(row.getFileNo());
        history.setSearchNewDate(row.getSearchNewTime().toLocalDate());
        history.setNextSearchDate(row.getNextSearchDatePersisted());
        history.setSearchNewResult(row.getSearchNewResult());
        history.setSearchNewSource(row.getSearchNewSource());
        history.setSearchNewWebsite(row.getSearchNewWebsite());
        history.setNewMethodName(row.getNewMethodName());
        history.setNewStandardNo(row.getNewStandardNo());
        history.setSearchNewUserId(userId);
        history.setCreateTime(LocalDateTime.now(CHINA_ZONE));
        processMethodSearchNewHistoryMapper.insert(history);
    }
    /** æ—§æ ‡å‡†åªä¿ç•™åœ¨æŸ¥æ–°è®°å½•;有新版时创建新的主列表记录。 */
    private void handleSearchResult(ProcessMethodSearchNew oldStandard) {
        String result = oldStandard.getSearchNewResult();
        if (!"废止(无替代)".equals(result) && !"已修订/有新版本替代".equals(result)) {
            return;
        }
        LocalDateTime now = LocalDateTime.now(CHINA_ZONE);
        update(Wrappers.<ProcessMethodSearchNew>lambdaUpdate()
                .eq(ProcessMethodSearchNew::getMethodSearchNewId, oldStandard.getMethodSearchNewId())
                .set(ProcessMethodSearchNew::getIsNewStandard, 0)
                .set(ProcessMethodSearchNew::getIsAutoArchived, 1)
                .set(ProcessMethodSearchNew::getCurrentAutoStandardId, null)
                .set(ProcessMethodSearchNew::getAutoArchivedTime, now));
        if (!"已修订/有新版本替代".equals(result)) {
            return;
        }
        if (count(Wrappers.<ProcessMethodSearchNew>lambdaQuery()
                .eq(ProcessMethodSearchNew::getStandardNo, oldStandard.getNewStandardNo())
                .eq(ProcessMethodSearchNew::getIsAutoArchived, 0)) > 0) {
            return;
        }
        ProcessMethodSearchNew replacement = new ProcessMethodSearchNew();
        replacement.setMethodName(oldStandard.getNewMethodName());
        replacement.setStandardNo(oldStandard.getNewStandardNo());
        replacement.setFileNo(nextFileNo(now.toLocalDate()));
        replacement.setIsNewStandard(1);
        replacement.setStandardMethodId(oldStandard.getStandardMethodId());
        replacement.setCurrentAutoStandardId(oldStandard.getCurrentAutoStandardId());
        replacement.setIsAutoArchived(0);
        save(replacement);
    }
    private String nextFileNo(LocalDate date) {
        String prefix = date.format(FILE_DATE);
        processMethodSearchNewFileSeqMapper.insertIfAbsent(date);
        Integer stored = processMethodSearchNewFileSeqMapper.selectForUpdate(date);
        Integer existing = processMethodSearchNewFileSeqMapper.selectMaxExisting(prefix);
        int next = Math.max(stored == null ? 0 : stored, existing == null ? 0 : existing) + 1;
        processMethodSearchNewFileSeqMapper.updateValue(date, next);
        return prefix + String.format("%03d", next);
    }
    /**
@@ -176,33 +352,40 @@
        for (ProcessMethodSearchNewBackups methodSearchNew : methodSearchNewBackups) {
            ProcessMethodSearchNewBackupsDto searchNewBackupsDto = new ProcessMethodSearchNewBackupsDto();
            BeanUtils.copyProperties(methodSearchNew, searchNewBackupsDto);
            // æ˜¯å¦æ˜¯è·Ÿæ–°æ ‡å‡†
            if (methodSearchNew.getIsNewStandard().equals(1)) {
                searchNewBackupsDto.setIsNewStandardString("是");
            } else {
                searchNewBackupsDto.setIsNewStandardString("否");
            }
            // å¤‡æ³¨
            if (methodSearchNew.getRemark() != null) {
                if (methodSearchNew.getRemark().equals(1)) {
                    searchNewBackupsDto.setRemarkString("替换");
                } else {
                    searchNewBackupsDto.setRemarkString("作废");
                }
            }
            searchNewBackupsDto.setSearchNewDateString(methodSearchNew.getSearchNewTime() == null
                    ? null : methodSearchNew.getSearchNewTime().format(timeFormatter));
            searchNewBackupsDto.setSearchNewResultString(methodSearchNew.getSearchNewResult());
            if (methodSearchNew.getSearchNewSource() != null) switch (methodSearchNew.getSearchNewSource()) {
                case 0:
                    searchNewBackupsDto.setStandardNet("√");
                    searchNewBackupsDto.setOther("√");
                    searchNewBackupsDto.setOtherWebsite("标准网(历史来源)");
                    break;
                case 1:
                    searchNewBackupsDto.setInformationOffices("√");
                    searchNewBackupsDto.setOther("√");
                    searchNewBackupsDto.setOtherWebsite("委托情报所(历史来源)");
                    break;
                case 2:
                    searchNewBackupsDto.setStandardBookstore("√");
                    searchNewBackupsDto.setOther("√");
                    searchNewBackupsDto.setOtherWebsite("标准书店(历史来源)");
                    break;
                case 4:
                    searchNewBackupsDto.setStandardizationAdministration("√");
                    break;
                case 5:
                    searchNewBackupsDto.setMarketRegulation("√");
                    break;
                case 6:
                    searchNewBackupsDto.setNationalStandardInfo("√");
                    break;
                case 7:
                    searchNewBackupsDto.setNationalStandardFullText("√");
                    break;
                case 8:
                    searchNewBackupsDto.setIndustryStandard("√");
                    break;
                case 3:
                    searchNewBackupsDto.setOther("√");
                    searchNewBackupsDto.setOtherWebsite(methodSearchNew.getSearchNewWebsite());
                    break;
            }
            searchNewBackupsDto.setIndex(index);
@@ -269,7 +452,7 @@
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode(
                    "原材料检测信息导出", "UTF-8");
                    "标准查新记录", "UTF-8");
            response.setHeader("Content-disposition",
                    "attachment;filename=" + fileName + ".xlsx");
        } catch (Exception e) {
@@ -312,31 +495,39 @@
            List<ProcessMethodSearchNew> collect = searchNewDtoList.stream().map(searchNewDto -> {
                ProcessMethodSearchNew searchNew = new ProcessMethodSearchNew();
                BeanUtils.copyProperties(searchNewDto, searchNew);
                searchNew.setIsNewStandard(1);
                searchNew.setIsAutoArchived(0);
                if (StringUtils.isNotBlank(searchNewDto.getSearchNewDateString())) {
                    String dateText = searchNewDto.getSearchNewDateString().trim();
                    searchNew.setSearchNewTime(LocalDate.parse(dateText.substring(0, Math.min(10, dateText.length()))).atStartOfDay());
                }
                searchNew.setSearchNewResult(searchNewDto.getSearchNewResultString());
                int sourceCount = 0;
                if (StringUtils.isNotBlank(searchNewDto.getStandardizationAdministration())) sourceCount++;
                if (StringUtils.isNotBlank(searchNewDto.getMarketRegulation())) sourceCount++;
                if (StringUtils.isNotBlank(searchNewDto.getNationalStandardInfo())) sourceCount++;
                if (StringUtils.isNotBlank(searchNewDto.getNationalStandardFullText())) sourceCount++;
                if (StringUtils.isNotBlank(searchNewDto.getIndustryStandard())) sourceCount++;
                if (StringUtils.isNotBlank(searchNewDto.getOther())) sourceCount++;
                if (sourceCount != 1) {
                    throw new ErrorException("标准号" + searchNew.getStandardNo() + "必须且只能选择一个查新来源");
                }
                // æŸ¥æ–°è®°å½•
                if (StringUtils.isNotBlank(searchNewDto.getStandardNet())) {
                    searchNew.setSearchNewSource(0);
                } else if (StringUtils.isNotBlank(searchNewDto.getInformationOffices())) {
                    searchNew.setSearchNewSource(1);
                } else if (StringUtils.isNotBlank(searchNewDto.getStandardBookstore())) {
                    searchNew.setSearchNewSource(2);
                if (StringUtils.isNotBlank(searchNewDto.getStandardizationAdministration())) {
                    searchNew.setSearchNewSource(4);
                } else if (StringUtils.isNotBlank(searchNewDto.getMarketRegulation())) {
                    searchNew.setSearchNewSource(5);
                } else if (StringUtils.isNotBlank(searchNewDto.getNationalStandardInfo())) {
                    searchNew.setSearchNewSource(6);
                } else if (StringUtils.isNotBlank(searchNewDto.getNationalStandardFullText())) {
                    searchNew.setSearchNewSource(7);
                } else if (StringUtils.isNotBlank(searchNewDto.getIndustryStandard())) {
                    searchNew.setSearchNewSource(8);
                } else if (StringUtils.isNotBlank(searchNewDto.getOther())) {
                    searchNew.setSearchNewSource(3);
                    searchNew.setSearchNewWebsite(searchNewDto.getOtherWebsite());
                }
                // å¤‡æ³¨
                if (StringUtils.isNotBlank(searchNewDto.getRemarkString())) {
                    if (searchNewDto.getRemarkString().equals("替换")) {
                        searchNew.setRemark(1);
                    } else if (searchNewDto.getRemarkString().equals("作废")) {
                        searchNew.setRemark(0);
                    }
                }
                return searchNew;
            }).collect(Collectors.toList());
            this.saveBatch(collect);
            addMethodSearchNew(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
@@ -365,6 +556,7 @@
        List<ProcessMethodSearchNewBackups> methodSearchNewBackups = searchNewList.stream().map(processMethodSearchNew -> {
            ProcessMethodSearchNewBackups backups = new ProcessMethodSearchNewBackups();
            BeanUtils.copyProperties(processMethodSearchNew, backups);
            backups.setNextSearchDate(processMethodSearchNew.getNextSearchDatePersisted());
            backups.setIsNewStandard(0);
            backups.setArchivedId(archived.getArchivedId());
            return backups;
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewBackupsMapper.xml
@@ -3,7 +3,8 @@
<mapper namespace="com.ruoyi.process.mapper.ProcessMethodSearchNewBackupsMapper">
    <select id="pageSearchNewBackups" resultType="com.ruoyi.process.pojo.ProcessMethodSearchNewBackups">
        select *
        select a.*,
               (select u.name from user u where u.id = a.search_new_user_id) as searchNewUserName
        from (select *
        from cnas_process_method_search_new_backups
        order by create_time desc
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewHistoryMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.ruoyi.process.mapper.ProcessMethodSearchNewHistoryMapper">
    <select id="selectByMainId" resultType="com.ruoyi.process.pojo.ProcessMethodSearchNewHistory">
        SELECT h.*, u.name AS searchNewUserName
        FROM cnas_process_method_search_new_history h
        LEFT JOIN user u ON u.id = h.search_new_user_id
        WHERE h.method_search_new_id = #{methodSearchNewId}
        ORDER BY h.search_new_date DESC, h.create_time DESC, h.id DESC
    </select>
    <select id="pageAll" resultType="com.ruoyi.process.pojo.ProcessMethodSearchNewHistory">
        SELECT h.*, u.name AS searchNewUserName
        FROM cnas_process_method_search_new_history h
        LEFT JOIN user u ON u.id = h.search_new_user_id
        <where>
            <if test="standardNo != null and standardNo != ''">
                h.standard_no LIKE CONCAT('%', #{standardNo}, '%')
            </if>
        </where>
        ORDER BY h.search_new_date DESC, h.create_time DESC, h.id DESC
    </select>
</mapper>
cnas-process/src/main/resources/mapper/ProcessMethodSearchNewMapper.xml
@@ -5,7 +5,11 @@
    <!-- æ ‡å‡†æŸ¥æ–°åˆ†é¡µæŸ¥è¯¢ -->
    <select id="pageMethodSearchNew" resultType="com.ruoyi.process.pojo.ProcessMethodSearchNew">
        select a.*,
               (select u.name from user u where u.id = a.create_user) as searchNewUserName
               CASE WHEN a.search_new_time IS NOT NULL THEN
                   (select u.name from user u where u.id = a.search_new_user_id)
               END as searchNewUserName,
               COALESCE(a.next_search_date_override,
                        DATE_ADD(DATE(COALESCE(a.search_new_time, a.create_time)), INTERVAL 3 MONTH)) as nextSearchDate
        from (select *
        from cnas_process_method_search_new
        <where>
@@ -21,15 +25,4 @@
        </if>
    </select>
    <select id="pageAutoArchived" resultType="com.ruoyi.process.pojo.ProcessMethodSearchNew">
        select *
        from (
            select * from cnas_process_method_search_new
            where is_auto_archived = 1
            order by auto_archived_time desc, method_search_new_id desc
        ) a
        <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
            ${ew.customSqlSegment}
        </if>
    </select>
</mapper>
cnas-process/src/main/resources/static/excel/check-records.xlsx
Binary files differ
inspect-server/src/main/java/com/ruoyi/inspect/controller/SealUsageLedgerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.inspect.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.Result;
import com.ruoyi.framework.exception.ErrorException;
import com.ruoyi.inspect.dto.SealUsageCopiesUpdate;
import com.ruoyi.inspect.dto.SealUsageExportRequest;
import com.ruoyi.inspect.dto.SealUsageLedgerQuery;
import com.ruoyi.inspect.pojo.SealUsageLedger;
import com.ruoyi.inspect.service.SealUsageLedgerService;
import com.ruoyi.inspect.util.SealUsageLedgerWordExporter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@RestController
@RequestMapping("/sealUsageLedger")
@Tag(name = "用印管理台账")
public class SealUsageLedgerController {
    @Resource
    private SealUsageLedgerService ledgerService;
    @Operation(summary = "分页查询用印管理台账")
    @GetMapping("/page")
    @PreAuthorize("@ss.hasPermi('structural:sealUsageLedger:list')")
    public Result page(Page<SealUsageLedger> page, SealUsageLedgerQuery query) {
        return Result.success(ledgerService.page(page, query));
    }
    @Operation(summary = "修改用印份数")
    @PostMapping("/updateCopies")
    @PreAuthorize("@ss.hasPermi('structural:sealUsageLedger:edit')")
    public Result updateCopies(@RequestBody SealUsageCopiesUpdate update) {
        ledgerService.updateCopies(update);
        return Result.success();
    }
    @Operation(summary = "导出印章使用登记表 Word")
    @PostMapping("/exportWord")
    @PreAuthorize("@ss.hasPermi('structural:sealUsageLedger:export')")
    public void exportWord(@RequestBody SealUsageExportRequest request, HttpServletResponse response) {
        List<SealUsageLedger> ledgers = ledgerService.listForExport(request);
        String fileName = "印章使用登记表_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".docx";
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''"
                + URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20"));
        try {
            SealUsageLedgerWordExporter.write(ledgers, response.getOutputStream());
        } catch (IOException e) {
            throw new ErrorException("用印台账导出失败");
        }
    }
}
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageCopiesUpdate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.ruoyi.inspect.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class SealUsageCopiesUpdate implements Serializable {
    private Long id;
    private Integer copies;
    private Integer version;
}
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageExportRequest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.inspect.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class SealUsageExportRequest implements Serializable {
    private String mode;
    private List<Long> ids;
    private SealUsageLedgerQuery query;
}
inspect-server/src/main/java/com/ruoyi/inspect/dto/SealUsageLedgerQuery.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.inspect.dto;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDate;
@Data
public class SealUsageLedgerQuery implements Serializable {
    private String reportCode;
    private String sealName;
    private String projectName;
    private String useUserName;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
}
inspect-server/src/main/java/com/ruoyi/inspect/mapper/SealUsageLedgerMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.inspect.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.inspect.dto.SealUsageLedgerQuery;
import com.ruoyi.inspect.pojo.SealUsageLedger;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface SealUsageLedgerMapper extends BaseMapper<SealUsageLedger> {
    IPage<SealUsageLedger> selectLedgerPage(IPage<SealUsageLedger> page,
                                             @Param("query") SealUsageLedgerQuery query);
    List<SealUsageLedger> selectForExport(@Param("query") SealUsageLedgerQuery query,
                                           @Param("ids") List<Long> ids);
    String selectProjectName(@Param("insOrderId") Integer insOrderId);
    int updateCopies(@Param("id") Long id,
                     @Param("copies") Integer copies,
                     @Param("version") Integer version,
                     @Param("updateUser") Integer updateUser);
}
inspect-server/src/main/java/com/ruoyi/inspect/pojo/SealUsageLedger.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.inspect.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@TableName("seal_usage_ledger")
@Schema(description = "用印管理台账")
public class SealUsageLedger implements Serializable {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Integer reportId;
    private String reportCode;
    private Integer insOrderId;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime useTime;
    private String sealIds;
    private String sealNames;
    private String projectName;
    private Integer useUserId;
    private String useUserName;
    private Integer copies;
    private Integer version;
    @TableField(fill = FieldFill.INSERT)
    private Integer createUser;
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
}
inspect-server/src/main/java/com/ruoyi/inspect/service/SealUsageLedgerService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.inspect.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.inspect.dto.SealUsageCopiesUpdate;
import com.ruoyi.inspect.dto.SealUsageExportRequest;
import com.ruoyi.inspect.dto.SealUsageLedgerQuery;
import com.ruoyi.inspect.pojo.InsReport;
import com.ruoyi.inspect.pojo.SealUsageLedger;
import java.util.List;
public interface SealUsageLedgerService {
    IPage<SealUsageLedger> page(Page<SealUsageLedger> page, SealUsageLedgerQuery query);
    void updateCopies(SealUsageCopiesUpdate update);
    List<SealUsageLedger> listForExport(SealUsageExportRequest request);
    void recordApprovedReport(InsReport report);
}
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
@@ -50,6 +50,7 @@
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.inspect.service.InsReportService;
import com.ruoyi.inspect.service.RawMaterialOrderService;
import com.ruoyi.inspect.service.SealUsageLedgerService;
import com.ruoyi.inspect.util.PreserveReportPicturePlaceholderHandler;
import com.ruoyi.inspect.util.ReportSealLayoutUtil;
import com.ruoyi.inspect.util.ReportSignaturePictureUtils;
@@ -107,6 +108,8 @@
    private UserMapper userMapper;
    @Resource
    private InsReportMapper insReportMapper;
    @Resource
    private SealUsageLedgerService sealUsageLedgerService;
    @Resource
    private InsOrderStateMapper insOrderStateMapper;
    @Resource
@@ -431,7 +434,18 @@
    @Transactional(rollbackFor = Exception.class)
    public int ratifyReport(Integer id, Integer isRatify, String ratifyTell, String sealIds,
                            Integer maSealId, Integer reportSealId, Integer seamSealId) {
        InsReport insReport = insReportMapper.selectById(id);
        if (id == null || (isRatify == null || (isRatify != 0 && isRatify != 1))) {
            throw new ErrorException("批准参数不正确");
        }
        InsReport insReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
                .eq(InsReport::getId, id)
                .last("FOR UPDATE"));
        if (insReport == null) {
            throw new ErrorException("报告不存在");
        }
        if (Objects.equals(insReport.getIsRatify(), 1)) {
            throw new ErrorException("报告已批准,请勿重复操作");
        }
        if (Objects.equals(insReport.getExamineUserId(), SecurityUtils.getUserId().intValue())) {
            throw new ErrorException("审核人和批准人不能为同一人,请重新选择批准人");
        }
@@ -602,7 +616,12 @@
            });
        }
        return insReportMapper.updateById(insReport);
        int affected = insReportMapper.updateById(insReport);
        if (affected != 1) {
            throw new ErrorException("报告批准状态保存失败");
        }
        sealUsageLedgerService.recordApprovedReport(insReport);
        return affected;
    }
    /**
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/SealUsageLedgerServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package com.ruoyi.inspect.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.exception.ErrorException;
import com.ruoyi.inspect.dto.SealUsageCopiesUpdate;
import com.ruoyi.inspect.dto.SealUsageExportRequest;
import com.ruoyi.inspect.dto.SealUsageLedgerQuery;
import com.ruoyi.inspect.mapper.SealUsageLedgerMapper;
import com.ruoyi.inspect.pojo.InsReport;
import com.ruoyi.inspect.pojo.SealUsageLedger;
import com.ruoyi.inspect.service.SealUsageLedgerService;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.system.mapper.UserMapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@Service
public class SealUsageLedgerServiceImpl implements SealUsageLedgerService {
    @Resource
    private SealUsageLedgerMapper ledgerMapper;
    @Resource
    private UserMapper userMapper;
    @Override
    public IPage<SealUsageLedger> page(Page<SealUsageLedger> page, SealUsageLedgerQuery query) {
        validateDateRange(query);
        return ledgerMapper.selectLedgerPage(page, query);
    }
    @Override
    public void updateCopies(SealUsageCopiesUpdate update) {
        if (update == null || update.getId() == null || update.getVersion() == null) {
            throw new ErrorException("缺少台账记录或版本信息");
        }
        if (update.getCopies() == null || update.getCopies() < 1) {
            throw new ErrorException("份数必须为正整数");
        }
        int affected = ledgerMapper.updateCopies(update.getId(), update.getCopies(), update.getVersion(),
                SecurityUtils.getUserId().intValue());
        if (affected != 1) {
            if (ledgerMapper.selectById(update.getId()) == null) {
                throw new ErrorException("用印台账记录不存在");
            }
            throw new ErrorException("记录已被修改,请刷新后重试");
        }
    }
    @Override
    public List<SealUsageLedger> listForExport(SealUsageExportRequest request) {
        if (request == null || StringUtils.isBlank(request.getMode())) {
            throw new ErrorException("请选择导出方式");
        }
        List<SealUsageLedger> result;
        if ("selected".equals(request.getMode())) {
            if (request.getIds() == null || request.getIds().isEmpty()) {
                throw new ErrorException("请选择要导出的记录");
            }
            List<Long> ids = new ArrayList<>(new LinkedHashSet<>(request.getIds()));
            if (ids.stream().anyMatch(Objects::isNull)) {
                throw new ErrorException("导出记录参数不正确");
            }
            result = ledgerMapper.selectForExport(null, ids);
            if (result.size() != ids.size()) {
                throw new ErrorException("部分用印台账记录不存在或无权访问");
            }
        } else if ("filtered".equals(request.getMode())) {
            SealUsageLedgerQuery query = request.getQuery() == null ? new SealUsageLedgerQuery() : request.getQuery();
            validateDateRange(query);
            result = ledgerMapper.selectForExport(query, null);
        } else {
            throw new ErrorException("不支持的导出方式");
        }
        if (result.isEmpty()) {
            throw new ErrorException("暂无可导出的记录");
        }
        return result;
    }
    @Override
    public void recordApprovedReport(InsReport report) {
        if (report == null || report.getId() == null || report.getRatifyTime() == null
                || report.getRatifyUserId() == null || StringUtils.isBlank(report.getCode())
                || StringUtils.isBlank(report.getSealNames())) {
            throw new ErrorException("报告批准信息不完整,无法生成用印台账");
        }
        User ratifyUser = userMapper.selectById(report.getRatifyUserId());
        if (ratifyUser == null || StringUtils.isBlank(ratifyUser.getName())) {
            throw new ErrorException("找不到报告批准人,无法生成用印台账");
        }
        String projectName = StringUtils.defaultString(ledgerMapper.selectProjectName(report.getInsOrderId()));
        SealUsageLedger ledger = new SealUsageLedger();
        ledger.setReportId(report.getId());
        ledger.setReportCode(report.getCode());
        ledger.setInsOrderId(report.getInsOrderId());
        ledger.setUseTime(report.getRatifyTime());
        ledger.setSealIds(report.getSealIds());
        ledger.setSealNames(report.getSealNames());
        ledger.setProjectName(projectName);
        ledger.setUseUserId(report.getRatifyUserId());
        ledger.setUseUserName(ratifyUser.getName());
        ledger.setCopies(1);
        ledger.setVersion(0);
        ledgerMapper.insert(ledger);
    }
    private void validateDateRange(SealUsageLedgerQuery query) {
        if (query != null && query.getStartDate() != null && query.getEndDate() != null
                && query.getStartDate().isAfter(query.getEndDate())) {
            throw new ErrorException("开始日期不能晚于结束日期");
        }
    }
}
inspect-server/src/main/java/com/ruoyi/inspect/util/SealUsageLedgerWordExporter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,114 @@
package com.ruoyi.inspect.util;
import com.ruoyi.inspect.pojo.SealUsageLedger;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.TableRowAlign;
import org.apache.poi.xwpf.usermodel.TextAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageMar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPageSz;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.time.format.DateTimeFormatter;
import java.util.List;
public final class SealUsageLedgerWordExporter {
    private static final String[] HEADERS = {"日期", "印章名称", "检测项目名称", "报告编号", "份数", "用印人签字"};
    private static final int[] WIDTHS = {1200, 2300, 1800, 1900, 800, 1400};
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy.MM.dd");
    private SealUsageLedgerWordExporter() {
    }
    public static void write(List<SealUsageLedger> ledgers, OutputStream outputStream) throws IOException {
        try (XWPFDocument document = new XWPFDocument()) {
            configurePage(document);
            addTitle(document);
            XWPFTable table = document.createTable(ledgers.size() + 1, HEADERS.length);
            table.setStyleID("TableGrid");
            table.setTableAlignment(TableRowAlign.CENTER);
            table.setWidth("100%");
            XWPFTableRow header = table.getRow(0);
            header.setRepeatHeader(true);
            header.setHeight(620);
            for (int col = 0; col < HEADERS.length; col++) {
                formatCell(header.getCell(col), HEADERS[col], true, WIDTHS[col]);
            }
            for (int index = 0; index < ledgers.size(); index++) {
                SealUsageLedger ledger = ledgers.get(index);
                XWPFTableRow row = table.getRow(index + 1);
                row.setHeight(780);
                formatCell(row.getCell(0), ledger.getUseTime().format(DATE_FORMAT), false, WIDTHS[0]);
                formatCell(row.getCell(1), lineBreakSeals(ledger.getSealNames()), false, WIDTHS[1]);
                formatCell(row.getCell(2), ledger.getProjectName(), false, WIDTHS[2]);
                formatCell(row.getCell(3), ledger.getReportCode(), false, WIDTHS[3]);
                formatCell(row.getCell(4), String.valueOf(ledger.getCopies()), false, WIDTHS[4]);
                formatCell(row.getCell(5), ledger.getUseUserName(), false, WIDTHS[5]);
            }
            document.write(outputStream);
        }
    }
    private static void configurePage(XWPFDocument document) {
        CTSectPr section = document.getDocument().getBody().addNewSectPr();
        CTPageSz pageSize = section.addNewPgSz();
        pageSize.setW(BigInteger.valueOf(11906));
        pageSize.setH(BigInteger.valueOf(16838));
        CTPageMar margin = section.addNewPgMar();
        margin.setTop(BigInteger.valueOf(1134));
        margin.setBottom(BigInteger.valueOf(1134));
        margin.setLeft(BigInteger.valueOf(850));
        margin.setRight(BigInteger.valueOf(850));
    }
    private static void addTitle(XWPFDocument document) {
        XWPFParagraph paragraph = document.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        paragraph.setSpacingAfter(220);
        XWPFRun run = paragraph.createRun();
        run.setText("印章使用登记表");
        run.setBold(true);
        run.setFontFamily("宋体");
        run.setFontSize(20);
    }
    private static void formatCell(XWPFTableCell cell, String text, boolean header, int width) {
        cell.setWidth(String.valueOf(width));
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        XWPFParagraph paragraph = cell.getParagraphs().get(0);
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        paragraph.setVerticalAlignment(TextAlignment.CENTER);
        paragraph.setSpacingBefore(0);
        paragraph.setSpacingAfter(0);
        XWPFRun run = paragraph.createRun();
        String[] lines = safe(text).split("\\n", -1);
        for (int i = 0; i < lines.length; i++) {
            if (i > 0) {
                run.addBreak();
            }
            run.setText(lines[i]);
        }
        run.setBold(header);
        run.setFontFamily("宋体");
        run.setFontSize(header ? 12 : 11);
    }
    private static String lineBreakSeals(String names) {
        return safe(names).replace(",", "\n").replace(",", "\n");
    }
    private static String safe(String value) {
        return value == null ? "" : value;
    }
}
inspect-server/src/main/resources/mapper/SealUsageLedgerMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inspect.mapper.SealUsageLedgerMapper">
    <sql id="LedgerColumns">
        ledger.id, ledger.report_id, ledger.report_code, ledger.ins_order_id,
        ledger.use_time, ledger.seal_ids, ledger.seal_names,
        COALESCE(io.sample_view, '') AS project_name,
        ledger.use_user_id, ledger.use_user_name, ledger.copies, ledger.version,
        ledger.create_user, ledger.create_time, ledger.update_user, ledger.update_time
    </sql>
    <sql id="LedgerFilters">
        <if test="query != null">
            <if test="query.reportCode != null and query.reportCode != ''">
                AND ledger.report_code LIKE CONCAT('%', #{query.reportCode}, '%')
            </if>
            <if test="query.sealName != null and query.sealName != ''">
                AND ledger.seal_names LIKE CONCAT('%', #{query.sealName}, '%')
            </if>
            <if test="query.projectName != null and query.projectName != ''">
                AND io.sample_view LIKE CONCAT('%', #{query.projectName}, '%')
            </if>
            <if test="query.useUserName != null and query.useUserName != ''">
                AND ledger.use_user_name LIKE CONCAT('%', #{query.useUserName}, '%')
            </if>
            <if test="query.startDate != null">
                AND ledger.use_time <![CDATA[ >= ]]> #{query.startDate}
            </if>
            <if test="query.endDate != null">
                AND ledger.use_time <![CDATA[ < ]]> DATE_ADD(#{query.endDate}, INTERVAL 1 DAY)
            </if>
        </if>
    </sql>
    <select id="selectLedgerPage" resultType="com.ruoyi.inspect.pojo.SealUsageLedger">
        SELECT <include refid="LedgerColumns"/>
        FROM seal_usage_ledger ledger
        LEFT JOIN ins_order io ON io.id = ledger.ins_order_id
        WHERE 1 = 1
        <include refid="LedgerFilters"/>
        ORDER BY ledger.use_time DESC, ledger.id DESC
    </select>
    <select id="selectForExport" resultType="com.ruoyi.inspect.pojo.SealUsageLedger">
        SELECT <include refid="LedgerColumns"/>
        FROM seal_usage_ledger ledger
        LEFT JOIN ins_order io ON io.id = ledger.ins_order_id
        WHERE 1 = 1
        <if test="ids != null and ids.size() > 0">
            AND ledger.id IN
            <foreach collection="ids" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </if>
        <include refid="LedgerFilters"/>
        ORDER BY ledger.use_time DESC, ledger.id DESC
    </select>
    <select id="selectProjectName" resultType="java.lang.String">
        SELECT io.sample_view
        FROM ins_order io
        WHERE io.id = #{insOrderId}
    </select>
    <update id="updateCopies">
        UPDATE seal_usage_ledger
        SET copies = #{copies},
            version = version + 1,
            update_user = #{updateUser},
            update_time = NOW()
        WHERE id = #{id}
          AND version = #{version}
    </update>
</mapper>
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
@@ -138,4 +138,7 @@
     * @return åŒ¹é…çš„菜单列表
     */
    public List<SysMenu> selectMenusByPathOrRouteName(@Param("path") String path, @Param("routeName") String routeName);
    /** å°ç« ä½¿ç”¨ç™»è®°å¯¹æ‰€æœ‰è§’色开放的父菜单、页面及操作菜单。 */
    public List<Long> selectSealUsageLedgerAccessMenuIds();
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -19,6 +19,7 @@
import com.ruoyi.system.domain.SysRoleMenu;
import com.ruoyi.system.domain.SysUserRole;
import com.ruoyi.system.mapper.SysRoleDeptMapper;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.mapper.SysRoleMapper;
import com.ruoyi.system.mapper.SysRoleMenuMapper;
import com.ruoyi.system.mapper.SysUserRoleMapper;
@@ -37,6 +38,9 @@
    @Autowired
    private SysRoleMenuMapper roleMenuMapper;
    @Autowired
    private SysMenuMapper menuMapper;
    @Autowired
    private SysUserRoleMapper userRoleMapper;
@@ -295,7 +299,14 @@
        int rows = 1;
        // æ–°å¢žç”¨æˆ·ä¸Žè§’色管理
        List<SysRoleMenu> list = new ArrayList<SysRoleMenu>();
        for (Long menuId : role.getMenuIds())
        Set<Long> menuIds = new HashSet<Long>();
        if (role.getMenuIds() != null)
        {
            menuIds.addAll(Arrays.asList(role.getMenuIds()));
        }
        // å°ç« ä½¿ç”¨ç™»è®°é¢å‘所有角色;新建或编辑角色时一并保留页面、父菜单及操作权限。
        menuIds.addAll(menuMapper.selectSealUsageLedgerAccessMenuIds());
        for (Long menuId : menuIds)
        {
            SysRoleMenu rm = new SysRoleMenu();
            rm.setRoleId(role.getRoleId());
ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -208,8 +208,22 @@
        update sys_menu set order_num = #{orderNum} where menu_id = #{menuId}
    </update>
    <select id="selectSealUsageLedgerAccessMenuIds" resultType="java.lang.Long">
        SELECT DISTINCT candidate.menu_id
        FROM sys_menu ledger
        JOIN sys_menu candidate
          ON candidate.menu_id = ledger.parent_id
          OR candidate.menu_id = ledger.menu_id
          OR (candidate.parent_id = ledger.menu_id
              AND candidate.perms IN ('structural:sealUsageLedger:edit',
                                      'structural:sealUsageLedger:export'))
        WHERE ledger.component = 'structural/sealManage/usageLedger/index'
          AND ledger.menu_type = 'C'
          AND ledger.status = '0'
    </select>
    <delete id="deleteMenuById" parameterType="Long">
        delete from sys_menu where menu_id = #{menuId}
    </delete>
</mapper>
</mapper>