云
3 天以前 e7dd8b490661c994c414c0edc2da1854d9bcef82
feat(excel): 优化Excel列宽与必填标记并支持巡检附件

- 优化表头列宽计算逻辑,支持中日韩宽字符占两格并预留边距
- 为各模块导入VO的必填字段添加 @ExcelRequired 注解
- 导出模板时自动注册 RequiredHeaderWriteHandler 标记必填表头
- 工艺路线导出模板各 Sheet 注册列宽自适应与必填表头处理器
- 后端质检附件接口与存储枚举新增 patrol 巡检类型支持
- 前端质检附件组件 qc-attachments 新增 patrol 类型兼容
已修改22个文件
121 ■■■■■ 文件已修改
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeeContractImportExcelVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeeImportExcelVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasureImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachineryImportExcelVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/metering/vo/MesDvMeteringImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/type/MesMdItemTypeImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/attachment/MesQcAttachmentController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockImportExcelVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/controller/admin/supplier/vo/SrmSupplierImportExcelVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptImportExcelVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostImportExcelVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/storage/StorageRecordTypeEnum.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
@@ -59,10 +59,31 @@
        }
    }
    /** 按 Excel 列宽估计表头:中日韩宽字符占两格,并为边距留出空间。 */
    private int headerWidth(String text) {
        int widest = 0;
        int line = 0;
        for (int offset = 0; offset < text.length();) {
            int cp = text.codePointAt(offset);
            offset += Character.charCount(cp);
            if (cp == '\n' || cp == '\r') {
                widest = Math.max(widest, line);
                line = 0;
                continue;
            }
            Character.UnicodeScript script = Character.UnicodeScript.of(cp);
            boolean wide = script == Character.UnicodeScript.HAN || script == Character.UnicodeScript.HIRAGANA
                    || script == Character.UnicodeScript.KATAKANA || script == Character.UnicodeScript.HANGUL
                    || (cp >= 0xff01 && cp <= 0xff60) || (cp >= 0xffe0 && cp <= 0xffe6);
            line += wide ? 2 : 1;
        }
        return Math.max(widest, line);
    }
    @SuppressWarnings("EnhancedSwitchMigration")
    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
            return Math.max(12, headerWidth(cell.getStringCellValue()) + 3);
        }
        WriteCellData<?> cellData = cellDataList.get(0);
        CellDataTypeEnum type = cellData.getType();
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -10,6 +10,7 @@
import cn.idev.excel.metadata.CellExtra;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
import cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler;
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
@@ -45,12 +46,13 @@
    public static <T> void write(HttpServletResponse response, String filename, String sheetName,
                                 Class<T> head, List<T> data) throws IOException {
        // 输出 Excel
        ExcelWriter excelWriter = FastExcelFactory.write(response.getOutputStream(), head)
        var builder = FastExcelFactory.write(response.getOutputStream(), head)
                .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
                .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
                .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
                .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
                .build();
                .registerConverter(new LongStringConverter()); // 避免 Long 类型丢失精度
        if (filename.contains("模板")) builder.registerWriteHandler(new RequiredHeaderWriteHandler());
        ExcelWriter excelWriter = builder.build();
        try {
            excelWriter.write(data, FastExcelFactory.writerSheet(sheetName).build());
        } finally {
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractImportExcelVO.java
@@ -32,10 +32,12 @@
    @ExcelProperty("合同名称")
    @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("客户名称")
    @Schema(description = "客户名称,需与 CRM 客户名称一致", requiredMode = Schema.RequiredMode.REQUIRED)
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String customerName;
    @ExcelProperty("下单时间")
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
@@ -24,6 +24,7 @@
public class CrmCustomerImportExcelVO {
    @ExcelProperty("客户名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("手机")
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordImportExcelVO.java
@@ -21,12 +21,14 @@
public class HrmAttendanceRecordImportExcelVO {
    @ExcelProperty("员工姓名")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String userName;
    @ExcelProperty("手机号")
    private String phone;
    @ExcelProperty("考勤日期")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private LocalDate date;
    @ExcelProperty("上班打卡时间")
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeeContractImportExcelVO.java
@@ -21,14 +21,17 @@
public class HrmEmployeeContractImportExcelVO {
    @ExcelProperty("员工姓名")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty(value = "合同类型", converter = DictConvert.class)
    @DictFormat("hrm_contract_type")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private Integer contractType;
    @ExcelProperty(value = "合同期限类型", converter = DictConvert.class)
    @DictFormat("hrm_contract_term_type")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private Integer contractTermType;
    @ExcelProperty("签约主体(公司全称)")
@@ -38,6 +41,7 @@
    private LocalDate signDate;
    @ExcelProperty("合同开始日期")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private LocalDate startDate;
    @ExcelProperty("合同结束日期")
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeeImportExcelVO.java
@@ -20,6 +20,7 @@
public class HrmEmployeeImportExcelVO {
    @ExcelProperty("员工姓名")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty(value = "性别", converter = DictConvert.class)
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemImportExcelVO.java
@@ -17,9 +17,11 @@
@NoArgsConstructor
public class MdmItemImportExcelVO {
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    @ExcelProperty("产品编号(修改时用于匹配,新增时自动生成)")
    private String code;
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    @ExcelProperty("物料名称")
    private String name;
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasureImportExcelVO.java
@@ -21,9 +21,11 @@
public class MdmUnitMeasureImportExcelVO {
    @ExcelProperty("单位编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("单位名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("是否主单位")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/machinery/vo/MesDvMachineryImportExcelVO.java
@@ -19,9 +19,11 @@
public class MesDvMachineryImportExcelVO {
    @ExcelProperty("设备编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("设备名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("品牌")
@@ -31,9 +33,11 @@
    private String specification;
    @ExcelProperty("设备类型编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String machineryTypeCode;
    @ExcelProperty("所属车间编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String workshopCode;
    @ExcelProperty(value = "设备状态", converter = DictConvert.class)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/dv/metering/vo/MesDvMeteringImportExcelVO.java
@@ -26,10 +26,12 @@
    @ExcelProperty("计量器具编码")
    @Schema(description = "计量器具编码,唯一", requiredMode = Schema.RequiredMode.REQUIRED)
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("计量器具名称")
    @Schema(description = "计量器具名称", requiredMode = Schema.RequiredMode.REQUIRED)
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty(value = "器具类别", converter = DictConvert.class)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/type/MesMdItemTypeImportExcelVO.java
@@ -16,9 +16,11 @@
public class MesMdItemTypeImportExcelVO {
    @ExcelProperty("分类编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("分类名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("父分类编码")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteController.java
@@ -159,15 +159,25 @@
        ExcelWriter writer = FastExcelFactory.write(response.getOutputStream()).autoCloseStream(false).build();
        try {
            writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(0, "路线")
                    .head(MesProRouteCompleteImportWorkbook.RouteRow.class).build());
                    .head(MesProRouteCompleteImportWorkbook.RouteRow.class)
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy())
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build());
            writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(1, "产品")
                    .head(MesProRouteCompleteImportWorkbook.ProductRow.class).build());
                    .head(MesProRouteCompleteImportWorkbook.ProductRow.class)
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy())
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build());
            writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(2, "工序")
                    .head(MesProRouteCompleteImportWorkbook.ProcessRow.class).build());
                    .head(MesProRouteCompleteImportWorkbook.ProcessRow.class)
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy())
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build());
            writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(3, "工序产出")
                    .head(MesProRouteCompleteImportWorkbook.OutputRow.class).build());
                    .head(MesProRouteCompleteImportWorkbook.OutputRow.class)
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy())
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build());
            writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(4, "工序投料")
                    .head(MesProRouteCompleteImportWorkbook.InputRow.class).build());
                    .head(MesProRouteCompleteImportWorkbook.InputRow.class)
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy())
                    .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build());
            writer.write(List.of(
                    new MesProRouteCompleteImportWorkbook.GuideRow("路线", "每行一条路线;路线编码唯一。完整导入仅新增,已有编码会报错,导入后默认停用。"),
                    new MesProRouteCompleteImportWorkbook.GuideRow("产品", "路线编码与路线表一致;每条路线至少一个产品。物料、仓库、库区、库位填写已有业务编码;时间单位填 MINUTE/HOUR/DAY。"),
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteCompleteImportWorkbook.java
@@ -18,7 +18,9 @@
    @Data
    public static class RouteRow {
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线编码") private String routeCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线名称") private String name;
        @ExcelProperty("路线说明") private String description;
        @ExcelProperty("备注") private String remark;
@@ -26,10 +28,15 @@
    @Data
    public static class ProductRow {
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线编码") private String routeCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("产品物料编码") private String itemCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("仓库编码") private String warehouseCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("库区编码") private String locationCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("库位编码") private String areaCode;
        @ExcelProperty("生产数量") private Integer quantity;
        @ExcelProperty("生产用时") private BigDecimal productionTime;
@@ -39,8 +46,11 @@
    @Data
    public static class ProcessRow {
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线编码") private String routeCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("工序编码") private String processCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("排序") private Integer sort;
        @ExcelProperty("工艺参数模板ID") private Long paramTemplateId;
        @ExcelProperty("工序收率") private BigDecimal yieldRate;
@@ -56,8 +66,11 @@
    @Data
    public static class OutputRow {
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线编码") private String routeCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("工序编码") private String processCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("产出物料编码") private String itemCode;
        @ExcelProperty("归属产品编码(空为通用)") private String productCode;
        @ExcelProperty("排序") private Integer sort;
@@ -66,9 +79,13 @@
    @Data
    public static class InputRow {
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("路线编码") private String routeCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("工序编码") private String processCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("归属产品编码") private String productCode;
        @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
        @ExcelProperty("投料物料编码") private String itemCode;
        @ExcelProperty("用料比例") private BigDecimal quantity;
        @ExcelProperty("备注") private String remark;
@@ -76,7 +93,9 @@
    @Data
    public static class GuideRow {
        @cn.idev.excel.annotation.write.style.ColumnWidth(16)
        @ExcelProperty("工作表") private String sheet;
        @cn.idev.excel.annotation.write.style.ColumnWidth(120)
        @ExcelProperty("填写说明") private String description;
        public GuideRow(String sheet, String description) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/MesProRouteImportExcelVO.java
@@ -15,9 +15,11 @@
@NoArgsConstructor
public class MesProRouteImportExcelVO {
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    @ExcelProperty("工艺路线编码")
    private String code;
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    @ExcelProperty("工艺路线名称")
    private String name;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/attachment/MesQcAttachmentController.java
@@ -31,13 +31,14 @@
    @Resource private MesQcIpqcService ipqcService;
    @Resource private MesQcOqcService oqcService;
    @Resource private MesQcRqcService rqcService;
    @Resource private cn.iocoder.yudao.module.mes.controller.admin.qc.patrol.PatrolMapper patrolMapper;
    @Resource private StorageAttachmentApi storageApi;
    @Resource private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
    @GetMapping("/list")
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':query')")
    public CommonResult<List<StorageBlobRespDTO>> list(
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc") String type,
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type,
            @RequestParam @NotNull Long id) {
        validateDocument(type, id);
        var files = storageApi.listAttachments(recordType(type), id);
@@ -74,7 +75,7 @@
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> remove(
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc") String type,
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type,
            @RequestParam @NotNull Long id, @RequestParam @NotNull Long blobId) {
        lockDocument(type, id);
        validateDocument(type, id);
@@ -84,7 +85,7 @@
    private String recordType(String type) {
        return switch (type) {
            case "iqc", "ipqc", "oqc", "rqc" -> "mes_qc_" + type + "_attachment";
            case "iqc", "ipqc", "oqc", "rqc", "patrol" -> "mes_qc_" + type + "_attachment";
            default -> throw new ServiceException(400, "质检类型无效");
        };
    }
@@ -95,6 +96,7 @@
            case "ipqc" -> "mes_qc_ipqc";
            case "oqc" -> "mes_qc_oqc";
            case "rqc" -> "mes_qc_rqc";
            case "patrol" -> "mes_qc_patrol";
            default -> throw new ServiceException(400, "质检类型无效");
        };
        var ids = jdbcTemplate.queryForList("SELECT id FROM " + table + " WHERE id = ? AND deleted = 0 FOR UPDATE", Long.class, id);
@@ -107,17 +109,18 @@
            case "ipqc" -> ipqcService.validateIpqcExists(id).getStatus();
            case "oqc" -> oqcService.validateOqcExists(id).getStatus();
            case "rqc" -> rqcService.validateRqcExists(id).getStatus();
            case "patrol" -> { if (patrolMapper.selectById(id) == null) throw new ServiceException(400, "巡检记录不存在"); yield 0; }
            default -> throw new ServiceException(400, "质检类型无效");
        };
    }
    @Data public static class SaveReq {
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc") private String type;
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type;
        @NotNull private Long id;
        @NotEmpty @Size(max = 50) private List<@NotNull Long> blobIds;
    }
    @Data public static class AppendReq {
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc") private String type;
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type;
        @NotNull private Long id;
        @NotNull private Long blobId;
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockImportExcelVO.java
@@ -18,9 +18,11 @@
public class MesWmMaterialStockImportExcelVO {
    @ExcelProperty("物料编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String itemCode;
    @ExcelProperty("仓库")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String warehouse;
    @ExcelProperty("库区")
@@ -36,9 +38,11 @@
    private String vendor;
    @ExcelProperty("操作类型")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String operationType;
    @ExcelProperty("在库数量")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private BigDecimal quantity;
}
yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/controller/admin/supplier/vo/SrmSupplierImportExcelVO.java
@@ -15,9 +15,11 @@
public class SrmSupplierImportExcelVO {
    @ExcelProperty("供应商编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("供应商名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("供应商简称")
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptImportExcelVO.java
@@ -16,12 +16,14 @@
public class DeptImportExcelVO {
    @ExcelProperty("部门名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("上级部门全路径")
    private String parentPath;
    @ExcelProperty("显示顺序")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private Integer sort;
    @ExcelProperty("联系电话")
@@ -32,6 +34,7 @@
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.COMMON_STATUS)
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private Integer status;
}
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostImportExcelVO.java
@@ -16,12 +16,15 @@
public class PostImportExcelVO {
    @ExcelProperty("岗位名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String name;
    @ExcelProperty("岗位编码")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String code;
    @ExcelProperty("岗位排序")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private Integer sort;
    @ExcelProperty(value = "状态", converter = DictConvert.class)
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java
@@ -19,6 +19,7 @@
public class UserImportExcelVO {
    @ExcelProperty("登录名称")
    @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired
    private String username;
    @ExcelProperty("用户名称")
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/storage/StorageRecordTypeEnum.java
@@ -71,6 +71,7 @@
    MES_QC_IQC_ATTACHMENT("mes_qc_iqc_attachment", "来料质检附件"),
    MES_QC_IPQC_ATTACHMENT("mes_qc_ipqc_attachment", "过程质检附件"),
    MES_QC_OQC_ATTACHMENT("mes_qc_oqc_attachment", "出货质检附件"),
    MES_QC_PATROL_ATTACHMENT("mes_qc_patrol_attachment", "质量巡检附件"),
    MES_QC_RQC_ATTACHMENT("mes_qc_rqc_attachment", "退货质检附件"),
    // ===== SRM =====