Explorar o código

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

YunaiV hai 1 ano
pai
achega
2aecdbf09e
Modificáronse 23 ficheiros con 845 adicións e 35 borrados
  1. 1 0
      yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
  2. 16 20
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java
  3. 116 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/Demo03StudentController.java
  4. 30 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentPageReqVO.java
  5. 41 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentRespVO.java
  6. 39 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentSaveReqVO.java
  7. 1 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java
  8. 43 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
  9. 43 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
  10. 50 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
  11. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java
  12. 25 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java
  13. 23 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java
  14. 27 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java
  15. 0 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/package-info.java
  16. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java
  17. 78 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java
  18. 146 0
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java
  19. 13 11
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm
  20. 2 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
  21. 1 1
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo02/Demo02CategoryMapper.xml
  22. 12 0
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo03/Demo03StudentMapper.xml
  23. 136 0
      yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo03/Demo03StudentServiceImplTest.java

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

@@ -66,4 +66,5 @@ public interface ErrorCodeConstants {
     ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, "不能设置自己为父示例分类");
     ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, "已经存在该名字的示例分类");
     ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, "不能设置自己的子示例分类为父示例分类");
+    ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在");
 }

+ 16 - 20
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java

@@ -1,33 +1,29 @@
 package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02;
 
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO;
 import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
-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 cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+import cn.iocoder.yudao.module.infra.service.demo.demo02.Demo02CategoryService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
 
-import javax.validation.*;
-import javax.servlet.http.*;
-import java.util.*;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
 import java.io.IOException;
+import java.util.List;
 
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 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.demo02.vo.*;
-import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
-import cn.iocoder.yudao.module.infra.service.demo.demo02.Demo02CategoryService;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
 
 @Tag(name = "管理后台 - 示例分类")
 @RestController

+ 116 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/Demo03StudentController.java

