Browse Source

代码生成:增加主子表的示例

YunaiV 1 year atrás
parent
commit
256c560d43
24 changed files with 1219 additions and 151 deletions
  1. 5 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
  2. 5 1
      yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
  3. 115 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/InfraDemo11StudentController.java
  4. 6 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/package-info.java
  5. 57 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentBaseVO.java
  6. 14 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentCreateReqVO.java
  7. 53 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentExcelVO.java
  8. 32 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentExportReqVO.java
  9. 34 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentPageReqVO.java
  10. 19 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentRespVO.java
  11. 18 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentUpdateReqVO.java
  12. 34 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/demo11/InfraDemo11StudentConvert.java
  13. 72 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentContactDO.java
  14. 67 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentDO.java
  15. 71 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentTeacherDO.java
  16. 28 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentContactMapper.java
  17. 40 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentMapper.java
  18. 28 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentTeacherMapper.java
  19. 86 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo11/InfraDemo11StudentService.java
  20. 153 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo11/InfraDemo11StudentServiceImpl.java
  21. 10 5
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
  22. 19 14
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm
  23. 12 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo11/InfraDemo11StudentMapper.xml
  24. 241 130
      yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java

+ 5 - 1
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java

@@ -128,7 +128,11 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
         Db.updateBatchById(entities, size);
     }
 
-    default void saveOrUpdateBatch(Collection<T> collection) {
+    default void insertOrUpdate(T entity) {
+        Db.saveOrUpdate(entity);
+    }
+
+    default void insertOrUpdateBatch(Collection<T> collection) {
         Db.saveOrUpdateBatch(collection);
     }
 

+ 5 - 1
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java

@@ -54,6 +54,10 @@ public interface ErrorCodeConstants {
     // ========== 数据源配置 1-001-107-000 ==========
     ErrorCode DEMO_STUDENT_NOT_EXISTS = new ErrorCode(1_001_107_000, "学生不存在");
 
-    // ========== 学生(CodegenTemplateTypeEnum.ONE 示例) 1-001-200-000 ==========
+    // ========== 学生(CodegenTemplateTypeEnum.ONE 示例) 1-001-201-000 ==========
     ErrorCode DEMO01_STUDENT_NOT_EXISTS = new ErrorCode(1_001_200_000, "学生不存在");
+
+    // ========== 学生(CodegenTemplateTypeEnum.ONE 示例) 1-001-211-000 ==========
+    ErrorCode DEMO11_STUDENT_NOT_EXISTS = new ErrorCode(1_001_211_000, "学生不存在");
+
 }

+ 115 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/InfraDemo11StudentController.java

@@ -0,0 +1,115 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentTeacherDO;
+import cn.iocoder.yudao.module.infra.convert.demo11.InfraDemo11StudentConvert;
+import cn.iocoder.yudao.module.infra.service.demo11.InfraDemo11StudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/demo11-student")
+@Validated
+public class InfraDemo11StudentController {
+
+    @Resource
+    private InfraDemo11StudentService demo11StudentService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建学生")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:create')")
+    public CommonResult<Long> createDemo11Student(@Valid @RequestBody InfraDemo11StudentCreateReqVO createReqVO) {
+        return success(demo11StudentService.createDemo11Student(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新学生")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:update')")
+    public CommonResult<Boolean> updateDemo11Student(@Valid @RequestBody InfraDemo11StudentUpdateReqVO updateReqVO) {
+        demo11StudentService.updateDemo11Student(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除学生")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:delete')")
+    public CommonResult<Boolean> deleteDemo11Student(@RequestParam("id") Long id) {
+        demo11StudentService.deleteDemo11Student(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得学生")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:query')")
+    public CommonResult<InfraDemo11StudentRespVO> getDemo11Student(@RequestParam("id") Long id) {
+        InfraDemo11StudentDO demo11Student = demo11StudentService.getDemo11Student(id);
+        return success(InfraDemo11StudentConvert.INSTANCE.convert(demo11Student));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得学生分页")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:query')")
+    public CommonResult<PageResult<InfraDemo11StudentRespVO>> getDemo11StudentPage(@Valid InfraDemo11StudentPageReqVO pageVO) {
+        PageResult<InfraDemo11StudentDO> pageResult = demo11StudentService.getDemo11StudentPage(pageVO);
+        return success(InfraDemo11StudentConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出学生 Excel")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:export')")
+    @OperateLog(type = EXPORT)
+    public void exportDemo11StudentExcel(@Valid InfraDemo11StudentExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<InfraDemo11StudentDO> list = demo11StudentService.getDemo11StudentList(exportReqVO);
+        // 导出 Excel
+        List<InfraDemo11StudentExcelVO> datas = InfraDemo11StudentConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "学生.xls", "数据", InfraDemo11StudentExcelVO.class, datas);
+    }
+
+    // ==================== 子表(学生联系人) ====================
+
+    @GetMapping("/demo11-student/list-by-student-id")
+    @Operation(summary = "获得学生联系人列表")
+    @Parameter(name = "studentId", description = "学生编号")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:query')")
+    public CommonResult<List<InfraDemo11StudentContactDO>> getDemo11StudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
+        return success(demo11StudentService.getDemo11StudentContactListByStudentId(studentId));
+    }
+
+    // ==================== 子表(学生班主任) ====================
+
+    @GetMapping("/demo11-student/get-by-student-id")
+    @Operation(summary = "获得学生班主任")
+    @Parameter(name = "studentId", description = "学生编号")
+    @PreAuthorize("@ss.hasPermission('infra:demo11-student:query')")
+    public CommonResult<InfraDemo11StudentTeacherDO> getDemo11StudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
+        return success(demo11StudentService.getDemo11StudentTeacherByStudentId(studentId));
+    }
+
+}

+ 6 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 代码生成示例:单表(增删改查)
+ *
+ * @see cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum#getType()
+ */
+package cn.iocoder.yudao.module.infra.controller.admin.demo11;

+ 57 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentBaseVO.java

@@ -0,0 +1,57 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import javax.validation.constraints.*;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentTeacherDO;
+
+/**
+ * 学生 Base VO,提供给添加、修改、详细的子 VO 使用
+ * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+ */
+@Data
+public class InfraDemo11StudentBaseVO {
+
+    @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+    @NotEmpty(message = "名字不能为空")
+    private String name;
+
+    @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+    @NotEmpty(message = "简介不能为空")
+    private String description;
+
+    @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "出生日期不能为空")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime birthday;
+
+    @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "性别不能为空")
+    private Integer sex;
+
+    @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    @NotNull(message = "是否有效不能为空")
+    private Boolean enabled;
+
+    @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+    @NotEmpty(message = "头像不能为空")
+    private String avatar;
+
+    @Schema(description = "附件", example = "https://www.iocoder.cn/1.mp4")
+    private String video;
+
+    @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+    @NotEmpty(message = "备注不能为空")
+    private String memo;
+
+    private List<InfraDemo11StudentContactDO> demo11StudentContacts;
+
+    private InfraDemo11StudentTeacherDO demo11StudentTeacher;
+
+}

