From 924abaab6b36ba91ff0bbd91eaf165ad7a6386fa Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Tue, 6 Jan 2026 16:59:42 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E8=B4=B4=E7=9A=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=AF=BC=E5=87=BA=E6=98=8E=E7=BB=86=E3=80=81?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=AF=BC=E5=85=A5=E6=A8=A1=E6=9D=BF=E3=80=81?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=AF=BC=E5=85=A5=E3=80=81=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E7=91=95=E7=96=B5=E6=8E=A5=E5=8F=A3=EF=BC=9B2=E3=80=81?= =?UTF-8?q?=E6=95=B4=E7=90=86=E5=AF=B9=E5=BA=94=E5=8F=82=E6=95=B0=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=9B=BD=E9=99=85=E5=8C=96=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/core/convert/EnumConvert.java | 4 +- .../executor/enums/ErrorCodeConstants.java | 5 + .../file/TemplateFilePathConstants.java | 34 +++ .../executor/constants/package-info.java | 5 + .../admin/veneer/VeneerController.java | 46 ++++ .../veneer/vo/VeneerDefectSaveReqVO.java | 22 ++ .../admin/veneer/vo/VeneerImportExcelVO.java | 86 ++++++ .../admin/veneer/vo/VeneerRespVO.java | 24 +- .../dal/dataobject/veneer/VeneerDO.java | 5 + .../dal/mysql/veneer/VeneerBatchMapper.java | 15 ++ .../dal/mysql/veneer/VeneerMapper.java | 4 + .../enums/veneer/VeneerProductLevelEnum.java | 6 +- .../service/veneer/VeneerExcelListener.java | 252 ++++++++++++++++++ .../service/veneer/VeneerService.java | 33 +++ .../service/veneer/VeneerServiceImpl.java | 49 ++++ .../executor/util/file/FileHelperUtil.java | 45 +++- .../src/main/resources/message.properties | 3 +- .../src/main/resources/message_cht.properties | 5 +- .../src/main/resources/message_en.properties | 3 +- .../main/resources/message_enum.properties | 5 +- .../resources/message_enum_cht.properties | 5 +- .../main/resources/message_enum_en.properties | 5 +- .../resources/message_errorcode.properties | 8 +- .../message_errorcode_cht.properties | 8 +- .../resources/message_errorcode_en.properties | 8 +- .../type/veneer/cht/貼皮導入範本.xlsx | Bin 0 -> 10340 bytes .../type/veneer/zh/贴皮导入模板.xlsx | Bin 0 -> 10338 bytes 27 files changed, 658 insertions(+), 27 deletions(-) create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/file/TemplateFilePathConstants.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/package-info.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDefectSaveReqVO.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerImportExcelVO.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerBatchMapper.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerExcelListener.java create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/type/veneer/cht/貼皮導入範本.xlsx create mode 100644 cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/type/veneer/zh/贴皮导入模板.xlsx diff --git a/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumConvert.java b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumConvert.java index 2f10431ef..f60f99a2d 100644 --- a/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumConvert.java +++ b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumConvert.java @@ -1,5 +1,6 @@ package com.cf.imes.framework.excel.core.convert; +import cn.hutool.core.util.ObjectUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.ReadConverterContext; import com.alibaba.excel.converters.WriteConverterContext; @@ -61,7 +62,8 @@ public class EnumConvert implements Converter { if (enumClass != null) { for (Object e : enumClass.getEnumConstants()) { BaseEnum baseEnum = (BaseEnum) e; - if (baseEnum.getDesc().equals(desc)) { + String i18nDesc = ResourceBundleUtils.getMessage(ResourceBundleConstants.MESSAGE_ENUM_PATH, baseEnum.getDesc(), baseEnum.getDesc(), LocaleContextHolder.getLocale(), null); + if (ObjectUtil.equals(desc, i18nDesc)) { return baseEnum.getCode(); } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java index f12c3df2a..f907b2f77 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java +++ b/cf-module-prod-executor/cf-module-prod-executor-api/src/main/java/com/cf/imes/module/executor/enums/ErrorCodeConstants.java @@ -172,4 +172,9 @@ public class ErrorCodeConstants { //=========== 贴皮管理 1-003-009-000 ============ public static final ErrorCode VENEER_NOT_EXIST = new ErrorCode(1_003_009_000, "veneer.not.exist"); public static final ErrorCode VENEER_NO_EXIST = new ErrorCode(1_003_009_001, "veneer.no.exist"); + public static final ErrorCode VENEER_IMPORT_REQUIRED_LACK = new ErrorCode(1_003_009_002, "veneer.import.required.lack"); + public static final ErrorCode VENEER_IMPORT_LENGTH_ERROR = new ErrorCode(1_003_009_003, "veneer.import.length.error"); + public static final ErrorCode VENEER_IMPORT_DIGITAL_TYPE_ERROR = new ErrorCode(1_003_009_004, "veneer.import.digital.type.error"); + public static final ErrorCode VENEER_IMPORT_POSITIVE_NUMBER_ERROR = new ErrorCode(1_003_009_005, "veneer.import.positive.number.error"); + public static final ErrorCode VENEER_IMPORT_NUMBER_LENGTH_ERROR = new ErrorCode(1_003_009_006, "veneer.import.number.length.error"); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/file/TemplateFilePathConstants.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/file/TemplateFilePathConstants.java new file mode 100644 index 000000000..df4a2c4c1 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/file/TemplateFilePathConstants.java @@ -0,0 +1,34 @@ +package com.cf.imes.module.executor.constants.file; + +import org.springframework.boot.system.ApplicationHome; + +import java.io.File; + +/** + * 模板文件路径 常量类 + * + * @author Gqr + * @since 2026/1/5 14:35 + */ +public final class TemplateFilePathConstants { + private TemplateFilePathConstants() { + } + + public static final String ROOT_PATH; + + static { + ApplicationHome home = new ApplicationHome(TemplateFilePathConstants.class); + File jarDir = home.getDir(); // jar 所在目录 + ROOT_PATH = new File(jarDir, "type").getAbsolutePath(); + } + + /** + * 根据语种获取贴皮导入模板 + * + * @param language 语种 + * @return + */ + public static String getVeneerImportTemplatePath(String language) { + return ROOT_PATH + File.separator + "veneer" + File.separator + language; + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/package-info.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/package-info.java new file mode 100644 index 000000000..38bf1e2f5 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/constants/package-info.java @@ -0,0 +1,5 @@ +/** + * executor 常量 + * + */ +package com.cf.imes.module.executor.constants; diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerController.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerController.java index 760ed2e27..88bdc3fea 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerController.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/VeneerController.java @@ -1,16 +1,26 @@ package com.cf.imes.module.executor.controller.admin.veneer; +import com.alibaba.excel.EasyExcelFactory; import com.cf.imes.framework.common.pojo.CommonResult; +import com.cf.imes.framework.common.pojo.PageParam; import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.excel.core.util.ExcelUtils; import com.cf.imes.framework.operatelog.core.annotations.OperateLog; +import com.cf.imes.module.executor.constants.file.TemplateFilePathConstants; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDefectSaveReqVO; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerImportExcelVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerSaveReqVO; +import com.cf.imes.module.executor.service.veneer.VeneerExcelListener; import com.cf.imes.module.executor.service.veneer.VeneerService; +import com.cf.imes.module.executor.util.file.FileHelperUtil; 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 jakarta.validation.Valid; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -24,9 +34,12 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.List; import static com.cf.imes.framework.common.pojo.CommonResult.success; import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.CREATE; +import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; +import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.IMPORT; /** * @@ -49,6 +62,33 @@ public class VeneerController { return success(veneerService.getPage(pageReqVO)); } + @GetMapping("/export") + @Operation(summary = "导出贴皮明细") + @OperateLog(type = EXPORT) + public void exportSalesDetailsPage(@Valid VeneerPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = veneerService.getPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "贴皮明细.xls", "数据", VeneerRespVO.class, list); + } + + @GetMapping("/import/template") + @Operation(summary = "下载贴皮导入模版") + @OperateLog(type = EXPORT) + public void importTemplate(HttpServletResponse response) { + String veneerImportTemplatePath = TemplateFilePathConstants.getVeneerImportTemplatePath(LocaleContextHolder.getLocale().getLanguage()); + FileHelperUtil.getFirstFile(veneerImportTemplatePath, response); + } + + @PostMapping("/import") + @Operation(summary = "导入贴皮") + @OperateLog(type = IMPORT) + public void importExcel(@RequestPart("file") MultipartFile file) throws IOException { + VeneerExcelListener listener = new VeneerExcelListener(veneerService); + EasyExcelFactory.read(file.getInputStream(), VeneerImportExcelVO.class, listener).headRowNumber(1).sheet().doRead(); + } + @GetMapping("/{id}") @Operation(summary = "根据id获取贴皮") public CommonResult getVeneer(@PathVariable Long id) { @@ -79,4 +119,10 @@ public class VeneerController { public CommonResult uploadVeneerPic(@RequestPart(value = "file") MultipartFile file) throws IOException { return success(veneerService.uploadPic(file)); } + + @PostMapping("/defect") + @Operation(summary = "保存瑕疵数据") + public CommonResult saveDefect(@Valid @RequestBody VeneerDefectSaveReqVO reqVO) { + return success(veneerService.saveDefect(reqVO) == 1); + } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDefectSaveReqVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDefectSaveReqVO.java new file mode 100644 index 000000000..e8a30b337 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerDefectSaveReqVO.java @@ -0,0 +1,22 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * + * + * @author Gqr + * @since 2026/1/6 15:57 + */ +@Schema(description = "生产管理 - 贴皮管理 保存瑕疵 Request VO") +@Data +public class VeneerDefectSaveReqVO { + @Schema( description = "贴皮id") + @NotNull(message = "{veneer.defect.save.id.not.null}") + private Long id; + + @Schema(description = "瑕疵数据") + private String defectData; +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerImportExcelVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerImportExcelVO.java new file mode 100644 index 000000000..f295d959a --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerImportExcelVO.java @@ -0,0 +1,86 @@ +package com.cf.imes.module.executor.controller.admin.veneer.vo; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.annotation.ExcelProperty; +import com.cf.imes.framework.excel.core.annotations.EnumFormat; +import com.cf.imes.framework.excel.core.convert.EnumConvert; +import com.cf.imes.module.executor.enums.veneer.VeneerProductLevelEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 贴皮 Excel导入 VO + * + * @author Gqr + * @since 2026/1/5 15:28 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题 +public class VeneerImportExcelVO { + + private Long id; + + private Long organId; + + @ExcelProperty(index = 0) + private String veneerNo; + + @ExcelProperty(index = 1) + private String batchNo; + + @ExcelProperty(index = 2) + private String classificationName; + + @ExcelProperty(index = 3) + private String veneerName; + + @ExcelProperty(index = 4, converter = EnumConvert.class) + @EnumFormat(value = VeneerProductLevelEnum.class) + private Integer productLevel; + + @ExcelProperty(index = 5) + private String color; + + @ExcelProperty(index = 6) + private String material; + + @ExcelProperty(index = 7) + private String length; + + @ExcelProperty(index = 8) + private String width; + + @ExcelProperty(index = 9) + private String thickness; + + @ExcelProperty(index = 10) + private String brand; + + @ExcelProperty(index = 11) + private String spec; + + @ExcelProperty(index = 12) + private String factory; + + @ExcelProperty(index = 13) + private String num; + + @ExcelProperty(index = 14) + private String remark; + + /** + * 校验必填 + * + * @return true:校验成功、false:校验失败(有必填项未填写) + */ + public boolean validRequired() { + return CharSequenceUtil.isAllNotEmpty(veneerNo, classificationName, veneerName, length, width, thickness, num) || ObjectUtil.isNull(productLevel); + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerRespVO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerRespVO.java index 08784851a..f4cd4f27c 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerRespVO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/controller/admin/veneer/vo/VeneerRespVO.java @@ -7,7 +7,7 @@ import com.cf.imes.framework.excel.core.convert.EnumBoolValConvert; import com.cf.imes.framework.excel.core.convert.EnumConvert; import com.cf.imes.framework.excel.core.enums.WhetherEnum; import com.cf.imes.module.executor.enums.veneer.VeneerProductLevelEnum; -import com.cf.imes.module.executor.enums.veneer.VeneerStatusEnum; +import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -22,6 +22,7 @@ import java.math.BigDecimal; @Schema(description = "生产管理 - 贴皮管理分页 Response VO") @Data @ExcelIgnoreUnannotated +@JsonInclude(JsonInclude.Include.NON_NULL) public class VeneerRespVO { @Schema(description = "贴皮id") private Long id; @@ -30,6 +31,10 @@ public class VeneerRespVO { @ExcelProperty("veneer.export.head.veneerNo") private String veneerNo; + @Schema(description = "批次号") + @ExcelProperty("veneer.export.head.batchNo") + private String batchNo; + @Schema(description = "分类名称") @ExcelProperty("veneer.export.head.classificationName") private String classificationName; @@ -43,10 +48,6 @@ public class VeneerRespVO { @EnumFormat(value = VeneerProductLevelEnum.class) private Integer productLevel; - @Schema(description = "批次号") - @ExcelProperty("veneer.export.head.batchNo") - private String batchNo; - @Schema(description = "颜色") @ExcelProperty("veneer.export.head.color") private String color; @@ -55,14 +56,14 @@ public class VeneerRespVO { @ExcelProperty("veneer.export.head.material") private String material; - @Schema(description = "宽") - @ExcelProperty("veneer.export.head.width") - private BigDecimal width; - @Schema(description = "长") @ExcelProperty("veneer.export.head.length") private BigDecimal length; + @Schema(description = "宽") + @ExcelProperty("veneer.export.head.width") + private BigDecimal width; + @Schema(description = "厚") @ExcelProperty("veneer.export.head.thickness") private BigDecimal thickness; @@ -93,7 +94,8 @@ public class VeneerRespVO { private String remark; @Schema(description = "状态,0:未使用、1:已使用") - @ExcelProperty(value = "veneer.export.head.status", converter = EnumConvert.class) - @EnumFormat(value = VeneerStatusEnum.class) private Integer status; + + @Schema(description = "瑕疵数据") + private String defectData; } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerDO.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerDO.java index 40420fd43..f72454a94 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerDO.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/dataobject/veneer/VeneerDO.java @@ -150,4 +150,9 @@ public class VeneerDO extends BaseDO { * 贴皮文件ID */ private Long fileId; + + /** + * 瑕疵数据 + */ + private String defectData; } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerBatchMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerBatchMapper.java new file mode 100644 index 000000000..2c0ed8875 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerBatchMapper.java @@ -0,0 +1,15 @@ +package com.cf.imes.module.executor.dal.mysql.veneer; + +import com.cf.imes.framework.mybatis.core.injector.BaseBatchMapper; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 贴皮 批量Mapper + * + * @author Gqr + * @since 2026/1/6 14:38 + */ +@Mapper +public interface VeneerBatchMapper extends BaseBatchMapper { +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerMapper.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerMapper.java index c9693b0d2..42bc51dfe 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerMapper.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/dal/mysql/veneer/VeneerMapper.java @@ -42,6 +42,10 @@ public interface VeneerMapper extends BaseMapperX { .eqIfPresent(VeneerDO::getSourcePlanNo, reqVO.getSourcePlanNo()) .eqIfPresent(VeneerDO::getPlanNo, reqVO.getPlanNo()) .eqIfPresent(VeneerDO::getStatus, reqVO.getStatus()) + // 查询字段 + .select(VeneerDO::getId, VeneerDO::getVeneerNo, VeneerDO::getBatchNo, VeneerDO::getClassificationName, VeneerDO::getVeneerName, + VeneerDO::getProductLevel, VeneerDO::getColor, VeneerDO::getMaterial, VeneerDO::getLength, VeneerDO::getWidth, VeneerDO::getThickness, + VeneerDO::getBrand, VeneerDO::getSpec, VeneerDO::getFactory, VeneerDO::getNum, VeneerDO::getDefect, VeneerDO::getRemark) .orderByDesc(VeneerDO::getId)); } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/enums/veneer/VeneerProductLevelEnum.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/enums/veneer/VeneerProductLevelEnum.java index d0ca51be9..1c9a41036 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/enums/veneer/VeneerProductLevelEnum.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/enums/veneer/VeneerProductLevelEnum.java @@ -16,9 +16,9 @@ import java.util.Arrays; @RequiredArgsConstructor @Getter public enum VeneerProductLevelEnum implements IntArrayValuable, BaseEnum { - A(0,"A类"), - B(1,"B类"), - C(2,"C类"); + A(0,"veneer.product.level.A"), + B(1,"veneer.product.level.B"), + C(2,"veneer.product.level.C"); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(VeneerProductLevelEnum::getCode).toArray(); diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerExcelListener.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerExcelListener.java new file mode 100644 index 000000000..44ba487f7 --- /dev/null +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerExcelListener.java @@ -0,0 +1,252 @@ +package com.cf.imes.module.executor.service.veneer; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.util.ListUtils; +import com.cf.imes.framework.common.exception.ServiceException; +import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerImportExcelVO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; +import com.cf.imes.module.executor.enums.veneer.VeneerStatusEnum; +import com.cf.imes.module.executor.util.fileConversion.admin.ToolUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Map; + +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_DIGITAL_TYPE_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_LENGTH_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_NUMBER_LENGTH_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_POSITIVE_NUMBER_ERROR; +import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_IMPORT_REQUIRED_LACK; + +/** + * 贴皮excel导入 解析器 + * + * @author Gqr + * @since 2026/1/5 18:18 + */ +@Slf4j +public class VeneerExcelListener extends AnalysisEventListener { + /** + * 每500条操作入库一次 + */ + private static final int BATCH_COUNT = 500; + private List cachedInsertDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + private List cachedUpdateDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + private VeneerService veneerService; + + // 贴皮编号:id + private Map orgVeneerNoMap; + + private Map headMap; + + private Long organId; + + public VeneerExcelListener(VeneerService veneerService) { + this.veneerService = veneerService; + // 查询机构下所有的贴皮编号 + orgVeneerNoMap = veneerService.getOrgVeneerNo(); + organId = SecurityFrameworkUtils.getUserOrganId(); + } + + /** + * 每解析一行都会回调invoke()方法 + * + * @param data 读取后的数据对象 + * @param context 内容 + */ + @Override + public void invoke(VeneerImportExcelVO data, AnalysisContext context) { + // 获取行号,标题算一行+1 + ReadRowHolder readRowHolder = context.readRowHolder(); + int rowIndex = readRowHolder.getRowIndex() + 1; + + // 必填属性校验 + if (!data.validRequired()) { + throw new ServiceException(VENEER_IMPORT_REQUIRED_LACK, rowIndex); + } + + // 校验贴皮是新增还是更新 + String veneerNo = data.getVeneerNo(); + Long existId = orgVeneerNoMap.get(veneerNo); + if (ObjectUtil.isNotNull(existId)) { + data.setId(existId); + // 累计更新列表 + cachedUpdateDataList.add(data); + + // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM + if (cachedUpdateDataList.size() >= BATCH_COUNT) { + updateData(); + // 存储完成清理 list + cachedUpdateDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + return; + } + + // 校验属性长度 + validFieldLength(headMap.get(0), veneerNo, 32, rowIndex); + validFieldLength(headMap.get(1), data.getBatchNo(), 32, rowIndex); + validFieldLength(headMap.get(3), data.getVeneerName(), 64, rowIndex); + validFieldLength(headMap.get(5), data.getColor(), 32, rowIndex); + validFieldLength(headMap.get(6), data.getMaterial(), 64, rowIndex); + validFieldLength(headMap.get(10), data.getBrand(), 64, rowIndex); + validFieldLength(headMap.get(11), data.getSpec(), 64, rowIndex); + validFieldLength(headMap.get(12), data.getFactory(), 128, rowIndex); + validFieldLength(headMap.get(14), data.getRemark(), 255, rowIndex); + + // 校验类型 + validPositiveFloatNumberSize(headMap.get(7), data.getLength(), Double.class, 5, 3, rowIndex); + validPositiveFloatNumberSize(headMap.get(8), data.getWidth(), Double.class, 5, 3, rowIndex); + validPositiveFloatNumberSize(headMap.get(9), data.getThickness(), Double.class, 5, 3, rowIndex); + validPositiveNumber(headMap.get(13), data.getNum(), rowIndex); + + // 累计新增列表 + cachedInsertDataList.add(data); + + // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM + if (cachedInsertDataList.size() >= BATCH_COUNT) { + insertData(); + // 存储完成清理 list + cachedInsertDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + super.extra(extra, context); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + insertData(); + updateData(); + log.debug("所有数据解析完成!"); + } + + @Override + public boolean hasNext(AnalysisContext context) { + return super.hasNext(context); + } + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.headMap = headMap; + } + + /** + * 批量新增贴皮 + */ + private void insertData() { + log.debug("{}条数据,开始新增贴皮数据!", cachedInsertDataList.size()); + List veneerDOS = BeanUtil.copyToList(cachedInsertDataList, VeneerDO.class); + for (VeneerDO veneerDO : veneerDOS) { + veneerDO.setOrganId(organId); + veneerDO.setDefect(false); + veneerDO.setRemain(false); + veneerDO.setStatus(VeneerStatusEnum.NOT_USE.getCode()); + veneerDO.setDeleted(false); + } + if (CollUtil.isNotEmpty(cachedInsertDataList)) { + veneerService.batchInsert(veneerDOS); + } + log.debug("excel导入贴皮数据保存成功!"); + } + + /** + * 批量更新贴皮 + */ + private void updateData() { + log.debug("{}条数据,开始更新贴皮数据!", cachedUpdateDataList.size()); + if (CollUtil.isNotEmpty(cachedUpdateDataList)) { + veneerService.batchUpdate(BeanUtil.copyToList(cachedUpdateDataList, VeneerDO.class)); + } + log.debug("excel导入贴皮数据保存成功!"); + } + + /** + * 校验长度 + * + * @param key + * @param val + * @param length + */ + private void validFieldLength(String key, String val, int length, int rowIndex) { + if (StringUtils.isNotEmpty(val) && length < val.length()) { + throw new ServiceException(VENEER_IMPORT_LENGTH_ERROR, rowIndex, key, length); + } + } + + + /** + * 检查值大于0、数据浮点类型和长度 + * + * @param key + * @param value + * @param targetType + * @param length + * @param decimalLength + * @param rowIndex + */ + private void validPositiveFloatNumberSize(String key, String value, Class targetType, Integer length, Integer decimalLength, int rowIndex) { + if (StringUtils.isNotEmpty(value)) { + if (Boolean.TRUE.equals(ToolUtil.convertToType(value, targetType))) { + if (Boolean.TRUE.equals(checkPositiveNumber(value))) { + if (!ToolUtil.checkPrecision(value, length, decimalLength)) { + throw new ServiceException(VENEER_IMPORT_NUMBER_LENGTH_ERROR, rowIndex, key, length, decimalLength); + } + } else { + throw new ServiceException(VENEER_IMPORT_POSITIVE_NUMBER_ERROR, rowIndex, key); + } + } else { + throw new ServiceException(VENEER_IMPORT_DIGITAL_TYPE_ERROR, rowIndex, key); + } + } + } + + /** + * 检查值大于0、integer类型 + * @param key + * @param value + * @param rowIndex + */ + private void validPositiveNumber(String key, String value, int rowIndex) { + if (StringUtils.isNotEmpty(value)) { + if (Boolean.TRUE.equals(ToolUtil.convertToType(value, Integer.class))) { + if (Boolean.FALSE.equals(checkPositiveNumber(value))) { + throw new ServiceException(VENEER_IMPORT_POSITIVE_NUMBER_ERROR, rowIndex, key); + } + } else { + throw new ServiceException(VENEER_IMPORT_DIGITAL_TYPE_ERROR, rowIndex, key); + } + } + } + + /** + * 检查是否正数(不包含0) + * + * @param numberStr + * @return + */ + public static Boolean checkPositiveNumber(String numberStr) { + try { + double number = Double.parseDouble(numberStr); + // 进行正负判断 + if (number > 0) { + return true; + } else { + return false; + } + } catch (NumberFormatException e) { + return false; + } + } +} diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerService.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerService.java index 9cfdb8b0e..940ed2538 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerService.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerService.java @@ -1,12 +1,16 @@ package com.cf.imes.module.executor.service.veneer; import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDefectSaveReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerSaveReqVO; +import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.List; +import java.util.Map; /** * 贴皮管理 Service接口 @@ -63,4 +67,33 @@ public interface VeneerService { * @return */ VeneerRespVO get(Long id); + + /** + * 获取组织下所有贴皮编号:id的组合 + * + * @return + */ + Map getOrgVeneerNo(); + + /** + * 批量新增贴皮 + * + * @param list + */ + void batchInsert(List list); + + /** + * 批量更新贴皮 + * + * @param list + */ + void batchUpdate(List list); + + /** + * 保存瑕疵数据 + * + * @param defectSaveReqVO + * @return + */ + int saveDefect(VeneerDefectSaveReqVO defectSaveReqVO); } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerServiceImpl.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerServiceImpl.java index be315e794..0df360975 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerServiceImpl.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/service/veneer/VeneerServiceImpl.java @@ -3,15 +3,20 @@ package com.cf.imes.module.executor.service.veneer; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; +import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerDefectSaveReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerPageReqVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerRespVO; import com.cf.imes.module.executor.controller.admin.veneer.vo.VeneerSaveReqVO; import com.cf.imes.module.executor.dal.dataobject.veneer.VeneerDO; +import com.cf.imes.module.executor.dal.mysql.veneer.VeneerBatchMapper; import com.cf.imes.module.executor.dal.mysql.veneer.VeneerMapper; import com.cf.imes.module.infra.api.file.FileApi; import com.cf.imes.module.system.api.organ.OrganApi; @@ -23,7 +28,10 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.time.LocalDateTime; +import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; import static com.cf.imes.module.executor.enums.ErrorCodeConstants.VENEER_NOT_EXIST; @@ -46,6 +54,9 @@ public class VeneerServiceImpl implements VeneerService { @Resource private VeneerMapper veneerMapper; + @Resource + private VeneerBatchMapper veneerBatchMapper; + @Override public Long create(VeneerSaveReqVO reqVO) { // 1、校验贴皮编号 @@ -127,9 +138,47 @@ public class VeneerServiceImpl implements VeneerService { if (ObjectUtil.isNull(veneerDO)) { throw new ServiceException(VENEER_NOT_EXIST); } + String defectData = veneerDO.getDefectData(); + if (StringUtils.isNotEmpty(defectData)) { + veneerDO.setDefectData(JsonUtils.unzipString(defectData)); + } return BeanUtil.copyProperties(veneerDO, VeneerRespVO.class); } + @Override + public Map getOrgVeneerNo() { + return veneerMapper.selectList(new LambdaQueryWrapper().select(VeneerDO::getId, VeneerDO::getVeneerNo)).stream().collect(Collectors.toMap(VeneerDO::getVeneerNo, VeneerDO::getId)); + } + + @Override + public void batchInsert(List list) { + veneerBatchMapper.insertBatchSomeColumn(list); + } + + @Override + public void batchUpdate(List list) { + veneerBatchMapper.updateBatch(list); + } + + @Override + public int saveDefect(VeneerDefectSaveReqVO defectSaveReqVO) { + Long id = defectSaveReqVO.getId(); + // 校验贴皮是否存在 + if (ObjectUtil.isNull(validateExist(id))) { + throw new ServiceException(VENEER_NOT_EXIST); + } + + String defectData = defectSaveReqVO.getDefectData(); + if(StringUtils.isNotEmpty(defectData)) { + // 更新瑕疵数据和状态 + return veneerMapper.update(new LambdaUpdateWrapper() + .eq(VeneerDO::getId, id) + .set(VeneerDO::getDefect, true) + .set(VeneerDO::getDefectData, JsonUtils.zipString(defectData))); + } + return 1; + } + /** * 校验贴皮是否存在 * diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/util/file/FileHelperUtil.java b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/util/file/FileHelperUtil.java index 6f60b1ccd..7068da420 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/util/file/FileHelperUtil.java +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/java/com/cf/imes/module/executor/util/file/FileHelperUtil.java @@ -1,5 +1,6 @@ package com.cf.imes.module.executor.util.file; +import cn.hutool.core.util.CharsetUtil; import com.cf.imes.framework.common.exception.ServiceException; import org.springframework.web.multipart.MultipartFile; @@ -34,8 +35,8 @@ public class FileHelperUtil { fis.close(); response.reset(); - response.setCharacterEncoding("UTF-8"); - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); + response.setCharacterEncoding(CharsetUtil.UTF_8); + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, CharsetUtil.UTF_8)); response.addHeader("Content-Length", "" + file.length()); response.setContentType("application/octet-stream"); @@ -64,4 +65,44 @@ public class FileHelperUtil { return true; } + + /** + * 下载目录下第一个文件 + * + * @param dirPath + * @param response + */ + public static void getFirstFile(String dirPath, HttpServletResponse response) { + File dir = new File(dirPath); + if (!dir.exists() || !dir.isDirectory()) { + throw new ServiceException(FILE_NOT_EXISTS); + } + + File[] files = dir.listFiles(File::isFile); + if (files == null || files.length == 0) { + throw new ServiceException(FILE_NOT_EXISTS); + } + + File file = files[0]; + // 获取文件名 + String filename = file.getName(); + // 将文件写入输入流 + try (FileInputStream fis = new FileInputStream(file); + OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) { + byte[] buffer = new byte[fis.available()]; + fis.read(buffer); + fis.close(); + + response.reset(); + response.setCharacterEncoding(CharsetUtil.UTF_8); + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, CharsetUtil.UTF_8)); + response.addHeader("Content-Length", "" + file.length()); + + response.setContentType("application/octet-stream"); + outputStream.write(buffer); + outputStream.flush(); + } catch (IOException ex) { + throw new ServiceException(FILE_NOT_EXISTS); + } + } } diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message.properties index e604e7ddc..d60296ba9 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message.properties @@ -14,4 +14,5 @@ veneer.save.veneerNo.length.max=贴皮编号不能超过32个字符 veneer.save.veneerNo.not.null=贴皮编号不能为空 veneer.save.width.not.null=宽度不能为空 veneer.save.veneerName.not.null=贴皮名称不能为空 -veneer.save.veneerName.length.max=贴皮名称不能超过64个字符 \ No newline at end of file +veneer.save.veneerName.length.max=贴皮名称不能超过64个字符 +veneer.defect.save.id.not.null=贴皮ID不能为空 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_cht.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_cht.properties index 0fecfcb8a..d86a20658 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_cht.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_cht.properties @@ -6,7 +6,7 @@ veneer.save.factory.length.max=廠家不能超過128個字元 veneer.save.length.not.null=長度不能為空 veneer.save.material.length.max=材質不能超過64個字元 veneer.save.num.not.null=數量不能為空 -veneer.save.productLevel.not.null=产品等级不能為空 +veneer.save.productLevel.not.null=產品等級不能為空 veneer.save.remark.length.max=備註不能超過255個字元 veneer.save.spec.length.max=規格不能超過64個字元 veneer.save.thickness.not.null=厚度不能為空 @@ -14,4 +14,5 @@ veneer.save.veneerNo.length.max=貼皮編號不能超過32個字元 veneer.save.veneerNo.not.null=貼皮編號不能為空 veneer.save.width.not.null=寬度不能為空 veneer.save.veneerName.not.null=貼皮名稱不能為空 -veneer.save.veneerName.length.max=貼皮名稱不能超过64个字符 \ No newline at end of file +veneer.save.veneerName.length.max=貼皮名稱不能超过64个字符 +veneer.defect.save.id.not.null=貼皮ID不能為空 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_en.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_en.properties index fb8474ef8..36aa2f147 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_en.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_en.properties @@ -14,4 +14,5 @@ veneer.save.veneerNo.length.max=veener no length cannot exceed 32 characters veneer.save.veneerNo.not.null=veener no cannot be empty veneer.save.width.not.null=width cannot be empty veneer.save.veneerName.not.null=veneer name cannot be empty -veneer.save.veneerName.length.max=veneer name length cannot exceed 64 characters \ No newline at end of file +veneer.save.veneerName.length.max=veneer name length cannot exceed 64 characters +veneer.defect.save.id.not.null=veneer id cannot be empty \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum.properties index b63f0a272..8e7a186f1 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum.properties @@ -17,4 +17,7 @@ veneer.export.head.factory=厂家 veneer.export.head.num=数量 veneer.export.head.defect=瑕疵 veneer.export.head.remark=备注 -veneer.export.head.status=状态 \ No newline at end of file + +veneer.product.level.A=A类 +veneer.product.level.B=B类 +veneer.product.level.C=C类 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_cht.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_cht.properties index 65aa883ad..c75984451 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_cht.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_cht.properties @@ -17,4 +17,7 @@ veneer.export.head.factory=廠家 veneer.export.head.num=數量 veneer.export.head.defect=瑕疵 veneer.export.head.remark=備註 -veneer.export.head.status=狀態 \ No newline at end of file + +veneer.product.level.A=A類 +veneer.product.level.B=B類 +veneer.product.level.C=C類 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_en.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_en.properties index f5b8e70ea..075deb5f5 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_en.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_enum_en.properties @@ -17,4 +17,7 @@ veneer.export.head.factory=factory veneer.export.head.num=num veneer.export.head.defect=defect veneer.export.head.remark=remark -veneer.export.head.status=status \ No newline at end of file + +veneer.product.level.A=Class A +veneer.product.level.B=Class B +veneer.product.level.C=Class C \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode.properties index c15bd3676..12de73fc6 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode.properties @@ -14,4 +14,10 @@ global.org.product.expired=组织产品套餐已过期,请续费后继续使 global.not.login=账号未登录 veneer.not.exist=贴皮不存在 -veneer.no.exist=贴皮编号已存在 \ No newline at end of file +veneer.no.exist=贴皮编号已存在 + +veneer.import.required.lack=第{0}行存在带*必填项为空,请检查文件 +veneer.import.length.error=第{0}行{1}长度不能超过{2}个字符,请检查文件 +veneer.import.digital.type.error=第{0}行{1}只能输入数字,请检查文件 +veneer.import.positive.number.error=第{0}行{1}必须大于0,请检查文件 +veneer.import.number.length.error=第{0}行{1}长度不能超过整数{2}位、小数{3}位,请检查文件 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties index 9fc0d1ff8..0ae72d7a4 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_cht.properties @@ -14,4 +14,10 @@ global.org.product.expired=組織產品套餐已過期,請續費後繼續使 global.not.login=賬號未登錄 veneer.not.exist=貼皮不存在 -veneer.no.exist=貼皮編號已存在 \ No newline at end of file +veneer.no.exist=貼皮編號已存在 + +veneer.import.required.lack=第{0}行存在帶*必填項為空,請檢查檔案 +veneer.import.length.error=第{0}行{1}長度不能超過{2}個字元,請檢查檔案 +veneer.import.digital.type.error=第{0}行{1}只能輸入數位,請檢查檔案 +veneer.import.positive.number.error=第{0}行{1}必須大於0,請檢查檔案 +veneer.import.number.length.error=第{0}行{1}長度不能超過整數{2}位、小數{3}位,請檢查檔案 \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties index da4d6559f..0cd8c16da 100644 --- a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties +++ b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/message_errorcode_en.properties @@ -14,4 +14,10 @@ global.org.product.expired=The organization's product package has expired, pleas global.not.login=The account is not logged in veneer.not.exist=veneer not exist -veneer.no.exist=veneer no already exists \ No newline at end of file +veneer.no.exist=veneer no already exists + +veneer.import.required.lack=The required field is empty in line {0}, please check the file +veneer.import.length.error=The length of the field cannot exceed {0} characters in line {1}, please check the file +veneer.import.digital.type.error=The field can only be filled in with numbers in line {0}, please check the file +veneer.import.positive.number.error=The field must be greater than 0 in line {0}, please check the file +veneer.import.number.length.error=The length of the field cannot exceed integer {0} digits and decimal {1} digits in line {2}, please check the file \ No newline at end of file diff --git a/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/type/veneer/cht/貼皮導入範本.xlsx b/cf-module-prod-executor/cf-module-prod-executor-biz/src/main/resources/type/veneer/cht/貼皮導入範本.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a4fe4a0b67dbf1c1f0baa584c1041d747cb08f1e GIT binary patch literal 10340 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