{
+
+ default InfraStudentTeacherDO selectByStudentId(Long studentId) {
+ return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index
new file mode 100644
index 0000000..b4e6ac5
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index
@@ -0,0 +1,74 @@
+import request from '@/utils/request'
+
+// 创建学生
+export function createStudent(data) {
+ return request({
+ url: '/infra/student/create',
+ method: 'post',
+ data: data
+ })
+}
+
+// 更新学生
+export function updateStudent(data) {
+ return request({
+ url: '/infra/student/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除学生
+export function deleteStudent(id) {
+ return request({
+ url: '/infra/student/delete?id=' + id,
+ method: 'delete'
+ })
+}
+
+// 获得学生
+export function getStudent(id) {
+ return request({
+ url: '/infra/student/get?id=' + id,
+ method: 'get'
+ })
+}
+
+// 获得学生分页
+export function getStudentPage(params) {
+ return request({
+ url: '/infra/student/page',
+ method: 'get',
+ params
+ })
+}
+// 导出学生 Excel
+export function exportStudentExcel(params) {
+ return request({
+ url: '/infra/student/export-excel',
+ method: 'get',
+ params,
+ responseType: 'blob'
+ })
+}
+
+// ==================== 子表(学生联系人) ====================
+
+ // 获得学生联系人列表
+ export function getStudentContactListByStudentId(studentId) {
+ return request({
+ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
+ method: 'get'
+ })
+ }
+
+// ==================== 子表(学生班主任) ====================
+
+ // 获得学生班主任
+ export function getStudentTeacherByStudentId(studentId) {
+ return request({
+ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
+ method: 'get'
+ })
+ }
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm
new file mode 100644
index 0000000..c953bfa
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —
+
+
+
+
+
+ + 添加学生联系人
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList
new file mode 100644
index 0000000..c0a8710
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.birthday) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm
new file mode 100644
index 0000000..6d93b61
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm
new file mode 100644
index 0000000..0dac19b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList
new file mode 100644
index 0000000..9f57274
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.birthday) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index
new file mode 100644
index 0000000..ddeafdf
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.birthday) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json
new file mode 100644
index 0000000..6f94535
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json
@@ -0,0 +1,67 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentContactDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentContactMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
+}, {
+ "contentPath": "js/index",
+ "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactForm",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherForm",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作
index f8be662..f8be662 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO
new file mode 100644
index 0000000..17c668e
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_contact")
+@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentContactDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper
new file mode 100644
index 0000000..35bbd53
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentContactMapper extends BaseMapperX {
+
+ default List selectListByStudentId(Long studentId) {
+ return selectList(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController
new file mode 100644
index 0000000..b9a587b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController
@@ -0,0 +1,117 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @GetMapping("/student-contact/list-by-student-id")
+ @Operation(summary = "获得学生联系人列表")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentContactListByStudentId(studentId));
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @GetMapping("/student-teacher/get-by-student-id")
+ @Operation(summary = "获得学生班主任")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentTeacherByStudentId(studentId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..faa491d
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+ @Schema(description = "学生联系人列表")
+ private List studentContacts;
+
+ @Schema(description = "学生班主任")
+ private InfraStudentTeacherDO studentTeacher;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService
new file mode 100644
index 0000000..afa7d22
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService
@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+ // ==================== 子表(学生联系人) ====================
+
+ /**
+ * 获得学生联系人列表
+ *
+ * @param studentId 学生编号
+ * @return 学生联系人列表
+ */
+ List getStudentContactListByStudentId(Long studentId);
+
+ // ==================== 子表(学生班主任) ====================
+
+ /**
+ * 获得学生班主任
+ *
+ * @param studentId 学生编号
+ * @return 学生班主任
+ */
+ InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..c57cba6
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl
@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+ @Resource
+ private InfraStudentContactMapper studentContactMapper;
+ @Resource
+ private InfraStudentTeacherMapper studentTeacherMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+
+ // 插入子表
+ createStudentContactList(student.getId(), createReqVO.getStudentContacts());
+ createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+
+ // 更新子表
+ updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
+ updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+
+ // 删除子表
+ deleteStudentContactByStudentId(id);
+ deleteStudentTeacherByStudentId(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @Override
+ public List getStudentContactListByStudentId(Long studentId) {
+ return studentContactMapper.selectListByStudentId(studentId);
+ }
+
+ private void createStudentContactList(Long studentId, List list) {
+ list.forEach(o -> o.setStudentId(studentId));
+ studentContactMapper.insertBatch(list);
+ }
+
+ private void updateStudentContactList(Long studentId, List list) {
+ deleteStudentContactByStudentId(studentId);
+ list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+ createStudentContactList(studentId, list);
+ }
+
+ private void deleteStudentContactByStudentId(Long studentId) {
+ studentContactMapper.deleteByStudentId(studentId);
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @Override
+ public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
+ return studentTeacherMapper.selectByStudentId(studentId);
+ }
+
+ private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacherMapper.insert(studentTeacher);
+ }
+
+ private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+ studentTeacherMapper.insertOrUpdate(studentTeacher);
+ }
+
+ private void deleteStudentTeacherByStudentId(Long studentId) {
+ studentTeacherMapper.deleteByStudentId(studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO
new file mode 100644
index 0000000..c19cf9f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_teacher")
+@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentTeacherDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper
new file mode 100644
index 0000000..0521bba
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentTeacherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班主任 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentTeacherMapper extends BaseMapperX {
+
+ default InfraStudentTeacherDO selectByStudentId(Long studentId) {
+ return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index
new file mode 100644
index 0000000..b4e6ac5
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index
@@ -0,0 +1,74 @@
+import request from '@/utils/request'
+
+// 创建学生
+export function createStudent(data) {
+ return request({
+ url: '/infra/student/create',
+ method: 'post',
+ data: data
+ })
+}
+
+// 更新学生
+export function updateStudent(data) {
+ return request({
+ url: '/infra/student/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除学生
+export function deleteStudent(id) {
+ return request({
+ url: '/infra/student/delete?id=' + id,
+ method: 'delete'
+ })
+}
+
+// 获得学生
+export function getStudent(id) {
+ return request({
+ url: '/infra/student/get?id=' + id,
+ method: 'get'
+ })
+}
+
+// 获得学生分页
+export function getStudentPage(params) {
+ return request({
+ url: '/infra/student/page',
+ method: 'get',
+ params
+ })
+}
+// 导出学生 Excel
+export function exportStudentExcel(params) {
+ return request({
+ url: '/infra/student/export-excel',
+ method: 'get',
+ params,
+ responseType: 'blob'
+ })
+}
+
+// ==================== 子表(学生联系人) ====================
+
+ // 获得学生联系人列表
+ export function getStudentContactListByStudentId(studentId) {
+ return request({
+ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId,
+ method: 'get'
+ })
+ }
+
+// ==================== 子表(学生班主任) ====================
+
+ // 获得学生班主任
+ export function getStudentTeacherByStudentId(studentId) {
+ return request({
+ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId,
+ method: 'get'
+ })
+ }
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm
new file mode 100644
index 0000000..c953bfa
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —
+
+
+
+
+
+ + 添加学生联系人
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm
new file mode 100644
index 0000000..6d93b61
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm
new file mode 100644
index 0000000..0dac19b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index
new file mode 100644
index 0000000..4607581
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.birthday) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json
new file mode 100644
index 0000000..5a37c37
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json
@@ -0,0 +1,49 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
+}, {
+ "contentPath": "js/index",
+ "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作
index d3201de..f8be662 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作
@@ -1,6 +1,3 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
// ========== 学生 TODO 补充编号 ==========
-ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
-ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在");
-ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在");
-ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在");
\ No newline at end of file
+ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController
new file mode 100644
index 0000000..3796982
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..43e7f14
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO
@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService
new file mode 100644
index 0000000..c4a0e17
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..2292a66
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Override
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index
new file mode 100644
index 0000000..44db468
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 创建学生
+export function createStudent(data) {
+ return request({
+ url: '/infra/student/create',
+ method: 'post',
+ data: data
+ })
+}
+
+// 更新学生
+export function updateStudent(data) {
+ return request({
+ url: '/infra/student/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除学生
+export function deleteStudent(id) {
+ return request({
+ url: '/infra/student/delete?id=' + id,
+ method: 'delete'
+ })
+}
+
+// 获得学生
+export function getStudent(id) {
+ return request({
+ url: '/infra/student/get?id=' + id,
+ method: 'get'
+ })
+}
+
+// 获得学生分页
+export function getStudentPage(params) {
+ return request({
+ url: '/infra/student/page',
+ method: 'get',
+ params
+ })
+}
+// 导出学生 Excel
+export function exportStudentExcel(params) {
+ return request({
+ url: '/infra/student/export-excel',
+ method: 'get',
+ params,
+ responseType: 'blob'
+ })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm
new file mode 100644
index 0000000..d89e506
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{dict.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index
new file mode 100644
index 0000000..4607581
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.birthday) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json
new file mode 100644
index 0000000..a7d2f56
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json
@@ -0,0 +1,49 @@
+[ {
+ "contentPath" : "java/InfraCategoryListReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java"
+}, {
+ "contentPath" : "java/InfraCategoryRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java"
+}, {
+ "contentPath" : "java/InfraCategorySaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraCategoryController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java"
+}, {
+ "contentPath" : "java/InfraCategoryDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java"
+}, {
+ "contentPath" : "java/InfraCategoryMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java"
+}, {
+ "contentPath" : "xml/InfraCategoryMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml"
+}, {
+ "contentPath" : "java/InfraCategoryServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraCategoryService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java"
+}, {
+ "contentPath" : "java/InfraCategoryServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue"
+}, {
+ "contentPath": "js/index",
+ "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js"
+}, {
+ "contentPath" : "vue/CategoryForm",
+ "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..36df675
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,8 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 分类 TODO 补充编号 ==========
+ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在");
+ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除");
+ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在");
+ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类");
+ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类");
+ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController
new file mode 100644
index 0000000..a7b2f81
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController
@@ -0,0 +1,94 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;
+
+@Tag(name = "管理后台 - 分类")
+@RestController
+@RequestMapping("/infra/category")
+@Validated
+public class InfraCategoryController {
+
+ @Resource
+ private InfraCategoryService categoryService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建分类")
+ @PreAuthorize("@ss.hasPermission('infra:category:create')")
+ public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {
+ return success(categoryService.createCategory(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新分类")
+ @PreAuthorize("@ss.hasPermission('infra:category:update')")
+ public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {
+ categoryService.updateCategory(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除分类")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:category:delete')")
+ public CommonResult deleteCategory(@RequestParam("id") Long id) {
+ categoryService.deleteCategory(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得分类")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:category:query')")
+ public CommonResult getCategory(@RequestParam("id") Long id) {
+ InfraCategoryDO category = categoryService.getCategory(id);
+ return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得分类列表")
+ @PreAuthorize("@ss.hasPermission('infra:category:query')")
+ public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {
+ List list = categoryService.getCategoryList(listReqVO);
+ return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出分类 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:category:export')")
+ @OperateLog(type = EXPORT)
+ public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,
+ HttpServletResponse response) throws IOException {
+ List list = categoryService.getCategoryList(listReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class,
+ BeanUtils.toBean(list, InfraCategoryRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO
new file mode 100644
index 0000000..9bf21c0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+import lombok.*;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 分类 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_category")
+@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraCategoryDO extends BaseDO {
+
+ public static final Long PARENT_ID_ROOT = 0L;
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 父编号
+ */
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO
new file mode 100644
index 0000000..e5c6f18
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 分类列表 Request VO")
+@Data
+public class InfraCategoryListReqVO {
+
+ @Schema(description = "名字", example = "芋头")
+ private String name;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper
new file mode 100644
index 0000000..9dadbf1
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraCategoryDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 分类 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraCategoryMapper extends BaseMapperX {
+
+ default List selectList(InfraCategoryListReqVO reqVO) {
+ return selectList(new LambdaQueryWrapperX()
+ .likeIfPresent(InfraCategoryDO::getName, reqVO.getName())
+ .orderByDesc(InfraCategoryDO::getId));
+ }
+
+ default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {
+ return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);
+ }
+
+ default Long selectCountByParentId(Long parentId) {
+ return selectCount(InfraCategoryDO::getParentId, parentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO
new file mode 100644
index 0000000..6325d86
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 分类 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class InfraCategoryRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+ @ExcelProperty("父编号")
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO
new file mode 100644
index 0000000..3c03b97
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 分类新增/修改 Request VO")
+@Data
+public class InfraCategorySaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+ @NotNull(message = "父编号不能为空")
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService
new file mode 100644
index 0000000..9d0ae1a
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 分类 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraCategoryService {
+
+ /**
+ * 创建分类
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);
+
+ /**
+ * 更新分类
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);
+
+ /**
+ * 删除分类
+ *
+ * @param id 编号
+ */
+ void deleteCategory(Long id);
+
+ /**
+ * 获得分类
+ *
+ * @param id 编号
+ * @return 分类
+ */
+ InfraCategoryDO getCategory(Long id);
+
+ /**
+ * 获得分类列表
+ *
+ * @param listReqVO 查询条件
+ * @return 分类列表
+ */
+ List getCategoryList(InfraCategoryListReqVO listReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl
new file mode 100644
index 0000000..351568b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl
@@ -0,0 +1,136 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
+
+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 InfraCategoryServiceImpl implements InfraCategoryService {
+
+ @Resource
+ private InfraCategoryMapper categoryMapper;
+
+ @Override
+ public Long createCategory(InfraCategorySaveReqVO createReqVO) {
+ // 校验父编号的有效性
+ validateParentCategory(null, createReqVO.getParentId());
+ // 校验名字的唯一性
+ validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
+
+ // 插入
+ InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);
+ categoryMapper.insert(category);
+ // 返回
+ return category.getId();
+ }
+
+ @Override
+ public void updateCategory(InfraCategorySaveReqVO updateReqVO) {
+ // 校验存在
+ validateCategoryExists(updateReqVO.getId());
+ // 校验父编号的有效性
+ validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());
+ // 校验名字的唯一性
+ validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
+
+ // 更新
+ InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);
+ categoryMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteCategory(Long id) {
+ // 校验存在
+ validateCategoryExists(id);
+ // 校验是否有子分类
+ if (categoryMapper.selectCountByParentId(id) > 0) {
+ throw exception(CATEGORY_EXITS_CHILDREN);
+ }
+ // 删除
+ categoryMapper.deleteById(id);
+ }
+
+ private void validateCategoryExists(Long id) {
+ if (categoryMapper.selectById(id) == null) {
+ throw exception(CATEGORY_NOT_EXISTS);
+ }
+ }
+
+ private void validateParentCategory(Long id, Long parentId) {
+ if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ return;
+ }
+ // 1. 不能设置自己为父分类
+ if (Objects.equals(id, parentId)) {
+ throw exception(CATEGORY_PARENT_ERROR);
+ }
+ // 2. 父分类不存在
+ CategoryDO parentCategory = categoryMapper.selectById(parentId);
+ if (parentCategory == null) {
+ throw exception(CATEGORY_PARENT_NOT_EXITS);
+ }
+ // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路
+ if (id == null) { // id 为空,说明新增,不需要考虑环路
+ return;
+ }
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ // 3.1 校验环路
+ parentId = parentCategory.getParentId();
+ if (Objects.equals(id, parentId)) {
+ throw exception(CATEGORY_PARENT_IS_CHILD);
+ }
+ // 3.2 继续递归下一级父分类
+ if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ break;
+ }
+ parentCategory = categoryMapper.selectById(parentId);
+ if (parentCategory == null) {
+ break;
+ }
+ }
+ }
+
+ private void validateCategoryNameUnique(Long id, Long parentId, String name) {
+ CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);
+ if (category == null) {
+ return;
+ }
+ // 如果 id 为空,说明不用比较是否为相同 id 的分类
+ if (id == null) {
+ throw exception(CATEGORY_NAME_DUPLICATE);
+ }
+ if (!Objects.equals(category.getId(), id)) {
+ throw exception(CATEGORY_NAME_DUPLICATE);
+ }
+ }
+
+ @Override
+ public InfraCategoryDO getCategory(Long id) {
+ return categoryMapper.selectById(id);
+ }
+
+ @Override
+ public List getCategoryList(InfraCategoryListReqVO listReqVO) {
+ return categoryMapper.selectList(listReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest
new file mode 100644
index 0000000..efb70fd
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest
@@ -0,0 +1,129 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraCategoryServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraCategoryServiceImpl.class)
+public class InfraCategoryServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraCategoryServiceImpl categoryService;
+
+ @Resource
+ private InfraCategoryMapper categoryMapper;
+
+ @Test
+ public void testCreateCategory_success() {
+ // 准备参数
+ InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null);
+
+ // 调用
+ Long categoryId = categoryService.createCategory(createReqVO);
+ // 断言
+ assertNotNull(categoryId);
+ // 校验记录的属性是否正确
+ InfraCategoryDO category = categoryMapper.selectById(categoryId);
+ assertPojoEquals(createReqVO, category, "id");
+ }
+
+ @Test
+ public void testUpdateCategory_success() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
+ categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> {
+ o.setId(dbCategory.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ categoryService.updateCategory(updateReqVO);
+ // 校验是否更新正确
+ InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, category);
+ }
+
+ @Test
+ public void testUpdateCategory_notExists() {
+ // 准备参数
+ InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteCategory_success() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
+ categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbCategory.getId();
+
+ // 调用
+ categoryService.deleteCategory(id);
+ // 校验数据不存在了
+ assertNull(categoryMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteCategory_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetCategoryList() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到
+ o.setName(null);
+ });
+ categoryMapper.insert(dbCategory);
+ // 测试 name 不匹配
+ categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
+ // 准备参数
+ InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO();
+ reqVO.setName(null);
+
+ // 调用
+ List list = categoryService.getCategoryList(reqVO);
+ // 断言
+ assertEquals(1, list.size());
+ assertPojoEquals(dbCategory, list.get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index
new file mode 100644
index 0000000..1e6ffdc
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index
@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 创建分类
+export function createCategory(data) {
+ return request({
+ url: '/infra/category/create',
+ method: 'post',
+ data: data
+ })
+}
+
+// 更新分类
+export function updateCategory(data) {
+ return request({
+ url: '/infra/category/update',
+ method: 'put',
+ data: data
+ })
+}
+
+// 删除分类
+export function deleteCategory(id) {
+ return request({
+ url: '/infra/category/delete?id=' + id,
+ method: 'delete'
+ })
+}
+
+// 获得分类
+export function getCategory(id) {
+ return request({
+ url: '/infra/category/get?id=' + id,
+ method: 'get'
+ })
+}
+
+// 获得分类列表
+export function getCategoryList(params) {
+ return request({
+ url: '/infra/category/list',
+ method: 'get',
+ params
+ })
+}
+// 导出分类 Excel
+export function exportCategoryExcel(params) {
+ return request({
+ url: '/infra/category/export-excel',
+ method: 'get',
+ params,
+ responseType: 'blob'
+ })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2
new file mode 100644
index 0000000..4141766
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2
@@ -0,0 +1,10 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_category" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" bigint NOT NULL,
+ PRIMARY KEY ("id")
+) COMMENT '分类表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_category";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql
new file mode 100644
index 0000000..8140948
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '分类管理', '', 2, 0, 888,
+ 'category', '', 'infra/demo/index', 0, 'InfraCategory'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类查询', 'infra:category:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类创建', 'infra:category:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类更新', 'infra:category:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类删除', 'infra:category:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类导出', 'infra:category:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm
new file mode 100644
index 0000000..7fa06e8
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index
new file mode 100644
index 0000000..88da682
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+
+ 新增
+
+
+ 导出
+
+
+
+ 展开/折叠
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper
new file mode 100644
index 0000000..025ac85
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json
new file mode 100644
index 0000000..0937ba9
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json
@@ -0,0 +1,73 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentContactDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentContactMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactList",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherList",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue"
+}, {
+ "contentPath" : "ts/index",
+ "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..d3201de
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,6 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 学生 TODO 补充编号 ==========
+ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
+ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在");
+ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在");
+ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO
new file mode 100644
index 0000000..17c668e
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_contact")
+@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentContactDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper
new file mode 100644
index 0000000..ca662d1
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentContactMapper extends BaseMapperX {
+
+ default PageResult selectPage(PageParam reqVO, Long studentId) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eq(InfraStudentContactDO::getStudentId, studentId)
+ .orderByDesc(InfraStudentContactDO::getId));
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController
new file mode 100644
index 0000000..d6f2018
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController
@@ -0,0 +1,183 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @GetMapping("/student-contact/page")
+ @Operation(summary = "获得学生联系人分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentContactPage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentContactPage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/student-contact/create")
+ @Operation(summary = "创建学生联系人")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
+ return success(studentService.createStudentContact(studentContact));
+ }
+
+ @PutMapping("/student-contact/update")
+ @Operation(summary = "更新学生联系人")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
+ studentService.updateStudentContact(studentContact);
+ return success(true);
+ }
+
+ @DeleteMapping("/student-contact/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生联系人")
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudentContact(@RequestParam("id") Long id) {
+ studentService.deleteStudentContact(id);
+ return success(true);
+ }
+
+ @GetMapping("/student-contact/get")
+ @Operation(summary = "获得学生联系人")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudentContact(@RequestParam("id") Long id) {
+ return success(studentService.getStudentContact(id));
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @GetMapping("/student-teacher/page")
+ @Operation(summary = "获得学生班主任分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentTeacherPage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentTeacherPage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/student-teacher/create")
+ @Operation(summary = "创建学生班主任")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
+ return success(studentService.createStudentTeacher(studentTeacher));
+ }
+
+ @PutMapping("/student-teacher/update")
+ @Operation(summary = "更新学生班主任")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
+ studentService.updateStudentTeacher(studentTeacher);
+ return success(true);
+ }
+
+ @DeleteMapping("/student-teacher/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生班主任")
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudentTeacher(@RequestParam("id") Long id) {
+ studentService.deleteStudentTeacher(id);
+ return success(true);
+ }
+
+ @GetMapping("/student-teacher/get")
+ @Operation(summary = "获得学生班主任")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudentTeacher(@RequestParam("id") Long id) {
+ return success(studentService.getStudentTeacher(id));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..eaadf74
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService
new file mode 100644
index 0000000..7df090d
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService
@@ -0,0 +1,139 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+ // ==================== 子表(学生联系人) ====================
+
+ /**
+ * 获得学生联系人分页
+ *
+ * @param pageReqVO 分页查询
+ * @param studentId 学生编号
+ * @return 学生联系人分页
+ */
+ PageResult getStudentContactPage(PageParam pageReqVO, Long studentId);
+
+ /**
+ * 创建学生联系人
+ *
+ * @param studentContact 创建信息
+ * @return 编号
+ */
+ Long createStudentContact(@Valid InfraStudentContactDO studentContact);
+
+ /**
+ * 更新学生联系人
+ *
+ * @param studentContact 更新信息
+ */
+ void updateStudentContact(@Valid InfraStudentContactDO studentContact);
+
+ /**
+ * 删除学生联系人
+ *
+ * @param id 编号
+ */
+ void deleteStudentContact(Long id);
+
+ /**
+ * 获得学生联系人
+ *
+ * @param id 编号
+ * @return 学生联系人
+ */
+ InfraStudentContactDO getStudentContact(Long id);
+
+ // ==================== 子表(学生班主任) ====================
+
+ /**
+ * 获得学生班主任分页
+ *
+ * @param pageReqVO 分页查询
+ * @param studentId 学生编号
+ * @return 学生班主任分页
+ */
+ PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId);
+
+ /**
+ * 创建学生班主任
+ *
+ * @param studentTeacher 创建信息
+ * @return 编号
+ */
+ Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
+
+ /**
+ * 更新学生班主任
+ *
+ * @param studentTeacher 更新信息
+ */
+ void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
+
+ /**
+ * 删除学生班主任
+ *
+ * @param id 编号
+ */
+ void deleteStudentTeacher(Long id);
+
+ /**
+ * 获得学生班主任
+ *
+ * @param id 编号
+ * @return 学生班主任
+ */
+ InfraStudentTeacherDO getStudentTeacher(Long id);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..793b2dd
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl
@@ -0,0 +1,180 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+ @Resource
+ private InfraStudentContactMapper studentContactMapper;
+ @Resource
+ private InfraStudentTeacherMapper studentTeacherMapper;
+
+ @Override
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+
+ // 删除子表
+ deleteStudentContactByStudentId(id);
+ deleteStudentTeacherByStudentId(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @Override
+ public PageResult getStudentContactPage(PageParam pageReqVO, Long studentId) {
+ return studentContactMapper.selectPage(pageReqVO, studentId);
+ }
+
+ @Override
+ public Long createStudentContact(InfraStudentContactDO studentContact) {
+ studentContactMapper.insert(studentContact);
+ return studentContact.getId();
+ }
+
+ @Override
+ public void updateStudentContact(InfraStudentContactDO studentContact) {
+ // 校验存在
+ validateStudentContactExists(studentContact.getId());
+ // 更新
+ studentContactMapper.updateById(studentContact);
+ }
+
+ @Override
+ public void deleteStudentContact(Long id) {
+ // 校验存在
+ validateStudentContactExists(id);
+ // 删除
+ studentContactMapper.deleteById(id);
+ }
+
+ @Override
+ public InfraStudentContactDO getStudentContact(Long id) {
+ return studentContactMapper.selectById(id);
+ }
+
+ private void validateStudentContactExists(Long id) {
+ if (studentContactMapper.selectById(id) == null) {
+ throw exception(STUDENT_CONTACT_NOT_EXISTS);
+ }
+ }
+
+ private void deleteStudentContactByStudentId(Long studentId) {
+ studentContactMapper.deleteByStudentId(studentId);
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @Override
+ public PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId) {
+ return studentTeacherMapper.selectPage(pageReqVO, studentId);
+ }
+
+ @Override
+ public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {
+ // 校验是否已经存在
+ if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {
+ throw exception(STUDENT_TEACHER_EXISTS);
+ }
+ // 插入
+ studentTeacherMapper.insert(studentTeacher);
+ return studentTeacher.getId();
+ }
+
+ @Override
+ public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {
+ // 校验存在
+ validateStudentTeacherExists(studentTeacher.getId());
+ // 更新
+ studentTeacherMapper.updateById(studentTeacher);
+ }
+
+ @Override
+ public void deleteStudentTeacher(Long id) {
+ // 校验存在
+ validateStudentTeacherExists(id);
+ // 删除
+ studentTeacherMapper.deleteById(id);
+ }
+
+ @Override
+ public InfraStudentTeacherDO getStudentTeacher(Long id) {
+ return studentTeacherMapper.selectById(id);
+ }
+
+ private void validateStudentTeacherExists(Long id) {
+ if (studentTeacherMapper.selectById(id) == null) {
+ throw exception(STUDENT_TEACHER_NOT_EXISTS);
+ }
+ }
+
+ private void deleteStudentTeacherByStudentId(Long studentId) {
+ studentTeacherMapper.deleteByStudentId(studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO
new file mode 100644
index 0000000..c19cf9f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_teacher")
+@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentTeacherDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper
new file mode 100644
index 0000000..994212d
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentTeacherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班主任 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentTeacherMapper extends BaseMapperX {
+
+ default PageResult selectPage(PageParam reqVO, Long studentId) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eq(InfraStudentTeacherDO::getStudentId, studentId)
+ .orderByDesc(InfraStudentTeacherDO::getId));
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index
new file mode 100644
index 0000000..2fe87b7
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index
@@ -0,0 +1,95 @@
+import request from '@/config/axios'
+
+export interface StudentVO {
+ id: number
+ name: string
+ description: string
+ birthday: Date
+ sex: number
+ enabled: boolean
+ avatar: string
+ video: string
+ memo: string
+}
+
+// 查询学生分页
+export const getStudentPage = async (params) => {
+ return await request.get({ url: `/infra/student/page`, params })
+}
+
+// 查询学生详情
+export const getStudent = async (id: number) => {
+ return await request.get({ url: `/infra/student/get?id=` + id })
+}
+
+// 新增学生
+export const createStudent = async (data: StudentVO) => {
+ return await request.post({ url: `/infra/student/create`, data })
+}
+
+// 修改学生
+export const updateStudent = async (data: StudentVO) => {
+ return await request.put({ url: `/infra/student/update`, data })
+}
+
+// 删除学生
+export const deleteStudent = async (id: number) => {
+ return await request.delete({ url: `/infra/student/delete?id=` + id })
+}
+
+// 导出学生 Excel
+export const exportStudent = async (params) => {
+ return await request.download({ url: `/infra/student/export-excel`, params })
+}
+
+// ==================== 子表(学生联系人) ====================
+
+// 获得学生联系人分页
+export const getStudentContactPage = async (params) => {
+ return await request.get({ url: `/infra/student/student-contact/page`, params })
+}
+// 新增学生联系人
+export const createStudentContact = async (data) => {
+ return await request.post({ url: `/infra/student/student-contact/create`, data })
+}
+
+// 修改学生联系人
+export const updateStudentContact = async (data) => {
+ return await request.put({ url: `/infra/student/student-contact/update`, data })
+}
+
+// 删除学生联系人
+export const deleteStudentContact = async (id: number) => {
+ return await request.delete({ url: `/infra/student/student-contact/delete?id=` + id })
+}
+
+// 获得学生联系人
+export const getStudentContact = async (id: number) => {
+ return await request.get({ url: `/infra/student/student-contact/get?id=` + id })
+}
+
+// ==================== 子表(学生班主任) ====================
+
+// 获得学生班主任分页
+export const getStudentTeacherPage = async (params) => {
+ return await request.get({ url: `/infra/student/student-teacher/page`, params })
+}
+// 新增学生班主任
+export const createStudentTeacher = async (data) => {
+ return await request.post({ url: `/infra/student/student-teacher/create`, data })
+}
+
+// 修改学生班主任
+export const updateStudentTeacher = async (data) => {
+ return await request.put({ url: `/infra/student/student-teacher/update`, data })
+}
+
+// 删除学生班主任
+export const deleteStudentTeacher = async (id: number) => {
+ return await request.delete({ url: `/infra/student/student-teacher/delete?id=` + id })
+}
+
+// 获得学生班主任
+export const getStudentTeacher = async (id: number) => {
+ return await request.get({ url: `/infra/student/student-teacher/get?id=` + id })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm
new file mode 100644
index 0000000..4a13935
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm
@@ -0,0 +1,155 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList
new file mode 100644
index 0000000..eada66a
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList
@@ -0,0 +1,146 @@
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm
new file mode 100644
index 0000000..0dabcb5
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm
@@ -0,0 +1,152 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm
new file mode 100644
index 0000000..f93c21c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm
@@ -0,0 +1,155 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList
new file mode 100644
index 0000000..1eba0a3
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList
@@ -0,0 +1,146 @@
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index
new file mode 100644
index 0000000..9d15146
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json
new file mode 100644
index 0000000..0937ba9
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json
@@ -0,0 +1,73 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentContactDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentContactMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactList",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherList",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue"
+}, {
+ "contentPath" : "ts/index",
+ "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..f8be662
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,3 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 学生 TODO 补充编号 ==========
+ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO
new file mode 100644
index 0000000..17c668e
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_contact")
+@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentContactDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper
new file mode 100644
index 0000000..35bbd53
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentContactMapper extends BaseMapperX {
+
+ default List selectListByStudentId(Long studentId) {
+ return selectList(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController
new file mode 100644
index 0000000..b9a587b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController
@@ -0,0 +1,117 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @GetMapping("/student-contact/list-by-student-id")
+ @Operation(summary = "获得学生联系人列表")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentContactListByStudentId(studentId));
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @GetMapping("/student-teacher/get-by-student-id")
+ @Operation(summary = "获得学生班主任")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentTeacherByStudentId(studentId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..faa491d
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+ @Schema(description = "学生联系人列表")
+ private List studentContacts;
+
+ @Schema(description = "学生班主任")
+ private InfraStudentTeacherDO studentTeacher;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService
new file mode 100644
index 0000000..afa7d22
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService
@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+ // ==================== 子表(学生联系人) ====================
+
+ /**
+ * 获得学生联系人列表
+ *
+ * @param studentId 学生编号
+ * @return 学生联系人列表
+ */
+ List getStudentContactListByStudentId(Long studentId);
+
+ // ==================== 子表(学生班主任) ====================
+
+ /**
+ * 获得学生班主任
+ *
+ * @param studentId 学生编号
+ * @return 学生班主任
+ */
+ InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..c57cba6
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl
@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+ @Resource
+ private InfraStudentContactMapper studentContactMapper;
+ @Resource
+ private InfraStudentTeacherMapper studentTeacherMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+
+ // 插入子表
+ createStudentContactList(student.getId(), createReqVO.getStudentContacts());
+ createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+
+ // 更新子表
+ updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
+ updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+
+ // 删除子表
+ deleteStudentContactByStudentId(id);
+ deleteStudentTeacherByStudentId(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @Override
+ public List getStudentContactListByStudentId(Long studentId) {
+ return studentContactMapper.selectListByStudentId(studentId);
+ }
+
+ private void createStudentContactList(Long studentId, List list) {
+ list.forEach(o -> o.setStudentId(studentId));
+ studentContactMapper.insertBatch(list);
+ }
+
+ private void updateStudentContactList(Long studentId, List list) {
+ deleteStudentContactByStudentId(studentId);
+ list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+ createStudentContactList(studentId, list);
+ }
+
+ private void deleteStudentContactByStudentId(Long studentId) {
+ studentContactMapper.deleteByStudentId(studentId);
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @Override
+ public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
+ return studentTeacherMapper.selectByStudentId(studentId);
+ }
+
+ private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacherMapper.insert(studentTeacher);
+ }
+
+ private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+ studentTeacherMapper.insertOrUpdate(studentTeacher);
+ }
+
+ private void deleteStudentTeacherByStudentId(Long studentId) {
+ studentTeacherMapper.deleteByStudentId(studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO
new file mode 100644
index 0000000..c19cf9f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_teacher")
+@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentTeacherDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper
new file mode 100644
index 0000000..0521bba
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentTeacherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班主任 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentTeacherMapper extends BaseMapperX {
+
+ default InfraStudentTeacherDO selectByStudentId(Long studentId) {
+ return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index
new file mode 100644
index 0000000..6112800
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index
@@ -0,0 +1,57 @@
+import request from '@/config/axios'
+
+export interface StudentVO {
+ id: number
+ name: string
+ description: string
+ birthday: Date
+ sex: number
+ enabled: boolean
+ avatar: string
+ video: string
+ memo: string
+}
+
+// 查询学生分页
+export const getStudentPage = async (params) => {
+ return await request.get({ url: `/infra/student/page`, params })
+}
+
+// 查询学生详情
+export const getStudent = async (id: number) => {
+ return await request.get({ url: `/infra/student/get?id=` + id })
+}
+
+// 新增学生
+export const createStudent = async (data: StudentVO) => {
+ return await request.post({ url: `/infra/student/create`, data })
+}
+
+// 修改学生
+export const updateStudent = async (data: StudentVO) => {
+ return await request.put({ url: `/infra/student/update`, data })
+}
+
+// 删除学生
+export const deleteStudent = async (id: number) => {
+ return await request.delete({ url: `/infra/student/delete?id=` + id })
+}
+
+// 导出学生 Excel
+export const exportStudent = async (params) => {
+ return await request.download({ url: `/infra/student/export-excel`, params })
+}
+
+// ==================== 子表(学生联系人) ====================
+
+// 获得学生联系人列表
+export const getStudentContactListByStudentId = async (studentId) => {
+ return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId })
+}
+
+// ==================== 子表(学生班主任) ====================
+
+// 获得学生班主任
+export const getStudentTeacherByStudentId = async (studentId) => {
+ return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm
new file mode 100644
index 0000000..55ca994
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —
+
+
+
+
+
+ + 添加学生联系人
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList
new file mode 100644
index 0000000..d0e89da
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm
new file mode 100644
index 0000000..d8e7bc3
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm
@@ -0,0 +1,184 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm
new file mode 100644
index 0000000..b22a480
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList
new file mode 100644
index 0000000..e510adc
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index
new file mode 100644
index 0000000..ee7c05f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json
new file mode 100644
index 0000000..60e7f47
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json
@@ -0,0 +1,67 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentContactDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentContactMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
+}, {
+ "contentPath" : "java/InfraStudentTeacherMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue"
+}, {
+ "contentPath" : "vue/StudentContactForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue"
+}, {
+ "contentPath" : "vue/StudentTeacherForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue"
+}, {
+ "contentPath" : "ts/index",
+ "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..f8be662
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,3 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 学生 TODO 补充编号 ==========
+ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO
new file mode 100644
index 0000000..17c668e
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_contact")
+@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentContactDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper
new file mode 100644
index 0000000..35bbd53
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentContactMapper extends BaseMapperX {
+
+ default List selectListByStudentId(Long studentId) {
+ return selectList(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentContactDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController
new file mode 100644
index 0000000..b9a587b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController
@@ -0,0 +1,117 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @GetMapping("/student-contact/list-by-student-id")
+ @Operation(summary = "获得学生联系人列表")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentContactListByStudentId(studentId));
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @GetMapping("/student-teacher/get-by-student-id")
+ @Operation(summary = "获得学生班主任")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(studentService.getStudentTeacherByStudentId(studentId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..faa491d
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+ @Schema(description = "学生联系人列表")
+ private List studentContacts;
+
+ @Schema(description = "学生班主任")
+ private InfraStudentTeacherDO studentTeacher;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService
new file mode 100644
index 0000000..afa7d22
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService
@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+ // ==================== 子表(学生联系人) ====================
+
+ /**
+ * 获得学生联系人列表
+ *
+ * @param studentId 学生编号
+ * @return 学生联系人列表
+ */
+ List getStudentContactListByStudentId(Long studentId);
+
+ // ==================== 子表(学生班主任) ====================
+
+ /**
+ * 获得学生班主任
+ *
+ * @param studentId 学生编号
+ * @return 学生班主任
+ */
+ InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..c57cba6
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl
@@ -0,0 +1,147 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+ @Resource
+ private InfraStudentContactMapper studentContactMapper;
+ @Resource
+ private InfraStudentTeacherMapper studentTeacherMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+
+ // 插入子表
+ createStudentContactList(student.getId(), createReqVO.getStudentContacts());
+ createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+
+ // 更新子表
+ updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
+ updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+
+ // 删除子表
+ deleteStudentContactByStudentId(id);
+ deleteStudentTeacherByStudentId(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+ // ==================== 子表(学生联系人) ====================
+
+ @Override
+ public List getStudentContactListByStudentId(Long studentId) {
+ return studentContactMapper.selectListByStudentId(studentId);
+ }
+
+ private void createStudentContactList(Long studentId, List list) {
+ list.forEach(o -> o.setStudentId(studentId));
+ studentContactMapper.insertBatch(list);
+ }
+
+ private void updateStudentContactList(Long studentId, List list) {
+ deleteStudentContactByStudentId(studentId);
+ list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+ createStudentContactList(studentId, list);
+ }
+
+ private void deleteStudentContactByStudentId(Long studentId) {
+ studentContactMapper.deleteByStudentId(studentId);
+ }
+
+ // ==================== 子表(学生班主任) ====================
+
+ @Override
+ public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
+ return studentTeacherMapper.selectByStudentId(studentId);
+ }
+
+ private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacherMapper.insert(studentTeacher);
+ }
+
+ private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
+ if (studentTeacher == null) {
+ return;
+ }
+ studentTeacher.setStudentId(studentId);
+ studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+ studentTeacherMapper.insertOrUpdate(studentTeacher);
+ }
+
+ private void deleteStudentTeacherByStudentId(Long studentId) {
+ studentTeacherMapper.deleteByStudentId(studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO
new file mode 100644
index 0000000..c19cf9f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student_teacher")
+@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentTeacherDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper
new file mode 100644
index 0000000..0521bba
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentTeacherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班主任 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentTeacherMapper extends BaseMapperX {
+
+ default InfraStudentTeacherDO selectByStudentId(Long studentId) {
+ return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(InfraStudentTeacherDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index
new file mode 100644
index 0000000..6112800
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index
@@ -0,0 +1,57 @@
+import request from '@/config/axios'
+
+export interface StudentVO {
+ id: number
+ name: string
+ description: string
+ birthday: Date
+ sex: number
+ enabled: boolean
+ avatar: string
+ video: string
+ memo: string
+}
+
+// 查询学生分页
+export const getStudentPage = async (params) => {
+ return await request.get({ url: `/infra/student/page`, params })
+}
+
+// 查询学生详情
+export const getStudent = async (id: number) => {
+ return await request.get({ url: `/infra/student/get?id=` + id })
+}
+
+// 新增学生
+export const createStudent = async (data: StudentVO) => {
+ return await request.post({ url: `/infra/student/create`, data })
+}
+
+// 修改学生
+export const updateStudent = async (data: StudentVO) => {
+ return await request.put({ url: `/infra/student/update`, data })
+}
+
+// 删除学生
+export const deleteStudent = async (id: number) => {
+ return await request.delete({ url: `/infra/student/delete?id=` + id })
+}
+
+// 导出学生 Excel
+export const exportStudent = async (params) => {
+ return await request.download({ url: `/infra/student/export-excel`, params })
+}
+
+// ==================== 子表(学生联系人) ====================
+
+// 获得学生联系人列表
+export const getStudentContactListByStudentId = async (studentId) => {
+ return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId })
+}
+
+// ==================== 子表(学生班主任) ====================
+
+// 获得学生班主任
+export const getStudentTeacherByStudentId = async (studentId) => {
+ return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm
new file mode 100644
index 0000000..55ca994
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —
+
+
+
+
+
+ + 添加学生联系人
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm
new file mode 100644
index 0000000..d8e7bc3
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm
@@ -0,0 +1,184 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm
new file mode 100644
index 0000000..b22a480
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index
new file mode 100644
index 0000000..b115b13
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index
@@ -0,0 +1,252 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json
new file mode 100644
index 0000000..5a0eebd
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json
@@ -0,0 +1,49 @@
+[ {
+ "contentPath" : "java/InfraStudentPageReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
+}, {
+ "contentPath" : "java/InfraStudentSaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraStudentController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
+}, {
+ "contentPath" : "java/InfraStudentDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
+}, {
+ "contentPath" : "java/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
+}, {
+ "contentPath" : "xml/InfraStudentMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml"
+}, {
+ "contentPath" : "java/InfraStudentServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraStudentService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
+}, {
+ "contentPath" : "java/InfraStudentServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue"
+}, {
+ "contentPath" : "vue/StudentForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue"
+}, {
+ "contentPath" : "ts/index",
+ "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..f8be662
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,3 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 学生 TODO 补充编号 ==========
+ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController
new file mode 100644
index 0000000..3796982
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/student")
+@Validated
+public class InfraStudentController {
+
+ @Resource
+ private InfraStudentService studentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:create')")
+ public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
+ return success(studentService.createStudent(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:student:update')")
+ public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
+ studentService.updateStudent(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:student:delete')")
+ public CommonResult deleteStudent(@RequestParam("id") Long id) {
+ studentService.deleteStudent(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult getStudent(@RequestParam("id") Long id) {
+ InfraStudentDO student = studentService.getStudent(id);
+ return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:student:query')")
+ public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
+ PageResult pageResult = studentService.getStudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = studentService.getStudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
+ BeanUtils.toBean(list, InfraStudentRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO
new file mode 100644
index 0000000..b0d4bd2
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+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_student")
+@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraStudentDO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper
new file mode 100644
index 0000000..34e70a0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraStudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraStudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(InfraStudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(InfraStudentDO::getName, reqVO.getName())
+ .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
+ .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
+ .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(InfraStudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO
new file mode 100644
index 0000000..41a3730
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.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 InfraStudentPageReqVO 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;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO
new file mode 100644
index 0000000..c41a550
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+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 InfraStudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @ExcelProperty(value = "是否有效", converter = DictConvert.class)
+ @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Boolean enabled;
+
+ @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @ExcelProperty("附件")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @ExcelProperty("备注")
+ private String memo;
+
+ @Schema(description = "创建时间")
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000..43e7f14
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO
@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class InfraStudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @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 = "出生日期不能为空")
+ 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 = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
+ @NotEmpty(message = "附件不能为空")
+ private String video;
+
+ @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
+ @NotEmpty(message = "备注不能为空")
+ private String memo;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService
new file mode 100644
index 0000000..c4a0e17
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraStudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteStudent(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ InfraStudentDO getStudent(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getStudentPage(InfraStudentPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl
new file mode 100644
index 0000000..2292a66
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+
+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 InfraStudentServiceImpl implements InfraStudentService {
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Override
+ public Long createStudent(InfraStudentSaveReqVO createReqVO) {
+ // 插入
+ InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
+ studentMapper.insert(student);
+ // 返回
+ return student.getId();
+ }
+
+ @Override
+ public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateStudentExists(updateReqVO.getId());
+ // 更新
+ InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
+ studentMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteStudent(Long id) {
+ // 校验存在
+ validateStudentExists(id);
+ // 删除
+ studentMapper.deleteById(id);
+ }
+
+ private void validateStudentExists(Long id) {
+ if (studentMapper.selectById(id) == null) {
+ throw exception(STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public InfraStudentDO getStudent(Long id) {
+ return studentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) {
+ return studentMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000..b5f4bf0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest
@@ -0,0 +1,146 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraStudentServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraStudentServiceImpl.class)
+public class InfraStudentServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraStudentServiceImpl studentService;
+
+ @Resource
+ private InfraStudentMapper studentMapper;
+
+ @Test
+ public void testCreateStudent_success() {
+ // 准备参数
+ InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
+
+ // 调用
+ Long studentId = studentService.createStudent(createReqVO);
+ // 断言
+ assertNotNull(studentId);
+ // 校验记录的属性是否正确
+ InfraStudentDO student = studentMapper.selectById(studentId);
+ assertPojoEquals(createReqVO, student, "id");
+ }
+
+ @Test
+ public void testUpdateStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
+ o.setId(dbStudent.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ studentService.updateStudent(updateReqVO);
+ // 校验是否更新正确
+ InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, student);
+ }
+
+ @Test
+ public void testUpdateStudent_notExists() {
+ // 准备参数
+ InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteStudent_success() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
+ studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbStudent.getId();
+
+ // 调用
+ studentService.deleteStudent(id);
+ // 校验数据不存在了
+ assertNull(studentMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteStudent_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetStudentPage() {
+ // mock 数据
+ InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
+ o.setName(null);
+ o.setBirthday(null);
+ o.setSex(null);
+ o.setEnabled(null);
+ o.setCreateTime(null);
+ });
+ studentMapper.insert(dbStudent);
+ // 测试 name 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
+ // 测试 birthday 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
+ // 测试 sex 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
+ // 测试 enabled 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
+ // 测试 createTime 不匹配
+ studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
+ // 准备参数
+ InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
+ reqVO.setName(null);
+ reqVO.setBirthday(null);
+ reqVO.setSex(null);
+ reqVO.setEnabled(null);
+ reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+
+ // 调用
+ PageResult pageResult = studentService.getStudentPage(reqVO);
+ // 断言
+ assertEquals(1, pageResult.getTotal());
+ assertEquals(1, pageResult.getList().size());
+ assertPojoEquals(dbStudent, pageResult.getList().get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2
new file mode 100644
index 0000000..6c1875f
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2
@@ -0,0 +1,17 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_student" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" varchar NOT NULL,
+ "birthday" varchar NOT NULL,
+ "sex" int NOT NULL,
+ "enabled" bit NOT NULL,
+ "avatar" varchar NOT NULL,
+ "video" varchar NOT NULL,
+ "memo" varchar NOT NULL,
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY ("id")
+) COMMENT '学生表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_student";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql
new file mode 100644
index 0000000..83df279
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '学生管理', '', 2, 0, 888,
+ 'student', '', 'infra/demo/index', 0, 'InfraStudent'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生查询', 'infra:student:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生创建', 'infra:student:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生更新', 'infra:student:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生删除', 'infra:student:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '学生导出', 'infra:student:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index
new file mode 100644
index 0000000..8cdf254
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index
@@ -0,0 +1,43 @@
+import request from '@/config/axios'
+
+export interface StudentVO {
+ id: number
+ name: string
+ description: string
+ birthday: Date
+ sex: number
+ enabled: boolean
+ avatar: string
+ video: string
+ memo: string
+}
+
+// 查询学生分页
+export const getStudentPage = async (params) => {
+ return await request.get({ url: `/infra/student/page`, params })
+}
+
+// 查询学生详情
+export const getStudent = async (id: number) => {
+ return await request.get({ url: `/infra/student/get?id=` + id })
+}
+
+// 新增学生
+export const createStudent = async (data: StudentVO) => {
+ return await request.post({ url: `/infra/student/create`, data })
+}
+
+// 修改学生
+export const updateStudent = async (data: StudentVO) => {
+ return await request.put({ url: `/infra/student/update`, data })
+}
+
+// 删除学生
+export const deleteStudent = async (id: number) => {
+ return await request.delete({ url: `/infra/student/delete?id=` + id })
+}
+
+// 导出学生 Excel
+export const exportStudent = async (params) => {
+ return await request.download({ url: `/infra/student/export-excel`, params })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm
new file mode 100644
index 0000000..0dabcb5
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm
@@ -0,0 +1,152 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index
new file mode 100644
index 0000000..b115b13
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index
@@ -0,0 +1,252 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper
new file mode 100644
index 0000000..155aa5c
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json
new file mode 100644
index 0000000..357df00
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json
@@ -0,0 +1,49 @@
+[ {
+ "contentPath" : "java/InfraCategoryListReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java"
+}, {
+ "contentPath" : "java/InfraCategoryRespVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java"
+}, {
+ "contentPath" : "java/InfraCategorySaveReqVO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java"
+}, {
+ "contentPath" : "java/InfraCategoryController",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java"
+}, {
+ "contentPath" : "java/InfraCategoryDO",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java"
+}, {
+ "contentPath" : "java/InfraCategoryMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java"
+}, {
+ "contentPath" : "xml/InfraCategoryMapper",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml"
+}, {
+ "contentPath" : "java/InfraCategoryServiceImpl",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java"
+}, {
+ "contentPath" : "java/InfraCategoryService",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java"
+}, {
+ "contentPath" : "java/InfraCategoryServiceImplTest",
+ "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java"
+}, {
+ "contentPath" : "java/ErrorCodeConstants_手动操作",
+ "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
+}, {
+ "contentPath" : "sql/sql",
+ "filePath" : "sql/sql.sql"
+}, {
+ "contentPath" : "sql/h2",
+ "filePath" : "sql/h2.sql"
+}, {
+ "contentPath" : "vue/index",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue"
+}, {
+ "contentPath" : "vue/CategoryForm",
+ "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/CategoryForm.vue"
+}, {
+ "contentPath" : "ts/index",
+ "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts"
+} ]
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000..36df675
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作
@@ -0,0 +1,8 @@
+// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
+// ========== 分类 TODO 补充编号 ==========
+ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在");
+ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除");
+ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在");
+ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类");
+ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类");
+ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory");
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController
new file mode 100644
index 0000000..a7b2f81
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController
@@ -0,0 +1,94 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
+
+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.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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService;
+
+@Tag(name = "管理后台 - 分类")
+@RestController
+@RequestMapping("/infra/category")
+@Validated
+public class InfraCategoryController {
+
+ @Resource
+ private InfraCategoryService categoryService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建分类")
+ @PreAuthorize("@ss.hasPermission('infra:category:create')")
+ public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) {
+ return success(categoryService.createCategory(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新分类")
+ @PreAuthorize("@ss.hasPermission('infra:category:update')")
+ public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) {
+ categoryService.updateCategory(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除分类")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:category:delete')")
+ public CommonResult deleteCategory(@RequestParam("id") Long id) {
+ categoryService.deleteCategory(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得分类")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:category:query')")
+ public CommonResult getCategory(@RequestParam("id") Long id) {
+ InfraCategoryDO category = categoryService.getCategory(id);
+ return success(BeanUtils.toBean(category, InfraCategoryRespVO.class));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得分类列表")
+ @PreAuthorize("@ss.hasPermission('infra:category:query')")
+ public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) {
+ List list = categoryService.getCategoryList(listReqVO);
+ return success(BeanUtils.toBean(list, InfraCategoryRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出分类 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:category:export')")
+ @OperateLog(type = EXPORT)
+ public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO,
+ HttpServletResponse response) throws IOException {
+ List list = categoryService.getCategoryList(listReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class,
+ BeanUtils.toBean(list, InfraCategoryRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO
new file mode 100644
index 0000000..9bf21c0
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
+
+import lombok.*;
+import java.util.*;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 分类 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_category")
+@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class InfraCategoryDO extends BaseDO {
+
+ public static final Long PARENT_ID_ROOT = 0L;
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 父编号
+ */
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO
new file mode 100644
index 0000000..e5c6f18
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+@Schema(description = "管理后台 - 分类列表 Request VO")
+@Data
+public class InfraCategoryListReqVO {
+
+ @Schema(description = "名字", example = "芋头")
+ private String name;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper
new file mode 100644
index 0000000..9dadbf1
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo;
+
+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.demo.InfraCategoryDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+
+/**
+ * 分类 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfraCategoryMapper extends BaseMapperX {
+
+ default List selectList(InfraCategoryListReqVO reqVO) {
+ return selectList(new LambdaQueryWrapperX()
+ .likeIfPresent(InfraCategoryDO::getName, reqVO.getName())
+ .orderByDesc(InfraCategoryDO::getId));
+ }
+
+ default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) {
+ return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name);
+ }
+
+ default Long selectCountByParentId(Long parentId) {
+ return selectCount(InfraCategoryDO::getParentId, parentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO
new file mode 100644
index 0000000..6325d86
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 分类 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class InfraCategoryRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+ @ExcelProperty("父编号")
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO
new file mode 100644
index 0000000..3c03b97
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 分类新增/修改 Request VO")
+@Data
+public class InfraCategorySaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+ @NotNull(message = "父编号不能为空")
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService
new file mode 100644
index 0000000..9d0ae1a
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 分类 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfraCategoryService {
+
+ /**
+ * 创建分类
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createCategory(@Valid InfraCategorySaveReqVO createReqVO);
+
+ /**
+ * 更新分类
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO);
+
+ /**
+ * 删除分类
+ *
+ * @param id 编号
+ */
+ void deleteCategory(Long id);
+
+ /**
+ * 获得分类
+ *
+ * @param id 编号
+ * @return 分类
+ */
+ InfraCategoryDO getCategory(Long id);
+
+ /**
+ * 获得分类列表
+ *
+ * @param listReqVO 查询条件
+ * @return 分类列表
+ */
+ List getCategoryList(InfraCategoryListReqVO listReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl
new file mode 100644
index 0000000..351568b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl
@@ -0,0 +1,136 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+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.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
+
+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 InfraCategoryServiceImpl implements InfraCategoryService {
+
+ @Resource
+ private InfraCategoryMapper categoryMapper;
+
+ @Override
+ public Long createCategory(InfraCategorySaveReqVO createReqVO) {
+ // 校验父编号的有效性
+ validateParentCategory(null, createReqVO.getParentId());
+ // 校验名字的唯一性
+ validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
+
+ // 插入
+ InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class);
+ categoryMapper.insert(category);
+ // 返回
+ return category.getId();
+ }
+
+ @Override
+ public void updateCategory(InfraCategorySaveReqVO updateReqVO) {
+ // 校验存在
+ validateCategoryExists(updateReqVO.getId());
+ // 校验父编号的有效性
+ validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());
+ // 校验名字的唯一性
+ validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
+
+ // 更新
+ InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class);
+ categoryMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteCategory(Long id) {
+ // 校验存在
+ validateCategoryExists(id);
+ // 校验是否有子分类
+ if (categoryMapper.selectCountByParentId(id) > 0) {
+ throw exception(CATEGORY_EXITS_CHILDREN);
+ }
+ // 删除
+ categoryMapper.deleteById(id);
+ }
+
+ private void validateCategoryExists(Long id) {
+ if (categoryMapper.selectById(id) == null) {
+ throw exception(CATEGORY_NOT_EXISTS);
+ }
+ }
+
+ private void validateParentCategory(Long id, Long parentId) {
+ if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ return;
+ }
+ // 1. 不能设置自己为父分类
+ if (Objects.equals(id, parentId)) {
+ throw exception(CATEGORY_PARENT_ERROR);
+ }
+ // 2. 父分类不存在
+ CategoryDO parentCategory = categoryMapper.selectById(parentId);
+ if (parentCategory == null) {
+ throw exception(CATEGORY_PARENT_NOT_EXITS);
+ }
+ // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路
+ if (id == null) { // id 为空,说明新增,不需要考虑环路
+ return;
+ }
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ // 3.1 校验环路
+ parentId = parentCategory.getParentId();
+ if (Objects.equals(id, parentId)) {
+ throw exception(CATEGORY_PARENT_IS_CHILD);
+ }
+ // 3.2 继续递归下一级父分类
+ if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ break;
+ }
+ parentCategory = categoryMapper.selectById(parentId);
+ if (parentCategory == null) {
+ break;
+ }
+ }
+ }
+
+ private void validateCategoryNameUnique(Long id, Long parentId, String name) {
+ CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);
+ if (category == null) {
+ return;
+ }
+ // 如果 id 为空,说明不用比较是否为相同 id 的分类
+ if (id == null) {
+ throw exception(CATEGORY_NAME_DUPLICATE);
+ }
+ if (!Objects.equals(category.getId(), id)) {
+ throw exception(CATEGORY_NAME_DUPLICATE);
+ }
+ }
+
+ @Override
+ public InfraCategoryDO getCategory(Long id) {
+ return categoryMapper.selectById(id);
+ }
+
+ @Override
+ public List getCategoryList(InfraCategoryListReqVO listReqVO) {
+ return categoryMapper.selectList(listReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest
new file mode 100644
index 0000000..efb70fd
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest
@@ -0,0 +1,129 @@
+package cn.iocoder.yudao.module.infra.service.demo;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import javax.annotation.Resource;
+
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import javax.annotation.Resource;
+import org.springframework.context.annotation.Import;
+import java.util.*;
+import java.time.LocalDateTime;
+
+import static cn.hutool.core.util.RandomUtil.*;
+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 cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * {@link InfraCategoryServiceImpl} 的单元测试类
+ *
+ * @author 芋道源码
+ */
+@Import(InfraCategoryServiceImpl.class)
+public class InfraCategoryServiceImplTest extends BaseDbUnitTest {
+
+ @Resource
+ private InfraCategoryServiceImpl categoryService;
+
+ @Resource
+ private InfraCategoryMapper categoryMapper;
+
+ @Test
+ public void testCreateCategory_success() {
+ // 准备参数
+ InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null);
+
+ // 调用
+ Long categoryId = categoryService.createCategory(createReqVO);
+ // 断言
+ assertNotNull(categoryId);
+ // 校验记录的属性是否正确
+ InfraCategoryDO category = categoryMapper.selectById(categoryId);
+ assertPojoEquals(createReqVO, category, "id");
+ }
+
+ @Test
+ public void testUpdateCategory_success() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
+ categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> {
+ o.setId(dbCategory.getId()); // 设置更新的 ID
+ });
+
+ // 调用
+ categoryService.updateCategory(updateReqVO);
+ // 校验是否更新正确
+ InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, category);
+ }
+
+ @Test
+ public void testUpdateCategory_notExists() {
+ // 准备参数
+ InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class);
+
+ // 调用, 并断言异常
+ assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
+ }
+
+ @Test
+ public void testDeleteCategory_success() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class);
+ categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
+ // 准备参数
+ Long id = dbCategory.getId();
+
+ // 调用
+ categoryService.deleteCategory(id);
+ // 校验数据不存在了
+ assertNull(categoryMapper.selectById(id));
+ }
+
+ @Test
+ public void testDeleteCategory_notExists() {
+ // 准备参数
+ Long id = randomLongId();
+
+ // 调用, 并断言异常
+ assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
+ }
+
+ @Test
+ @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+ public void testGetCategoryList() {
+ // mock 数据
+ InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到
+ o.setName(null);
+ });
+ categoryMapper.insert(dbCategory);
+ // 测试 name 不匹配
+ categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
+ // 准备参数
+ InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO();
+ reqVO.setName(null);
+
+ // 调用
+ List list = categoryService.getCategoryList(reqVO);
+ // 断言
+ assertEquals(1, list.size());
+ assertPojoEquals(dbCategory, list.get(0));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2
new file mode 100644
index 0000000..4141766
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2
@@ -0,0 +1,10 @@
+-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
+CREATE TABLE IF NOT EXISTS "infra_category" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar NOT NULL,
+ "description" bigint NOT NULL,
+ PRIMARY KEY ("id")
+) COMMENT '分类表';
+
+-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
+DELETE FROM "infra_category";
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql
new file mode 100644
index 0000000..8140948
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql
@@ -0,0 +1,55 @@
+-- 菜单 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status, component_name
+)
+VALUES (
+ '分类管理', '', 2, 0, 888,
+ 'category', '', 'infra/demo/index', 0, 'InfraCategory'
+);
+
+-- 按钮父菜单ID
+-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
+SELECT @parentId := LAST_INSERT_ID();
+
+-- 按钮 SQL
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类查询', 'infra:category:query', 3, 1, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类创建', 'infra:category:create', 3, 2, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类更新', 'infra:category:update', 3, 3, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类删除', 'infra:category:delete', 3, 4, @parentId,
+ '', '', '', 0
+);
+INSERT INTO system_menu(
+ name, permission, type, sort, parent_id,
+ path, icon, component, status
+)
+VALUES (
+ '分类导出', 'infra:category:export', 3, 5, @parentId,
+ '', '', '', 0
+);
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index
new file mode 100644
index 0000000..453c885
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index
@@ -0,0 +1,37 @@
+import request from '@/config/axios'
+
+export interface CategoryVO {
+ id: number
+ name: string
+ parentId: number
+}
+
+// 查询分类列表
+export const getCategoryList = async (params) => {
+ return await request.get({ url: `/infra/category/list`, params })
+}
+
+// 查询分类详情
+export const getCategory = async (id: number) => {
+ return await request.get({ url: `/infra/category/get?id=` + id })
+}
+
+// 新增分类
+export const createCategory = async (data: CategoryVO) => {
+ return await request.post({ url: `/infra/category/create`, data })
+}
+
+// 修改分类
+export const updateCategory = async (data: CategoryVO) => {
+ return await request.put({ url: `/infra/category/update`, data })
+}
+
+// 删除分类
+export const deleteCategory = async (id: number) => {
+ return await request.delete({ url: `/infra/category/delete?id=` + id })
+}
+
+// 导出分类 Excel
+export const exportCategory = async (params) => {
+ return await request.download({ url: `/infra/category/export-excel`, params })
+}
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm
new file mode 100644
index 0000000..8e139fb
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm
@@ -0,0 +1,114 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index
new file mode 100644
index 0000000..46902e7
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+ 展开/折叠
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper
new file mode 100644
index 0000000..025ac85
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/src/test/resources/logback.xml b/yudao-module-infra/src/test/resources/logback.xml
new file mode 100644
index 0000000..daf756b
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/logback.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/yudao-module-infra/src/test/resources/sql/clean.sql b/yudao-module-infra/src/test/resources/sql/clean.sql
new file mode 100644
index 0000000..58345ed
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/sql/clean.sql
@@ -0,0 +1,11 @@
+DELETE FROM "infra_config";
+DELETE FROM "infra_file_config";
+DELETE FROM "infra_file";
+DELETE FROM "infra_job";
+DELETE FROM "infra_job_log";
+DELETE FROM "infra_api_access_log";
+DELETE FROM "infra_api_error_log";
+DELETE FROM "infra_file_config";
+DELETE FROM "infra_data_source_config";
+DELETE FROM "infra_codegen_table";
+DELETE FROM "infra_codegen_column";
diff --git a/yudao-module-infra/src/test/resources/sql/create_tables.sql b/yudao-module-infra/src/test/resources/sql/create_tables.sql
new file mode 100644
index 0000000..d4b19c9
--- /dev/null
+++ b/yudao-module-infra/src/test/resources/sql/create_tables.sql
@@ -0,0 +1,216 @@
+
+CREATE TABLE IF NOT EXISTS "infra_config" (
+ "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
+ "category" varchar(50) NOT NULL,
+ "type" tinyint NOT NULL,
+ "name" varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
+ "config_key" varchar(100) NOT NULL DEFAULT '',
+ "value" varchar(500) NOT NULL DEFAULT '',
+ "visible" bit NOT NULL,
+ "remark" varchar(500) DEFAULT NULL,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ PRIMARY KEY ("id")
+) COMMENT '参数配置表';
+
+CREATE TABLE IF NOT EXISTS "infra_file_config" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar(63) NOT NULL,
+ "storage" tinyint NOT NULL,
+ "remark" varchar(255),
+ "master" bit(1) NOT NULL,
+ "config" varchar(4096) NOT NULL,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ PRIMARY KEY ("id")
+) COMMENT '文件配置表';
+
+CREATE TABLE IF NOT EXISTS "infra_file" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "config_id" bigint NOT NULL,
+ "name" varchar(256),
+ "path" varchar(512),
+ "url" varchar(1024),
+ "type" varchar(63) DEFAULT NULL,
+ "size" bigint NOT NULL,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ "tenant_id" bigint not null default '0',
+ PRIMARY KEY ("id")
+) COMMENT '文件表';
+
+CREATE TABLE IF NOT EXISTS "infra_job" (
+ "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号',
+ "name" varchar(32) NOT NULL COMMENT '任务名称',
+ "status" tinyint(4) NOT NULL COMMENT '任务状态',
+ "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
+ "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
+ "cron_expression" varchar(32) NOT NULL COMMENT 'CRON 表达式',
+ "retry_count" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数',
+ "retry_interval" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔',
+ "monitor_timeout" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间',
+ "creator" varchar(64) DEFAULT '' COMMENT '创建者',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ "updater" varchar(64) DEFAULT '' COMMENT '更新者',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ "deleted" bit NOT NULL DEFAULT FALSE COMMENT '是否删除',
+ PRIMARY KEY ("id")
+) COMMENT='定时任务表';
+
+CREATE TABLE IF NOT EXISTS "infra_job_log" (
+ "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号',
+ "job_id" bigint(20) NOT NULL COMMENT '任务编号',
+ "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
+ "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
+ "execute_index" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行',
+ "begin_time" datetime NOT NULL COMMENT '开始执行时间',
+ "end_time" datetime DEFAULT NULL COMMENT '结束执行时间',
+ "duration" int(11) DEFAULT NULL COMMENT '执行时长',
+ "status" tinyint(4) NOT NULL COMMENT '任务状态',
+ "result" varchar(4000) DEFAULT '' COMMENT '结果数据',
+ "creator" varchar(64) DEFAULT '' COMMENT '创建者',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ "updater" varchar(64) DEFAULT '' COMMENT '更新者',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ "deleted" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除',
+ PRIMARY KEY ("id")
+)COMMENT='定时任务日志表';
+
+CREATE TABLE IF NOT EXISTS "infra_api_access_log" (
+ "id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
+ "trace_id" varchar(64) not null default '',
+ "user_id" bigint not null default '0',
+ "user_type" tinyint not null default '0',
+ "application_name" varchar(50) not null,
+ "request_method" varchar(16) not null default '',
+ "request_url" varchar(255) not null default '',
+ "request_params" varchar(8000) not null default '',
+ "response_body" varchar(8000) not null default '',
+ "user_ip" varchar(50) not null,
+ "user_agent" varchar(512) not null,
+ `operate_module` varchar(50) NOT NULL,
+ `operate_name` varchar(50) NOT NULL,
+ `operate_type` bigint(4) NOT NULL DEFAULT '0',
+ "begin_time" timestamp not null,
+ "end_time" timestamp not null,
+ "duration" integer not null,
+ "result_code" integer not null default '0',
+ "result_msg" varchar(512) default '',
+ "creator" varchar(64) default '',
+ "create_time" timestamp not null default current_timestamp,
+ "updater" varchar(64) default '',
+ "update_time" timestamp not null default current_timestamp,
+ "deleted" bit not null default false,
+ "tenant_id" bigint not null default '0',
+ primary key ("id")
+) COMMENT 'API 访问日志表';
+
+CREATE TABLE IF NOT EXISTS "infra_api_error_log" (
+ "id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
+ "trace_id" varchar(64) not null,
+ "user_id" bigint not null default '0',
+ "user_type" tinyint not null default '0',
+ "application_name" varchar(50) not null,
+ "request_method" varchar(16) not null,
+ "request_url" varchar(255) not null,
+ "request_params" varchar(8000) not null,
+ "user_ip" varchar(50) not null,
+ "user_agent" varchar(512) not null,
+ "exception_time" timestamp not null,
+ "exception_name" varchar(128) not null default '',
+ "exception_message" clob not null,
+ "exception_root_cause_message" clob not null,
+ "exception_stack_trace" clob not null,
+ "exception_class_name" varchar(512) not null,
+ "exception_file_name" varchar(512) not null,
+ "exception_method_name" varchar(512) not null,
+ "exception_line_number" integer not null,
+ "process_status" tinyint not null,
+ "process_time" timestamp default null,
+ "process_user_id" bigint default '0',
+ "creator" varchar(64) default '',
+ "create_time" timestamp not null default current_timestamp,
+ "updater" varchar(64) default '',
+ "update_time" timestamp not null default current_timestamp,
+ "deleted" bit not null default false,
+ "tenant_id" bigint not null default '0',
+ primary key ("id")
+) COMMENT '系统异常日志';
+
+CREATE TABLE IF NOT EXISTS "infra_data_source_config" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "name" varchar(100) NOT NULL,
+ "url" varchar(1024) NOT NULL,
+ "username" varchar(255) NOT NULL,
+ "password" varchar(255) NOT NULL,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ PRIMARY KEY ("id")
+) COMMENT '数据源配置表';
+
+CREATE TABLE IF NOT EXISTS "infra_codegen_table" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "data_source_config_id" bigint not null,
+ "scene" tinyint not null DEFAULT 1,
+ "table_name" varchar(200) NOT NULL,
+ "table_comment" varchar(500) NOT NULL,
+ "remark" varchar(500) NOT NULL,
+ "module_name" varchar(30) NOT NULL,
+ "business_name" varchar(30) NOT NULL,
+ "class_name" varchar(100) NOT NULL,
+ "class_comment" varchar(50) NOT NULL,
+ "author" varchar(50) NOT NULL,
+ "template_type" tinyint not null DEFAULT 1,
+ "front_type" tinyint not null,
+ "parent_menu_id" bigint not null,
+ "master_table_id" bigint not null,
+ "sub_join_column_id" bigint not null,
+ "sub_join_many" bit not null,
+ "tree_parent_column_id" bigint not null,
+ "tree_name_column_id" bigint not null,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ PRIMARY KEY ("id")
+) COMMENT '代码生成表定义表';
+
+CREATE TABLE IF NOT EXISTS "infra_codegen_column" (
+ "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ "table_id" bigint not null,
+ "column_name" varchar(200) NOT NULL,
+ "data_type" varchar(100) NOT NULL,
+ "column_comment" varchar(500) NOT NULL,
+ "nullable" tinyint not null,
+ "primary_key" tinyint not null,
+ "ordinal_position" int not null,
+ "java_type" varchar(32) NOT NULL,
+ "java_field" varchar(64) NOT NULL,
+ "dict_type" varchar(200) NOT NULL,
+ "example" varchar(64) NOT NULL,
+ "create_operation" bit not null,
+ "update_operation" bit not null,
+ "list_operation" bit not null,
+ "list_operation_condition" varchar(32) not null,
+ "list_operation_result" bit not null,
+ "html_type" varchar(32) NOT NULL,
+ "creator" varchar(64) DEFAULT '',
+ "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updater" varchar(64) DEFAULT '',
+ "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ "deleted" bit NOT NULL DEFAULT FALSE,
+ PRIMARY KEY ("id")
+) COMMENT '代码生成表字段定义表';
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-api/pom.xml b/yudao-module-infra/yudao-module-infra-api/pom.xml
deleted file mode 100644
index 168ea64..0000000
--- a/yudao-module-infra/yudao-module-infra-api/pom.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- cn.iocoder.boot
- yudao-module-infra
- ${revision}
-
- 4.0.0
- yudao-module-infra-api
- jar
-
- ${project.artifactId}
-
- infra 模块 API,暴露给其它模块调用
-
-
-
-
- cn.iocoder.boot
- yudao-common
-
-
-
-
- org.springframework.boot
- spring-boot-starter-validation
- true
-
-
-
-
diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml
deleted file mode 100644
index e6fba74..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/pom.xml
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
- cn.iocoder.boot
- yudao-module-infra
- ${revision}
-
- 4.0.0
- yudao-module-infra-biz
- jar
-
- ${project.artifactId}
-
- infra 模块,主要提供两块能力:
- 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
- 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
-
-
-
-
- cn.iocoder.boot
- yudao-module-infra-api
- ${revision}
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-biz-tenant
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-security
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-websocket
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-mybatis
-
-
- com.baomidou
- mybatis-plus-generator
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-redis
-
-
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-job
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-mq
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-test
- test
-
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-excel
-
-
-
- org.apache.velocity
- velocity-engine-core
-
-
-
-
- cn.iocoder.boot
- yudao-spring-boot-starter-monitor
-
-
-
- de.codecentric
- spring-boot-admin-starter-server
-
-
-
-
- commons-net
- commons-net
-
-
- com.jcraft
- jsch
-
-
-
- software.amazon.awssdk
- s3
-
-
-
- org.apache.tika
- tika-core
-
-
-
-
-
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java
deleted file mode 100644
index af01680..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package cn.iocoder.yudao.module.infra.api;
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm
deleted file mode 100644
index b7f2651..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm
+++ /dev/null
@@ -1,32 +0,0 @@
-import { defHttp } from '@/utils/http/axios'
-#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
-
-// 查询${table.classComment}列表
-export function get${simpleClassName}Page(params) {
- return defHttp.get({ url: '${baseURL}/page', params })
-}
-
-// 查询${table.classComment}详情
-export function get${simpleClassName}(id: number) {
- return defHttp.get({ url: `${baseURL}/get?id=${id}` })
-}
-
-// 新增${table.classComment}
-export function create${simpleClassName}(data) {
- return defHttp.post({ url: '${baseURL}/create', data })
-}
-
-// 修改${table.classComment}
-export function update${simpleClassName}(data) {
- return defHttp.put({ url: '${baseURL}/update', data })
-}
-
-// 删除${table.classComment}
-export function delete${simpleClassName}(id: number) {
- return defHttp.delete({ url: `${baseURL}/delete?id=${id}` })
-}
-
-// 导出${table.classComment} Excel
-export function export${simpleClassName}(params) {
- return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm
deleted file mode 100644
index 1804365..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
deleted file mode 100644
index 9e59b12..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
- {{ t('action.create') }}
-
-
- {{ t('action.export') }}
-
-
-
-
-
-
-
-
- <${simpleClassName}Modal @register="registerModal" @success="reload()" />
-
-
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm
deleted file mode 100644
index c71e0b4..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm
+++ /dev/null
@@ -1,90 +0,0 @@
-#set ($subTable = $subTables.get($subIndex))##当前表
-#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
-#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
-#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
-
-
-
-
-
-
-
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java
deleted file mode 100644
index 8db47da..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package cn.iocoder.yudao.module.infra.framework.file.core.ftp;
-
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.hutool.extra.ftp.FtpMode;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * {@link FtpFileClient} 集成测试
- *
- * @author 芋道源码
- */
-public class FtpFileClientTest {
-
-// docker run -d \
-// -p 2121:21 -p 30000-30009:30000-30009 \
-// -e FTP_USER=foo \
-// -e FTP_PASS=pass \
-// -e PASV_ADDRESS=127.0.0.1 \
-// -e PASV_MIN_PORT=30000 \
-// -e PASV_MAX_PORT=30009 \
-// -v $(pwd)/ftp-data:/home/vsftpd \
-// fauria/vsftpd
-
- @Test
- @Disabled
- public void test() {
- // 创建客户端
- FtpFileClientConfig config = new FtpFileClientConfig();
- config.setDomain("http://127.0.0.1:48080");
- config.setBasePath("/home/ftp");
- config.setHost("127.0.0.1");
- config.setPort(2121);
- config.setUsername("foo");
- config.setPassword("pass");
- config.setMode(FtpMode.Passive.name());
- FtpFileClient client = new FtpFileClient(0L, config);
- client.init();
- // 上传文件
- String path = IdUtil.fastSimpleUUID() + ".jpg";
- byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
- String fullPath = client.upload(content, path, "image/jpeg");
- System.out.println("访问地址:" + fullPath);
- if (false) {
- byte[] bytes = client.getContent(path);
- System.out.println("文件内容:" + bytes);
- }
- if (false) {
- client.delete(path);
- }
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java
deleted file mode 100644
index 7c622a5..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.infra.framework.file.core.local;
-
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-public class LocalFileClientTest {
-
- @Test
- @Disabled
- public void test() {
- // 创建客户端
- LocalFileClientConfig config = new LocalFileClientConfig();
- config.setDomain("http://127.0.0.1:48080");
- config.setBasePath("/Users/yunai/file_test");
- LocalFileClient client = new LocalFileClient(0L, config);
- client.init();
- // 上传文件
- String path = IdUtil.fastSimpleUUID() + ".jpg";
- byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
- String fullPath = client.upload(content, path, "image/jpeg");
- System.out.println("访问地址:" + fullPath);
- client.delete(path);
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java
deleted file mode 100644
index 3c40ce2..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package cn.iocoder.yudao.module.infra.framework.file.core.s3;
-
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig;
-import jakarta.validation.Validation;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-public class S3FileClientTest {
-
- @Test
- @Disabled // MinIO,如果要集成测试,可以注释本行
- public void testMinIO() throws Exception {
- S3FileClientConfig config = new S3FileClientConfig();
- // 配置成你自己的
- config.setAccessKey("admin");
- config.setAccessSecret("password");
- config.setBucket("yudaoyuanma");
- config.setDomain(null);
- // 默认 9000 endpoint
- config.setEndpoint("http://127.0.0.1:9000");
-
- // 执行上传
- testExecuteUpload(config);
- }
-
- @Test
- @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行
- public void testAliyun() throws Exception {
- S3FileClientConfig config = new S3FileClientConfig();
- // 配置成你自己的
- config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY"));
- config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY"));
- config.setBucket("yunai-aoteman");
- config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn
- // 默认北京的 endpoint
- config.setEndpoint("oss-cn-beijing.aliyuncs.com");
-
- // 执行上传
- testExecuteUpload(config);
- }
-
- @Test
- @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行
- public void testQCloud() throws Exception {
- S3FileClientConfig config = new S3FileClientConfig();
- // 配置成你自己的
- config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY"));
- config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY"));
- config.setBucket("aoteman-1255880240");
- config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn
- // 默认上海的 endpoint
- config.setEndpoint("cos.ap-shanghai.myqcloud.com");
-
- // 执行上传
- testExecuteUpload(config);
- }
-
- @Test
- @Disabled // 七牛云存储,如果要集成测试,可以注释本行
- public void testQiniu() throws Exception {
- S3FileClientConfig config = new S3FileClientConfig();
- // 配置成你自己的
-// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY"));
-// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY"));
- config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8");
- config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP");
- config.setBucket("ruoyi-vue-pro");
- config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn
- // 默认上海的 endpoint
- config.setEndpoint("s3-cn-south-1.qiniucs.com");
-
- // 执行上传
- testExecuteUpload(config);
- }
-
- @Test
- @Disabled // 华为云存储,如果要集成测试,可以注释本行
- public void testHuaweiCloud() throws Exception {
- S3FileClientConfig config = new S3FileClientConfig();
- // 配置成你自己的
-// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY"));
-// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY"));
- config.setBucket("yudao");
- config.setDomain(null); // 如果有自定义域名,则可以设置。
- // 默认上海的 endpoint
- config.setEndpoint("obs.cn-east-3.myhuaweicloud.com");
-
- // 执行上传
- testExecuteUpload(config);
- }
-
- private void testExecuteUpload(S3FileClientConfig config) throws Exception {
- // 校验配置
- ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config);
- // 创建 Client
- S3FileClient client = new S3FileClient(0L, config);
- client.init();
- // 上传文件
- String path = IdUtil.fastSimpleUUID() + ".jpg";
- byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
- String fullPath = client.upload(content, path, "image/jpeg");
- System.out.println("访问地址:" + fullPath);
- // 读取文件
- if (true) {
- byte[] bytes = client.getContent(path);
- System.out.println("文件内容:" + bytes.length);
- }
- // 删除文件
- if (false) {
- client.delete(path);
- }
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java
deleted file mode 100644
index 53c904e..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.infra.framework.file.core.sftp;
-
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.util.IdUtil;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient;
-import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * {@link SftpFileClient} 集成测试
- *
- * @author 芋道源码
- */
-public class SftpFileClientTest {
-
-// docker run -p 2222:22 -d \
-// -v $(pwd)/sftp-data:/home/foo/upload \
-// atmoz/sftp \
-// foo:pass:1001
-
- @Test
- @Disabled
- public void test() {
- // 创建客户端
- SftpFileClientConfig config = new SftpFileClientConfig();
- config.setDomain("http://127.0.0.1:48080");
- config.setBasePath("/upload"); // 注意,这个是相对路径,不是实际 linux 上的路径!!!
- config.setHost("127.0.0.1");
- config.setPort(2222);
- config.setUsername("foo");
- config.setPassword("pass");
- SftpFileClient client = new SftpFileClient(0L, config);
- client.init();
- // 上传文件
- String path = IdUtil.fastSimpleUUID() + ".jpg";
- byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
- String fullPath = client.upload(content, path, "image/jpeg");
- System.out.println("访问地址:" + fullPath);
- if (false) {
- byte[] bytes = client.getContent(path);
- System.out.println("文件内容:" + bytes);
- }
- if (false) {
- client.delete(path);
- }
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java
deleted file mode 100644
index a6de8d9..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.infra.service;
-
-import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.generator.query.DefaultQuery;
-import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
-import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
-import com.baomidou.mybatisplus.generator.config.po.TableInfo;
-
-import java.util.List;
-
-public class DefaultDatabaseQueryTest {
-
- public static void main(String[] args) {
-// DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:oracle:thin:@127.0.0.1:1521:xe",
-// "root", "123456").build();
- DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro",
- "root", "123456").build();
-// StrategyConfig strategyConfig = new StrategyConfig.Builder().build();
-
- ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null);
-
- DefaultQuery query = new DefaultQuery(builder);
-
- long time = System.currentTimeMillis();
- List tableInfos = query.queryTables();
- for (TableInfo tableInfo : tableInfos) {
- if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) {
- continue;
- }
- System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 1;", tableInfo.getName()));
-// System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName()));
- }
- System.out.println(tableInfos.size());
- System.out.println(System.currentTimeMillis() - time);
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java
deleted file mode 100644
index 7a26ea9..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package cn.iocoder.yudao.module.infra.service.codegen.inner;
-
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
-import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
-import com.baomidou.mybatisplus.generator.config.po.TableField;
-import com.baomidou.mybatisplus.generator.config.po.TableInfo;
-import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
-import org.apache.ibatis.type.JdbcType;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-
-import java.util.Collections;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class CodegenBuilderTest extends BaseMockitoUnitTest {
-
- @InjectMocks
- private CodegenBuilder codegenBuilder;
-
- @Test
- public void testBuildTable() {
- // 准备参数
- TableInfo tableInfo = mock(TableInfo.class);
- // mock 方法
- when(tableInfo.getName()).thenReturn("system_user");
- when(tableInfo.getComment()).thenReturn("用户");
-
- // 调用
- CodegenTableDO table = codegenBuilder.buildTable(tableInfo);
- // 断言
- assertEquals("system_user", table.getTableName());
- assertEquals("用户", table.getTableComment());
- assertEquals("system", table.getModuleName());
- assertEquals("user", table.getBusinessName());
- assertEquals("User", table.getClassName());
- assertEquals("用户", table.getClassComment());
- }
-
- @Test
- public void testBuildColumns() {
- // 准备参数
- Long tableId = randomLongId();
- TableField tableField = mock(TableField.class);
- List tableFields = Collections.singletonList(tableField);
- // mock 方法
- TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class);
- when(tableField.getMetaInfo()).thenReturn(metaInfo);
- when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT);
- when(tableField.getComment()).thenReturn("编号");
- when(tableField.isKeyFlag()).thenReturn(true);
- IColumnType columnType = mock(IColumnType.class);
- when(tableField.getColumnType()).thenReturn(columnType);
- when(columnType.getType()).thenReturn("Long");
- when(tableField.getName()).thenReturn("id2");
- when(tableField.getPropertyName()).thenReturn("id");
-
- // 调用
- List columns = codegenBuilder.buildColumns(tableId, tableFields);
- // 断言
- assertEquals(1, columns.size());
- CodegenColumnDO column = columns.get(0);
- assertEquals(tableId, column.getTableId());
- assertEquals("id2", column.getColumnName());
- assertEquals("BIGINT", column.getDataType());
- assertEquals("编号", column.getColumnComment());
- assertFalse(column.getNullable());
- assertTrue(column.getPrimaryKey());
- assertEquals(1, column.getOrdinalPosition());
- assertEquals("Long", column.getJavaType());
- assertEquals("id", column.getJavaField());
- assertNull(column.getDictType());
- assertNotNull(column.getExample());
- assertFalse(column.getCreateOperation());
- assertTrue(column.getUpdateOperation());
- assertFalse(column.getListOperation());
- assertEquals("=", column.getListOperationCondition());
- assertTrue(column.getListOperationResult());
- assertEquals("input", column.getHtmlType());
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
deleted file mode 100644
index 3c7390a..0000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package cn.iocoder.yudao.module.infra.service.codegen.inner;
-
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.io.resource.ResourceUtil;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.core.util.ZipUtil;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
-import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
-import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
-import org.junit.jupiter.api.BeforeEach;
-import org.mockito.InjectMocks;
-import org.mockito.Spy;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link CodegenEngine} 的单元测试抽象基类
- *
- * @author 芋道源码
- */
-public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
-
- /**
- * 测试文件资源目录
- */
- private String resourcesPath = "";
-
- @InjectMocks
- protected CodegenEngine codegenEngine;
-
- @Spy
- protected CodegenProperties codegenProperties = new CodegenProperties()
- .setBasePackage("cn.iocoder.yudao");
-
- @BeforeEach
- public void setUp() {
- codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言
- codegenEngine.initGlobalBindingMap();
- // 单测强制使用
- // 获取测试文件 resources 路径
- String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml");
- // 系统不一样生成的文件也有差异,那就各自生成各自的
- resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/";
- }
-
- protected static CodegenTableDO getTable(String name) {
- String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
- return JsonUtils.parseObject(content, "table", CodegenTableDO.class);
- }
-
- protected static List getColumnList(String name) {
- String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
- List list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class);
- list.forEach(column -> {
- if (column.getNullable() == null) {
- column.setNullable(false);
- }
- if (column.getCreateOperation() == null) {
- column.setCreateOperation(false);
- }
- if (column.getUpdateOperation() == null) {
- column.setUpdateOperation(false);
- }
- if (column.getListOperation() == null) {
- column.setListOperation(false);
- }
- if (column.getListOperationResult() == null) {
- column.setListOperationResult(false);
- }
- });
- return list;
- }
-
- @SuppressWarnings("rawtypes")
- protected static void assertResult(Map result, String path) {
- String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json");
- List asserts = JsonUtils.parseArray(assertContent, HashMap.class);
- assertEquals(asserts.size(), result.size());
- // 校验每个文件
- asserts.forEach(assertMap -> {
- String contentPath = (String) assertMap.get("contentPath");
- String filePath = (String) assertMap.get("filePath");
- String content = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath);
- assertEquals(content, result.get(filePath), filePath + ":不匹配");
- });
- }
-
- // ==================== 调试专用 ====================
-
- /**
- * 【调试使用】将生成的代码,写入到文件
- *
- * @param result 生成的代码
- * @param path 写入文件的路径
- */
- protected void writeFile(Map 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);
- }
-
- /**
- * 【调试使用】将生成的结果,写入到文件
- *
- * @param result 生成的代码
- * @param basePath 写入文件的路径(绝对路径)
- */
- protected void writeResult(Map result, String basePath) {
- // 写入文件内容
- List