+ 14 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 学生创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfraDemo11StudentCreateReqVO extends InfraDemo11StudentBaseVO {
+
+}

+ 53 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentExcelVO.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+
+/**
+ * 学生 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class InfraDemo11StudentExcelVO {
+
+    @ExcelProperty("编号")
+    private Long id;
+
+    @ExcelProperty("名字")
+    private String name;
+
+    @ExcelProperty("简介")
+    private String description;
+
+    @ExcelProperty("出生日期")
+    private LocalDateTime birthday;
+
+    @ExcelProperty(value = "性别", converter = DictConvert.class)
+    @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Integer sex;
+
+    @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+    @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
+    private Boolean enabled;
+
+    @ExcelProperty("头像")
+    private String avatar;
+
+    @ExcelProperty("附件")
+    private String video;
+
+    @ExcelProperty("备注")
+    private String memo;
+
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 32 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentExportReqVO.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import java.time.LocalDateTime;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 学生 Excel 导出 Request VO,参数和 InfraDemo11StudentPageReqVO 是一致的")
+@Data
+public class InfraDemo11StudentExportReqVO {
+
+    @Schema(description = "名字", example = "芋头")
+    private String name;
+
+    @Schema(description = "出生日期")
+    private LocalDateTime birthday;
+
+    @Schema(description = "性别", example = "1")
+    private Integer sex;
+
+    @Schema(description = "是否有效", example = "true")
+    private Boolean enabled;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 34 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentPageReqVO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 学生分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfraDemo11StudentPageReqVO extends PageParam {
+
+    @Schema(description = "名字", example = "芋头")
+    private String name;
+
+    @Schema(description = "出生日期")
+    private LocalDateTime birthday;
+
+    @Schema(description = "性别", example = "1")
+    private Integer sex;
+
+    @Schema(description = "是否有效", example = "true")
+    private Boolean enabled;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 19 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 学生 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfraDemo11StudentRespVO extends InfraDemo11StudentBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+}

+ 18 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo11/vo/InfraDemo11StudentUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo11.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+
+@Schema(description = "管理后台 - 学生更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfraDemo11StudentUpdateReqVO extends InfraDemo11StudentBaseVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 34 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/demo11/InfraDemo11StudentConvert.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.convert.demo11;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentDO;
+
+/**
+ * 学生 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraDemo11StudentConvert {
+
+    InfraDemo11StudentConvert INSTANCE = Mappers.getMapper(InfraDemo11StudentConvert.class);
+
+    InfraDemo11StudentDO convert(InfraDemo11StudentCreateReqVO bean);
+
+    InfraDemo11StudentDO convert(InfraDemo11StudentUpdateReqVO bean);
+
+    InfraDemo11StudentRespVO convert(InfraDemo11StudentDO bean);
+
+    List<InfraDemo11StudentRespVO> convertList(List<InfraDemo11StudentDO> list);
+
+    PageResult<InfraDemo11StudentRespVO> convertPage(PageResult<InfraDemo11StudentDO> page);
+
+    List<InfraDemo11StudentExcelVO> convertList02(List<InfraDemo11StudentDO> list);
+
+}

+ 72 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentContactDO.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo11;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 学生联系人 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo11_student_contact")
+@KeySequence("infra_demo11_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraDemo11StudentContactDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 学生编号
+     */
+    private Long studentId;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 简介
+     */
+    private String description;
+    /**
+     * 出生日期
+     */
+    private LocalDateTime birthday;
+    /**
+     * 性别
+     *
+     * 枚举 {@link TODO system_user_sex 对应的类}
+     */
+    private Integer sex;
+    /**
+     * 是否有效
+     *
+     * 枚举 {@link TODO infra_boolean_string 对应的类}
+     */
+    private Boolean enabled;
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 附件
+     */
+    private String video;
+    /**
+     * 备注
+     */
+    private String memo;
+
+}

+ 67 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentDO.java