@@ -0,0 +1,116 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal;
+
+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.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+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.demo.demo03.normal.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.module.infra.service.demo.demo03.Demo03StudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/demo03-student")
+@Validated
+public class Demo03StudentController {
+
+    @Resource
+    private Demo03StudentService demo03StudentService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建学生")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+    public CommonResult<Long> createDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO createReqVO) {
+        return success(demo03StudentService.createDemo03Student(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新学生")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+    public CommonResult<Boolean> updateDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO updateReqVO) {
+        demo03StudentService.updateDemo03Student(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除学生")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+    public CommonResult<Boolean> deleteDemo03Student(@RequestParam("id") Long id) {
+        demo03StudentService.deleteDemo03Student(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得学生")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+    public CommonResult<Demo03StudentRespVO> getDemo03Student(@RequestParam("id") Long id) {
+        Demo03StudentDO demo03Student = demo03StudentService.getDemo03Student(id);
+        return success(BeanUtils.toBean(demo03Student, Demo03StudentRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得学生分页")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+    public CommonResult<PageResult<Demo03StudentRespVO>> getDemo03StudentPage(@Valid Demo03StudentPageReqVO pageReqVO) {
+        PageResult<Demo03StudentDO> pageResult = demo03StudentService.getDemo03StudentPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, Demo03StudentRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出学生 Excel")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
+    @OperateLog(type = EXPORT)
+    public void exportDemo03StudentExcel(@Valid Demo03StudentPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<Demo03StudentDO> list = demo03StudentService.getDemo03StudentPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentRespVO.class,
+                        BeanUtils.toBean(list, Demo03StudentRespVO.class));
+    }
+
+    // ==================== 子表(学生课程) ====================
+
+    @GetMapping("/demo03-course/list-by-student-id")
+    @Operation(summary = "获得学生课程列表")
+    @Parameter(name = "studentId", description = "学生编号")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+    public CommonResult<List<Demo03CourseDO>> getDemo03CourseListByStudentId(@RequestParam("studentId") Long studentId) {
+        return success(demo03StudentService.getDemo03CourseListByStudentId(studentId));
+    }
+
+    // ==================== 子表(学生班级) ====================
+
+    @GetMapping("/demo03-grade/get-by-student-id")
+    @Operation(summary = "获得学生班级")
+    @Parameter(name = "studentId", description = "学生编号")
+    @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+    public CommonResult<Demo03GradeDO> getDemo03GradeByStudentId(@RequestParam("studentId") Long studentId) {
+        return success(demo03StudentService.getDemo03GradeByStudentId(studentId));
+    }
+
+}

+ 30 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentPageReqVO.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;
+
+import lombok.*;
+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 Demo03StudentPageReqVO extends PageParam {
+
+    @Schema(description = "名字", example = "芋艿")
+    private String name;
+
+    @Schema(description = "性别")
+    private Integer sex;
+
+    @Schema(description = "简介", example = "随便")
+    private String description;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 41 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentRespVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 学生 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class Demo03StudentRespVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+    @ExcelProperty("编号")
+    private Long id;
+
+    @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+    @ExcelProperty("名字")
+    private String name;
+
+    @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty(value = "性别", converter = DictConvert.class)
+    @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+    private Integer sex;
+
+    @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("出生日期")
+    private LocalDateTime birthday;
+
+    @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+    @ExcelProperty("简介")
+    private String description;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 39 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/normal/vo/Demo03StudentSaveReqVO.java

@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class Demo03StudentSaveReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+    private Long id;
+
+    @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+    @NotEmpty(message = "名字不能为空")
+    private String name;
+
+    @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "性别不能为空")
+    private Integer sex;
+
+    @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "出生日期不能为空")
+    private LocalDateTime birthday;
+
+    @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+    @NotEmpty(message = "简介不能为空")
+    private String description;
+
+
+    private List<Demo03CourseDO> demo03Courses;
+
+    private Demo03GradeDO demo03Grade;
+
+}

+ 1 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03;

+ 43 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+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_demo03_course")
+@KeySequence("infra_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03CourseDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 学生编号
+     */
+    private Long studentId;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 分数
+     */
+    private Integer score;
+
+}

+ 43 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+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_demo03_grade")
+@KeySequence("infra_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03GradeDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 学生编号
+     */
+    private Long studentId;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 班主任
+     */
+    private String teacher;
+
+}

+ 50 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+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_demo03_student")
+@KeySequence("infra_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03StudentDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 名字
+     */
+    private String name;
+    /**
+     * 性别
+     *
+     * 枚举 {@link TODO system_user_sex 对应的类}
+     */
+    private Integer sex;
+    /**
+     * 出生日期
+     */
+    private LocalDateTime birthday;
+    /**
+     * 简介
+     */
+    private String description;
+
+}

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo02/Demo02CategoryMapper.java → yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.infra.dal.mysql.demo02;
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02;
 
 import java.util.*;
 

+ 25 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生课程 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03CourseMapper extends BaseMapperX<Demo03CourseDO> {
+
+    default List<Demo03CourseDO> selectListByStudentId(Long studentId) {
+        return selectList(Demo03CourseDO::getStudentId, studentId);
+    }
+
+    default int deleteByStudentId(Long studentId) {
+        return delete(Demo03CourseDO::getStudentId, studentId);
+    }
+
+}

+ 23 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班级 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03GradeMapper extends BaseMapperX<Demo03GradeDO> {
+
+    default Demo03GradeDO selectByStudentId(Long studentId) {
+        return selectOne(Demo03GradeDO::getStudentId, studentId);
+    }
+
+    default int deleteByStudentId(Long studentId) {
+        return delete(Demo03GradeDO::getStudentId, studentId);
+    }
+
+}

+ 27 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+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.demo.demo03.Demo03StudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03StudentMapper extends BaseMapperX<Demo03StudentDO> {
+
+    default PageResult<Demo03StudentDO> selectPage(Demo03StudentPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<Demo03StudentDO>()
+                .likeIfPresent(Demo03StudentDO::getName, reqVO.getName())
+                .eqIfPresent(Demo03StudentDO::getSex, reqVO.getSex())
+                .eqIfPresent(Demo03StudentDO::getDescription, reqVO.getDescription())
+                .betweenIfPresent(Demo03StudentDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(Demo03StudentDO::getId));
+    }
+
+}

+ 0 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.module.infra.dal.mysql.demo;

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java

@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
-import cn.iocoder.yudao.module.infra.dal.mysql.demo02.Demo02CategoryMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02.Demo02CategoryMapper;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 

+ 78 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo03;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface Demo03StudentService {
+
+    /**
+     * 创建学生
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDemo03Student(@Valid Demo03StudentSaveReqVO createReqVO);
+
+    /**
+     * 更新学生
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateDemo03Student(@Valid Demo03StudentSaveReqVO updateReqVO);
+
+    /**
+     * 删除学生
+     *
+     * @param id 编号
+     */
+    void deleteDemo03Student(Long id);
+
+    /**
+     * 获得学生
+     *
+     * @param id 编号
+     * @return 学生
+     */
+    Demo03StudentDO getDemo03Student(Long id);
+
+    /**
+     * 获得学生分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 学生分页
+     */
+    PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentPageReqVO pageReqVO);
+
+
+    // ==================== 子表(学生课程) ====================
+
+    /**
+     * 获得学生课程列表
+     *
+     * @param studentId 学生编号
+     * @return 学生课程列表
+     */
+    List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId);
+
+
+    // ==================== 子表(学生班级) ====================
+
+    /**
+     * 获得学生班级
+     *
+     * @param studentId 学生编号
+     * @return 学生班级
+     */
+    Demo03GradeDO getDemo03GradeByStudentId(Long studentId);
+
+}

+ 146 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java

@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo03;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03StudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03CourseMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03GradeMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
+
+/**
+ * 学生 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class Demo03StudentServiceImpl implements Demo03StudentService {
+
+    @Resource
+    private Demo03StudentMapper demo03StudentMapper;
+    @Resource
+    private Demo03CourseMapper demo03CourseMapper;
+    @Resource
+    private Demo03GradeMapper demo03GradeMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createDemo03Student(Demo03StudentSaveReqVO createReqVO) {
+        // 插入
+        Demo03StudentDO demo03Student = BeanUtils.toBean(createReqVO, Demo03StudentDO.class);
+        demo03StudentMapper.insert(demo03Student);
+
+        // 插入子表
+        createDemo03CourseList(demo03Student.getId(), createReqVO.getDemo03Courses());
+        createDemo03Grade(demo03Student.getId(), createReqVO.getDemo03Grade());
+        // 返回
+        return demo03Student.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateDemo03Student(Demo03StudentSaveReqVO updateReqVO) {
+        // 校验存在
+        validateDemo03StudentExists(updateReqVO.getId());
+        // 更新
+        Demo03StudentDO updateObj = BeanUtils.toBean(updateReqVO, Demo03StudentDO.class);
+        demo03StudentMapper.updateById(updateObj);
+
+        // 更新子表
+        updateDemo03CourseList(updateReqVO.getId(), updateReqVO.getDemo03Courses());
+        updateDemo03Grade(updateReqVO.getId(), updateReqVO.getDemo03Grade());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDemo03Student(Long id) {
+        // 校验存在
+        validateDemo03StudentExists(id);
+        // 删除
+        demo03StudentMapper.deleteById(id);
+
+        // 删除子表
+        deleteDemo03CourseByStudentId(id);
+        deleteDemo03GradeByStudentId(id);
+    }
+
+    private void validateDemo03StudentExists(Long id) {
+        if (demo03StudentMapper.selectById(id) == null) {
+            throw exception(DEMO03_STUDENT_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public Demo03StudentDO getDemo03Student(Long id) {
+        return demo03StudentMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<Demo03StudentDO> getDemo03StudentPage(Demo03StudentPageReqVO pageReqVO) {
+        return demo03StudentMapper.selectPage(pageReqVO);
+    }
+
+    // ==================== 子表(学生课程) ====================
+
+    @Override
+    public List<Demo03CourseDO> getDemo03CourseListByStudentId(Long studentId) {
+        return demo03CourseMapper.selectListByStudentId(studentId);
+    }
+
+    private void createDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {
+        list.forEach(o -> o.setStudentId(studentId));
+        demo03CourseMapper.insertBatch(list);
+    }
+
+    private void updateDemo03CourseList(Long studentId, List<Demo03CourseDO> list) {
+        deleteDemo03CourseByStudentId(studentId);
+		list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+        createDemo03CourseList(studentId, list);
+    }
+
+    private void deleteDemo03CourseByStudentId(Long studentId) {
+        demo03CourseMapper.deleteByStudentId(studentId);
+    }
+
+    // ==================== 子表(学生班级) ====================
+
+    @Override
+    public Demo03GradeDO getDemo03GradeByStudentId(Long studentId) {
+        return demo03GradeMapper.selectByStudentId(studentId);
+    }
+
+    private void createDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {
+        if (demo03Grade == null) {
+            return;
+        }
+        demo03Grade.setStudentId(studentId);
+        demo03GradeMapper.insert(demo03Grade);
+    }
+
+    private void updateDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {
+        if (demo03Grade == null) {
+			return;
+        }
+        demo03Grade.setStudentId(studentId);
+        demo03Grade.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+        demo03GradeMapper.insertOrUpdate(demo03Grade);
+    }
+
+    private void deleteDemo03GradeByStudentId(Long studentId) {
+        demo03GradeMapper.deleteByStudentId(studentId);
+    }
+
+}

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

@@ -31,8 +31,9 @@
         #elseif ($javaType == "Boolean")
             #set ($dictMethod = "getBoolDictOptions")
         #end
-        #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 TODO 芋艿:这里要忽略下 join 字段;
-      <el-table-column label="${comment}" width="150">
+        #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+        #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+      <el-table-column label="${comment}" min-width="150">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-input v-model="row.${javaField}" placeholder="请输入${comment}" />
@@ -40,7 +41,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "imageUpload")## 图片上传
-      <el-table-column label="${comment}" width="200">
+      <el-table-column label="${comment}" min-width="200">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <UploadImg v-model="row.${javaField}" />
@@ -48,7 +49,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "fileUpload")## 文件上传
-      <el-table-column label="${comment}" width="200">
+      <el-table-column label="${comment}" min-width="200">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <UploadFile v-model="row.${javaField}" />
@@ -56,7 +57,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "editor")## 文本编辑器
-      <el-table-column label="${comment}" width="400">
+      <el-table-column label="${comment}" min-width="400">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <Editor v-model="row.${javaField}" height="150px" />
@@ -64,7 +65,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "select")## 下拉框
-      <el-table-column label="${comment}" width="150">
+      <el-table-column label="${comment}" min-width="150">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-select v-model="row.${javaField}" placeholder="请选择${comment}">
@@ -83,7 +84,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "checkbox")## 多选框
-      <el-table-column label="${comment}" width="150">
+      <el-table-column label="${comment}" min-width="150">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-checkbox-group v-model="row.${javaField}">
@@ -103,7 +104,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "radio")## 单选框
-      <el-table-column label="${comment}" width="150">
+      <el-table-column label="${comment}" min-width="150">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-radio-group v-model="row.${javaField}">
@@ -123,7 +124,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "datetime")## 时间框
-      <el-table-column label="${comment}" width="150">
+      <el-table-column label="${comment}" min-width="150">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-date-picker
@@ -136,7 +137,7 @@
         </template>
       </el-table-column>
         #elseif($column.htmlType == "textarea")## 文本框
-      <el-table-column label="${comment}" width="200">
+      <el-table-column label="${comment}" min-width="200">
         <template #default="{ row, $index }">
           <el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
             <el-input v-model="row.${javaField}" type="textarea" placeholder="请输入${comment}" />
@@ -179,7 +180,8 @@
       #elseif ($javaType == "Boolean")
           #set ($dictMethod = "getBoolDictOptions")
       #end
-      #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 TODO 芋艿:这里要忽略下 join 字段;
+      #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+      #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
     <el-form-item label="${comment}" prop="${javaField}">
       <el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
     </el-form-item>

+ 2 - 0
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm

@@ -243,9 +243,11 @@ import download from '@/utils/download'
 import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}'
 import ${simpleClassName}Form from './${simpleClassName}Form.vue'
 ## 特殊:主子表专属逻辑
+#if ( $table.templateType != 10 )
 #foreach ($subSimpleClassName in $subSimpleClassNames)
 import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'
 #end
+#end
 
 defineOptions({ name: '${table.className}' })
 

+ 1 - 1
yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo02/Demo02CategoryMapper.xml

@@ -1,6 +1,6 @@
 <?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.demo02.Demo02CategoryMapper">
+<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02.Demo02CategoryMapper">
 
     <!--
         一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。

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

+ 136 - 0
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo03/Demo03StudentServiceImplTest.java

@@ -0,0 +1,136 @@
+package cn.iocoder.yudao.module.infra.service.demo03;
+
+import cn.iocoder.yudao.module.infra.service.demo.demo03.Demo03StudentServiceImpl;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.normal.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03StudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.springframework.context.annotation.Import;
+
+import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
+import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link Demo03StudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(Demo03StudentServiceImpl.class)
+public class Demo03StudentServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private Demo03StudentServiceImpl demo03StudentService;
+
+    @Resource
+    private Demo03StudentMapper demo03StudentMapper;
+
+    @Test
+    public void testCreateDemo03Student_success() {
+        // 准备参数
+        Demo03StudentSaveReqVO createReqVO = randomPojo(Demo03StudentSaveReqVO.class).setId(null);
+
+        // 调用
+        Long demo03StudentId = demo03StudentService.createDemo03Student(createReqVO);
+        // 断言
+        assertNotNull(demo03StudentId);
+        // 校验记录的属性是否正确
+        Demo03StudentDO demo03Student = demo03StudentMapper.selectById(demo03StudentId);
+        assertPojoEquals(createReqVO, demo03Student, "id");
+    }
+
+    @Test
+    public void testUpdateDemo03Student_success() {
+        // mock 数据
+        Demo03StudentDO dbDemo03Student = randomPojo(Demo03StudentDO.class);
+        demo03StudentMapper.insert(dbDemo03Student);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Demo03StudentSaveReqVO updateReqVO = randomPojo(Demo03StudentSaveReqVO.class, o -> {
+            o.setId(dbDemo03Student.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        demo03StudentService.updateDemo03Student(updateReqVO);
+        // 校验是否更新正确
+        Demo03StudentDO demo03Student = demo03StudentMapper.selectById(updateReqVO.getId()); // 获取最新的
+        assertPojoEquals(updateReqVO, demo03Student);
+    }
+
+    @Test
+    public void testUpdateDemo03Student_notExists() {
+        // 准备参数
+        Demo03StudentSaveReqVO updateReqVO = randomPojo(Demo03StudentSaveReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> demo03StudentService.updateDemo03Student(updateReqVO), DEMO03_STUDENT_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteDemo03Student_success() {
+        // mock 数据
+        Demo03StudentDO dbDemo03Student = randomPojo(Demo03StudentDO.class);
+        demo03StudentMapper.insert(dbDemo03Student);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbDemo03Student.getId();
+
+        // 调用
+        demo03StudentService.deleteDemo03Student(id);
+       // 校验数据不存在了
+       assertNull(demo03StudentMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteDemo03Student_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> demo03StudentService.deleteDemo03Student(id), DEMO03_STUDENT_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetDemo03StudentPage() {
+       // mock 数据
+       Demo03StudentDO dbDemo03Student = randomPojo(Demo03StudentDO.class, o -> { // 等会查询到
+           o.setName(null);
+           o.setSex(null);
+           o.setDescription(null);
+           o.setCreateTime(null);
+       });
+       demo03StudentMapper.insert(dbDemo03Student);
+       // 测试 name 不匹配
+       demo03StudentMapper.insert(cloneIgnoreId(dbDemo03Student, o -> o.setName(null)));
+       // 测试 sex 不匹配
+       demo03StudentMapper.insert(cloneIgnoreId(dbDemo03Student, o -> o.setSex(null)));
+       // 测试 description 不匹配
+       demo03StudentMapper.insert(cloneIgnoreId(dbDemo03Student, o -> o.setDescription(null)));
+       // 测试 createTime 不匹配
+       demo03StudentMapper.insert(cloneIgnoreId(dbDemo03Student, o -> o.setCreateTime(null)));
+       // 准备参数
+       Demo03StudentPageReqVO reqVO = new Demo03StudentPageReqVO();
+       reqVO.setName(null);
+       reqVO.setSex(null);
+       reqVO.setDescription(null);
+       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+       // 调用
+       PageResult<Demo03StudentDO> pageResult = demo03StudentService.getDemo03StudentPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbDemo03Student, pageResult.getList().get(0));
+    }
+
+}