From 6646aafae810e2068ac98d8ccf62e543db7e2848 Mon Sep 17 00:00:00 2001 From: Bujidao <3317431882@qq.com> Date: Thu, 5 Mar 2026 17:26:01 +0800 Subject: [PATCH] feat(codegen): add backend excel import template generation --- yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java | 2 ++ yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm | 25 ++++++++++++++++++++++++- yudao-module-infra/src/main/resources/codegen/java/controller/vo/importExcelVO.vm | 38 ++++++++++++++++++++++++++++++++++++++ yudao-module-infra/src/main/resources/codegen/java/controller/vo/importRespVO.vm | 23 +++++++++++++++++++++++ yudao-module-infra/src/main/resources/codegen/java/service/service.vm | 10 +++++++++- yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm | 35 ++++++++++++++++++++++++++++++++++- yudao-module-infra/src/main/resources/codegen/sql/sql.vm | 4 ++-- 7 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 yudao-module-infra/src/main/resources/codegen/java/controller/vo/importExcelVO.vm create mode 100644 yudao-module-infra/src/main/resources/codegen/java/controller/vo/importRespVO.vm diff --git a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index 458acb5..9b55bd8 100644 --- a/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java @@ -72,6 +72,8 @@ public class CodegenEngine { .put(javaTemplatePath("controller/vo/listReqVO"), javaModuleImplVOFilePath("ListReqVO")) .put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO")) .put(javaTemplatePath("controller/vo/saveReqVO"), javaModuleImplVOFilePath("SaveReqVO")) + .put(javaTemplatePath("controller/vo/importExcelVO"), javaModuleImplVOFilePath("ImportExcelVO")) + .put(javaTemplatePath("controller/vo/importRespVO"), javaModuleImplVOFilePath("ImportRespVO")) .put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath()) .put(javaTemplatePath("dal/do"), javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${table.className}DO")) diff --git a/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm index 9f63e24..efe073d 100644 --- a/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm @@ -1,6 +1,7 @@ package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import ${jakartaPackage}.annotation.Resource; import org.springframework.validation.annotation.Validated; #if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end @@ -160,6 +161,28 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { } #end + @GetMapping("/get-import-template") + @Operation(summary = "获得导入${table.classComment}模板") +#if ($sceneEnum.scene == 1) + @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')") +#end + public void import${simpleClassName}Template(HttpServletResponse response) throws IOException { + ExcelUtils.write(response, "${table.classComment}导入模板.xls", "数据", + ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class, Collections.emptyList()); + } + + @PostMapping("/import") + @Operation(summary = "导入${table.classComment}") + @Parameter(name = "file", description = "Excel 文件", required = true) +#if ($sceneEnum.scene == 1) + @PreAuthorize("@ss.hasPermission('${permissionPrefix}:import')") +#end + @ApiAccessLog(operateType = IMPORT) + public CommonResult<${sceneEnum.prefixClass}${table.className}ImportRespVO> import${simpleClassName}(@RequestParam("file") MultipartFile file) throws Exception { + List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> list = ExcelUtils.read(file, ${sceneEnum.prefixClass}${table.className}ImportExcelVO.class); + return success(${classNameVar}Service.import${simpleClassName}List(list)); + } + ## 特殊:主子表专属逻辑 #foreach ($subTable in $subTables) #set ($index = $foreach.count - 1) @@ -268,4 +291,4 @@ public class ${sceneEnum.prefixClass}${table.className}Controller { #end #end -} \ No newline at end of file +} diff --git a/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importExcelVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importExcelVO.vm new file mode 100644 index 0000000..51b0751 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importExcelVO.vm @@ -0,0 +1,38 @@ +package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +#foreach ($column in $columns) +#if (${column.createOperation} && "$!column.dictType" != "") +import ${DictFormatClassName}; +import ${DictConvertClassName}; +#break +#end +#end + +/** + * ${table.classComment} Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ${sceneEnum.prefixClass}${table.className}ImportExcelVO { + +## 逐个处理字段 +#foreach ($column in $columns) +#if (${column.createOperation}) +#if ("$!column.dictType" != "") + @ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class) + @DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 +#else + @ExcelProperty("${column.columnComment}") +#end + private ${column.javaType} ${column.javaField}; + +#end +#end +} diff --git a/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importRespVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importRespVO.vm new file mode 100644 index 0000000..c3d1520 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/importRespVO.vm @@ -0,0 +1,23 @@ +package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +@Schema(description = "${sceneEnum.name} - ${table.classComment}导入 Response VO") +@Data +@Builder +public class ${sceneEnum.prefixClass}${table.className}ImportRespVO { + + @Schema(description = "创建成功的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer successCount; + + @Schema(description = "导入失败的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer failureCount; + + @Schema(description = "导入失败的数据集合,key 为行号,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureRows; + +} diff --git a/yudao-module-infra/src/main/resources/codegen/java/service/service.vm b/yudao-module-infra/src/main/resources/codegen/java/service/service.vm index 1cc68bf..fda518c 100644 --- a/yudao-module-infra/src/main/resources/codegen/java/service/service.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/service/service.vm @@ -27,6 +27,14 @@ public interface ${table.className}Service { ${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar}); /** + * 导入${table.classComment} + * + * @param importList 导入信息 + * @return 导入结果 + */ + ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList); + + /** * 更新${table.classComment} * * @param ${updateReqVOVar} 更新信息 @@ -162,4 +170,4 @@ public interface ${table.className}Service { #end #end -} \ No newline at end of file +} diff --git a/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm b/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm index 42afdf9..deb5e96 100644 --- a/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm @@ -7,6 +7,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*; import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; ## 特殊:主子表专属逻辑 @@ -93,6 +94,35 @@ public class ${table.className}ServiceImpl implements ${table.className}Service } @Override + @Transactional(rollbackFor = Exception.class) + public ${sceneEnum.prefixClass}${table.className}ImportRespVO import${simpleClassName}List(List<${sceneEnum.prefixClass}${table.className}ImportExcelVO> importList) { + if (CollUtil.isEmpty(importList)) { + return ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder() + .successCount(0) + .failureCount(0) + .failureRows(new LinkedHashMap<>()) + .build(); + } + AtomicInteger successCount = new AtomicInteger(); + Map failureRows = new LinkedHashMap<>(); + AtomicInteger index = new AtomicInteger(1); + importList.forEach(importItem -> { + int currentIndex = index.getAndIncrement(); + try { + create${simpleClassName}(BeanUtils.toBean(importItem, ${saveReqVOClass}.class)); + successCount.incrementAndGet(); + } catch (Exception ex) { + failureRows.put(currentIndex, ex.getMessage()); + } + }); + return ${sceneEnum.prefixClass}${table.className}ImportRespVO.builder() + .successCount(successCount.get()) + .failureCount(failureRows.size()) + .failureRows(failureRows) + .build(); + } + + @Override ## 特殊:主子表专属逻辑(非 ERP 模式) #if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 ) @Transactional(rollbackFor = Exception.class) @@ -359,6 +389,9 @@ public class ${table.className}ServiceImpl implements ${table.className}Service #else #if ( $subTable.subJoinMany) private void create${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) { + if (CollUtil.isEmpty(list)) { + return; + } list.forEach(o -> o.set${SubJoinColumnName}(${subJoinColumn.javaField}).clean()); ${subClassNameVars.get($index)}Mapper.insertBatch(list); } @@ -416,4 +449,4 @@ public class ${table.className}ServiceImpl implements ${table.className}Service #end #end -} \ No newline at end of file +} diff --git a/yudao-module-infra/src/main/resources/codegen/sql/sql.vm b/yudao-module-infra/src/main/resources/codegen/sql/sql.vm index 6e50837..82a5cb0 100644 --- a/yudao-module-infra/src/main/resources/codegen/sql/sql.vm +++ b/yudao-module-infra/src/main/resources/codegen/sql/sql.vm @@ -1,6 +1,6 @@ ## 通用变量定义 -#set ($functionNames = ['查询', '创建', '更新', '删除', '导出']) -#set ($functionOps = ['query', 'create', 'update', 'delete', 'export']) +#set ($functionNames = ['查询', '创建', '更新', '删除', '导出', '导入']) +#set ($functionOps = ['query', 'create', 'update', 'delete', 'export', 'import']) ## ## 宏定义:生成按钮 SQL(通用部分) #macro(insertButtonSql $parentIdVar) -- libgit2 0.21.4