@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo11;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 学生 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo11_student")
+@KeySequence("infra_demo11_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraDemo11StudentDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 简介
+     */
+    private String description;
+    /**
+     * 出生日期
+     */
+    private LocalDateTime birthday;
+    /**
+     * 性别
+     *
+     * 枚举 {@link TODO system_user_sex 对应的类}
+     */
+    private Integer sex;
+    /**
+     * 是否有效
+     *
+     * 枚举 {@link TODO infra_boolean_string 对应的类}
+     */
+    private Boolean enabled;
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 附件
+     */
+    private String video;
+    /**
+     * 备注
+     */
+    private String memo;
+
+}

+ 71 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo11/InfraDemo11StudentTeacherDO.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo11;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 学生班主任 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo11_student_teacher")
+@KeySequence("infra_demo11_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraDemo11StudentTeacherDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 学生编号
+     */
+    private Long studentId;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 简介
+     */
+    private String description;
+    /**
+     * 出生日期
+     */
+    private LocalDateTime birthday;
+    /**
+     * 性别
+     *
+     * 枚举 {@link TODO system_user_sex 对应的类}
+     */
+    private Integer sex;
+    /**
+     * 是否有效
+     *
+     * 枚举 {@link TODO infra_boolean_string 对应的类}
+     */
+    private Boolean enabled;
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 附件
+     */
+    private String video;
+    /**
+     * 备注
+     */
+    private String memo;
+
+}

+ 28 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentContactMapper.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo11;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraDemo11StudentContactMapper extends BaseMapperX<InfraDemo11StudentContactDO> {
+
+    default List<InfraDemo11StudentContactDO> selectListByStudentId(Long studentId) {
+        return selectList(InfraDemo11StudentContactDO::getStudentId, studentId);
+    }
+
+    default int deleteByStudentId(Long studentId) {
+        return delete(InfraDemo11StudentContactDO::getStudentId, studentId);
+    }
+
+}

+ 40 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentMapper.java

@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo11;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraDemo11StudentMapper extends BaseMapperX<InfraDemo11StudentDO> {
+
+    default PageResult<InfraDemo11StudentDO> selectPage(InfraDemo11StudentPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<InfraDemo11StudentDO>()
+                .likeIfPresent(InfraDemo11StudentDO::getName, reqVO.getName())
+                .eqIfPresent(InfraDemo11StudentDO::getBirthday, reqVO.getBirthday())
+                .eqIfPresent(InfraDemo11StudentDO::getSex, reqVO.getSex())
+                .eqIfPresent(InfraDemo11StudentDO::getEnabled, reqVO.getEnabled())
+                .betweenIfPresent(InfraDemo11StudentDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(InfraDemo11StudentDO::getId));
+    }
+
+    default List<InfraDemo11StudentDO> selectList(InfraDemo11StudentExportReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<InfraDemo11StudentDO>()
+                .likeIfPresent(InfraDemo11StudentDO::getName, reqVO.getName())
+                .eqIfPresent(InfraDemo11StudentDO::getBirthday, reqVO.getBirthday())
+                .eqIfPresent(InfraDemo11StudentDO::getSex, reqVO.getSex())
+                .eqIfPresent(InfraDemo11StudentDO::getEnabled, reqVO.getEnabled())
+                .betweenIfPresent(InfraDemo11StudentDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(InfraDemo11StudentDO::getId));
+    }
+
+}

+ 28 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo11/InfraDemo11StudentTeacherMapper.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo11;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentTeacherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班主任 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraDemo11StudentTeacherMapper extends BaseMapperX<InfraDemo11StudentTeacherDO> {
+
+    default InfraDemo11StudentTeacherDO selectByStudentId(Long studentId) {
+        return selectOne(InfraDemo11StudentTeacherDO::getStudentId, studentId);
+    }
+
+    default int deleteByStudentId(Long studentId) {
+        return delete(InfraDemo11StudentTeacherDO::getStudentId, studentId);
+    }
+
+}

+ 86 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo11/InfraDemo11StudentService.java

@@ -0,0 +1,86 @@
+package cn.iocoder.yudao.module.infra.service.demo11;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraDemo11StudentService {
+
+    /**
+     * 创建学生
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDemo11Student(@Valid InfraDemo11StudentCreateReqVO createReqVO);
+
+    /**
+     * 更新学生
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateDemo11Student(@Valid InfraDemo11StudentUpdateReqVO updateReqVO);
+
+    /**
+     * 删除学生
+     *
+     * @param id 编号
+     */
+    void deleteDemo11Student(Long id);
+
+    /**
+     * 获得学生
+     *
+     * @param id 编号
+     * @return 学生
+     */
+    InfraDemo11StudentDO getDemo11Student(Long id);
+
+    /**
+     * 获得学生分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 学生分页
+     */
+    PageResult<InfraDemo11StudentDO> getDemo11StudentPage(InfraDemo11StudentPageReqVO pageReqVO);
+
+    /**
+     * 获得学生列表, 用于 Excel 导出
+     *
+     * @param exportReqVO 查询条件
+     * @return 学生列表
+     */
+    List<InfraDemo11StudentDO> getDemo11StudentList(InfraDemo11StudentExportReqVO exportReqVO);
+
+
+    // ==================== 子表(学生联系人) ====================
+
+    /**
+     * 获得学生联系人列表
+     *
+     * @param studentId 学生编号
+     * @return 学生联系人列表
+     */
+    List<InfraDemo11StudentContactDO> getDemo11StudentContactListByStudentId(Long studentId);
+
+
+    // ==================== 子表(学生班主任) ====================
+
+    /**
+     * 获得学生班主任
+     *
+     * @param studentId 学生编号
+     * @return 学生班主任
+     */
+    InfraDemo11StudentTeacherDO getDemo11StudentTeacherByStudentId(Long studentId);
+
+}

+ 153 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo11/InfraDemo11StudentServiceImpl.java

@@ -0,0 +1,153 @@
+package cn.iocoder.yudao.module.infra.service.demo11;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.InfraDemo11StudentCreateReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.InfraDemo11StudentExportReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.InfraDemo11StudentPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo11.vo.InfraDemo11StudentUpdateReqVO;
+import cn.iocoder.yudao.module.infra.convert.demo11.InfraDemo11StudentConvert;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo11.InfraDemo11StudentTeacherDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo11.InfraDemo11StudentContactMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo11.InfraDemo11StudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo11.InfraDemo11StudentTeacherMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DEMO11_STUDENT_NOT_EXISTS;
+
+/**
+ * 学生 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class InfraDemo11StudentServiceImpl implements InfraDemo11StudentService {
+
+    @Resource
+    private InfraDemo11StudentMapper demo11StudentMapper;
+    @Resource
+    private InfraDemo11StudentContactMapper demo11StudentContactMapper;
+    @Resource
+    private InfraDemo11StudentTeacherMapper demo11StudentTeacherMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createDemo11Student(InfraDemo11StudentCreateReqVO createReqVO) {
+        // 插入
+        InfraDemo11StudentDO demo11Student = InfraDemo11StudentConvert.INSTANCE.convert(createReqVO);
+        demo11StudentMapper.insert(demo11Student);
+
+        // 插入子表
+        createDemo11StudentContactList(demo11Student.getId(), createReqVO.getDemo11StudentContacts());
+        createDemo11StudentTeacher(demo11Student.getId(), createReqVO.getDemo11StudentTeacher());
+        // 返回
+        return demo11Student.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateDemo11Student(InfraDemo11StudentUpdateReqVO updateReqVO) {
+        // 校验存在
+        validateDemo11StudentExists(updateReqVO.getId());
+        // 更新
+        InfraDemo11StudentDO updateObj = InfraDemo11StudentConvert.INSTANCE.convert(updateReqVO);
+        demo11StudentMapper.updateById(updateObj);
+
+        // 更新子表
+        updateDemo11StudentContactList(updateReqVO.getId(), updateReqVO.getDemo11StudentContacts());
+        updateDemo11StudentTeacher(updateReqVO.getId(), updateReqVO.getDemo11StudentTeacher());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDemo11Student(Long id) {
+        // 校验存在
+        validateDemo11StudentExists(id);
+        // 删除
+        demo11StudentMapper.deleteById(id);
+
+        // 删除子表
+        deleteDemo11StudentContactByStudentId(id);
+        deleteDemo11StudentTeacherByStudentId(id);
+    }
+
+    private void validateDemo11StudentExists(Long id) {
+        if (demo11StudentMapper.selectById(id) == null) {
+            throw exception(DEMO11_STUDENT_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public InfraDemo11StudentDO getDemo11Student(Long id) {
+        return demo11StudentMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<InfraDemo11StudentDO> getDemo11StudentPage(InfraDemo11StudentPageReqVO pageReqVO) {
+        return demo11StudentMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<InfraDemo11StudentDO> getDemo11StudentList(InfraDemo11StudentExportReqVO exportReqVO) {
+        return demo11StudentMapper.selectList(exportReqVO);
+    }
+
+    // ==================== 子表(学生联系人) ====================
+
+    @Override
+    public List<InfraDemo11StudentContactDO> getDemo11StudentContactListByStudentId(Long studentId) {
+        return demo11StudentContactMapper.selectListByStudentId(studentId);
+    }
+
+    private void createDemo11StudentContactList(Long studentId, List<InfraDemo11StudentContactDO> list) {
+        list.forEach(o -> o.setStudentId(studentId));
+        demo11StudentContactMapper.insertBatch(list);
+    }
+
+    private void updateDemo11StudentContactList(Long studentId, List<InfraDemo11StudentContactDO> list) {
+        deleteDemo11StudentContactByStudentId(studentId);
+        list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+        createDemo11StudentContactList(studentId, list);
+    }
+
+    private void deleteDemo11StudentContactByStudentId(Long studentId) {
+        demo11StudentContactMapper.deleteByStudentId(studentId);
+    }
+
+    // ==================== 子表(学生班主任) ====================
+
+    @Override
+    public InfraDemo11StudentTeacherDO getDemo11StudentTeacherByStudentId(Long studentId) {
+        return demo11StudentTeacherMapper.selectByStudentId(studentId);
+    }
+
+    private void createDemo11StudentTeacher(Long studentId, InfraDemo11StudentTeacherDO demo11StudentTeacher) {
+        if (demo11StudentTeacher == null) {
+            return;
+        }
+        demo11StudentTeacher.setStudentId(studentId);
+        demo11StudentTeacherMapper.insert(demo11StudentTeacher);
+    }
+
+    private void updateDemo11StudentTeacher(Long studentId, InfraDemo11StudentTeacherDO demo11StudentTeacher) {
+        if (demo11StudentTeacher == null) {
+            return;
+        }
+        demo11StudentTeacher.setStudentId(studentId);
+        demo11StudentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+        demo11StudentTeacherMapper.insertOrUpdate(demo11StudentTeacher);
+    }
+
+    private void deleteDemo11StudentTeacherByStudentId(Long studentId) {
+        demo11StudentTeacherMapper.deleteByStudentId(studentId);
+    }
+
+}

+ 10 - 5
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm

@@ -25,9 +25,6 @@ import ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.busines
 import static ${ServiceExceptionUtilClassName}.exception;
 import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.ListUtil;
-
 /**
  * ${table.classComment} Service 实现类
  *
@@ -58,7 +55,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
 ## 特殊:主子表专属逻辑
 #if ( $subTables && $subTables.size() > 0)
 
-        // 插入子表($subTable.classComment)
+        // 插入子表
 #foreach ($subTable in $subTables)
 #set ($index = $foreach.count - 1)
 #set ($subSimpleClassName = $subSimpleClassNames.get($index))
@@ -210,18 +207,26 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
 
     private void update${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {
         delete${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField});
+		list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
         create${subSimpleClassName}List(${subJoinColumn.javaField}, list);
     }
 
     #else
     private void create${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
+        if (${subClassNameVar} == null) {
+            return;
+        }
         ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});
         ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});
     }
 
     private void update${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
+        if (${subClassNameVar} == null) {
+			return;
+        }
         ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});
-        ${subClassNameVars.get($index)}Mapper.updateById(${subClassNameVar});
+        ${subClassNameVar}.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+        ${subClassNameVars.get($index)}Mapper.insertOrUpdate(${subClassNameVar});
     }
 
     #end

+ 19 - 14
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm

@@ -253,6 +253,7 @@
       #end
   #end
 #end
+  </el-form>
 #end
 </template>
 <script setup lang="ts">
@@ -278,32 +279,36 @@ const formRef = ref() // 表单 Ref
 watch(
   () => props.${subJoinColumn.javaField},
   async (val) => {
-    // 情况一:val 为空,说明是新增,则置空
-    if (!val) {
+    // 1. 重置表单
 #if ( $subTable.subJoinMany )
-      formData.value = []
+    formData.value = []
 #else
-      formData.value = {
-      #foreach ($column in $subColumns)
-        #if ($column.createOperation || $column.updateOperation)
-          #if ($column.htmlType == "checkbox")
-        $column.javaField: [],
-          #else
-        $column.javaField: undefined,
-          #end
+    formData.value = {
+    #foreach ($column in $subColumns)
+      #if ($column.createOperation || $column.updateOperation)
+        #if ($column.htmlType == "checkbox")
+      $column.javaField: [],
+        #else
+      $column.javaField: undefined,
         #end
       #end
-      }
+    #end
+    }
 #end
+    // 2. val 非空,则加载数据
+    if (!val) {
       return;
     }
-    // 情况二:val 非空,说明是修改,则加载数据
     try {
       formLoading.value = true
 #if ( $subTable.subJoinMany )
       formData.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val)
 #else
-      formData.value = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val)
+      const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val)
+      if (!data) {
+        return
+      }
+      formData.value = data
 #end
     } finally {
       formLoading.value = false

+ 12 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo11/InfraDemo11StudentMapper.xml

@@ -0,0 +1,12 @@
+<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo11.InfraDemo11StudentMapper">
+
+    <!--
+        一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
+        无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
+        代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
+        文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
+     -->
+
+</mapper>

+ 241 - 130
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineTest.java

@@ -129,12 +129,7 @@ public class CodegenEngineTest extends BaseMockitoUnitTest {
         Map<String, String> result = codegenEngine.execute(table, columns, null, null);
 
         // 构建 zip 包
-        String[] paths = result.keySet().toArray(new String[0]);
-        ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        ZipUtil.zip(outputStream, paths, ins);
-
-        FileUtil.writeBytes(outputStream.toByteArray(), "/Users/yunai/test/demo01.zip");
+        writeFile(result, "/Users/yunai/test/demo01.zip");
 
         // 断言
         assertEquals(21, result.size());
@@ -186,175 +181,273 @@ public class CodegenEngineTest extends BaseMockitoUnitTest {
     public void testExecute_vue3_masterSub() {
         // 准备请求参数
         // 主表
+        CodegenTableDO table = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene()).setParentMenuId(888L)
+                .setTableName("infra_demo11_student").setTableComment("学生表")
+                .setModuleName("infra").setBusinessName("demo11").setClassName("InfraDemo11Student")
+                .setClassComment("学生").setAuthor("芋道源码")
+                .setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType())
+                .setFrontType(CodegenFrontTypeEnum.VUE3.getType());
+        CodegenColumnDO idColumn = new CodegenColumnDO().setColumnName("id").setDataType(JdbcType.BIGINT.name())
+                .setId(100L)
+                .setColumnComment("编号").setNullable(false).setPrimaryKey(true).setAutoIncrement(true)
+                .setJavaType("Long").setJavaField("id").setExample("1024")
+                .setCreateOperation(false).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true);
+        CodegenColumnDO nameColumn = new CodegenColumnDO().setColumnName("name").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("名字").setNullable(false)
+                .setJavaType("String").setJavaField("name").setExample("芋头")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
+        CodegenColumnDO descriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("简介").setNullable(false)
+                .setJavaType("String").setJavaField("description").setExample("我是介绍")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType());
+        CodegenColumnDO birthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name())
+                .setColumnComment("出生日期").setNullable(false)
+                .setJavaType("LocalDateTime").setJavaField("birthday")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
+        CodegenColumnDO sexColumn = new CodegenColumnDO().setColumnName("sex").setDataType(JdbcType.INTEGER.name())
+                .setColumnComment("性别").setNullable(false)
+                .setJavaType("Integer").setJavaField("sex").setExample("1")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType())
+                .setDictType("system_user_sex");
+        CodegenColumnDO enabledColumn = new CodegenColumnDO().setColumnName("enabled").setDataType(JdbcType.BOOLEAN.name())
+                .setColumnComment("是否有效").setNullable(false)
+                .setJavaType("Boolean").setJavaField("enabled").setExample("true")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType())
+                .setDictType("infra_boolean_string");
+        CodegenColumnDO avatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("头像").setNullable(false)
+                .setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD.getType());
+        CodegenColumnDO videoColumn = new CodegenColumnDO().setColumnName("video").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("附件").setNullable(true)
+                .setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.FILE_UPLOAD.getType());
+        CodegenColumnDO memoColumn = new CodegenColumnDO().setColumnName("memo").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("备注").setNullable(false)
+                .setJavaType("String").setJavaField("memo").setExample("我是备注")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.EDITOR.getType());
+        CodegenColumnDO createTimeColumn = new CodegenColumnDO().setColumnName("create_time").setDataType(JdbcType.DATE.name())
+                .setColumnComment("创建时间").setNullable(true)
+                .setJavaType("LocalDateTime").setJavaField("createTime")
+                .setCreateOperation(false).setUpdateOperation(false)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
+        List<CodegenColumnDO> columns = Arrays.asList(idColumn, nameColumn, descriptionColumn, birthdayColumn,
+                sexColumn, enabledColumn, avatarColumn, videoColumn, memoColumn, createTimeColumn);
 
         // 子表(联系人)
         CodegenTableDO contactTable = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene())
-                .setTableName("infra_demo_student_contact").setTableComment("学生联系人表")
-                .setModuleName("infra").setBusinessName("demo02").setClassName("InfraDemoStudentContact")
+                .setTableName("infra_demo11_student_contact").setTableComment("学生联系人表")
+                .setModuleName("infra").setBusinessName("demo11").setClassName("InfraDemo11StudentContact")
                 .setClassComment("学生联系人").setAuthor("芋道源码")
                 .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
                 .setFrontType(CodegenFrontTypeEnum.VUE3.getType())
                 .setSubJoinColumnId(100L).setSubJoinMany(true);
         CodegenColumnDO contactIdColumn = new CodegenColumnDO().setColumnName("id").setDataType(JdbcType.BIGINT.name())
                 .setColumnComment("编号").setNullable(false).setPrimaryKey(true).setAutoIncrement(true)
-                .setOrdinalPosition(1).setJavaType("Long").setJavaField("id").setExample("1024")
-                .setCreateOperation(false).setUpdateOperation(true).setListOperation(false)
+                .setJavaType("Long").setJavaField("id").setExample("1024")
+                .setCreateOperation(false).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true);
         CodegenColumnDO contactStudentIdColumn = new CodegenColumnDO().setColumnName("student_id").setDataType(JdbcType.BIGINT.name())
                 .setColumnComment("学生编号").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(2).setJavaType("Long").setJavaField("studentId").setExample("2048")
-                .setCreateOperation(false).setUpdateOperation(true).setListOperation(false)
+                .setJavaType("Long").setJavaField("studentId").setExample("2048")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setId(100L);
         CodegenColumnDO contactNameColumn = new CodegenColumnDO().setColumnName("name").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("名字").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(3).setJavaType("String").setJavaField("name").setExample("芋头")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition()).setListOperationResult(true)
+                .setColumnComment("名字").setNullable(false)
+                .setJavaType("String").setJavaField("name").setExample("芋头")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition())
+                .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
-        CodegenColumnDO contactsAvatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("头像").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(4).setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+        CodegenColumnDO contactDescriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("简介").setNullable(false)
+                .setJavaType("String").setJavaField("description").setExample("我是介绍")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType());
+        CodegenColumnDO contactBirthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name())
+                .setColumnComment("出生日期").setNullable(false)
+                .setJavaType("LocalDateTime").setJavaField("birthday")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
+        CodegenColumnDO contactSexColumn = new CodegenColumnDO().setColumnName("sex").setDataType(JdbcType.INTEGER.name())
+                .setColumnComment("性别").setNullable(false)
+                .setJavaType("Integer").setJavaField("sex").setExample("1")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType())
+                .setDictType("system_user_sex");
+        CodegenColumnDO contactEnabledColumn = new CodegenColumnDO().setColumnName("enabled").setDataType(JdbcType.BOOLEAN.name())
+                .setColumnComment("是否有效").setNullable(false)
+                .setJavaType("Boolean").setJavaField("enabled").setExample("true")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType())
+                .setDictType("infra_boolean_string");
+        CodegenColumnDO contactAvatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("头像").setNullable(false)
+                .setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD.getType());
         CodegenColumnDO contactVideoColumn = new CodegenColumnDO().setColumnName("video").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("视频").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(5).setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+                .setColumnComment("附件").setNullable(true)
+                .setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.FILE_UPLOAD.getType());
-        CodegenColumnDO contactDescriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("个人简介").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(6).setJavaType("String").setJavaField("description").setExample("我是介绍")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
-                .setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.EDITOR.getType());
-        CodegenColumnDO contactSex1Column = new CodegenColumnDO().setColumnName("sex1").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("性别 1").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(7).setJavaType("String").setJavaField("sex1").setExample("男")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType()).setDictType("system_sex1");
-        CodegenColumnDO contactSex2Column = new CodegenColumnDO().setColumnName("sex2").setDataType(JdbcType.INTEGER.name())
-                .setColumnComment("性别 2").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(8).setJavaType("Integer").setJavaField("sex2").setExample("1")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.CHECKBOX.getType()).setDictType("system_sex2");
-        CodegenColumnDO contactSex3Column = new CodegenColumnDO().setColumnName("sex3").setDataType(JdbcType.BOOLEAN.name())
-                .setColumnComment("性别 3").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(9).setJavaType("Boolean").setJavaField("sex3").setExample("true")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType()).setDictType("system_sex3");
-        CodegenColumnDO contactBirthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name())
-                .setColumnComment("出生日期").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(10).setJavaType("LocalDateTime").setJavaField("birthday")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
         CodegenColumnDO contactMemoColumn = new CodegenColumnDO().setColumnName("memo").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("备注").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(11).setJavaType("String").setJavaField("memo").setExample("我是备注")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+                .setColumnComment("备注").setNullable(false)
+                .setJavaType("String").setJavaField("memo").setExample("我是备注")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType());
+                .setHtmlType(CodegenColumnHtmlTypeEnum.EDITOR.getType());
         CodegenColumnDO contactCreateTimeColumn = new CodegenColumnDO().setColumnName("create_time").setDataType(JdbcType.DATE.name())
-                .setColumnComment("创建时间").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(12).setJavaType("LocalDateTime").setJavaField("createTime")
-                .setCreateOperation(false).setUpdateOperation(false).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition()).setListOperationResult(true)
+                .setColumnComment("创建时间").setNullable(true)
+                .setJavaType("LocalDateTime").setJavaField("createTime")
+                .setCreateOperation(false).setUpdateOperation(false)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition())
+                .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
         List<CodegenColumnDO> contactColumns = Arrays.asList(contactIdColumn, contactStudentIdColumn,
-                contactNameColumn, contactsAvatarColumn, contactVideoColumn, contactDescriptionColumn,
-                contactSex1Column, contactSex2Column, contactSex3Column, contactBirthdayColumn, contactMemoColumn, contactCreateTimeColumn);
-        // 子表(地址)
-        CodegenTableDO addressTable = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene())
-                .setTableName("infra_demo_student_address").setTableComment("学生地址表")
-                .setModuleName("infra").setBusinessName("demo02").setClassName("InfraDemoStudentAddress")
-                .setClassComment("学生地址").setAuthor("芋道源码")
+                contactNameColumn, contactDescriptionColumn, contactBirthdayColumn,
+                contactSexColumn, contactEnabledColumn, contactAvatarColumn, contactVideoColumn, contactMemoColumn, contactCreateTimeColumn);
+
+        // 子表(班主任)
+        CodegenTableDO teacherTable = new CodegenTableDO().setScene(CodegenSceneEnum.ADMIN.getScene())
+                .setTableName("infra_demo11_student_teacher").setTableComment("学生班主任表")
+                .setModuleName("infra").setBusinessName("demo11").setClassName("InfraDemo11StudentTeacher")
+                .setClassComment("学生班主任").setAuthor("芋道源码")
                 .setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
                 .setFrontType(CodegenFrontTypeEnum.VUE3.getType())
                 .setSubJoinColumnId(200L).setSubJoinMany(false);
-        CodegenColumnDO addressIdColumn = new CodegenColumnDO().setColumnName("id").setDataType(JdbcType.BIGINT.name())
+        CodegenColumnDO teacherIdColumn = new CodegenColumnDO().setColumnName("id").setDataType(JdbcType.BIGINT.name())
                 .setColumnComment("编号").setNullable(false).setPrimaryKey(true).setAutoIncrement(true)
-                .setOrdinalPosition(1).setJavaType("Long").setJavaField("id").setExample("1024")
-                .setCreateOperation(false).setUpdateOperation(true).setListOperation(false)
+                .setJavaType("Long").setJavaField("id").setExample("1024")
+                .setCreateOperation(false).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true);
-        CodegenColumnDO addressStudentIdColumn = new CodegenColumnDO().setColumnName("student_id").setDataType(JdbcType.BIGINT.name())
+        CodegenColumnDO teacherStudentIdColumn = new CodegenColumnDO().setColumnName("student_id").setDataType(JdbcType.BIGINT.name())
                 .setColumnComment("学生编号").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(2).setJavaType("Long").setJavaField("studentId").setExample("2048")
-                .setCreateOperation(false).setUpdateOperation(true).setListOperation(false)
+                .setJavaType("Long").setJavaField("studentId").setExample("2048")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setId(200L);
-        CodegenColumnDO addressNameColumn = new CodegenColumnDO().setColumnName("name").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("名字").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(3).setJavaType("String").setJavaField("name").setExample("芋头")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition()).setListOperationResult(true)
+        CodegenColumnDO teacherNameColumn = new CodegenColumnDO().setColumnName("name").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("名字").setNullable(false)
+                .setJavaType("String").setJavaField("name").setExample("芋头")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.LIKE.getCondition())
+                .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
-        CodegenColumnDO addressAvatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("头像").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(4).setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+        CodegenColumnDO teacherDescriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("简介").setNullable(false)
+                .setJavaType("String").setJavaField("description").setExample("我是介绍")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType());
+        CodegenColumnDO teacherBirthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name())
+                .setColumnComment("出生日期").setNullable(false)
+                .setJavaType("LocalDateTime").setJavaField("birthday")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
+        CodegenColumnDO teacherSexColumn = new CodegenColumnDO().setColumnName("sex").setDataType(JdbcType.INTEGER.name())
+                .setColumnComment("性别").setNullable(false)
+                .setJavaType("Integer").setJavaField("sex").setExample("1")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType())
+                .setDictType("system_user_sex");
+        CodegenColumnDO teacherEnabledColumn = new CodegenColumnDO().setColumnName("enabled").setDataType(JdbcType.BOOLEAN.name())
+                .setColumnComment("是否有效").setNullable(false)
+                .setJavaType("Boolean").setJavaField("enabled").setExample("true")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition())
+                .setListOperationResult(true)
+                .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType())
+                .setDictType("infra_boolean_string");
+        CodegenColumnDO teacherAvatarColumn = new CodegenColumnDO().setColumnName("avatar").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("头像").setNullable(false)
+                .setJavaType("String").setJavaField("avatar").setExample("https://www.iocoder.cn/1.png")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD.getType());
-        CodegenColumnDO addressVideoColumn = new CodegenColumnDO().setColumnName("video").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("视频").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(5).setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+        CodegenColumnDO teacherVideoColumn = new CodegenColumnDO().setColumnName("video").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("附件").setNullable(true)
+                .setJavaType("String").setJavaField("video").setExample("https://www.iocoder.cn/1.mp4")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.FILE_UPLOAD.getType());
-        CodegenColumnDO addressDescriptionColumn = new CodegenColumnDO().setColumnName("description").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("个人简介").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(6).setJavaType("String").setJavaField("description").setExample("我是介绍")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+        CodegenColumnDO teacherMemoColumn = new CodegenColumnDO().setColumnName("memo").setDataType(JdbcType.VARCHAR.name())
+                .setColumnComment("备注").setNullable(false)
+                .setJavaType("String").setJavaField("memo").setExample("我是备注")
+                .setCreateOperation(true).setUpdateOperation(true)
+                .setListOperation(false)
                 .setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.EDITOR.getType());
-        CodegenColumnDO addressSex1Column = new CodegenColumnDO().setColumnName("sex1").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("性别 1").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(7).setJavaType("String").setJavaField("sex1").setExample("男")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.SELECT.getType()).setDictType("system_sex1");
-        CodegenColumnDO addressSex2Column = new CodegenColumnDO().setColumnName("sex2").setDataType(JdbcType.INTEGER.name())
-                .setColumnComment("性别 2").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(8).setJavaType("Integer").setJavaField("sex2").setExample("1")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.CHECKBOX.getType()).setDictType("system_sex2");
-        CodegenColumnDO addressSex3Column = new CodegenColumnDO().setColumnName("sex3").setDataType(JdbcType.BOOLEAN.name())
-                .setColumnComment("性别 3").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(9).setJavaType("Boolean").setJavaField("sex3").setExample("true")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType()).setDictType("system_sex3");
-        CodegenColumnDO addressBirthdayColumn = new CodegenColumnDO().setColumnName("birthday").setDataType(JdbcType.DATE.name())
-                .setColumnComment("出生日期").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(10).setJavaType("LocalDateTime").setJavaField("birthday")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition()).setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
-        CodegenColumnDO addressMemoColumn = new CodegenColumnDO().setColumnName("memo").setDataType(JdbcType.VARCHAR.name())
-                .setColumnComment("备注").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(11).setJavaType("String").setJavaField("memo").setExample("我是备注")
-                .setCreateOperation(true).setUpdateOperation(true).setListOperation(false)
+        CodegenColumnDO teacherCreateTimeColumn = new CodegenColumnDO().setColumnName("create_time").setDataType(JdbcType.DATE.name())
+                .setColumnComment("创建时间").setNullable(true)
+                .setJavaType("LocalDateTime").setJavaField("createTime")
+                .setCreateOperation(false).setUpdateOperation(false)
+                .setListOperation(true).setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition())
                 .setListOperationResult(true)
-                .setHtmlType(CodegenColumnHtmlTypeEnum.TEXTAREA.getType());
-        CodegenColumnDO addressCreateTimeColumn = new CodegenColumnDO().setColumnName("create_time").setDataType(JdbcType.DATE.name())
-                .setColumnComment("创建时间").setNullable(false).setPrimaryKey(false)
-                .setOrdinalPosition(12).setJavaType("LocalDateTime").setJavaField("createTime")
-                .setCreateOperation(false).setUpdateOperation(false).setListOperation(true)
-                .setListOperationCondition(CodegenColumnListConditionEnum.BETWEEN.getCondition()).setListOperationResult(true)
                 .setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
-        List<CodegenColumnDO> addressColumns = Arrays.asList(addressIdColumn, addressStudentIdColumn,
-                addressNameColumn, addressAvatarColumn, addressVideoColumn, addressDescriptionColumn,
-                addressSex1Column, addressSex2Column, addressSex3Column, addressBirthdayColumn, addressMemoColumn, addressCreateTimeColumn);
+        List<CodegenColumnDO> teacherColumns = Arrays.asList(teacherIdColumn, teacherStudentIdColumn,
+                teacherNameColumn, teacherDescriptionColumn, teacherBirthdayColumn,
+                teacherSexColumn, teacherEnabledColumn, teacherAvatarColumn, teacherVideoColumn, teacherMemoColumn, teacherCreateTimeColumn);
 
         // 调用
-        Map<String, String> result = codegenEngine.execute(null, null,
-                Arrays.asList(contactTable, addressTable), Arrays.asList(contactColumns, addressColumns));
+        Map<String, String> result = codegenEngine.execute(table, columns,
+                Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
+
+        // 构建 zip 包
+        writeFile(result, "/Users/yunai/test/demo11.zip");
 
         // 断言
         assertEquals(27, result.size());
@@ -374,4 +467,22 @@ public class CodegenEngineTest extends BaseMockitoUnitTest {
         assertEquals(pathContent, valueContent);
     }
 
+    /**
+     * 将生成的代码,写入到文件
+     *
+     * 用途:方便本地调试
+     *
+     * @param result 生成的代码
+     * @param path 写入文件的路径
+     */
+    private void writeFile(Map<String, String> result, String path) {
+        // 生成压缩包
+        String[] paths = result.keySet().toArray(new String[0]);
+        ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ZipUtil.zip(outputStream, paths, ins);
+        // 写入文件
+        FileUtil.writeBytes(outputStream.toByteArray(), path);
+    }
+
 }