diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql
index 7b13fbec0a..a9cca3ba8c 100644
--- a/sql/mysql/ruoyi-vue-pro.sql
+++ b/sql/mysql/ruoyi-vue-pro.sql
@@ -11,7 +11,7 @@
Target Server Version : 80034
File Encoding : 65001
- Date: 04/11/2023 20:42:49
+ Date: 18/11/2023 17:48:18
*/
SET NAMES utf8mb4;
@@ -385,7 +385,7 @@ CREATE TABLE `infra_api_error_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1781 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 1964 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
-- ----------------------------
-- Records of infra_api_error_log
@@ -423,7 +423,7 @@ CREATE TABLE `infra_codegen_column` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1804 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 1905 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
-- ----------------------------
-- Records of infra_codegen_column
@@ -450,13 +450,18 @@ CREATE TABLE `infra_codegen_table` (
`template_type` tinyint NOT NULL DEFAULT 1 COMMENT '模板类型',
`front_type` tinyint NOT NULL COMMENT '前端类型',
`parent_menu_id` bigint NULL DEFAULT NULL COMMENT '父菜单编号',
+ `master_table_id` bigint NULL DEFAULT NULL COMMENT '主表的编号',
+ `sub_join_column_id` bigint NULL DEFAULT NULL COMMENT '子表关联主表的字段编号',
+ `sub_join_many` bit(1) NULL DEFAULT NULL COMMENT '主表与子表是否一对多',
+ `tree_parent_column_id` bigint NULL DEFAULT NULL COMMENT '树表的父字段编号',
+ `tree_name_column_id` bigint NULL DEFAULT NULL COMMENT '树表的名字字段编号',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 136 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
+) ENGINE = InnoDB AUTO_INCREMENT = 146 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
-- ----------------------------
-- Records of infra_codegen_table
@@ -521,6 +526,150 @@ CREATE TABLE `infra_data_source_config` (
BEGIN;
COMMIT;
+-- ----------------------------
+-- Table structure for infra_demo01_contact
+-- ----------------------------
+DROP TABLE IF EXISTS `infra_demo01_contact`;
+CREATE TABLE `infra_demo01_contact` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
+ `sex` tinyint(1) NOT NULL COMMENT '性别',
+ `birthday` datetime NOT NULL COMMENT '出生年',
+ `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介',
+ `avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '头像',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '示例联系人表';
+
+-- ----------------------------
+-- Records of infra_demo01_contact
+-- ----------------------------
+BEGIN;
+INSERT INTO `infra_demo01_contact` (`id`, `name`, `sex`, `birthday`, `description`, `avatar`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '土豆', 2, '2023-11-07 00:00:00', '
天蚕土豆!呀
', 'http://127.0.0.1:48080/admin-api/infra/file/4/get/46f8fa1a37db3f3960d8910ff2fe3962ab3b2db87cf2f8ccb4dc8145b8bdf237.jpeg', '1', '2023-11-15 23:34:30', '1', '2023-11-15 23:47:39', b'0', 1);
+COMMIT;
+
+-- ----------------------------
+-- Table structure for infra_demo02_category
+-- ----------------------------
+DROP TABLE IF EXISTS `infra_demo02_category`;
+CREATE TABLE `infra_demo02_category` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
+ `parent_id` bigint NOT NULL COMMENT '父级编号',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '示例分类表';
+
+-- ----------------------------
+-- Records of infra_demo02_category
+-- ----------------------------
+BEGIN;
+INSERT INTO `infra_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '土豆', 0, '1', '2023-11-15 23:34:30', '1', '2023-11-16 20:24:23', b'0', 1);
+INSERT INTO `infra_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '番茄', 0, '1', '2023-11-16 20:24:00', '1', '2023-11-16 20:24:15', b'0', 1);
+INSERT INTO `infra_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, '怪怪', 0, '1', '2023-11-16 20:24:32', '1', '2023-11-16 20:24:32', b'0', 1);
+INSERT INTO `infra_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '小番茄', 2, '1', '2023-11-16 20:24:39', '1', '2023-11-16 20:24:39', b'0', 1);
+INSERT INTO `infra_demo02_category` (`id`, `name`, `parent_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, '大番茄', 2, '1', '2023-11-16 20:24:46', '1', '2023-11-16 20:24:46', b'0', 1);
+COMMIT;
+
+-- ----------------------------
+-- Table structure for infra_demo03_course
+-- ----------------------------
+DROP TABLE IF EXISTS `infra_demo03_course`;
+CREATE TABLE `infra_demo03_course` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `student_id` bigint NOT NULL COMMENT '学生编号',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
+ `score` tinyint NOT NULL COMMENT '分数',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生课程表';
+
+-- ----------------------------
+-- Records of infra_demo03_course
+-- ----------------------------
+BEGIN;
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2023-11-16 23:21:49', b'0', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2023-11-16 23:21:49', b'0', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', b'1', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:44:40', b'1', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', b'1', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 15:47:09', b'1', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (10, 5, '体育', 23, '1', '2023-11-16 23:22:46', '1', '2023-11-16 23:47:10', b'0', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (11, 5, '计算机', 11, '1', '2023-11-16 23:22:46', '1', '2023-11-16 23:47:10', b'0', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (12, 2, '电脑', 33, '1', '2023-11-17 00:20:42', '1', '2023-11-16 16:20:45', b'1', 1);
+INSERT INTO `infra_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (13, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2023-11-17 13:13:20', b'0', 1);
+COMMIT;
+
+-- ----------------------------
+-- Table structure for infra_demo03_grade
+-- ----------------------------
+DROP TABLE IF EXISTS `infra_demo03_grade`;
+CREATE TABLE `infra_demo03_grade` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `student_id` bigint NOT NULL COMMENT '学生编号',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
+ `teacher` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '班主任',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生班级表';
+
+-- ----------------------------
+-- Records of infra_demo03_grade
+-- ----------------------------
+BEGIN;
+INSERT INTO `infra_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 2, '三年 2 班', '周杰伦', '1', '2023-11-16 23:21:49', '1', '2023-11-16 23:21:49', b'0', 1);
+INSERT INTO `infra_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 5, '华为', '遥遥领先', '1', '2023-11-16 23:22:46', '1', '2023-11-16 23:47:10', b'0', 1);
+INSERT INTO `infra_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 9, '小图', '小娃111', '1', '2023-11-17 13:10:23', '1', '2023-11-17 13:10:23', b'0', 1);
+COMMIT;
+
+-- ----------------------------
+-- Table structure for infra_demo03_student
+-- ----------------------------
+DROP TABLE IF EXISTS `infra_demo03_student`;
+CREATE TABLE `infra_demo03_student` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
+ `sex` tinyint NOT NULL COMMENT '性别',
+ `birthday` datetime NOT NULL COMMENT '出生日期',
+ `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '简介',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '学生表';
+
+-- ----------------------------
+-- Records of infra_demo03_student
+-- ----------------------------
+BEGIN;
+INSERT INTO `infra_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '小白', 1, '2023-11-16 00:00:00', '厉害
', '1', '2023-11-16 23:21:49', '1', '2023-11-17 16:49:06', b'0', 1);
+INSERT INTO `infra_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, '大黑', 2, '2023-11-13 00:00:00', '你在教我做事?
', '1', '2023-11-16 23:22:46', '1', '2023-11-17 16:49:07', b'0', 1);
+INSERT INTO `infra_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, '小花', 1, '2023-11-07 00:00:00', '哈哈哈
', '1', '2023-11-17 00:04:47', '1', '2023-11-17 16:49:08', b'0', 1);
+COMMIT;
+
-- ----------------------------
-- Table structure for infra_file
-- ----------------------------
@@ -539,7 +688,7 @@ CREATE TABLE `infra_file` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1108 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1128 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file
@@ -588,7 +737,7 @@ CREATE TABLE `infra_file_content` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 202 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
+) ENGINE = InnoDB AUTO_INCREMENT = 221 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file_content
@@ -665,31 +814,6 @@ CREATE TABLE `infra_job_log` (
BEGIN;
COMMIT;
--- ----------------------------
--- Table structure for infra_test_demo
--- ----------------------------
-DROP TABLE IF EXISTS `infra_test_demo`;
-CREATE TABLE `infra_test_demo` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
- `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名字',
- `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态',
- `type` tinyint NOT NULL COMMENT '类型',
- `category` tinyint NOT NULL COMMENT '分类',
- `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
- `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
- PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
-
--- ----------------------------
--- Records of infra_test_demo
--- ----------------------------
-BEGIN;
-COMMIT;
-
-- ----------------------------
-- Table structure for member_address
-- ----------------------------
@@ -1131,7 +1255,7 @@ CREATE TABLE `system_dept` (
-- Records of system_dept
-- ----------------------------
BEGIN;
-INSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-06-19 00:29:10', b'0', 1);
+INSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, '芋道源码', 0, 0, 1, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2023-11-14 23:30:36', b'0', 1);
INSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (101, '深圳总公司', 100, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '1', '2022-05-16 20:25:23', b'0', 1);
INSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (102, '长沙分公司', 100, 2, NULL, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '', '2021-12-15 05:01:40', b'0', 1);
INSERT INTO `system_dept` (`id`, `name`, `parent_id`, `sort`, `leader_user_id`, `phone`, `email`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, '研发部门', 101, 1, 104, '15888888888', 'ry@qq.com', 0, 'admin', '2021-01-05 17:03:47', '103', '2022-01-14 01:04:14', b'0', 1);
@@ -1165,14 +1289,14 @@ CREATE TABLE `system_dict_data` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1435 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
+) ENGINE = InnoDB AUTO_INCREMENT = 1447 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
-- ----------------------------
-- Records of system_dict_data
-- ----------------------------
BEGIN;
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 1, '男', '1', 'system_user_sex', 0, 'default', 'A', '性别男', 'admin', '2021-01-05 17:03:48', '1', '2022-03-29 00:14:39', b'0');
-INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 2, '女', '2', 'system_user_sex', 1, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 01:30:51', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 2, '女', '2', 'system_user_sex', 0, 'success', '', '性别女', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 23:30:37', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 1, '正常', '1', 'infra_job_status', 0, 'success', '', '正常状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:38', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 2, '暂停', '2', 'infra_job_status', 0, 'danger', '', '停用状态', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:33:45', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12, 1, '系统内置', '1', 'infra_config_type', 0, 'danger', '', '参数类型 - 系统内置', 'admin', '2021-01-05 17:03:48', '1', '2022-02-16 19:06:02', b'0');
@@ -1474,6 +1598,12 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1438, 31, '微信公众平台', '31', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:18', '1', '2023-11-04 13:05:18', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1439, 32, '微信开放平台', '32', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:30', '1', '2023-11-04 13:05:30', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1440, 34, '微信小程序', '34', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1441, 1, '上架', '1', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:34', '1', '2023-10-30 21:49:34', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1442, 0, '下架', '0', 'crm_product_status', 0, 'success', '', '', '1', '2023-10-30 21:49:13', '1', '2023-10-30 21:49:13', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1443, 15, '子表', '15', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-13 23:06:16', '1', '2023-11-13 23:06:16', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1444, 10, '主表(标准模式)', '10', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:32:49', '1', '2023-11-14 12:32:49', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1445, 11, '主表(ERP 模式)', '11', 'infra_codegen_template_type', 0, 'default', '', '', '1', '2023-11-14 12:33:05', '1', '2023-11-14 12:33:05', b'0');
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1446, 12, '主表(内嵌模式)', '12', 'infra_codegen_template_type', 0, '', '', '', '1', '2023-11-14 12:33:31', '1', '2023-11-14 12:33:31', b'0');
COMMIT;
-- ----------------------------
@@ -1494,7 +1624,7 @@ CREATE TABLE `system_dict_type` (
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 600 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
+) ENGINE = InnoDB AUTO_INCREMENT = 605 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
-- ----------------------------
-- Records of system_dict_type
@@ -1573,6 +1703,7 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (188, '客户来源', 'crm_customer_source', 0, 'CRM 客户来源', '1', '2023-10-28 23:00:34', '1', '2023-10-28 15:11:16', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (600, 'Banner 位置', 'promotion_banner_position', 0, '', '1', '2023-10-08 07:24:25', '1', '2023-11-04 13:04:02', b'0', '1970-01-01 00:00:00');
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (601, '社交类型', 'system_social_type', 0, '', '1', '2023-11-04 13:03:54', '1', '2023-11-04 13:03:54', b'0', '1970-01-01 00:00:00');
+INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (604, '产品状态', 'crm_product_status', 0, '', '1', '2023-10-30 21:47:59', '1', '2023-10-30 21:48:45', b'0', '1970-01-01 00:00:00');
COMMIT;
-- ----------------------------
@@ -1621,7 +1752,7 @@ CREATE TABLE `system_login_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2631 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 2647 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
-- ----------------------------
-- Records of system_login_log
@@ -1751,7 +1882,7 @@ CREATE TABLE `system_menu` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2449 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
+) ENGINE = InnoDB AUTO_INCREMENT = 2504 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
-- ----------------------------
-- Records of system_menu
@@ -1839,11 +1970,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1065, '设置用户角色', 'system:permission:assign-user-role', 3, 8, 101, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-07 10:23:28', '', '2022-04-20 17:03:10', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1066, '获得 Redis 监控信息', 'infra:redis:get-monitor-info', 3, 1, 113, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-26 01:02:31', '', '2022-04-20 17:03:10', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1067, '获得 Redis Key 列表', 'infra:redis:get-key-list', 3, 2, 113, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-01-26 01:02:52', '', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1070, '代码生成示例', 'infra:test-demo:query', 2, 1, 2, 'test-demo', 'validCode', 'infra/testDemo/index', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1071, '测试示例表创建', 'infra:test-demo:create', 3, 1, 1070, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1072, '测试示例表更新', 'infra:test-demo:update', 3, 2, 1070, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1073, '测试示例表删除', 'infra:test-demo:delete', 3, 3, 1070, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2022-04-20 17:03:10', b'0');
-INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1074, '测试示例表导出', 'infra:test-demo:export', 3, 4, 1070, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2022-04-20 17:03:10', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1070, '代码生成案例', '', 1, 1, 2, 'demo', 'ep:aim', 'infra/testDemo/index', NULL, 0, b'1', b'1', b'1', '', '2021-02-06 12:42:49', '1', '2023-11-15 23:45:53', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1075, '任务触发', 'infra:job:trigger', 3, 8, 110, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-02-07 13:03:10', '', '2022-04-20 17:03:10', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1076, '数据库文档', '', 2, 4, 2, 'db-doc', 'table', 'infra/dbDoc/index', 'InfraDBDoc', 0, b'1', b'1', b'1', '', '2021-02-08 01:41:47', '1', '2023-04-08 09:13:38', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1077, '监控平台', '', 2, 13, 2, 'skywalking', 'eye-open', 'infra/skywalking/index', 'InfraSkyWalking', 0, b'1', b'1', b'1', '', '2021-02-08 20:41:31', '1', '2023-04-08 10:39:06', b'0');
@@ -2310,6 +2437,26 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2451, '三方应用更新', 'system:social-client:update', 3, 3, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:44:27', '1', '2023-11-04 12:44:27', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2452, '三方应用删除', 'system:social-client:delete', 3, 4, 2448, '', '', '', '', 0, b'1', b'1', b'1', '1', '2023-11-04 12:44:43', '1', '2023-11-04 12:44:43', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2453, '三方用户', 'system:social-user:query', 2, 2, 2447, 'user', 'ep:avatar', 'system/social/user/index.vue', 'SocialUser', 0, b'1', b'1', b'1', '1', '2023-11-04 14:01:05', '1', '2023-11-04 14:01:05', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2472, '主子表(内嵌)', '', 2, 12, 1070, 'demo03-inner', 'fa:power-off', 'infra/demo/demo03/inner/index', 'Demo03StudentInner', 0, b'1', b'1', b'1', '', '2023-11-13 04:39:51', '1', '2023-11-16 23:53:46', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2478, '单表(增删改查)', '', 2, 1, 1070, 'demo01-contact', 'ep:bicycle', 'infra/demo/demo01/index', 'Demo01Contact', 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '1', '2023-11-16 20:34:40', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2479, '示例联系人查询', 'infra:demo01-contact:query', 3, 1, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2480, '示例联系人创建', 'infra:demo01-contact:create', 3, 2, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2481, '示例联系人更新', 'infra:demo01-contact:update', 3, 3, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2482, '示例联系人删除', 'infra:demo01-contact:delete', 3, 4, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2483, '示例联系人导出', 'infra:demo01-contact:export', 3, 5, 2478, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-15 14:42:30', '', '2023-11-15 14:42:30', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2484, '树表(增删改查)', '', 2, 2, 1070, 'demo02-category', 'fa:tree', 'infra/demo/demo02/index', 'Demo02Category', 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '1', '2023-11-16 20:35:01', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2485, '示例分类查询', 'infra:demo02-category:query', 3, 1, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2486, '示例分类创建', 'infra:demo02-category:create', 3, 2, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2487, '示例分类更新', 'infra:demo02-category:update', 3, 3, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2488, '示例分类删除', 'infra:demo02-category:delete', 3, 4, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2489, '示例分类导出', 'infra:demo02-category:export', 3, 5, 2484, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:18:27', '', '2023-11-16 12:18:27', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2490, '主子表(标准)', '', 2, 10, 1070, 'demo03-normal', 'fa:battery-3', 'infra/demo/demo03/normal/index', 'Demo03StudentNormal', 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '1', '2023-11-16 23:10:03', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2491, '学生查询', 'infra:demo03-student:query', 3, 1, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2492, '学生创建', 'infra:demo03-student:create', 3, 2, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2493, '学生更新', 'infra:demo03-student:update', 3, 3, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2494, '学生删除', 'infra:demo03-student:delete', 3, 4, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2495, '学生导出', 'infra:demo03-student:export', 3, 5, 2490, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-11-16 12:53:37', '', '2023-11-16 12:53:37', b'0');
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2497, '主子表(ERP)', '', 2, 11, 1070, 'demo03-erp', 'ep:calendar', 'infra/demo/demo03/erp/index', 'Demo03StudentERP', 0, b'1', b'1', b'1', '', '2023-11-16 15:50:59', '1', '2023-11-17 13:19:56', b'0');
COMMIT;
-- ----------------------------
@@ -2336,7 +2483,7 @@ CREATE TABLE `system_notice` (
-- ----------------------------
BEGIN;
INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, '芋道的公众', '新版本内容133
', 1, 0, 'admin', '2021-01-05 17:03:48', '1', '2022-05-04 21:00:20', b'0', 1);
-INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '维护通知:2018-07-01 若依系统凌晨维护', '维护内容
', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2022-05-11 12:34:24', b'0', 1);
+INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '维护通知:2018-07-01 若依系统凌晨维护', '1111
', 2, 1, 'admin', '2021-01-05 17:03:48', '1', '2023-11-11 12:51:11', b'0', 1);
INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '我是测试标题', '哈哈哈哈123
', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', b'0', 121);
COMMIT;
@@ -2430,7 +2577,7 @@ CREATE TABLE `system_oauth2_access_token` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 3152 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 3467 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
-- ----------------------------
-- Records of system_oauth2_access_token
@@ -2552,7 +2699,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1099 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
+) ENGINE = InnoDB AUTO_INCREMENT = 1115 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
-- ----------------------------
-- Records of system_oauth2_refresh_token
@@ -2592,7 +2739,7 @@ CREATE TABLE `system_operate_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 8845 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
+) ENGINE = InnoDB AUTO_INCREMENT = 9090 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
-- ----------------------------
-- Records of system_operate_log
@@ -2618,15 +2765,15 @@ CREATE TABLE `system_post` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '岗位信息表';
+) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '岗位信息表';
-- ----------------------------
-- Records of system_post
-- ----------------------------
BEGIN;
INSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'ceo', '董事长', 1, 0, '', 'admin', '2021-01-06 17:03:48', '1', '2023-02-11 15:19:04', b'0', 1);
-INSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2021-12-12 10:47:47', b'0', 1);
-INSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 'user', '普通员工', 4, 0, '111', 'admin', '2021-01-05 17:03:48', '1', '2023-02-11 15:19:00', b'0', 1);
+INSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 'se', '项目经理', 2, 0, '', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:20', b'0', 1);
+INSERT INTO `system_post` (`id`, `code`, `name`, `sort`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 'user', '普通员工', 4, 0, '111', 'admin', '2021-01-05 17:03:48', '1', '2023-11-15 09:18:18', b'0', 1);
COMMIT;
-- ----------------------------
@@ -2819,10 +2966,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1657, 101, 1066, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1658, 101, 1067, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1659, 101, 1070, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1660, 101, 1071, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1661, 101, 1072, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1662, 101, 1073, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1663, 101, 1074, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1664, 101, 1075, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1665, 101, 1076, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1666, 101, 1077, '1', '2022-04-01 22:21:37', '1', '2022-04-01 22:21:37', b'0', 1);
@@ -3140,10 +3283,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2030, 2, 1071, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2031, 2, 1072, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2032, 2, 1073, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
-INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2033, 2, 1074, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2035, 2, 1076, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
@@ -3643,7 +3782,7 @@ CREATE TABLE `system_sms_log` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 503 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
+) ENGINE = InnoDB AUTO_INCREMENT = 502 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
-- ----------------------------
-- Records of system_sms_log
@@ -3673,7 +3812,7 @@ CREATE TABLE `system_sms_template` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板';
+) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板';
-- ----------------------------
-- Records of system_sms_template
@@ -3803,9 +3942,9 @@ CREATE TABLE `system_tenant` (
-- Records of system_tenant
-- ----------------------------
BEGIN;
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'https://www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2022-02-23 12:15:11', b'0');
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'http://www.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-09-16 16:59:42', b'0');
-INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'https://www.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-09-16 16:59:27', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2023-11-06 11:41:41', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn', 111, '2024-03-11 00:00:00', 20, '1', '2022-02-22 00:56:14', '1', '2023-11-06 11:41:47', b'0');
+INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn', 111, '2022-04-30 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2023-11-06 11:41:53', b'0');
COMMIT;
-- ----------------------------
@@ -3944,7 +4083,7 @@ CREATE TABLE `system_users` (
-- Records of system_users
-- ----------------------------
BEGIN;
-INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2023-11-04 10:33:16', 'admin', '2021-01-05 17:03:47', NULL, '2023-11-04 10:33:16', b'0', 1);
+INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '0:0:0:0:0:0:0:1', '2023-11-18 17:19:30', 'admin', '2021-01-05 17:03:47', NULL, '2023-11-18 17:19:30', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2023-09-24 18:21:19', '', '2021-01-21 02:13:53', NULL, '2023-09-24 18:21:19', b'0', 1);
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
index 5738ded676..97819f9938 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageParam.java
@@ -15,6 +15,13 @@ public class PageParam implements Serializable {
private static final Integer PAGE_NO = 1;
private static final Integer PAGE_SIZE = 10;
+ /**
+ * 每页条数 - 不分页
+ *
+ * 例如说,导出接口,可以设置 {@link #pageSize} 为 -1 不分页,查询所有数据。
+ */
+ public static final Integer PAGE_SIZE_NONE = -1;
+
@Schema(description = "页码,从 1 开始", requiredMode = Schema.RequiredMode.REQUIRED,example = "1")
@NotNull(message = "页码不能为空")
@Min(value = 1, message = "页码最小值为 1")
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
index 366e78e23d..919173da6c 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -238,7 +238,7 @@ public class CollectionUtils {
if (CollUtil.isEmpty(from)) {
return null;
}
- assert from.size() > 0; // 断言,避免告警
+ assert !from.isEmpty(); // 断言,避免告警
T t = from.stream().max(Comparator.comparing(valueFunc)).get();
return valueFunc.apply(t);
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
index 624a0d9a7c..2c7288efa6 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.json;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
@@ -30,6 +31,7 @@ public class JsonUtils {
static {
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略 null 值
objectMapper.registerModules(new JavaTimeModule()); // 解决 LocalDateTime 的序列化
}
@@ -71,6 +73,20 @@ public class JsonUtils {
}
}
+ public static T parseObject(String text, String path, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ JsonNode treeNode = objectMapper.readTree(text);
+ JsonNode pathNode = treeNode.path(path);
+ return objectMapper.readValue(pathNode.toString(), clazz);
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
public static T parseObject(String text, Type type) {
if (StrUtil.isEmpty(text)) {
return null;
@@ -132,6 +148,20 @@ public class JsonUtils {
}
}
+ public static List parseArray(String text, String path, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ JsonNode treeNode = objectMapper.readTree(text);
+ JsonNode pathNode = treeNode.path(path);
+ return objectMapper.readValue(pathNode.toString(), objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+ } catch (IOException e) {
+ log.error("json parse err,json:{}", text, e);
+ throw new RuntimeException(e);
+ }
+ }
+
public static JsonNode parseTree(String text) {
try {
return objectMapper.readTree(text);
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java
new file mode 100644
index 0000000000..e14572a7ff
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/BeanUtils.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.framework.common.util.object;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * Bean 工具类
+ *
+ * 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
+ * 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
+ *
+ * @author 芋道源码
+ */
+public class BeanUtils {
+
+ public static T toBean(Object source, Class targetClass) {
+ return BeanUtil.toBean(source, targetClass);
+ }
+
+ public static List toBean(List source, Class targetType) {
+ if (source == null) {
+ return null;
+ }
+ return CollectionUtils.convertList(source, s -> toBean(s, targetType));
+ }
+
+ public static PageResult toBean(PageResult source, Class targetType) {
+ if (source == null) {
+ return null;
+ }
+ return new PageResult<>(toBean(source.getList(), targetType), source.getTotal());
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
index cd5db713c9..08cea833f1 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
@@ -50,4 +50,20 @@ public class StrUtils {
return Arrays.stream(integers).boxed().collect(Collectors.toList());
}
+ /**
+ * 移除字符串中,包含指定字符串的行
+ *
+ * @param content 字符串
+ * @param sequence 包含的字符串
+ * @return 移除后的字符串
+ */
+ public static String removeLineContains(String content, String sequence) {
+ if (StrUtil.isEmpty(content) || StrUtil.isEmpty(sequence)) {
+ return content;
+ }
+ return Arrays.stream(content.split("\n"))
+ .filter(line -> !line.contains(sequence))
+ .collect(Collectors.joining("\n"));
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
index d70c216260..0bc54d532f 100644
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
+++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -26,6 +26,12 @@ import java.util.List;
public interface BaseMapperX extends MPJBaseMapper {
default PageResult selectPage(PageParam pageParam, @Param("ew") Wrapper queryWrapper) {
+ // 特殊:不分页,直接查询全部
+ if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageNo())) {
+ List list = selectList(queryWrapper);
+ return new PageResult<>(list, (long) list.size());
+ }
+
// MyBatis Plus 查询
IPage mpPage = MyBatisUtils.buildPage(pageParam);
selectPage(mpPage, queryWrapper);
@@ -93,10 +99,15 @@ public interface BaseMapperX extends MPJBaseMapper {
return selectList(new LambdaQueryWrapper().in(field, values));
}
+ @Deprecated
default List selectList(SFunction leField, SFunction geField, Object value) {
return selectList(new LambdaQueryWrapper().le(leField, value).ge(geField, value));
}
+ default List selectList(SFunction field1, Object value1, SFunction field2, Object value2) {
+ return selectList(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2));
+ }
+
/**
* 批量插入,适合大量数据插入
*
@@ -128,8 +139,20 @@ public interface BaseMapperX extends MPJBaseMapper {
Db.updateBatchById(entities, size);
}
- default void saveOrUpdateBatch(Collection collection) {
+ default void insertOrUpdate(T entity) {
+ Db.saveOrUpdate(entity);
+ }
+
+ default void insertOrUpdateBatch(Collection collection) {
Db.saveOrUpdateBatch(collection);
}
+ default int delete(String field, String value) {
+ return delete(new QueryWrapper().eq(field, value));
+ }
+
+ default int delete(SFunction field, Object value) {
+ return delete(new LambdaQueryWrapper().eq(field, value));
+ }
+
}
diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
index d956e763dc..471950017e 100644
--- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
+++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
@@ -42,9 +42,10 @@ public interface ErrorCodeConstants {
ErrorCode CODEGEN_SYNC_NONE_CHANGE = new ErrorCode(1_003_001_007, "同步失败,不存在改变");
ErrorCode CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL = new ErrorCode(1_003_001_008, "数据库的表注释未填写");
ErrorCode CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL = new ErrorCode(1_003_001_009, "数据库的表字段({})注释未填写");
-
- // ========== 字典类型(测试)1-001-005-000 ==========
- ErrorCode TEST_DEMO_NOT_EXISTS = new ErrorCode(1_001_005_000, "测试示例不存在");
+ ErrorCode CODEGEN_MASTER_TABLE_NOT_EXISTS = new ErrorCode(1_003_001_010, "主表(id={})定义不存在,请检查");
+ ErrorCode CODEGEN_SUB_COLUMN_NOT_EXISTS = new ErrorCode(1_003_001_011, "子表的字段(id={})不存在,请检查");
+ ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE = new ErrorCode(1_003_001_012, "主表生成代码失败,原因:它没有子表");
+ ErrorCode CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_COLUMN = new ErrorCode(1_003_001_013, "主表生成代码失败,原因:它的子表({})没有字段");
// ========== 文件配置 1-001-006-000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
@@ -54,4 +55,19 @@ public interface ErrorCodeConstants {
ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_007_000, "数据源配置不存在");
ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1_001_007_001, "数据源配置不正确,无法进行连接");
+ // ========== 数据源配置 1-001-107-000 ==========
+ ErrorCode DEMO_STUDENT_NOT_EXISTS = new ErrorCode(1_001_107_000, "学生不存在");
+
+ // ========== 学生 1-001-201-000 ==========
+ ErrorCode DEMO01_CONTACT_NOT_EXISTS = new ErrorCode(1_001_201_000, "示例联系人不存在");
+ ErrorCode DEMO02_CATEGORY_NOT_EXISTS = new ErrorCode(1_001_201_001, "示例分类不存在");
+ ErrorCode DEMO02_CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_201_002, "存在存在子示例分类,无法删除");
+ ErrorCode DEMO02_CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_201_003,"父级示例分类不存在");
+ ErrorCode DEMO02_CATEGORY_PARENT_ERROR = new ErrorCode(1_001_201_004, "不能设置自己为父示例分类");
+ ErrorCode DEMO02_CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_201_005, "已经存在该名字的示例分类");
+ ErrorCode DEMO02_CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_201_006, "不能设置自己的子示例分类为父示例分类");
+ ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在");
+ ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生班级不存在");
+ ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_009, "学生班级已存在");
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java
index 76e5b6a83b..81d7522ef8 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java
@@ -60,10 +60,19 @@ public class CodegenController {
return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));
}
+ @GetMapping("/table/list")
+ @Operation(summary = "获得表定义列表")
+ @Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1")
+ @PreAuthorize("@ss.hasPermission('infra:codegen:query')")
+ public CommonResult> getCodegenTableList(@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
+ List list = codegenService.getCodegenTableList(dataSourceConfigId);
+ return success(CodegenConvert.INSTANCE.convertList05(list));
+ }
+
@GetMapping("/table/page")
@Operation(summary = "获得表定义分页")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
- public CommonResult> getCodeGenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
+ public CommonResult> getCodegenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
PageResult pageResult = codegenService.getCodegenTablePage(pageReqVO);
return success(CodegenConvert.INSTANCE.convertPage(pageResult));
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java
index 2531fd7682..a43aa987ab 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java
@@ -4,6 +4,8 @@ import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnBaseVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableBaseVO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
+import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -37,12 +39,27 @@ public class CodegenUpdateReqVO {
private Long id;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
+ @JsonIgnore
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
+ @AssertTrue(message = "关联的父表信息不全")
+ @JsonIgnore
+ public boolean isSubValid() {
+ return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
+ || (ObjectUtil.isAllNotEmpty(getMasterTableId(), getSubJoinColumnId(), getSubJoinMany()));
+ }
+
+ @AssertTrue(message = "关联的树表信息不全")
+ @JsonIgnore
+ public boolean isTreeValid() {
+ return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.TREE)
+ || (ObjectUtil.isAllNotEmpty(getTreeParentColumnId(), getTreeNameColumnId()));
+ }
+
}
@Schema(description = "更新表定义")
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableBaseVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableBaseVO.java
index ede10fccd0..dff1115f38 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableBaseVO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableBaseVO.java
@@ -58,4 +58,16 @@ public class CodegenTableBaseVO {
@Schema(description = "父菜单编号", example = "1024")
private Long parentMenuId;
+ @Schema(description = "主表的编号", example = "2048")
+ private Long masterTableId;
+ @Schema(description = "子表关联主表的字段编号", example = "4096")
+ private Long subJoinColumnId;
+ @Schema(description = "主表与子表是否一对多", example = "4096")
+ private Boolean subJoinMany;
+
+ @Schema(description = "树表的父字段编号", example = "8192")
+ private Long treeParentColumnId;
+ @Schema(description = "树表的名字字段编号", example = "16384")
+ private Long treeNameColumnId;
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java
new file mode 100644
index 0000000000..5ab9d1e973
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java
@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
+import cn.iocoder.yudao.module.infra.service.demo.demo01.Demo01ContactService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 示例联系人")
+@RestController
+@RequestMapping("/infra/demo01-contact")
+@Validated
+public class Demo01ContactController {
+
+ @Resource
+ private Demo01ContactService demo01ContactService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建示例联系人")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:create')")
+ public CommonResult createDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO createReqVO) {
+ return success(demo01ContactService.createDemo01Contact(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新示例联系人")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:update')")
+ public CommonResult updateDemo01Contact(@Valid @RequestBody Demo01ContactSaveReqVO updateReqVO) {
+ demo01ContactService.updateDemo01Contact(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除示例联系人")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:delete')")
+ public CommonResult deleteDemo01Contact(@RequestParam("id") Long id) {
+ demo01ContactService.deleteDemo01Contact(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得示例联系人")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
+ public CommonResult getDemo01Contact(@RequestParam("id") Long id) {
+ Demo01ContactDO demo01Contact = demo01ContactService.getDemo01Contact(id);
+ return success(BeanUtils.toBean(demo01Contact, Demo01ContactRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得示例联系人分页")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:query')")
+ public CommonResult> getDemo01ContactPage(@Valid Demo01ContactPageReqVO pageReqVO) {
+ PageResult pageResult = demo01ContactService.getDemo01ContactPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, Demo01ContactRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出示例联系人 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo01-contact:export')")
+ @OperateLog(type = EXPORT)
+ public void exportDemo01ContactExcel(@Valid Demo01ContactPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = demo01ContactService.getDemo01ContactPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "示例联系人.xls", "数据", Demo01ContactRespVO.class,
+ BeanUtils.toBean(list, Demo01ContactRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java
new file mode 100644
index 0000000000..d337d2d7db
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.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 Demo01ContactPageReqVO extends PageParam {
+
+ @Schema(description = "名字", example = "张三")
+ private String name;
+
+ @Schema(description = "性别", example = "1")
+ private Integer sex;
+
+ @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/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java
new file mode 100644
index 0000000000..5d176c262a
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.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 Demo01ContactRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+ @ExcelProperty("名字")
+ private String name;
+
+ @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)
+ @ExcelProperty("出生年")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "头像")
+ @ExcelProperty("头像")
+ private String avatar;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java
new file mode 100644
index 0000000000..94157ed4df
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 示例联系人新增/修改 Request VO")
+@Data
+public class Demo01ContactSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21555")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "性别不能为空")
+ private Integer sex;
+
+ @Schema(description = "出生年", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "出生年不能为空")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "你说的对")
+ @NotEmpty(message = "简介不能为空")
+ private String description;
+
+ @Schema(description = "头像")
+ private String avatar;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java
new file mode 100644
index 0000000000..ebf0193ef1
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java
@@ -0,0 +1,90 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+import cn.iocoder.yudao.module.infra.service.demo.demo02.Demo02CategoryService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 示例分类")
+@RestController
+@RequestMapping("/infra/demo02-category")
+@Validated
+public class Demo02CategoryController {
+
+ @Resource
+ private Demo02CategoryService demo02CategoryService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建示例分类")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:create')")
+ public CommonResult createDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO createReqVO) {
+ return success(demo02CategoryService.createDemo02Category(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新示例分类")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:update')")
+ public CommonResult updateDemo02Category(@Valid @RequestBody Demo02CategorySaveReqVO updateReqVO) {
+ demo02CategoryService.updateDemo02Category(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除示例分类")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:delete')")
+ public CommonResult deleteDemo02Category(@RequestParam("id") Long id) {
+ demo02CategoryService.deleteDemo02Category(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得示例分类")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
+ public CommonResult getDemo02Category(@RequestParam("id") Long id) {
+ Demo02CategoryDO demo02Category = demo02CategoryService.getDemo02Category(id);
+ return success(BeanUtils.toBean(demo02Category, Demo02CategoryRespVO.class));
+ }
+
+ @GetMapping("/list")
+ @Operation(summary = "获得示例分类列表")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:query')")
+ public CommonResult> getDemo02CategoryList(@Valid Demo02CategoryListReqVO listReqVO) {
+ List list = demo02CategoryService.getDemo02CategoryList(listReqVO);
+ return success(BeanUtils.toBean(list, Demo02CategoryRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出示例分类 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo02-category:export')")
+ @OperateLog(type = EXPORT)
+ public void exportDemo02CategoryExcel(@Valid Demo02CategoryListReqVO listReqVO,
+ HttpServletResponse response) throws IOException {
+ List list = demo02CategoryService.getDemo02CategoryList(listReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "示例分类.xls", "数据", Demo02CategoryRespVO.class,
+ BeanUtils.toBean(list, Demo02CategoryRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java
new file mode 100644
index 0000000000..69fcfd9368
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+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
+public class Demo02CategoryListReqVO {
+
+ @Schema(description = "名字", example = "芋艿")
+ private String name;
+
+ @Schema(description = "父级编号", example = "6080")
+ private Long parentId;
+
+ @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/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java
new file mode 100644
index 0000000000..1f2efd46a7
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 示例分类 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class Demo02CategoryRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
+ @ExcelProperty("父级编号")
+ private Long parentId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java
new file mode 100644
index 0000000000..3cc5012516
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 示例分类新增/修改 Request VO")
+@Data
+public class Demo02CategorySaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10304")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "父级编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6080")
+ @NotNull(message = "父级编号不能为空")
+ private Long parentId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java
new file mode 100644
index 0000000000..e97f44e031
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java
@@ -0,0 +1,197 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.service.demo.demo03.Demo03StudentService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Tag(name = "管理后台 - 学生")
+@RestController
+@RequestMapping("/infra/demo03-student")
+@Validated
+public class Demo03StudentController {
+
+ @Resource
+ private Demo03StudentService demo03StudentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建学生")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO createReqVO) {
+ return success(demo03StudentService.createDemo03Student(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新学生")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Student(@Valid @RequestBody Demo03StudentSaveReqVO updateReqVO) {
+ demo03StudentService.updateDemo03Student(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除学生")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Student(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Student(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得学生")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Student(@RequestParam("id") Long id) {
+ Demo03StudentDO demo03Student = demo03StudentService.getDemo03Student(id);
+ return success(BeanUtils.toBean(demo03Student, Demo03StudentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得学生分页")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03StudentPage(@Valid Demo03StudentPageReqVO pageReqVO) {
+ PageResult pageResult = demo03StudentService.getDemo03StudentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, Demo03StudentRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出学生 Excel")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:export')")
+ @OperateLog(type = EXPORT)
+ public void exportDemo03StudentExcel(@Valid Demo03StudentPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = demo03StudentService.getDemo03StudentPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "学生.xls", "数据", Demo03StudentRespVO.class,
+ BeanUtils.toBean(list, Demo03StudentRespVO.class));
+ }
+
+ // ==================== 子表(学生课程) ====================
+
+ @GetMapping("/demo03-course/page")
+ @Operation(summary = "获得学生课程分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03CoursePage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03CoursePage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/demo03-course/create")
+ @Operation(summary = "创建学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
+ return success(demo03StudentService.createDemo03Course(demo03Course));
+ }
+
+ @PutMapping("/demo03-course/update")
+ @Operation(summary = "更新学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Course(@Valid @RequestBody Demo03CourseDO demo03Course) {
+ demo03StudentService.updateDemo03Course(demo03Course);
+ return success(true);
+ }
+
+ @DeleteMapping("/demo03-course/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生课程")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Course(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Course(id);
+ return success(true);
+ }
+
+ @GetMapping("/demo03-course/get")
+ @Operation(summary = "获得学生课程")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Course(@RequestParam("id") Long id) {
+ return success(demo03StudentService.getDemo03Course(id));
+ }
+
+ @GetMapping("/demo03-course/list-by-student-id")
+ @Operation(summary = "获得学生课程列表")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03CourseListByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03CourseListByStudentId(studentId));
+ }
+
+ // ==================== 子表(学生班级) ====================
+
+ @GetMapping("/demo03-grade/page")
+ @Operation(summary = "获得学生班级分页")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult> getDemo03GradePage(PageParam pageReqVO,
+ @RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03GradePage(pageReqVO, studentId));
+ }
+
+ @PostMapping("/demo03-grade/create")
+ @Operation(summary = "创建学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:create')")
+ public CommonResult createDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
+ return success(demo03StudentService.createDemo03Grade(demo03Grade));
+ }
+
+ @PutMapping("/demo03-grade/update")
+ @Operation(summary = "更新学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:update')")
+ public CommonResult updateDemo03Grade(@Valid @RequestBody Demo03GradeDO demo03Grade) {
+ demo03StudentService.updateDemo03Grade(demo03Grade);
+ return success(true);
+ }
+
+ @DeleteMapping("/demo03-grade/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除学生班级")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:delete')")
+ public CommonResult deleteDemo03Grade(@RequestParam("id") Long id) {
+ demo03StudentService.deleteDemo03Grade(id);
+ return success(true);
+ }
+
+ @GetMapping("/demo03-grade/get")
+ @Operation(summary = "获得学生班级")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03Grade(@RequestParam("id") Long id) {
+ return success(demo03StudentService.getDemo03Grade(id));
+ }
+
+ @GetMapping("/demo03-grade/get-by-student-id")
+ @Operation(summary = "获得学生班级")
+ @Parameter(name = "studentId", description = "学生编号")
+ @PreAuthorize("@ss.hasPermission('infra:demo03-student:query')")
+ public CommonResult getDemo03GradeByStudentId(@RequestParam("studentId") Long studentId) {
+ return success(demo03StudentService.getDemo03GradeByStudentId(studentId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java
new file mode 100644
index 0000000000..79682e2034
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java
@@ -0,0 +1 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03;
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java
old mode 100755
new mode 100644
similarity index 54%
rename from yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java
rename to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java
index abb5843dba..6834991755
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java
@@ -1,36 +1,30 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo;
-import java.time.LocalDateTime;
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-@Schema(description = "管理后台 - 字典类型分页 Request VO")
+@Schema(description = "管理后台 - 学生分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
-public class TestDemoPageReqVO extends PageParam {
+public class Demo03StudentPageReqVO extends PageParam {
- @Schema(description = "名字")
+ @Schema(description = "名字", example = "芋艿")
private String name;
- @Schema(description = "状态")
- private Integer status;
+ @Schema(description = "性别")
+ private Integer sex;
- @Schema(description = "类型")
- private Integer type;
+ @Schema(description = "简介", example = "随便")
+ private String description;
- @Schema(description = "分类")
- private Integer category;
-
- @Schema(description = "备注")
- private String remark;
-
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@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/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java
new file mode 100644
index 0000000000..5ae784fa04
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+
+@Schema(description = "管理后台 - 学生 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class Demo03StudentRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @ExcelProperty("名字")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty(value = "性别", converter = DictConvert.class)
+ @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+ private Integer sex;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("出生日期")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+ @ExcelProperty("简介")
+ private String description;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java
new file mode 100644
index 0000000000..d3393a76d0
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.time.LocalDateTime;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+
+@Schema(description = "管理后台 - 学生新增/修改 Request VO")
+@Data
+public class Demo03StudentSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8525")
+ private Long id;
+
+ @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
+ @NotEmpty(message = "名字不能为空")
+ private String name;
+
+ @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "性别不能为空")
+ private Integer sex;
+
+ @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "出生日期不能为空")
+ private LocalDateTime birthday;
+
+ @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
+ @NotEmpty(message = "简介不能为空")
+ private String description;
+
+
+ private List demo03Courses;
+
+ private Demo03GradeDO demo03Grade;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java
new file mode 100644
index 0000000000..c448646361
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * 代码生成示例
+ *
+ * 1. demo01:单表(增删改查)
+ * 2. demo02:单表(树形结构)
+ * 3. demo03:主子表(标准模式)+ 主子表(ERP 模式)+ 主子表(内嵌模式)
+ */
+package cn.iocoder.yudao.module.infra.controller.admin.demo;
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.http b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.http
deleted file mode 100644
index ed65d0b86a..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.http
+++ /dev/null
@@ -1,19 +0,0 @@
-### 请求 /infra/test-demo/get 接口 => 成功
-GET {{baseUrl}}/infra/test-demo/get?id=106
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenentId}}
-
-### 请求 /infra/test-demo/update 接口 => 成功
-PUT {{baseUrl}}/infra/test-demo/update
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenentId}}
-Content-Type: application/json
-
-
-{
- "id": 106,
- "name": "测试",
- "status": "0",
- "type": 1,
- "category": 1
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.java
deleted file mode 100755
index b64b491782..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/TestDemoController.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.*;
-import cn.iocoder.yudao.module.infra.convert.test.TestDemoConvert;
-import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
-import cn.iocoder.yudao.module.infra.service.test.TestDemoService;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.Operation;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.Valid;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
-
-@Tag(name = "管理后台 - 字典类型")
-@RestController
-@RequestMapping("/infra/test-demo")
-@Validated
-public class TestDemoController {
-
- @Resource
- private TestDemoService testDemoService;
-
- @PostMapping("/create")
- @Operation(summary = "创建字典类型")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:create')")
- public CommonResult createTestDemo(@Valid @RequestBody TestDemoCreateReqVO createReqVO) {
- return success(testDemoService.createTestDemo(createReqVO));
- }
-
- @PutMapping("/update")
- @Operation(summary = "更新字典类型")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:update')")
- public CommonResult updateTestDemo(@Valid @RequestBody TestDemoUpdateReqVO updateReqVO) {
- testDemoService.updateTestDemo(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "删除字典类型")
- @Parameter(name = "id", description = "编号", required = true)
- @PreAuthorize("@ss.hasPermission('infra:test-demo:delete')")
- public CommonResult deleteTestDemo(@RequestParam("id") Long id) {
- testDemoService.deleteTestDemo(id);
- return success(true);
- }
-
- @GetMapping("/get")
- @Operation(summary = "获得字典类型")
- @Parameter(name = "id", description = "编号", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
- public CommonResult getTestDemo(@RequestParam("id") Long id) {
- TestDemoDO testDemo = testDemoService.getTestDemo(id);
- return success(TestDemoConvert.INSTANCE.convert(testDemo));
- }
-
- @GetMapping("/list")
- @Operation(summary = "获得字典类型列表")
- @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:query')")
- public CommonResult> getTestDemoList(@RequestParam("ids") Collection ids) {
- List list = testDemoService.getTestDemoList(ids);
- return success(TestDemoConvert.INSTANCE.convertList(list));
- }
-
- @GetMapping("/page")
- @Operation(summary = "获得字典类型分页")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:query')") public CommonResult> getTestDemoPage(@Valid TestDemoPageReqVO pageVO) {
- PageResult pageResult = testDemoService.getTestDemoPage(pageVO);
- return success(TestDemoConvert.INSTANCE.convertPage(pageResult));
- }
-
- @GetMapping("/export-excel")
- @Operation(summary = "导出字典类型 Excel")
- @PreAuthorize("@ss.hasPermission('infra:test-demo:export')") @OperateLog(type = EXPORT)
- public void exportTestDemoExcel(@Valid TestDemoExportReqVO exportReqVO,
- HttpServletResponse response) throws IOException {
- List list = testDemoService.getTestDemoList(exportReqVO);
- // 导出 Excel
- List datas = TestDemoConvert.INSTANCE.convertList02(list);
- ExcelUtils.write(response, "字典类型.xls", "数据", TestDemoExcelVO.class, datas);
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoBaseVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoBaseVO.java
deleted file mode 100755
index 660c31c784..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoBaseVO.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import javax.validation.constraints.*;
-
-/**
-* 字典类型 Base VO,提供给添加、修改、详细的子 VO 使用
-* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
-*/
-@Data
-public class TestDemoBaseVO {
-
- @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "名字不能为空")
- private String name;
-
- @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "状态不能为空")
- private Integer status;
-
- @Schema(description = "类型", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "类型不能为空")
- private Integer type;
-
- @Schema(description = "分类", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "分类不能为空")
- private Integer category;
-
- @Schema(description = "备注")
- private String remark;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoCreateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoCreateReqVO.java
deleted file mode 100755
index 3b46fd1cdf..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoCreateReqVO.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
-@Schema(description = "管理后台 - 字典类型创建 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class TestDemoCreateReqVO extends TestDemoBaseVO {
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java
deleted file mode 100755
index a135885fe2..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-
-import lombok.*;
-
-import java.time.LocalDateTime;
-
-import com.alibaba.excel.annotation.ExcelProperty;
-
-/**
- * 字典类型 Excel VO
- *
- * @author 芋道源码
- */
-@Data
-public class TestDemoExcelVO {
-
- @ExcelProperty("编号")
- private Long id;
-
- @ExcelProperty("名字")
- private String name;
-
- @ExcelProperty("状态")
- private Integer status;
-
- @ExcelProperty("类型")
- private Integer type;
-
- @ExcelProperty("分类")
- private Integer category;
-
- @ExcelProperty("备注")
- private String remark;
-
- @ExcelProperty("创建时间")
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java
deleted file mode 100755
index f219499ffe..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
-import java.time.LocalDateTime;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "管理后台 - 字典类型 Excel 导出 Request VO,参数和 TestDemoPageReqVO 是一致的")
-@Data
-public class TestDemoExportReqVO {
-
- @Schema(description = "名字")
- private String name;
-
- @Schema(description = "状态")
- private Integer status;
-
- @Schema(description = "类型")
- private Integer type;
-
- @Schema(description = "分类")
- private Integer category;
-
- @Schema(description = "备注")
- private String remark;
-
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- @Schema(description = "创建时间")
- private LocalDateTime[] createTime;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java
deleted file mode 100755
index eaef24df32..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "管理后台 - 字典类型 Response VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class TestDemoRespVO extends TestDemoBaseVO {
-
- @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
- private Long id;
-
- @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoUpdateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoUpdateReqVO.java
deleted file mode 100755
index 25af58ed73..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoUpdateReqVO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.module.infra.controller.admin.test.vo;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import javax.validation.constraints.*;
-
-@Schema(description = "管理后台 - 字典类型更新 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class TestDemoUpdateReqVO extends TestDemoBaseVO {
-
- @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotNull(message = "编号不能为空")
- private Long id;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java
index 1d286c52f7..235e5f0d5c 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java
@@ -56,7 +56,7 @@ public interface CodegenConvert {
// ========== CodegenTableDO 相关 ==========
-// List convertList02(List list);
+ List convertList05(List list);
CodegenTableRespVO convert(CodegenTableDO bean);
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/test/TestDemoConvert.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/test/TestDemoConvert.java
deleted file mode 100755
index 07a29f5a25..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/test/TestDemoConvert.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.module.infra.convert.test;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExcelVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoRespVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
-import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-
-/**
- * 字典类型 Convert
- *
- * @author 芋道源码
- */
-@Mapper
-public interface TestDemoConvert {
-
- TestDemoConvert INSTANCE = Mappers.getMapper(TestDemoConvert.class);
-
- TestDemoDO convert(TestDemoCreateReqVO bean);
-
- TestDemoDO convert(TestDemoUpdateReqVO bean);
-
- TestDemoRespVO convert(TestDemoDO bean);
-
- List convertList(List list);
-
- PageResult convertPage(PageResult page);
-
- List convertList02(List list);
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java
index 02cd782e18..faee8177ec 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java
@@ -116,4 +116,43 @@ public class CodegenTableDO extends BaseDO {
*/
private Long parentMenuId;
+ // ========== 主子表相关字段 ==========
+
+ /**
+ * 主表的编号
+ *
+ * 关联 {@link CodegenTableDO#getId()}
+ */
+ private Long masterTableId;
+ /**
+ * 【自己】子表关联主表的字段编号
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long subJoinColumnId;
+ /**
+ * 主表与子表是否一对多
+ *
+ * true:一对多
+ * false:一对一
+ */
+ private Boolean subJoinMany;
+
+ // ========== 树表相关字段 ==========
+
+ /**
+ * 树表的父字段编号
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long treeParentColumnId;
+ /**
+ * 树表的名字字段编号
+ *
+ * 名字的用途:新增或修改时,select 框展示的字段
+ *
+ * 关联 {@link CodegenColumnDO#getId()}
+ */
+ private Long treeNameColumnId;
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java
new file mode 100644
index 0000000000..f9294a474a
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 示例联系人 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo01_contact")
+@KeySequence("infra_demo01_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo01ContactDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 性别
+ *
+ * 枚举 {@link TODO system_user_sex 对应的类}
+ */
+ private Integer sex;
+ /**
+ * 出生年
+ */
+ private LocalDateTime birthday;
+ /**
+ * 简介
+ */
+ private String description;
+ /**
+ * 头像
+ */
+ private String avatar;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java
new file mode 100644
index 0000000000..1c3d4eadbc
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02;
+
+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_demo02_category")
+@KeySequence("infra_demo02_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo02CategoryDO 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/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
new file mode 100644
index 0000000000..bf265a8b0b
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 学生课程 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo03_course")
+@KeySequence("infra_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03CourseDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 学生编号
+ */
+ private Long studentId;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 分数
+ */
+ private Integer score;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
new file mode 100644
index 0000000000..a0e8ed7673
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 学生班级 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo03_grade")
+@KeySequence("infra_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03GradeDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 学生编号
+ */
+ private Long studentId;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 班主任
+ */
+ private String teacher;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
new file mode 100644
index 0000000000..cf88d2554b
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 学生 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("infra_demo03_student")
+@KeySequence("infra_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Demo03StudentDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 名字
+ */
+ private String name;
+ /**
+ * 性别
+ *
+ * 枚举 {@link TODO system_user_sex 对应的类}
+ */
+ private Integer sex;
+ /**
+ * 出生日期
+ */
+ private LocalDateTime birthday;
+ /**
+ * 简介
+ */
+ private String description;
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/test/TestDemoDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/test/TestDemoDO.java
deleted file mode 100755
index da1f4cb53c..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/test/TestDemoDO.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.module.infra.dal.dataobject.test;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-/**
- * 字典类型 DO
- *
- * @author 芋道源码
- */
-@TableName("infra_test_demo")
-@KeySequence("infra_test_demo_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class TestDemoDO extends BaseDO {
-
- /**
- * 编号
- */
- @TableId
- private Long id;
- /**
- * 名字
- */
- private String name;
- /**
- * 状态
- */
- private Integer status;
- /**
- * 类型
- */
- private Integer type;
- /**
- * 分类
- */
- private Integer category;
- /**
- * 备注
- */
- private String remark;
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java
index 006e24bd15..1e4697d333 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java
@@ -29,4 +29,9 @@ public interface CodegenTableMapper extends BaseMapperX {
return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
+ default List selectListByTemplateTypeAndMasterTableId(Integer templateType, Long masterTableId) {
+ return selectList(CodegenTableDO::getTemplateType, templateType,
+ CodegenTableDO::getMasterTableId, masterTableId);
+ }
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java
new file mode 100644
index 0000000000..f5f3cdbbe8
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo01;
+
+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.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 示例联系人 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo01ContactMapper extends BaseMapperX {
+
+ default PageResult selectPage(Demo01ContactPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(Demo01ContactDO::getName, reqVO.getName())
+ .eqIfPresent(Demo01ContactDO::getSex, reqVO.getSex())
+ .betweenIfPresent(Demo01ContactDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(Demo01ContactDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java
new file mode 100644
index 0000000000..b16e18feee
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 示例分类 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo02CategoryMapper extends BaseMapperX {
+
+ default List selectList(Demo02CategoryListReqVO reqVO) {
+ return selectList(new LambdaQueryWrapperX()
+ .likeIfPresent(Demo02CategoryDO::getName, reqVO.getName())
+ .eqIfPresent(Demo02CategoryDO::getParentId, reqVO.getParentId())
+ .betweenIfPresent(Demo02CategoryDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(Demo02CategoryDO::getId));
+ }
+
+ default Demo02CategoryDO selectByParentIdAndName(Long parentId, String name) {
+ return selectOne(Demo02CategoryDO::getParentId, parentId, Demo02CategoryDO::getName, name);
+ }
+
+ default Long selectCountByParentId(Long parentId) {
+ return selectCount(Demo02CategoryDO::getParentId, parentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java
new file mode 100644
index 0000000000..3cb3aa5920
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 学生课程 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03CourseMapper extends BaseMapperX {
+
+ default PageResult selectPage(PageParam reqVO, Long studentId) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eq(Demo03CourseDO::getStudentId, studentId)
+ .orderByDesc(Demo03CourseDO::getId));
+ }
+
+ default List selectListByStudentId(Long studentId) {
+ return selectList(Demo03CourseDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(Demo03CourseDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java
new file mode 100644
index 0000000000..0440cc49e3
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 学生班级 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03GradeMapper extends BaseMapperX {
+
+ default PageResult selectPage(PageParam reqVO, Long studentId) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eq(Demo03GradeDO::getStudentId, studentId)
+ .orderByDesc(Demo03GradeDO::getId));
+ }
+
+ default Demo03GradeDO selectByStudentId(Long studentId) {
+ return selectOne(Demo03GradeDO::getStudentId, studentId);
+ }
+
+ default int deleteByStudentId(Long studentId) {
+ return delete(Demo03GradeDO::getStudentId, studentId);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java
new file mode 100644
index 0000000000..00659d0520
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.*;
+
+/**
+ * 学生 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface Demo03StudentMapper extends BaseMapperX {
+
+ default PageResult selectPage(Demo03StudentPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(Demo03StudentDO::getName, reqVO.getName())
+ .eqIfPresent(Demo03StudentDO::getSex, reqVO.getSex())
+ .eqIfPresent(Demo03StudentDO::getDescription, reqVO.getDescription())
+ .betweenIfPresent(Demo03StudentDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(Demo03StudentDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/test/TestDemoMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/test/TestDemoMapper.java
deleted file mode 100755
index d600b3f4b7..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/test/TestDemoMapper.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.module.infra.dal.mysql.test;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
-import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * 字典类型 Mapper
- *
- * @author 芋道源码
- */
-@Mapper
-public interface TestDemoMapper extends BaseMapperX {
-
- default PageResult selectPage(TestDemoPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .likeIfPresent(TestDemoDO::getName, reqVO.getName())
- .eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
- .eqIfPresent(TestDemoDO::getType, reqVO.getType())
- .eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
- .eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
- .betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(TestDemoDO::getId));
- }
-
- default List selectList(TestDemoExportReqVO reqVO) {
- return selectList(new LambdaQueryWrapperX()
- .likeIfPresent(TestDemoDO::getName, reqVO.getName())
- .eqIfPresent(TestDemoDO::getStatus, reqVO.getStatus())
- .eqIfPresent(TestDemoDO::getType, reqVO.getType())
- .eqIfPresent(TestDemoDO::getCategory, reqVO.getCategory())
- .eqIfPresent(TestDemoDO::getRemark, reqVO.getRemark())
- .betweenIfPresent(TestDemoDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(TestDemoDO::getId));
- }
-
- List selectList2();
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java
index 5474ba1a8b..2c0cdbb8a9 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java
@@ -16,8 +16,8 @@ public enum CodegenColumnHtmlTypeEnum {
RADIO("radio"), // 单选框
CHECKBOX("checkbox"), // 复选框
DATETIME("datetime"), // 日期控件
- UPLOAD_IMAGE("upload_image"), // 上传图片
- UPLOAD_FILE("upload_file"), // 上传文件
+ IMAGE_UPLOAD("imageUpload"), // 上传图片
+ FILE_UPLOAD("fileUpload"), // 上传文件
EDITOR("editor"), // 富文本控件
;
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java
index ce2799e34a..9a218331c3 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java
@@ -1,8 +1,11 @@
package cn.iocoder.yudao.module.infra.enums.codegen;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
+import java.util.Objects;
+
/**
* 代码生成模板类型
*
@@ -12,8 +15,13 @@ import lombok.Getter;
@Getter
public enum CodegenTemplateTypeEnum {
- CRUD(1), // 单表(增删改查)
+ ONE(1), // 单表(增删改查)
TREE(2), // 树表(增删改查)
+
+ MASTER_NORMAL(10), // 主子表 - 主表 - 普通模式
+ MASTER_ERP(11), // 主子表 - 主表 - ERP 模式
+ MASTER_INNER(12), // 主子表 - 主表 - 内嵌模式
+ SUB(15), // 主子表 - 子表
;
/**
@@ -21,4 +29,25 @@ public enum CodegenTemplateTypeEnum {
*/
private final Integer type;
+ /**
+ * 是否为主表
+ *
+ * @param type 类型
+ * @return 是否主表
+ */
+ public static boolean isMaster(Integer type) {
+ return ObjectUtils.equalsAny(type,
+ MASTER_NORMAL.type, MASTER_ERP.type, MASTER_INNER.type);
+ }
+
+ /**
+ * 是否为树表
+ *
+ * @param type 类型
+ * @return 是否树表
+ */
+ public static boolean isTree(Integer type) {
+ return Objects.equals(type, TREE.type);
+ }
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java
index d4d266a720..a84e854a0e 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java
@@ -48,6 +48,14 @@ public interface CodegenService {
*/
void deleteCodegen(Long tableId);
+ /**
+ * 获得表定义列表
+ *
+ * @param dataSourceConfigId 数据源配置的编号
+ * @return 表定义列表
+ */
+ List getCodegenTableList(Long dataSourceConfigId);
+
/**
* 获得表定义分页
*
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
index 26d07d326c..574c27476e 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
+import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
@@ -25,10 +26,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
@@ -129,6 +127,16 @@ public class CodegenServiceImpl implements CodegenService {
if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
+ // 校验主表字段存在
+ if (Objects.equals(updateReqVO.getTable().getTemplateType(), CodegenTemplateTypeEnum.SUB.getType())) {
+ if (codegenTableMapper.selectById(updateReqVO.getTable().getMasterTableId()) == null) {
+ throw exception(CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());
+ }
+ if (CollUtil.findOne(updateReqVO.getColumns(), // 关联主表的字段不存在
+ column -> column.getId().equals(updateReqVO.getTable().getSubJoinColumnId())) == null) {
+ throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());
+ }
+ }
// 更新 table 表定义
CodegenTableDO updateTableObj = CodegenConvert.INSTANCE.convert(updateReqVO.getTable());
@@ -208,6 +216,11 @@ public class CodegenServiceImpl implements CodegenService {
codegenColumnMapper.deleteListByTableId(tableId);
}
+ @Override
+ public List getCodegenTableList(Long dataSourceConfigId) {
+ return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);
+ }
+
@Override
public PageResult getCodegenTablePage(CodegenTablePageReqVO pageReqVO) {
return codegenTableMapper.selectPage(pageReqVO);
@@ -235,14 +248,34 @@ public class CodegenServiceImpl implements CodegenService {
throw exception(CODEGEN_COLUMN_NOT_EXISTS);
}
+ // 如果是主子表,则加载对应的子表信息
+ List subTables = null;
+ List> subColumnsList = null;
+ if (CodegenTemplateTypeEnum.isMaster(table.getTemplateType())) {
+ // 校验子表存在
+ subTables = codegenTableMapper.selectListByTemplateTypeAndMasterTableId(
+ CodegenTemplateTypeEnum.SUB.getType(), tableId);
+ if (CollUtil.isEmpty(subTables)) {
+ throw exception(CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);
+ }
+ // 校验子表的关联字段存在
+ subColumnsList = new ArrayList<>();
+ for (CodegenTableDO subTable : subTables) {
+ List subColumns = codegenColumnMapper.selectListByTableId(subTable.getId());
+ if (CollUtil.findOne(subColumns, column -> column.getId().equals(subTable.getSubJoinColumnId())) == null) {
+ throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());
+ }
+ subColumnsList.add(subColumns);
+ }
+ }
+
// 执行生成
- return codegenEngine.execute(table, columns);
+ return codegenEngine.execute(table, columns, subTables, subColumnsList);
}
@Override
public List getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
List tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
- // 移除已经生成的表
// 移除在 Codegen 中,已经存在的
Set existsTables = CollectionUtils.convertSet(
codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
index 8293ffef0b..b529c49818 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java
@@ -15,6 +15,7 @@ import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Component;
+import java.time.LocalDateTime;
import java.util.*;
import static cn.hutool.core.text.CharSequenceUtil.*;
@@ -49,8 +50,8 @@ public class CodegenBuilder {
.put("status", CodegenColumnHtmlTypeEnum.RADIO)
.put("sex", CodegenColumnHtmlTypeEnum.RADIO)
.put("type", CodegenColumnHtmlTypeEnum.SELECT)
- .put("image", CodegenColumnHtmlTypeEnum.UPLOAD_IMAGE)
- .put("file", CodegenColumnHtmlTypeEnum.UPLOAD_FILE)
+ .put("image", CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD)
+ .put("file", CodegenColumnHtmlTypeEnum.FILE_UPLOAD)
.put("content", CodegenColumnHtmlTypeEnum.EDITOR)
.put("description", CodegenColumnHtmlTypeEnum.EDITOR)
.put("demo", CodegenColumnHtmlTypeEnum.EDITOR)
@@ -118,7 +119,7 @@ public class CodegenBuilder {
table.setClassName(upperFirst(toCamelCase(subAfter(tableName, '_', false))));
// 去除结尾的表,作为类描述
table.setClassComment(StrUtil.removeSuffixIgnoreCase(table.getTableComment(), "表"));
- table.setTemplateType(CodegenTemplateTypeEnum.CRUD.getType());
+ table.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
}
public List buildColumns(Long tableId, List tableFields) {
@@ -127,6 +128,10 @@ public class CodegenBuilder {
for (CodegenColumnDO column : columns) {
column.setTableId(tableId);
column.setOrdinalPosition(index++);
+ // 特殊处理:Byte => Integer
+ if (Byte.class.getSimpleName().equals(column.getJavaType())) {
+ column.setJavaType(Integer.class.getSimpleName());
+ }
// 初始化 Column 列的默认字段
processColumnOperation(column); // 处理 CRUD 相关的字段的默认值
processColumnUI(column); // 处理 UI 相关的字段的默认值
@@ -162,10 +167,13 @@ public class CodegenBuilder {
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时,设置为 radio 类型.
- // 其它类型,因为字段名可以相对保障,所以不进行处理。例如说 date 对应 datetime 类型.
if (Boolean.class.getSimpleName().equals(column.getJavaType())) {
column.setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType());
}
+ // 如果是 LocalDateTime 类型,则设置为 datetime 类型
+ if (LocalDateTime.class.getSimpleName().equals(column.getJavaType())) {
+ column.setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
+ }
// 兜底,设置默认为 input 类型
if (column.getHtmlType() == null) {
column.setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
index 40c7a41c7d..4ce4c2f98b 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
@@ -12,7 +14,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
@@ -25,7 +29,9 @@ 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.enums.codegen.CodegenFrontTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
+import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
@@ -33,10 +39,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import static cn.hutool.core.map.MapUtil.getStr;
import static cn.hutool.core.text.CharSequenceUtil.*;
@@ -60,20 +63,19 @@ public class CodegenEngine {
*/
private static final Map SERVER_TEMPLATES = MapUtil.builder(new LinkedHashMap<>()) // 有序
// Java module-biz Main
- .put(javaTemplatePath("controller/vo/baseVO"), javaModuleImplVOFilePath("BaseVO"))
- .put(javaTemplatePath("controller/vo/createReqVO"), javaModuleImplVOFilePath("CreateReqVO"))
.put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO"))
+ .put(javaTemplatePath("controller/vo/listReqVO"), javaModuleImplVOFilePath("ListReqVO"))
.put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO"))
- .put(javaTemplatePath("controller/vo/updateReqVO"), javaModuleImplVOFilePath("UpdateReqVO"))
- .put(javaTemplatePath("controller/vo/exportReqVO"), javaModuleImplVOFilePath("ExportReqVO"))
- .put(javaTemplatePath("controller/vo/excelVO"), javaModuleImplVOFilePath("ExcelVO"))
+ .put(javaTemplatePath("controller/vo/saveReqVO"), javaModuleImplVOFilePath("SaveReqVO"))
.put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath())
- .put(javaTemplatePath("convert/convert"),
- javaModuleImplMainFilePath("convert/${table.businessName}/${table.className}Convert"))
.put(javaTemplatePath("dal/do"),
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${table.className}DO"))
+ .put(javaTemplatePath("dal/do_sub"), // 特殊:主子表专属逻辑
+ javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${subTable.className}DO"))
.put(javaTemplatePath("dal/mapper"),
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${table.className}Mapper"))
+ .put(javaTemplatePath("dal/mapper_sub"), // 特殊:主子表专属逻辑
+ javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${subTable.className}Mapper"))
.put(javaTemplatePath("dal/mapper.xml"), mapperXmlFilePath())
.put(javaTemplatePath("service/serviceImpl"),
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}ServiceImpl"))
@@ -99,34 +101,44 @@ public class CodegenEngine {
private static final Table FRONT_TEMPLATES = ImmutableTable.builder()
// Vue2 标准模版
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("views/index.vue"),
- vueFilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
+ vueFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE2.getType(), vueTemplatePath("api/api.js"),
vueFilePath("api/${table.moduleName}/${classNameVar}.js"))
// Vue3 标准模版
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/index.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/form.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Form.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
+ .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
+ .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
+ .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
+ .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
+ .put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
.put(CodegenFrontTypeEnum.VUE3.getType(), vue3TemplatePath("api/api.ts"),
- vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts"))
+ vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
// Vue3 Schema 模版
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/data.ts"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/${classNameVar}.data.ts"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/index.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Form.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("api/api.ts"),
- vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts"))
+ vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
// Vue3 vben 模版
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/${classNameVar}.data.ts"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/index.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/form.vue"),
- vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Modal.vue"))
+ vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Modal.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("api/api.ts"),
- vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts"))
+ vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.build();
@Resource
@@ -149,7 +161,8 @@ public class CodegenEngine {
}
@PostConstruct
- private void initGlobalBindingMap() {
+ @VisibleForTesting
+ void initGlobalBindingMap() {
// 全局配置
globalBindingMap.put("basePackage", codegenProperties.getBasePackage());
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage()
@@ -174,9 +187,122 @@ public class CodegenEngine {
globalBindingMap.put("DictConvertClassName", DictConvert.class.getName());
globalBindingMap.put("OperateLogClassName", OperateLog.class.getName());
globalBindingMap.put("OperateTypeEnumClassName", OperateTypeEnum.class.getName());
+ globalBindingMap.put("BeanUtils", BeanUtils.class.getName());
}
- public Map execute(CodegenTableDO table, List columns) {
+ /**
+ * 生成代码
+ *
+ * @param table 表定义
+ * @param columns table 的字段定义数组
+ * @param subTables 子表数组,当且仅当主子表时使用
+ * @param subColumnsList subTables 的字段定义数组
+ * @return 生成的代码,key 是路径,value 是对应代码
+ */
+ public Map execute(CodegenTableDO table, List columns,
+ List subTables, List> subColumnsList) {
+ // 1.1 初始化 bindMap 上下文
+ Map bindingMap = initBindingMap(table, columns, subTables, subColumnsList);
+ // 1.2 获得模版
+ Map templates = getTemplates(table.getFrontType());
+
+ // 2. 执行生成
+ Map result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序
+ templates.forEach((vmPath, filePath) -> {
+ // 2.1 特殊:主子表专属逻辑
+ if (isSubTemplate(vmPath)) {
+ generateSubCode(table, subTables, result, vmPath, filePath, bindingMap);
+ return;
+ // 2.2 特殊:树表专属逻辑
+ } else if (isPageReqVOTemplate(vmPath)) {
+ // 减少多余的类生成,例如说 PageVO.java 类
+ if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
+ return;
+ }
+ } else if (isListReqVOTemplate(vmPath)) {
+ // 减少多余的类生成,例如说 ListVO.java 类
+ if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
+ return;
+ }
+ }
+ // 2.3 默认生成
+ generateCode(result, vmPath, filePath, bindingMap);
+ });
+ return result;
+ }
+
+ private void generateCode(Map result, String vmPath,
+ String filePath, Map bindingMap) {
+ filePath = formatFilePath(filePath, bindingMap);
+ String content = templateEngine.getTemplate(vmPath).render(bindingMap);
+ // 格式化代码
+ content = prettyCode(content);
+ result.put(filePath, content);
+ }
+
+ private void generateSubCode(CodegenTableDO table, List subTables,
+ Map result, String vmPath,
+ String filePath, Map bindingMap) {
+ // 没有子表,所以不生成
+ if (CollUtil.isEmpty(subTables)) {
+ return;
+ }
+ // 主子表的模式匹配。目的:过滤掉个性化的模版
+ if (vmPath.contains("_normal")
+ && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType())) {
+ return;
+ }
+ if (vmPath.contains("_erp")
+ && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType())) {
+ return;
+ }
+ if (vmPath.contains("_inner")
+ && ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType())) {
+ return;
+ }
+
+ // 逐个生成
+ for (int i = 0; i < subTables.size(); i++) {
+ bindingMap.put("subIndex", i);
+ generateCode(result, vmPath, filePath, bindingMap);
+ }
+ bindingMap.remove("subIndex");
+ }
+
+ /**
+ * 格式化生成后的代码
+ *
+ * 因为尽量让 vm 模版简单,所以统一的处理都在这个方法。
+ * 如果不处理,Vue 的 Pretty 格式校验可能会报错
+ *
+ * @param content 格式化前的代码
+ * @return 格式化后的代码
+ */
+ private String prettyCode(String content) {
+ // Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错
+ content = content.replaceAll(",\n}", "\n}").replaceAll(",\n }", "\n }");
+ // Vue 界面:去除多的 dateFormatter,只有一个的情况下,说明没使用到
+ if (StrUtil.count(content, "dateFormatter") == 1) {
+ content = StrUtils.removeLineContains(content, "dateFormatter");
+ }
+ // Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到
+ if (StrUtil.count(content, "getIntDictOptions") == 1) {
+ content = content.replace("getIntDictOptions, ", "");
+ }
+ if (StrUtil.count(content, "getStrDictOptions") == 1) {
+ content = content.replace("getStrDictOptions, ", "");
+ }
+ if (StrUtil.count(content, "getBoolDictOptions") == 1) {
+ content = content.replace("getBoolDictOptions, ", "");
+ }
+ if (StrUtil.count(content, "DICT_TYPE.") == 0) {
+ content = StrUtils.removeLineContains(content, "DICT_TYPE");
+ }
+ return content;
+ }
+
+ private Map initBindingMap(CodegenTableDO table, List columns,
+ List subTables, List> subColumnsList) {
// 创建 bindingMap
Map bindingMap = new HashMap<>(globalBindingMap);
bindingMap.put("table", table);
@@ -196,17 +322,54 @@ public class CodegenEngine {
// permission 前缀
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
- // 执行生成
- Map templates = getTemplates(table.getFrontType());
- Map result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序
- templates.forEach((vmPath, filePath) -> {
- filePath = formatFilePath(filePath, bindingMap);
- String content = templateEngine.getTemplate(vmPath).render(bindingMap);
- // 去除字段后面多余的 , 逗号
- content = content.replaceAll(",\n}", "\n}").replaceAll(",\n }", "\n }");
- result.put(filePath, content);
- });
- return result;
+ // 特殊:树表专属逻辑
+ if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
+ CodegenColumnDO treeParentColumn = CollUtil.findOne(columns,
+ column -> Objects.equals(column.getId(), table.getTreeParentColumnId()));
+ bindingMap.put("treeParentColumn", treeParentColumn);
+ bindingMap.put("treeParentColumn_javaField_underlineCase", toUnderlineCase(treeParentColumn.getJavaField()));
+ CodegenColumnDO treeNameColumn = CollUtil.findOne(columns,
+ column -> Objects.equals(column.getId(), table.getTreeNameColumnId()));
+ bindingMap.put("treeNameColumn", treeNameColumn);
+ bindingMap.put("treeNameColumn_javaField_underlineCase", toUnderlineCase(treeNameColumn.getJavaField()));
+ }
+
+ // 特殊:主子表专属逻辑
+ if (CollUtil.isNotEmpty(subTables)) {
+ // 创建 bindingMap
+ bindingMap.put("subTables", subTables);
+ bindingMap.put("subColumnsList", subColumnsList);
+ List subPrimaryColumns = new ArrayList<>();
+ List subJoinColumns = new ArrayList<>();
+ List subJoinColumnStrikeCases = new ArrayList<>();
+ List subSimpleClassNames = new ArrayList<>();
+ List subClassNameVars = new ArrayList<>();
+ List simpleClassNameUnderlineCases = new ArrayList<>();
+ List subSimpleClassNameStrikeCases = new ArrayList<>();
+ for (int i = 0; i < subTables.size(); i++) {
+ CodegenTableDO subTable = subTables.get(i);
+ List subColumns = subColumnsList.get(i);
+ subPrimaryColumns.add(CollectionUtils.findFirst(subColumns, CodegenColumnDO::getPrimaryKey)); //
+ CodegenColumnDO subColumn = CollectionUtils.findFirst(subColumns, // 关联的字段
+ column -> Objects.equals(column.getId(), subTable.getSubJoinColumnId()));
+ subJoinColumns.add(subColumn);
+ subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type
+ // className 相关
+ String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName()));
+ subSimpleClassNames.add(subSimpleClassName);
+ simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type
+ subClassNameVars.add(lowerFirst(subSimpleClassName)); // 将 DictType 转换成 dictType,用于变量
+ subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type
+ }
+ bindingMap.put("subPrimaryColumns", subPrimaryColumns);
+ bindingMap.put("subJoinColumns", subJoinColumns);
+ bindingMap.put("subJoinColumn_strikeCases", subJoinColumnStrikeCases);
+ bindingMap.put("subSimpleClassNames", subSimpleClassNames);
+ bindingMap.put("simpleClassNameUnderlineCases", simpleClassNameUnderlineCases);
+ bindingMap.put("subClassNameVars", subClassNameVars);
+ bindingMap.put("subSimpleClassName_strikeCases", subSimpleClassNameStrikeCases);
+ }
+ return bindingMap;
}
private Map getTemplates(Integer frontType) {
@@ -216,6 +379,7 @@ public class CodegenEngine {
return templates;
}
+ @SuppressWarnings("unchecked")
private String formatFilePath(String filePath, Map bindingMap) {
filePath = StrUtil.replace(filePath, "${basePackage}",
getStr(bindingMap, "basePackage").replaceAll("\\.", "/"));
@@ -232,6 +396,16 @@ public class CodegenEngine {
filePath = StrUtil.replace(filePath, "${table.moduleName}", table.getModuleName());
filePath = StrUtil.replace(filePath, "${table.businessName}", table.getBusinessName());
filePath = StrUtil.replace(filePath, "${table.className}", table.getClassName());
+ // 特殊:主子表专属逻辑
+ Integer subIndex = (Integer) bindingMap.get("subIndex");
+ if (subIndex != null) {
+ CodegenTableDO subTable = ((List) bindingMap.get("subTables")).get(subIndex);
+ filePath = StrUtil.replace(filePath, "${subTable.moduleName}", subTable.getModuleName());
+ filePath = StrUtil.replace(filePath, "${subTable.businessName}", subTable.getBusinessName());
+ filePath = StrUtil.replace(filePath, "${subTable.className}", subTable.getClassName());
+ filePath = StrUtil.replace(filePath, "${subSimpleClassName}",
+ ((List) bindingMap.get("subSimpleClassNames")).get(subIndex));
+ }
return filePath;
}
@@ -298,4 +472,17 @@ public class CodegenEngine {
private static String vue3VbenTemplatePath(String path) {
return "codegen/vue3_vben/" + path + ".vm";
}
+
+ private static boolean isSubTemplate(String path) {
+ return path.contains("_sub");
+ }
+
+ private static boolean isPageReqVOTemplate(String path) {
+ return path.contains("pageReqVO");
+ }
+
+ private static boolean isListReqVOTemplate(String path) {
+ return path.contains("listReqVO");
+ }
+
}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java
new file mode 100644
index 0000000000..e3a1b6dd5b
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo01;
+
+import javax.validation.*;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 示例联系人 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface Demo01ContactService {
+
+ /**
+ * 创建示例联系人
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createDemo01Contact(@Valid Demo01ContactSaveReqVO createReqVO);
+
+ /**
+ * 更新示例联系人
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDemo01Contact(@Valid Demo01ContactSaveReqVO updateReqVO);
+
+ /**
+ * 删除示例联系人
+ *
+ * @param id 编号
+ */
+ void deleteDemo01Contact(Long id);
+
+ /**
+ * 获得示例联系人
+ *
+ * @param id 编号
+ * @return 示例联系人
+ */
+ Demo01ContactDO getDemo01Contact(Long id);
+
+ /**
+ * 获得示例联系人分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 示例联系人分页
+ */
+ PageResult getDemo01ContactPage(Demo01ContactPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java
new file mode 100644
index 0000000000..cde4906381
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java
@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo01;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo01.vo.Demo01ContactSaveReqVO;
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01.Demo01ContactDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo01.Demo01ContactMapper;
+
+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 Demo01ContactServiceImpl implements Demo01ContactService {
+
+ @Resource
+ private Demo01ContactMapper demo01ContactMapper;
+
+ @Override
+ public Long createDemo01Contact(Demo01ContactSaveReqVO createReqVO) {
+ // 插入
+ Demo01ContactDO demo01Contact = BeanUtils.toBean(createReqVO, Demo01ContactDO.class);
+ demo01ContactMapper.insert(demo01Contact);
+ // 返回
+ return demo01Contact.getId();
+ }
+
+ @Override
+ public void updateDemo01Contact(Demo01ContactSaveReqVO updateReqVO) {
+ // 校验存在
+ validateDemo01ContactExists(updateReqVO.getId());
+ // 更新
+ Demo01ContactDO updateObj = BeanUtils.toBean(updateReqVO, Demo01ContactDO.class);
+ demo01ContactMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteDemo01Contact(Long id) {
+ // 校验存在
+ validateDemo01ContactExists(id);
+ // 删除
+ demo01ContactMapper.deleteById(id);
+ }
+
+ private void validateDemo01ContactExists(Long id) {
+ if (demo01ContactMapper.selectById(id) == null) {
+ throw exception(DEMO01_CONTACT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public Demo01ContactDO getDemo01Contact(Long id) {
+ return demo01ContactMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getDemo01ContactPage(Demo01ContactPageReqVO pageReqVO) {
+ return demo01ContactMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java
new file mode 100644
index 0000000000..7980fdb8a4
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo02;
+
+import java.util.*;
+import javax.validation.*;
+
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+
+/**
+ * 示例分类 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface Demo02CategoryService {
+
+ /**
+ * 创建示例分类
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createDemo02Category(@Valid Demo02CategorySaveReqVO createReqVO);
+
+ /**
+ * 更新示例分类
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDemo02Category(@Valid Demo02CategorySaveReqVO updateReqVO);
+
+ /**
+ * 删除示例分类
+ *
+ * @param id 编号
+ */
+ void deleteDemo02Category(Long id);
+
+ /**
+ * 获得示例分类
+ *
+ * @param id 编号
+ * @return 示例分类
+ */
+ Demo02CategoryDO getDemo02Category(Long id);
+
+ /**
+ * 获得示例分类列表
+ *
+ * @param listReqVO 查询条件
+ * @return 示例分类列表
+ */
+ List getDemo02CategoryList(Demo02CategoryListReqVO listReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java
new file mode 100644
index 0000000000..7ab4ec56be
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java
@@ -0,0 +1,134 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo02;
+
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategoryListReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo02.vo.Demo02CategorySaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02.Demo02CategoryDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo02.Demo02CategoryMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
+
+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 Demo02CategoryServiceImpl implements Demo02CategoryService {
+
+ @Resource
+ private Demo02CategoryMapper demo02CategoryMapper;
+
+ @Override
+ public Long createDemo02Category(Demo02CategorySaveReqVO createReqVO) {
+ // 校验父级编号的有效性
+ validateParentDemo02Category(null, createReqVO.getParentId());
+ // 校验名字的唯一性
+ validateDemo02CategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
+
+ // 插入
+ Demo02CategoryDO demo02Category = BeanUtils.toBean(createReqVO, Demo02CategoryDO.class);
+ demo02CategoryMapper.insert(demo02Category);
+ // 返回
+ return demo02Category.getId();
+ }
+
+ @Override
+ public void updateDemo02Category(Demo02CategorySaveReqVO updateReqVO) {
+ // 校验存在
+ validateDemo02CategoryExists(updateReqVO.getId());
+ // 校验父级编号的有效性
+ validateParentDemo02Category(updateReqVO.getId(), updateReqVO.getParentId());
+ // 校验名字的唯一性
+ validateDemo02CategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
+
+ // 更新
+ Demo02CategoryDO updateObj = BeanUtils.toBean(updateReqVO, Demo02CategoryDO.class);
+ demo02CategoryMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteDemo02Category(Long id) {
+ // 校验存在
+ validateDemo02CategoryExists(id);
+ // 校验是否有子示例分类
+ if (demo02CategoryMapper.selectCountByParentId(id) > 0) {
+ throw exception(DEMO02_CATEGORY_EXITS_CHILDREN);
+ }
+ // 删除
+ demo02CategoryMapper.deleteById(id);
+ }
+
+ private void validateDemo02CategoryExists(Long id) {
+ if (demo02CategoryMapper.selectById(id) == null) {
+ throw exception(DEMO02_CATEGORY_NOT_EXISTS);
+ }
+ }
+
+ private void validateParentDemo02Category(Long id, Long parentId) {
+ if (parentId == null || Demo02CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ return;
+ }
+ // 1. 不能设置自己为父示例分类
+ if (Objects.equals(id, parentId)) {
+ throw exception(DEMO02_CATEGORY_PARENT_ERROR);
+ }
+ // 2. 父示例分类不存在
+ Demo02CategoryDO parentDemo02Category = demo02CategoryMapper.selectById(parentId);
+ if (parentDemo02Category == null) {
+ throw exception(DEMO02_CATEGORY_PARENT_NOT_EXITS);
+ }
+ // 3. 递归校验父示例分类,如果父示例分类是自己的子示例分类,则报错,避免形成环路
+ if (id == null) { // id 为空,说明新增,不需要考虑环路
+ return;
+ }
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ // 3.1 校验环路
+ parentId = parentDemo02Category.getParentId();
+ if (Objects.equals(id, parentId)) {
+ throw exception(DEMO02_CATEGORY_PARENT_IS_CHILD);
+ }
+ // 3.2 继续递归下一级父示例分类
+ if (parentId == null || Demo02CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
+ break;
+ }
+ parentDemo02Category = demo02CategoryMapper.selectById(parentId);
+ if (parentDemo02Category == null) {
+ break;
+ }
+ }
+ }
+
+ private void validateDemo02CategoryNameUnique(Long id, Long parentId, String name) {
+ Demo02CategoryDO demo02Category = demo02CategoryMapper.selectByParentIdAndName(parentId, name);
+ if (demo02Category == null) {
+ return;
+ }
+ // 如果 id 为空,说明不用比较是否为相同 id 的示例分类
+ if (id == null) {
+ throw exception(DEMO02_CATEGORY_NAME_DUPLICATE);
+ }
+ if (!Objects.equals(demo02Category.getId(), id)) {
+ throw exception(DEMO02_CATEGORY_NAME_DUPLICATE);
+ }
+ }
+
+ @Override
+ public Demo02CategoryDO getDemo02Category(Long id) {
+ return demo02CategoryMapper.selectById(id);
+ }
+
+ @Override
+ public List getDemo02CategoryList(Demo02CategoryListReqVO listReqVO) {
+ return demo02CategoryMapper.selectList(listReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java
new file mode 100644
index 0000000000..c9a4c592ee
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java
@@ -0,0 +1,158 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 学生 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface Demo03StudentService {
+
+ /**
+ * 创建学生
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createDemo03Student(@Valid Demo03StudentSaveReqVO createReqVO);
+
+ /**
+ * 更新学生
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDemo03Student(@Valid Demo03StudentSaveReqVO updateReqVO);
+
+ /**
+ * 删除学生
+ *
+ * @param id 编号
+ */
+ void deleteDemo03Student(Long id);
+
+ /**
+ * 获得学生
+ *
+ * @param id 编号
+ * @return 学生
+ */
+ Demo03StudentDO getDemo03Student(Long id);
+
+ /**
+ * 获得学生分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 学生分页
+ */
+ PageResult getDemo03StudentPage(Demo03StudentPageReqVO pageReqVO);
+
+
+ // ==================== 子表(学生课程) ====================
+
+ /**
+ * 获得学生课程列表
+ *
+ * @param studentId 学生编号
+ * @return 学生课程列表
+ */
+ List getDemo03CourseListByStudentId(Long studentId);
+
+ /**
+ * 获得学生课程分页
+ *
+ * @param pageReqVO 分页查询
+ * @param studentId 学生编号
+ * @return 学生课程分页
+ */
+ PageResult getDemo03CoursePage(PageParam pageReqVO, Long studentId);
+
+ /**
+ * 创建学生课程
+ *
+ * @param demo03Course 创建信息
+ * @return 编号
+ */
+ Long createDemo03Course(@Valid Demo03CourseDO demo03Course);
+
+ /**
+ * 更新学生课程
+ *
+ * @param demo03Course 更新信息
+ */
+ void updateDemo03Course(@Valid Demo03CourseDO demo03Course);
+
+ /**
+ * 删除学生课程
+ *
+ * @param id 编号
+ */
+ void deleteDemo03Course(Long id);
+
+ /**
+ * 获得学生课程
+ *
+ * @param id 编号
+ * @return 学生课程
+ */
+ Demo03CourseDO getDemo03Course(Long id);
+
+ // ==================== 子表(学生班级) ====================
+
+ /**
+ * 获得学生班级
+ *
+ * @param studentId 学生编号
+ * @return 学生班级
+ */
+ Demo03GradeDO getDemo03GradeByStudentId(Long studentId);
+
+ /**
+ * 获得学生班级分页
+ *
+ * @param pageReqVO 分页查询
+ * @param studentId 学生编号
+ * @return 学生班级分页
+ */
+ PageResult getDemo03GradePage(PageParam pageReqVO, Long studentId);
+
+ /**
+ * 创建学生班级
+ *
+ * @param demo03Grade 创建信息
+ * @return 编号
+ */
+ Long createDemo03Grade(@Valid Demo03GradeDO demo03Grade);
+
+ /**
+ * 更新学生班级
+ *
+ * @param demo03Grade 更新信息
+ */
+ void updateDemo03Grade(@Valid Demo03GradeDO demo03Grade);
+
+ /**
+ * 删除学生班级
+ *
+ * @param id 编号
+ */
+ void deleteDemo03Grade(Long id);
+
+ /**
+ * 获得学生班级
+ *
+ * @param id 编号
+ * @return 学生班级
+ */
+ Demo03GradeDO getDemo03Grade(Long id);
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java
new file mode 100644
index 0000000000..7b69e4ac76
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java
@@ -0,0 +1,217 @@
+package cn.iocoder.yudao.module.infra.service.demo.demo03;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentPageReqVO;
+import cn.iocoder.yudao.module.infra.controller.admin.demo.demo03.vo.Demo03StudentSaveReqVO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03CourseDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03GradeDO;
+import cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03.Demo03StudentDO;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03CourseMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03GradeMapper;
+import cn.iocoder.yudao.module.infra.dal.mysql.demo.demo03.Demo03StudentMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
+
+/**
+ * 学生 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class Demo03StudentServiceImpl implements Demo03StudentService {
+
+ @Resource
+ private Demo03StudentMapper demo03StudentMapper;
+ @Resource
+ private Demo03CourseMapper demo03CourseMapper;
+ @Resource
+ private Demo03GradeMapper demo03GradeMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createDemo03Student(Demo03StudentSaveReqVO createReqVO) {
+ // 插入
+ Demo03StudentDO demo03Student = BeanUtils.toBean(createReqVO, Demo03StudentDO.class);
+ demo03StudentMapper.insert(demo03Student);
+
+ // 插入子表
+ createDemo03CourseList(demo03Student.getId(), createReqVO.getDemo03Courses());
+ createDemo03Grade(demo03Student.getId(), createReqVO.getDemo03Grade());
+ // 返回
+ return demo03Student.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateDemo03Student(Demo03StudentSaveReqVO updateReqVO) {
+ // 校验存在
+ validateDemo03StudentExists(updateReqVO.getId());
+ // 更新
+ Demo03StudentDO updateObj = BeanUtils.toBean(updateReqVO, Demo03StudentDO.class);
+ demo03StudentMapper.updateById(updateObj);
+
+ // 更新子表
+ updateDemo03CourseList(updateReqVO.getId(), updateReqVO.getDemo03Courses());
+ updateDemo03Grade(updateReqVO.getId(), updateReqVO.getDemo03Grade());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteDemo03Student(Long id) {
+ // 校验存在
+ validateDemo03StudentExists(id);
+ // 删除
+ demo03StudentMapper.deleteById(id);
+
+ // 删除子表
+ deleteDemo03CourseByStudentId(id);
+ deleteDemo03GradeByStudentId(id);
+ }
+
+ private void validateDemo03StudentExists(Long id) {
+ if (demo03StudentMapper.selectById(id) == null) {
+ throw exception(DEMO03_STUDENT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public Demo03StudentDO getDemo03Student(Long id) {
+ return demo03StudentMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getDemo03StudentPage(Demo03StudentPageReqVO pageReqVO) {
+ return demo03StudentMapper.selectPage(pageReqVO);
+ }
+
+ // ==================== 子表(学生课程) ====================
+
+ @Override
+ public List getDemo03CourseListByStudentId(Long studentId) {
+ return demo03CourseMapper.selectListByStudentId(studentId);
+ }
+
+ private void createDemo03CourseList(Long studentId, List list) {
+ if (list != null) {
+ list.forEach(o -> o.setStudentId(studentId));
+ }
+ demo03CourseMapper.insertBatch(list);
+ }
+
+ private void updateDemo03CourseList(Long studentId, List list) {
+ deleteDemo03CourseByStudentId(studentId);
+ list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+ createDemo03CourseList(studentId, list);
+ }
+
+ private void deleteDemo03CourseByStudentId(Long studentId) {
+ demo03CourseMapper.deleteByStudentId(studentId);
+ }
+
+ @Override
+ public PageResult getDemo03CoursePage(PageParam pageReqVO, Long studentId) {
+ return demo03CourseMapper.selectPage(pageReqVO, studentId);
+ }
+
+ @Override
+ public Long createDemo03Course(Demo03CourseDO demo03Course) {
+ demo03CourseMapper.insert(demo03Course);
+ return demo03Course.getId();
+ }
+
+ @Override
+ public void updateDemo03Course(Demo03CourseDO demo03Course) {
+ demo03CourseMapper.updateById(demo03Course);
+ }
+
+ @Override
+ public void deleteDemo03Course(Long id) {
+ demo03CourseMapper.deleteById(id);
+ }
+
+ @Override
+ public Demo03CourseDO getDemo03Course(Long id) {
+ return demo03CourseMapper.selectById(id);
+ }
+
+ // ==================== 子表(学生班级) ====================
+
+ @Override
+ public Demo03GradeDO getDemo03GradeByStudentId(Long studentId) {
+ return demo03GradeMapper.selectByStudentId(studentId);
+ }
+
+ private void createDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {
+ if (demo03Grade == null) {
+ return;
+ }
+ demo03Grade.setStudentId(studentId);
+ demo03GradeMapper.insert(demo03Grade);
+ }
+
+ private void updateDemo03Grade(Long studentId, Demo03GradeDO demo03Grade) {
+ if (demo03Grade == null) {
+ return;
+ }
+ demo03Grade.setStudentId(studentId);
+ demo03Grade.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+ demo03GradeMapper.insertOrUpdate(demo03Grade);
+ }
+
+ private void deleteDemo03GradeByStudentId(Long studentId) {
+ demo03GradeMapper.deleteByStudentId(studentId);
+ }
+
+ @Override
+ public PageResult getDemo03GradePage(PageParam pageReqVO, Long studentId) {
+ return demo03GradeMapper.selectPage(pageReqVO, studentId);
+ }
+
+ @Override
+ public Long createDemo03Grade(Demo03GradeDO demo03Grade) {
+ // 校验是否已经存在
+ if (demo03GradeMapper.selectByStudentId(demo03Grade.getStudentId()) != null) {
+ throw exception(DEMO03_GRADE_EXISTS);
+ }
+ demo03GradeMapper.insert(demo03Grade);
+ return demo03Grade.getId();
+ }
+
+ @Override
+ public void updateDemo03Grade(Demo03GradeDO demo03Grade) {
+ // 校验存在
+ validateDemo03GradeExists(demo03Grade.getId());
+ // 更新
+ demo03GradeMapper.updateById(demo03Grade);
+ }
+
+ @Override
+ public void deleteDemo03Grade(Long id) {
+ // 校验存在
+ validateDemo03GradeExists(id);
+ // 删除
+ demo03GradeMapper.deleteById(id);
+ }
+
+ @Override
+ public Demo03GradeDO getDemo03Grade(Long id) {
+ return demo03GradeMapper.selectById(id);
+ }
+
+ private void validateDemo03GradeExists(Long id) {
+ if (demo03GradeMapper.selectById(id) == null) {
+ throw exception(DEMO03_GRADE_NOT_EXISTS);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoService.java
deleted file mode 100755
index 8b33f2824a..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoService.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package cn.iocoder.yudao.module.infra.service.test;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
-import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
-
-import javax.validation.Valid;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * 字典类型 Service 接口
- *
- * @author 芋道源码
- */
-public interface TestDemoService {
-
- /**
- * 创建字典类型
- *
- * @param createReqVO 创建信息
- * @return 编号
- */
- Long createTestDemo(@Valid TestDemoCreateReqVO createReqVO);
-
- /**
- * 更新字典类型
- *
- * @param updateReqVO 更新信息
- */
- void updateTestDemo(@Valid TestDemoUpdateReqVO updateReqVO);
-
- /**
- * 删除字典类型
- *
- * @param id 编号
- */
- void deleteTestDemo(Long id);
-
- /**
- * 获得字典类型
- *
- * @param id 编号
- * @return 字典类型
- */
- TestDemoDO getTestDemo(Long id);
-
- /**
- * 获得字典类型列表
- *
- * @param ids 编号
- * @return 字典类型列表
- */
- List getTestDemoList(Collection ids);
-
- /**
- * 获得字典类型分页
- *
- * @param pageReqVO 分页查询
- * @return 字典类型分页
- */
- PageResult getTestDemoPage(TestDemoPageReqVO pageReqVO);
-
- /**
- * 获得字典类型列表, 用于 Excel 导出
- *
- * @param exportReqVO 查询条件
- * @return 字典类型列表
- */
- List getTestDemoList(TestDemoExportReqVO exportReqVO);
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoServiceImpl.java
deleted file mode 100755
index f476b5a72a..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoServiceImpl.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package cn.iocoder.yudao.module.infra.service.test;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoCreateReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoExportReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoPageReqVO;
-import cn.iocoder.yudao.module.infra.controller.admin.test.vo.TestDemoUpdateReqVO;
-import cn.iocoder.yudao.module.infra.convert.test.TestDemoConvert;
-import cn.iocoder.yudao.module.infra.dal.dataobject.test.TestDemoDO;
-import cn.iocoder.yudao.module.infra.dal.mysql.test.TestDemoMapper;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import javax.annotation.Resource;
-import java.util.Collection;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.TEST_DEMO_NOT_EXISTS;
-
-/**
- * 字典类型 Service 实现类
- *
- * @author 芋道源码
- */
-@Service
-@Validated
-public class TestDemoServiceImpl implements TestDemoService {
-
- @Resource
- private TestDemoMapper testDemoMapper;
-
- @Override
- public Long createTestDemo(TestDemoCreateReqVO createReqVO) {
- // 插入
- TestDemoDO testDemo = TestDemoConvert.INSTANCE.convert(createReqVO);
- testDemoMapper.insert(testDemo);
- // 返回
- return testDemo.getId();
- }
-
- @Override
- @CacheEvict(value = "test", key = "#updateReqVO.id")
- public void updateTestDemo(TestDemoUpdateReqVO updateReqVO) {
- // 校验存在
- validateTestDemoExists(updateReqVO.getId());
- // 更新
- TestDemoDO updateObj = TestDemoConvert.INSTANCE.convert(updateReqVO);
- testDemoMapper.updateById(updateObj);
- }
-
- @Override
- @CacheEvict(value = "test", key = "#id")
- public void deleteTestDemo(Long id) {
- // 校验存在
- validateTestDemoExists(id);
- // 删除
- testDemoMapper.deleteById(id);
- }
-
- private void validateTestDemoExists(Long id) {
- if (testDemoMapper.selectById(id) == null) {
- throw exception(TEST_DEMO_NOT_EXISTS);
- }
- }
-
- @Override
- @Cacheable(cacheNames = "test", key = "#id")
- public TestDemoDO getTestDemo(Long id) {
- return testDemoMapper.selectById(id);
- }
-
- @Override
- public List getTestDemoList(Collection ids) {
- return testDemoMapper.selectBatchIds(ids);
- }
-
- @Override
- public PageResult getTestDemoPage(TestDemoPageReqVO pageReqVO) {
-// testDemoMapper.selectList2();
- return testDemoMapper.selectPage(pageReqVO);
- }
-
- @Override
- public List getTestDemoList(TestDemoExportReqVO exportReqVO) {
- return testDemoMapper.selectList(exportReqVO);
- }
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm
index a8c1f62c5d..4c047c9487 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm
@@ -15,8 +15,10 @@ import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
+import ${PageParamClassName};
import ${PageResultClassName};
import ${CommonResultClassName};
+import ${BeanUtils};
import static ${CommonResultClassName}.success;
import ${ExcelUtilsClassName};
@@ -26,7 +28,10 @@ import static ${OperateTypeEnumClassName}.*;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
-import ${basePackage}.module.${table.moduleName}.convert.${table.businessName}.${table.className}Convert;
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
+#end
import ${basePackage}.module.${table.moduleName}.service.${table.businessName}.${table.className}Service;
@Tag(name = "${sceneEnum.name} - ${table.classComment}")
@@ -41,17 +46,19 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
@PostMapping("/create")
@Operation(summary = "创建${table.classComment}")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")#end
-
- public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO) {
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")
+#end
+ public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO) {
return success(${classNameVar}Service.create${simpleClassName}(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新${table.classComment}")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")#end
-
- public CommonResult update${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO) {
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")
+#end
+ public CommonResult update${simpleClassName}(@Valid @RequestBody ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO) {
${classNameVar}Service.update${simpleClassName}(updateReqVO);
return success(true);
}
@@ -59,8 +66,9 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
@DeleteMapping("/delete")
@Operation(summary = "删除${table.classComment}")
@Parameter(name = "id", description = "编号", required = true)
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")#end
-
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
+#end
public CommonResult delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
${classNameVar}Service.delete${simpleClassName}(id);
return success(true);
@@ -69,43 +77,157 @@ public class ${sceneEnum.prefixClass}${table.className}Controller {
@GetMapping("/get")
@Operation(summary = "获得${table.classComment}")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
-
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
public CommonResult<${sceneEnum.prefixClass}${table.className}RespVO> get${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
${table.className}DO ${classNameVar} = ${classNameVar}Service.get${simpleClassName}(id);
- return success(${table.className}Convert.INSTANCE.convert(${classNameVar}));
- }
-
- @GetMapping("/list")
- @Operation(summary = "获得${table.classComment}列表")
- @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
-
- public CommonResult> get${simpleClassName}List(@RequestParam("ids") Collection<${primaryColumn.javaType}> ids) {
- List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(ids);
- return success(${table.className}Convert.INSTANCE.convertList(list));
+ return success(BeanUtils.toBean(${classNameVar}, ${sceneEnum.prefixClass}${table.className}RespVO.class));
}
+#if ( $table.templateType != 2 )
@GetMapping("/page")
@Operation(summary = "获得${table.classComment}分页")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")#end
-
- public CommonResult> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageVO) {
- PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageVO);
- return success(${table.className}Convert.INSTANCE.convertPage(pageResult));
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
+ PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, ${sceneEnum.prefixClass}${table.className}RespVO.class));
}
+## 特殊:树表专属逻辑(树不需要分页接口)
+#else
+ @GetMapping("/list")
+ @Operation(summary = "获得${table.classComment}列表")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult> get${simpleClassName}List(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
+ List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
+ return success(BeanUtils.toBean(list, ${sceneEnum.prefixClass}${table.className}RespVO.class));
+ }
+
+#end
@GetMapping("/export-excel")
@Operation(summary = "导出${table.classComment} Excel")
-#if ($sceneEnum.scene == 1) @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')")#end
-
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')")
+#end
@OperateLog(type = EXPORT)
- public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO,
+#if ( $table.templateType != 2 )
+ public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
- List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(exportReqVO);
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO).getList();
// 导出 Excel
- List<${sceneEnum.prefixClass}${table.className}ExcelVO> datas = ${table.className}Convert.INSTANCE.convertList02(list);
- ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${sceneEnum.prefixClass}${table.className}ExcelVO.class, datas);
+ ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${table.className}RespVO.class,
+ BeanUtils.toBean(list, ${table.className}RespVO.class));
+ }
+## 特殊:树表专属逻辑(树不需要分页接口)
+#else
+ public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO,
+ HttpServletResponse response) throws IOException {
+ List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${table.className}RespVO.class,
+ BeanUtils.toBean(list, ${table.className}RespVO.class));
+ }
+#end
+
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+#set ($subClassNameVar = $subClassNameVars.get($index))
+ // ==================== 子表($subTable.classComment) ====================
+
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+ @GetMapping("/${subSimpleClassName_strikeCase}/page")
+ @Operation(summary = "获得${subTable.classComment}分页")
+ @Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult> get${subSimpleClassName}Page(PageParam pageReqVO,
+ @RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return success(${classNameVar}Service.get${subSimpleClassName}Page(pageReqVO, ${subJoinColumn.javaField}));
}
-}
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+ #if ( $subTable.subJoinMany )
+ @GetMapping("/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}")
+ @Operation(summary = "获得${subTable.classComment}列表")
+ @Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult> get${subSimpleClassName}ListBy${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return success(${classNameVar}Service.get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}));
+ }
+
+ #else
+ @GetMapping("/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}")
+ @Operation(summary = "获得${subTable.classComment}")
+ @Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult<${subTable.className}DO> get${subSimpleClassName}By${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return success(${classNameVar}Service.get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}));
+ }
+
+ #end
+#end
+## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
+#if ( $table.templateType == 11 )
+ @PostMapping("/${subSimpleClassName_strikeCase}/create")
+ @Operation(summary = "创建${subTable.classComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")
+#end
+ public CommonResult<${subPrimaryColumn.javaType}> create${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {
+ return success(${classNameVar}Service.create${subSimpleClassName}(${subClassNameVar}));
+ }
+
+ @PutMapping("/${subSimpleClassName_strikeCase}/update")
+ @Operation(summary = "更新${subTable.classComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")
+#end
+ public CommonResult update${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {
+ ${classNameVar}Service.update${subSimpleClassName}(${subClassNameVar});
+ return success(true);
+ }
+
+ @DeleteMapping("/${subSimpleClassName_strikeCase}/delete")
+ @Parameter(name = "id", description = "编号", required = true)
+ @Operation(summary = "删除${subTable.classComment}")
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
+#end
+ public CommonResult delete${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) {
+ ${classNameVar}Service.delete${subSimpleClassName}(id);
+ return success(true);
+ }
+
+ @GetMapping("/${subSimpleClassName_strikeCase}/get")
+ @Operation(summary = "获得${subTable.classComment}")
+ @Parameter(name = "id", description = "编号", required = true)
+#if ($sceneEnum.scene == 1)
+ @PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
+#end
+ public CommonResult<${subTable.className}DO> get${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) {
+ return success(${classNameVar}Service.get${subSimpleClassName}(id));
+ }
+
+#end
+#end
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm
deleted file mode 100644
index 98b09f2728..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm
+++ /dev/null
@@ -1,13 +0,0 @@
-## 提供给 baseVO、createVO、updateVO 生成字段
- @Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
-#if (!${column.nullable})## 判断 @NotEmpty 和 @NotNull 注解
-#if (${field.fieldType} == 'String')
- @NotEmpty(message = "${column.columnComment}不能为空")
-#else
- @NotNull(message = "${column.columnComment}不能为空")
-#end
-#end
-#if (${column.javaType} == "LocalDateTime")## 时间类型
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
-#end
- private ${column.javaType} ${column.javaField};
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm
deleted file mode 100644
index cc612e337f..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm
+++ /dev/null
@@ -1,39 +0,0 @@
-package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-#foreach ($column in $columns)
-#if (${column.javaType} == "BigDecimal")
-import java.math.BigDecimal;
-#end
-#if (${column.javaType} == "LocalDateTime")
-import java.time.LocalDateTime;
-#end
-#end
-import javax.validation.constraints.*;
-## 处理 Date 字段的引入
-#foreach ($column in $columns)
-#if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult}
- && ${column.javaType} == "LocalDateTime")## 时间类型
-import org.springframework.format.annotation.DateTimeFormat;
-
-import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-#break
-#end
-#end
-
-/**
- * ${table.classComment} Base VO,提供给添加、修改、详细的子 VO 使用
- * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
- */
-@Data
-public class ${sceneEnum.prefixClass}${table.className}BaseVO {
-
-#foreach ($column in $columns)
-#if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult})##通用操作
- #parse("codegen/java/controller/vo/_column.vm")
-
-#end
-#end
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm
deleted file mode 100644
index d4f6f8ea98..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm
+++ /dev/null
@@ -1,30 +0,0 @@
-package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
-
-import lombok.*;
-import java.util.*;
-import io.swagger.v3.oas.annotations.media.Schema;
-import javax.validation.constraints.*;
-## 处理 Date 字段的引入
-#foreach ($column in $columns)
-#if (${column.createOperation} && (!${column.updateOperation} || !${column.listOperationResult})
- && ${column.javaType} == "LocalDateTime")## 时间类型
-import org.springframework.format.annotation.DateTimeFormat;
-import java.time.LocalDateTime;
-import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-#break
-#end
-#end
-
-@Schema(description = "${sceneEnum.name} - ${table.classComment}创建 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class ${sceneEnum.prefixClass}${table.className}CreateReqVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
-
-#foreach ($column in $columns)
-#if (${column.createOperation} && (!${column.updateOperation} || !${column.listOperationResult}))##不是通用字段
- #parse("codegen/java/controller/vo/_column.vm")
-
-#end
-#end
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm
deleted file mode 100644
index 15c6660c86..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm
+++ /dev/null
@@ -1,45 +0,0 @@
-package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-#foreach ($column in $columns)
-#if (${column.javaType} == "BigDecimal")
-import java.math.BigDecimal;
-#end
-#if (${column.javaType} == "LocalDateTime")
-import java.time.LocalDateTime;
-#end
-#end
-
-import com.alibaba.excel.annotation.ExcelProperty;
-#foreach ($column in $columns)
-#if ("$!column.dictType" != "")## 有设置数据字典
-import ${DictFormatClassName};
-import ${DictConvertClassName};
-
-#break
-#end
-#end
-
-/**
- * ${table.classComment} Excel VO
- *
- * @author ${table.author}
- */
-@Data
-public class ${sceneEnum.prefixClass}${table.className}ExcelVO {
-
-#foreach ($column in $columns)
- #if (${column.listOperationResult})##返回字段
- #if ("$!column.dictType" != "")##处理枚举值
- @ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class)
- @DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
- #else
- @ExcelProperty("${column.columnComment}")
- #end
- private ${column.javaType} ${column.javaField};
-
- #end
-#end
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm
similarity index 84%
rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm
rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm
index d3ef4aacde..46b6a259d1 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm
@@ -4,9 +4,15 @@ import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import ${PageParamClassName};
-## 处理 Date 字段的引入
#foreach ($column in $columns)
-#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")## 时间类型
+#if (${column.javaType} == "BigDecimal")
+import java.math.BigDecimal;
+#break
+#end
+#end
+## 处理 LocalDateTime 字段的引入
+#foreach ($column in $columns)
+#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
@@ -20,9 +26,9 @@ import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end;
#end
-@Schema(description = "${sceneEnum.name} - ${table.classComment} Excel 导出 Request VO,参数和 ${table.className}PageReqVO 是一致的")
+@Schema(description = "${sceneEnum.name} - ${table.classComment}列表 Request VO")
@Data
-public class ${sceneEnum.prefixClass}${table.className}ExportReqVO {
+public class ${sceneEnum.prefixClass}${table.className}ListReqVO {
#foreach ($column in $columns)
#if (${column.listOperation})##查询操作
@@ -36,4 +42,4 @@ public class ${sceneEnum.prefixClass}${table.className}ExportReqVO {
#end
#end
-}
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm
index 6f9868da3c..003bac902f 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm
@@ -4,9 +4,15 @@ import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import ${PageParamClassName};
-## 处理 Date 字段的引入
#foreach ($column in $columns)
-#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")## 时间类型
+#if (${column.javaType} == "BigDecimal")
+import java.math.BigDecimal;
+#break
+#end
+#end
+## 处理 LocalDateTime 字段的引入
+#foreach ($column in $columns)
+#if (${column.listOperationCondition} && ${column.javaType} == "LocalDateTime")
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@@ -38,4 +44,4 @@ public class ${sceneEnum.prefixClass}${table.className}PageReqVO extends PagePar
#end
#end
-}
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm
index 517d8bcd5a..54c16671d6 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm
@@ -2,24 +2,53 @@ package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePac
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
+import java.util.*;
+## 处理 BigDecimal 字段的引入
+import java.util.*;
#foreach ($column in $columns)
-#if (${column.javaType} == "LocalDateTime")
+#if (${column.javaType} == "BigDecimal")
+import java.math.BigDecimal;
+#break
+#end
+#end
+## 处理 LocalDateTime 字段的引入
+#foreach ($column in $columns)
+#if (${column.listOperationResult} && ${column.javaType} == "LocalDateTime")
+import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
#break
#end
#end
+## 处理 Excel 导出
+import com.alibaba.excel.annotation.*;
+#foreach ($column in $columns)
+#if ("$!column.dictType" != "")## 有设置数据字典
+import ${DictFormatClassName};
+import ${DictConvertClassName};
+#break
+#end
+#end
@Schema(description = "${sceneEnum.name} - ${table.classComment} Response VO")
@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class ${sceneEnum.prefixClass}${table.className}RespVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
+@ExcelIgnoreUnannotated
+public class ${sceneEnum.prefixClass}${table.className}RespVO {
+## 逐个处理字段
#foreach ($column in $columns)
-#if (${column.listOperationResult} && (!${column.createOperation} || !${column.updateOperation}))##不是通用字段
+#if (${column.listOperationResult})
+## 1. 处理 Swagger 注解
@Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
+## 2. 处理 Excel 导出
+#if ("$!column.dictType" != "")##处理枚举值
+ @ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class)
+ @DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
+#else
+ @ExcelProperty("${column.columnComment}")
+#end
+## 3. 处理字段定义
private ${column.javaType} ${column.javaField};
#end
#end
-}
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/saveReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/saveReqVO.vm
new file mode 100644
index 0000000000..89829cc993
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/saveReqVO.vm
@@ -0,0 +1,65 @@
+package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+## 处理 BigDecimal 字段的引入
+import java.util.*;
+#foreach ($column in $columns)
+#if (${column.javaType} == "BigDecimal")
+import java.math.BigDecimal;
+#break
+#end
+#end
+## 处理 LocalDateTime 字段的引入
+#foreach ($column in $columns)
+#if ((${column.createOperation} || ${column.updateOperation}) && ${column.javaType} == "LocalDateTime")
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+#break
+#end
+#end
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
+#end
+
+@Schema(description = "${sceneEnum.name} - ${table.classComment}新增/修改 Request VO")
+@Data
+public class ${sceneEnum.prefixClass}${table.className}SaveReqVO {
+
+## 逐个处理字段
+#foreach ($column in $columns)
+#if (${column.createOperation} || ${column.updateOperation})
+## 1. 处理 Swagger 注解
+ @Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
+## 2. 处理 Validator 参数校验
+#if (!${column.nullable} && !${column.primaryKey})
+#if (${column.javaType} == 'String')
+ @NotEmpty(message = "${column.columnComment}不能为空")
+#else
+ @NotNull(message = "${column.columnComment}不能为空")
+#end
+#end
+## 3. 处理字段定义
+ private ${column.javaType} ${column.javaField};
+
+#end
+#end
+## 特殊:主子表专属逻辑(非 ERP 模式)
+#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+ #if ( $subTable.subJoinMany)
+ @Schema(description = "${subTable.classComment}列表")
+ private List<${subTable.className}DO> ${subClassNameVars.get($index)}s;
+
+ #else
+ @Schema(description = "${subTable.classComment}")
+ private ${subTable.className}DO ${subClassNameVars.get($index)};
+
+ #end
+#end
+#end
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm
deleted file mode 100644
index 48d74321d5..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm
+++ /dev/null
@@ -1,30 +0,0 @@
-package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-import javax.validation.constraints.*;
-## 处理 Date 字段的引入
-#foreach ($column in $columns)
-#if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult})
- && ${column.javaType} == "LocalDateTime")## 时间类型
-import org.springframework.format.annotation.DateTimeFormat;
-import java.time.LocalDateTime;
-import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-#break
-#end
-#end
-
-@Schema(description = "${sceneEnum.name} - ${table.classComment}更新 Request VO")
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class ${sceneEnum.prefixClass}${table.className}UpdateReqVO extends ${sceneEnum.prefixClass}${table.className}BaseVO {
-
-#foreach ($column in $columns)
-#if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult}))##不是通用字段
- #parse("codegen/java/controller/vo/_column.vm")
-
-#end
-#end
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/convert/convert.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/convert/convert.vm
deleted file mode 100644
index 6176e0f548..0000000000
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/convert/convert.vm
+++ /dev/null
@@ -1,34 +0,0 @@
-package ${basePackage}.module.${table.moduleName}.convert.${table.businessName};
-
-import java.util.*;
-
-import ${PageResultClassName};
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
-import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
-
-/**
- * ${table.classComment} Convert
- *
- * @author ${table.author}
- */
-@Mapper
-public interface ${table.className}Convert {
-
- ${table.className}Convert INSTANCE = Mappers.getMapper(${table.className}Convert.class);
-
- ${table.className}DO convert(${sceneEnum.prefixClass}${table.className}CreateReqVO bean);
-
- ${table.className}DO convert(${sceneEnum.prefixClass}${table.className}UpdateReqVO bean);
-
- ${sceneEnum.prefixClass}${table.className}RespVO convert(${table.className}DO bean);
-
- List<${sceneEnum.prefixClass}${table.className}RespVO> convertList(List<${table.className}DO> list);
-
- PageResult<${sceneEnum.prefixClass}${table.className}RespVO> convertPage(PageResult<${table.className}DO> page);
-
- List<${sceneEnum.prefixClass}${table.className}ExcelVO> convertList02(List<${table.className}DO> list);
-
-}
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm
index d551d4b30c..b019d6e12f 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm
@@ -28,6 +28,11 @@ import ${BaseDOClassName};
@AllArgsConstructor
public class ${table.className}DO extends BaseDO {
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+ public static final Long ${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT = 0L;
+
+#end
#foreach ($column in $columns)
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
/**
@@ -44,4 +49,4 @@ public class ${table.className}DO extends BaseDO {
#end
#end
-}
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do_sub.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do_sub.vm
new file mode 100644
index 0000000000..16be55e8a5
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do_sub.vm
@@ -0,0 +1,49 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+package ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName};
+
+import lombok.*;
+import java.util.*;
+#foreach ($column in $subColumns)
+#if (${column.javaType} == "BigDecimal")
+import java.math.BigDecimal;
+#end
+#if (${column.javaType} == "LocalDateTime")
+import java.time.LocalDateTime;
+#end
+#end
+import com.baomidou.mybatisplus.annotation.*;
+import ${BaseDOClassName};
+
+/**
+ * ${subTable.classComment} DO
+ *
+ * @author ${subTable.author}
+ */
+@TableName("${subTable.tableName.toLowerCase()}")
+@KeySequence("${subTable.tableName.toLowerCase()}_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ${subTable.className}DO extends BaseDO {
+
+#foreach ($column in $subColumns)
+#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
+ /**
+ * ${column.columnComment}
+ #if ("$!column.dictType" != "")##处理枚举值
+ *
+ * 枚举 {@link TODO ${column.dictType} 对应的类}
+ #end
+ */
+ #if (${column.primaryKey})##处理主键
+ @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end
+ #end
+ private ${column.javaType} ${column.javaField};
+#end
+#end
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm
index 615ae337bf..b98b471fd3 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm
@@ -49,18 +49,34 @@ import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePack
@Mapper
public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> {
+## 特殊:树表专属逻辑(树不需要分页接口)
+#if ( $table.templateType != 2 )
default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>()
#listCondition()
.orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序
}
-
- default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ExportReqVO reqVO) {
+#else
+ default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<${table.className}DO>()
#listCondition()
.orderByDesc(${table.className}DO::getId));## 大多数情况下,id 倒序
}
+#end
-}
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
+#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
+ default ${table.className}DO selectBy${TreeParentJavaField}And${TreeNameJavaField}(Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {
+ return selectOne(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField}, ${table.className}DO::get${TreeNameJavaField}, ${treeNameColumn.javaField});
+ }
+
+ default Long selectCountBy${TreeParentJavaField}(${treeParentColumn.javaType} ${treeParentColumn.javaField}) {
+ return selectCount(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField});
+ }
+
+#end
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm
index d930db916c..290378d361 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm
@@ -9,4 +9,4 @@
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
-
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper_sub.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper_sub.vm
new file mode 100644
index 0000000000..e5589e99df
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper_sub.vm
@@ -0,0 +1,51 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subJoinColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+package ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName};
+
+import java.util.*;
+
+import ${PageResultClassName};
+import ${PageParamClassName};
+import ${QueryWrapperClassName};
+import ${BaseMapperClassName};
+import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * ${subTable.classComment} Mapper
+ *
+ * @author ${subTable.author}
+ */
+@Mapper
+public interface ${subTable.className}Mapper extends BaseMapperX<${subTable.className}DO> {
+
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+ default PageResult<${subTable.className}DO> selectPage(PageParam reqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return selectPage(reqVO, new LambdaQueryWrapperX<${subTable.className}DO>()
+ .eq(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField})
+ .orderByDesc(${subTable.className}DO::getId));## 大多数情况下,id 倒序
+
+ }
+
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+ #if ( $subTable.subJoinMany)
+ default List<${subTable.className}DO> selectListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return selectList(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
+ }
+
+ #else
+ default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
+ }
+
+ #end
+ #end
+ default int deleteBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return delete(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm
index a9a6daf25b..b7e21e6315 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm
@@ -1,3 +1,22 @@
// TODO 待办:请将下面的错误码复制到 yudao-module-${table.moduleName}-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
// ========== ${table.classComment} TODO 补充编号 ==========
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${table.classComment}不存在");
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子${table.classComment},无法删除");
+ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级${table.classComment}不存在");
+ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父${table.classComment}");
+ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该${treeNameColumn.columnComment}的${table.classComment}");
+ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子${table.className}为父${table.className}");
+#end
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 11 )## 特殊:ERP 情况
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))
+ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${subTable.classComment}不存在");
+#if ( !$subTable.subJoinMany )
+ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS = new ErrorCode(TODO 补充编号, "${subTable.classComment}已存在");
+#end
+#end
+#end
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm
index b8c6376988..4085889d90 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm
@@ -4,7 +4,12 @@ import java.util.*;
import javax.validation.*;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
+#end
import ${PageResultClassName};
+import ${PageParamClassName};
/**
* ${table.classComment} Service 接口
@@ -19,14 +24,14 @@ public interface ${table.className}Service {
* @param createReqVO 创建信息
* @return 编号
*/
- ${primaryColumn.javaType} create${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO);
+ ${primaryColumn.javaType} create${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO);
/**
* 更新${table.classComment}
*
* @param updateReqVO 更新信息
*/
- void update${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO);
+ void update${simpleClassName}(@Valid ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO);
/**
* 删除${table.classComment}
@@ -43,14 +48,8 @@ public interface ${table.className}Service {
*/
${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id);
- /**
- * 获得${table.classComment}列表
- *
- * @param ids 编号
- * @return ${table.classComment}列表
- */
- List<${table.className}DO> get${simpleClassName}List(Collection<${primaryColumn.javaType}> ids);
-
+## 特殊:树表专属逻辑(树不需要分页接口)
+#if ( $table.templateType != 2 )
/**
* 获得${table.classComment}分页
*
@@ -58,13 +57,91 @@ public interface ${table.className}Service {
* @return ${table.classComment}分页
*/
PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO);
-
+#else
/**
- * 获得${table.classComment}列表, 用于 Excel 导出
+ * 获得${table.classComment}列表
*
- * @param exportReqVO 查询条件
+ * @param listReqVO 查询条件
* @return ${table.classComment}列表
*/
- List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO);
+ List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO);
+#end
-}
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+#set ($subClassNameVar = $subClassNameVars.get($index))
+ // ==================== 子表($subTable.classComment) ====================
+
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+ /**
+ * 获得${subTable.classComment}分页
+ *
+ * @param pageReqVO 分页查询
+ * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
+ * @return ${subTable.classComment}分页
+ */
+ PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField});
+
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+ #if ( $subTable.subJoinMany )
+ /**
+ * 获得${subTable.classComment}列表
+ *
+ * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
+ * @return ${subTable.classComment}列表
+ */
+ List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});
+
+ #else
+ /**
+ * 获得${subTable.classComment}
+ *
+ * @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
+ * @return ${subTable.classComment}
+ */
+ ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});
+
+ #end
+#end
+## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
+#if ( $table.templateType == 11 )
+ /**
+ * 创建${subTable.classComment}
+ *
+ * @param ${subClassNameVar} 创建信息
+ * @return 编号
+ */
+ ${subPrimaryColumn.javaType} create${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});
+
+ /**
+ * 更新${subTable.classComment}
+ *
+ * @param ${subClassNameVar} 更新信息
+ */
+ void update${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});
+
+ /**
+ * 删除${subTable.classComment}
+ *
+ * @param id 编号
+ */
+ void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id);
+
+ /**
+ * 获得${subTable.classComment}
+ *
+ * @param id 编号
+ * @return ${subTable.classComment}
+ */
+ ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id);
+
+#end
+#end
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
index 9cb6e8b27d..6aa2fb2e9d 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm
@@ -3,21 +3,29 @@ package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
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 ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
+#end
import ${PageResultClassName};
+import ${PageParamClassName};
+import ${BeanUtils};
-import ${basePackage}.module.${table.moduleName}.convert.${table.businessName}.${table.className}Convert;
import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+import ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName}.${subTable.className}Mapper;
+#end
import static ${ServiceExceptionUtilClassName}.exception;
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.collection.ListUtil;
-
/**
* ${table.classComment} Service 实现类
*
@@ -29,31 +37,121 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
@Resource
private ${table.className}Mapper ${classNameVar}Mapper;
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+ @Resource
+ private ${subTable.className}Mapper ${subClassNameVars.get($index)}Mapper;
+#end
@Override
- public ${primaryColumn.javaType} create${simpleClassName}(${sceneEnum.prefixClass}${table.className}CreateReqVO createReqVO) {
+## 特殊:主子表专属逻辑(非 ERP 模式)
+#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
+ @Transactional(rollbackFor = Exception.class)
+#end
+ public ${primaryColumn.javaType} create${simpleClassName}(${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO) {
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
+#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
+ // 校验${treeParentColumn.columnComment}的有效性
+ validateParent${simpleClassName}(null, createReqVO.get${TreeParentJavaField}());
+ // 校验${treeNameColumn.columnComment}的唯一性
+ validate${simpleClassName}${TreeNameJavaField}Unique(null, createReqVO.get${TreeParentJavaField}(), createReqVO.get${TreeNameJavaField}());
+
+#end
// 插入
- ${table.className}DO ${classNameVar} = ${table.className}Convert.INSTANCE.convert(createReqVO);
+ ${table.className}DO ${classNameVar} = BeanUtils.toBean(createReqVO, ${table.className}DO.class);
${classNameVar}Mapper.insert(${classNameVar});
+## 特殊:主子表专属逻辑(非 ERP 模式)
+#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
+
+ // 插入子表
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+ #if ( $subTable.subJoinMany)
+ create${subSimpleClassName}List(${classNameVar}.getId(), createReqVO.get${subSimpleClassNames.get($index)}s());
+ #else
+ create${subSimpleClassName}(${classNameVar}.getId(), createReqVO.get${subSimpleClassNames.get($index)}());
+ #end
+#end
+#end
// 返回
return ${classNameVar}.getId();
}
@Override
- public void update${simpleClassName}(${sceneEnum.prefixClass}${table.className}UpdateReqVO updateReqVO) {
+## 特殊:主子表专属逻辑(非 ERP 模式)
+#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
+ @Transactional(rollbackFor = Exception.class)
+#end
+ public void update${simpleClassName}(${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO) {
// 校验存在
validate${simpleClassName}Exists(updateReqVO.getId());
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
+#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
+ // 校验${treeParentColumn.columnComment}的有效性
+ validateParent${simpleClassName}(updateReqVO.getId(), updateReqVO.get${TreeParentJavaField}());
+ // 校验${treeNameColumn.columnComment}的唯一性
+ validate${simpleClassName}${TreeNameJavaField}Unique(updateReqVO.getId(), updateReqVO.get${TreeParentJavaField}(), updateReqVO.get${TreeNameJavaField}());
+
+#end
// 更新
- ${table.className}DO updateObj = ${table.className}Convert.INSTANCE.convert(updateReqVO);
+ ${table.className}DO updateObj = BeanUtils.toBean(updateReqVO, ${table.className}DO.class);
${classNameVar}Mapper.updateById(updateObj);
+## 特殊:主子表专属逻辑(非 ERP 模式)
+#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11)
+
+ // 更新子表
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+ #if ( $subTable.subJoinMany)
+ update${subSimpleClassName}List(updateReqVO.getId(), updateReqVO.get${subSimpleClassNames.get($index)}s());
+ #else
+ update${subSimpleClassName}(updateReqVO.getId(), updateReqVO.get${subSimpleClassNames.get($index)}());
+ #end
+#end
+#end
}
@Override
+## 特殊:主子表专属逻辑
+#if ( $subTables && $subTables.size() > 0)
+ @Transactional(rollbackFor = Exception.class)
+#end
public void delete${simpleClassName}(${primaryColumn.javaType} id) {
// 校验存在
validate${simpleClassName}Exists(id);
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
+ // 校验是否有子${table.classComment}
+ if (${classNameVar}Mapper.selectCountBy${ParentJavaField}(id) > 0) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN);
+ }
+#end
// 删除
${classNameVar}Mapper.deleteById(id);
+## 特殊:主子表专属逻辑
+#if ( $subTables && $subTables.size() > 0)
+
+ // 删除子表
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+ delete${subSimpleClassName}By${SubJoinColumnName}(id);
+#end
+#end
}
private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) {
@@ -62,27 +160,191 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
}
}
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
+#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
+ private void validateParent${simpleClassName}(Long id, Long ${treeParentColumn.javaField}) {
+ if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {
+ return;
+ }
+ // 1. 不能设置自己为父${table.classComment}
+ if (Objects.equals(id, ${treeParentColumn.javaField})) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR);
+ }
+ // 2. 父${table.classComment}不存在
+ ${simpleClassName}DO parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});
+ if (parent${simpleClassName} == null) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS);
+ }
+ // 3. 递归校验父${table.classComment},如果父${table.classComment}是自己的子${table.classComment},则报错,避免形成环路
+ if (id == null) { // id 为空,说明新增,不需要考虑环路
+ return;
+ }
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ // 3.1 校验环路
+ ${treeParentColumn.javaField} = parent${simpleClassName}.get${TreeParentJavaField}();
+ if (Objects.equals(id, ${treeParentColumn.javaField})) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD);
+ }
+ // 3.2 继续递归下一级父${table.classComment}
+ if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {
+ break;
+ }
+ parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});
+ if (parent${simpleClassName} == null) {
+ break;
+ }
+ }
+ }
+
+ private void validate${simpleClassName}${TreeNameJavaField}Unique(Long id, Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {
+ ${simpleClassName}DO ${classNameVar} = ${classNameVar}Mapper.selectBy${TreeParentJavaField}And${TreeNameJavaField}(${treeParentColumn.javaField}, ${treeNameColumn.javaField});
+ if (${classNameVar} == null) {
+ return;
+ }
+ // 如果 id 为空,说明不用比较是否为相同 id 的${table.classComment}
+ if (id == null) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);
+ }
+ if (!Objects.equals(${classNameVar}.getId(), id)) {
+ throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);
+ }
+ }
+
+#end
@Override
public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {
return ${classNameVar}Mapper.selectById(id);
}
- @Override
- public List<${table.className}DO> get${simpleClassName}List(Collection<${primaryColumn.javaType}> ids) {
- if (CollUtil.isEmpty(ids)) {
- return ListUtil.empty();
- }
- return ${classNameVar}Mapper.selectBatchIds(ids);
- }
-
+## 特殊:树表专属逻辑(树不需要分页接口)
+#if ( $table.templateType != 2 )
@Override
public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
return ${classNameVar}Mapper.selectPage(pageReqVO);
}
-
+#else
@Override
- public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ExportReqVO exportReqVO) {
- return ${classNameVar}Mapper.selectList(exportReqVO);
+ public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
+ return ${classNameVar}Mapper.selectList(listReqVO);
+ }
+#end
+
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))
+#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+#set ($subClassNameVar = $subClassNameVars.get($index))
+ // ==================== 子表($subTable.classComment) ====================
+
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+ @Override
+ public PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return ${subClassNameVars.get($index)}Mapper.selectPage(pageReqVO, ${subJoinColumn.javaField});
}
-}
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+ #if ( $subTable.subJoinMany )
+ @Override
+ public List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return ${subClassNameVars.get($index)}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField});
+ }
+
+ #else
+ @Override
+ public ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
+ return ${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subJoinColumn.javaField});
+ }
+
+ #end
+#end
+## 情况一:MASTER_ERP 时,支持单个的新增、修改、删除操作
+#if ( $table.templateType == 11 )
+ @Override
+ public ${subPrimaryColumn.javaType} create${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {
+## 特殊:一对一时,需要保证只有一条,不能重复插入
+#if ( !$subTable.subJoinMany)
+ // 校验是否已经存在
+ if (${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subClassNameVar}.get${SubJoinColumnName}()) != null) {
+ throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS);
+ }
+ // 插入
+#end
+ ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});
+ return ${subClassNameVar}.getId();
+ }
+
+ @Override
+ public void update${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {
+ // 校验存在
+ validate${subSimpleClassName}Exists(${subClassNameVar}.getId());
+ // 更新
+ ${subClassNameVars.get($index)}Mapper.updateById(${subClassNameVar});
+ }
+
+ @Override
+ public void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id) {
+ // 校验存在
+ validate${subSimpleClassName}Exists(id);
+ // 删除
+ ${subClassNameVars.get($index)}Mapper.deleteById(id);
+ }
+
+ @Override
+ public ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id) {
+ return ${subClassNameVars.get($index)}Mapper.selectById(id);
+ }
+
+ private void validate${subSimpleClassName}Exists(${subPrimaryColumn.javaType} id) {
+ if (${subClassNameVar}Mapper.selectById(id) == null) {
+ throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS);
+ }
+ }
+
+## 情况二:非 MASTER_ERP 时,支持批量的新增、修改操作
+#else
+ #if ( $subTable.subJoinMany)
+ private void create${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {
+ list.forEach(o -> o.set$SubJoinColumnName(${subJoinColumn.javaField}));
+ ${subClassNameVars.get($index)}Mapper.insertBatch(list);
+ }
+
+ private void update${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {
+ delete${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField});
+ list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
+ create${subSimpleClassName}List(${subJoinColumn.javaField}, list);
+ }
+
+ #else
+ private void create${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
+ if (${subClassNameVar} == null) {
+ return;
+ }
+ ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});
+ ${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});
+ }
+
+ private void update${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
+ if (${subClassNameVar} == null) {
+ return;
+ }
+ ${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});
+ ${subClassNameVar}.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新
+ ${subClassNameVars.get($index)}Mapper.insertOrUpdate(${subClassNameVar});
+ }
+
+ #end
+#end
+ private void delete${subSimpleClassName}By${SubJoinColumnName}(${primaryColumn.javaType} ${subJoinColumn.javaField}) {
+ ${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}(${subJoinColumn.javaField});
+ }
+
+#end
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm
index ec1b45a96c..eeac3ce664 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm
@@ -77,15 +77,15 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
@Test
public void testCreate${simpleClassName}_success() {
// 准备参数
- ${sceneEnum.prefixClass}${table.className}CreateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}CreateReqVO.class);
+ ${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class).setId(null);
// 调用
- ${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(reqVO);
+ ${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(createReqVO);
// 断言
assertNotNull(${classNameVar}Id);
// 校验记录的属性是否正确
${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id);
- assertPojoEquals(reqVO, ${classNameVar});
+ assertPojoEquals(createReqVO, ${classNameVar}, "id");
}
@Test
@@ -94,24 +94,24 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
// 准备参数
- ${sceneEnum.prefixClass}${table.className}UpdateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}UpdateReqVO.class, o -> {
+ ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class, o -> {
o.setId(db${simpleClassName}.getId()); // 设置更新的 ID
});
// 调用
- ${classNameVar}Service.update${simpleClassName}(reqVO);
+ ${classNameVar}Service.update${simpleClassName}(updateReqVO);
// 校验是否更新正确
- ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(reqVO.getId()); // 获取最新的
- assertPojoEquals(reqVO, ${classNameVar});
+ ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(updateReqVO.getId()); // 获取最新的
+ assertPojoEquals(updateReqVO, ${classNameVar});
}
@Test
public void testUpdate${simpleClassName}_notExists() {
// 准备参数
- ${sceneEnum.prefixClass}${table.className}UpdateReqVO reqVO = randomPojo(${sceneEnum.prefixClass}${table.className}UpdateReqVO.class);
+ ${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class);
// 调用, 并断言异常
- assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(reqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
+ assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(updateReqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
}
@Test
@@ -137,6 +137,8 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
}
+## 特殊:树表专属逻辑(树不需要分页接口)
+#if ( $table.templateType != 2 )
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGet${simpleClassName}Page() {
@@ -149,11 +151,11 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
assertEquals(1, pageResult.getList().size());
assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0));
}
-
+#else
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGet${simpleClassName}List() {
- #getPageCondition("ExportReqVO")
+ #getPageCondition("ListReqVO")
// 调用
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO);
@@ -161,5 +163,6 @@ public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
assertEquals(1, list.size());
assertPojoEquals(db${simpleClassName}, list.get(0));
}
+#end
-}
+}
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm
index 20c33ad59c..b22389b0b0 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm
@@ -23,6 +23,8 @@ CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
"${column.columnName}" ${dataType} DEFAULT '',
#elseif (${column.columnName} == 'deleted')
"deleted" bit NOT NULL DEFAULT FALSE,
+ #elseif (${column.columnName} == 'tenantId')
+ "tenant_id" bigint NOT NULL DEFAULT 0,
#else
"${column.columnName.toLowerCase()}" ${dataType}#if (${column.nullable} == false) NOT NULL#end,
#end
@@ -32,4 +34,4 @@ CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
) COMMENT '${table.tableComment}';
-- 将该删表 SQL 语句,添加到 yudao-module-${table.moduleName}-biz 模块的 test/resources/sql/clean.sql 文件里
-DELETE FROM "${table.tableName}";
+DELETE FROM "${table.tableName}";
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm
index 902ca74148..41b107dbac 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm
@@ -5,7 +5,7 @@ INSERT INTO system_menu(
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
- '${simpleClassName_strikeCase}', '', '${table.moduleName}/${classNameVar}/index', 0, '${table.className}'
+ '${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
@@ -25,4 +25,4 @@ VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
'', '', '', 0
);
-#end
+#end
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
index 401796db83..c4b0b4332f 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
@@ -15,10 +15,17 @@ export interface ${simpleClassName}VO {
#end
}
-// 查询${table.classComment}列表
+#if ( $table.templateType != 2 )
+// 查询${table.classComment}分页
export const get${simpleClassName}Page = async (params) => {
return await request.get({ url: `${baseURL}/page`, params })
}
+#else
+// 查询${table.classComment}列表
+export const get${simpleClassName}List = async (params) => {
+ return await request.get({ url: `${baseURL}/list`, params })
+}
+#end
// 查询${table.classComment}详情
export const get${simpleClassName} = async (id: number) => {
@@ -44,3 +51,61 @@ export const delete${simpleClassName} = async (id: number) => {
export const export${simpleClassName} = async (params) => {
return await request.download({ url: `${baseURL}/export-excel`, params })
}
+## 特殊:主子表专属逻辑
+#foreach ($subTable in $subTables)
+#set ($index = $foreach.count - 1)
+#set ($subSimpleClassName = $subSimpleClassNames.get($index))
+#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
+#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
+#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+#set ($subClassNameVar = $subClassNameVars.get($index))
+
+// ==================== 子表($subTable.classComment) ====================
+## 情况一:MASTER_ERP 时,需要分查询页子表
+#if ( $table.templateType == 11 )
+
+// 获得${subTable.classComment}分页
+export const get${subSimpleClassName}Page = async (params) => {
+ return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/page`, params })
+}
+## 情况二:非 MASTER_ERP 时,需要列表查询子表
+#else
+ #if ( $subTable.subJoinMany )
+
+// 获得${subTable.classComment}列表
+export const get${subSimpleClassName}ListBy${SubJoinColumnName} = async (${subJoinColumn.javaField}) => {
+ return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })
+}
+ #else
+
+// 获得${subTable.classComment}
+export const get${subSimpleClassName}By${SubJoinColumnName} = async (${subJoinColumn.javaField}) => {
+ return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })
+}
+ #end
+#end
+## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
+#if ( $table.templateType == 11 )
+// 新增${subTable.classComment}
+export const create${subSimpleClassName} = async (data) => {
+ return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data })
+}
+
+// 修改${subTable.classComment}
+export const update${subSimpleClassName} = async (data) => {
+ return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data })
+}
+
+// 删除${subTable.classComment}
+export const delete${subSimpleClassName} = async (id: number) => {
+ return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id })
+}
+
+// 获得${subTable.classComment}
+export const get${subSimpleClassName} = async (id: number) => {
+ return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id })
+}
+#end
+#end
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm
new file mode 100644
index 0000000000..ed318875e4
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm
@@ -0,0 +1,205 @@
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm
new file mode 100644
index 0000000000..d8542c3d5c
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm
@@ -0,0 +1,2 @@
+## 主表的 normal 和 inner 使用相同的 form 表单
+#parse("codegen/vue3/views/components/form_sub_normal.vue.vm")
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm
new file mode 100644
index 0000000000..90df798124
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm
@@ -0,0 +1,362 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+
+#if ( $subTable.subJoinMany )## 情况一:一对多,table + form
+
+
+
+#foreach($column in $subColumns)
+ #if ($column.createOperation || $column.updateOperation)
+ #set ($dictType = $column.dictType)
+ #set ($javaField = $column.javaField)
+ #set ($javaType = $column.javaType)
+ #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+ #set ($comment = $column.columnComment)
+ #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
+ #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+ #set ($dictMethod = "getIntDictOptions")
+ #elseif ($javaType == "String")
+ #set ($dictMethod = "getStrDictOptions")
+ #elseif ($javaType == "Boolean")
+ #set ($dictMethod = "getBoolDictOptions")
+ #end
+ #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+ #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+
+
+
+
+
+
+
+ #elseif($column.htmlType == "imageUpload")## 图片上传
+
+
+
+
+
+
+
+ #elseif($column.htmlType == "fileUpload")## 文件上传
+
+
+
+
+
+
+
+ #elseif($column.htmlType == "editor")## 文本编辑器
+
+
+
+
+
+
+
+ #elseif($column.htmlType == "select")## 下拉框
+
+
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ #else##没数据字典
+
+ #end
+
+
+
+
+ #elseif($column.htmlType == "checkbox")## 多选框
+
+
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+ 请选择字典生成
+ #end
+
+
+
+
+ #elseif($column.htmlType == "radio")## 单选框
+
+
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+ 请选择字典生成
+ #end
+
+
+
+
+ #elseif($column.htmlType == "datetime")## 时间框
+
+
+
+
+
+
+
+ #elseif($column.htmlType == "textarea")## 文本框
+
+
+
+
+
+
+
+ #end
+ #end
+#end
+
+
+ —
+
+
+
+
+
+ + 添加${subTable.classComment}
+
+#else## 情况二:一对一,form
+
+#foreach($column in $subColumns)
+ #if ($column.createOperation || $column.updateOperation)
+ #set ($dictType = $column.dictType)
+ #set ($javaField = $column.javaField)
+ #set ($javaType = $column.javaType)
+ #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+ #set ($comment = $column.columnComment)
+ #set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
+ #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
+ #set ($dictMethod = "getIntDictOptions")
+ #elseif ($javaType == "String")
+ #set ($dictMethod = "getStrDictOptions")
+ #elseif ($javaType == "Boolean")
+ #set ($dictMethod = "getBoolDictOptions")
+ #end
+ #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+ #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+
+
+
+ #elseif($column.htmlType == "imageUpload")## 图片上传
+
+
+
+ #elseif($column.htmlType == "fileUpload")## 文件上传
+
+
+
+ #elseif($column.htmlType == "editor")## 文本编辑器
+
+
+
+ #elseif($column.htmlType == "select")## 下拉框
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ #else##没数据字典
+
+ #end
+
+
+ #elseif($column.htmlType == "checkbox")## 多选框
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+ 请选择字典生成
+ #end
+
+
+ #elseif($column.htmlType == "radio")## 单选框
+
+
+ #if ("" != $dictType)## 有数据字典
+
+ {{ dict.label }}
+
+ #else##没数据字典
+ 请选择字典生成
+ #end
+
+
+ #elseif($column.htmlType == "datetime")## 时间框
+
+
+
+ #elseif($column.htmlType == "textarea")## 文本框
+
+
+
+ #end
+ #end
+#end
+
+#end
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm
new file mode 100644
index 0000000000..5ad208b3b4
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm
@@ -0,0 +1,181 @@
+#set ($subTable = $subTables.get($subIndex))##当前表
+#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
+#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
+#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
+
+
+
+#if ($table.templateType == 11)
+
+ 新增
+
+#end
+
+ #foreach($column in $subColumns)
+ #if ($column.listOperationResult)
+ #set ($dictType=$column.dictType)
+ #set ($javaField = $column.javaField)
+ #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
+ #set ($comment=$column.columnComment)
+ #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
+ #elseif ($column.javaType == "LocalDateTime")## 时间类型
+
+ #elseif($column.dictType && "" != $column.dictType)## 数据字典
+
+
+
+
+
+ #else
+
+ #end
+ #end
+ #end
+ #if ($table.templateType == 11)
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+ #end
+
+ #if ($table.templateType == 11)
+
+
+ #end
+
+#if ($table.templateType == 11)
+
+ <${subSimpleClassName}Form ref="formRef" @success="getList" />
+#end
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm
new file mode 100644
index 0000000000..3fe648892a
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm
@@ -0,0 +1,4 @@
+## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
+## 1)inner 使用 list 不分页,erp 使用 page 分页
+## 2)erp 支持单个子表的新增、修改、删除,inner 不支持
+#parse("codegen/vue3/views/components/list_sub_erp.vue.vm")
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm
index 4e23f2f08d..1c15536229 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm
@@ -7,7 +7,6 @@
label-width="100px"
v-loading="formLoading"
>
-#set ($dictMethods = [])## 使用到的 dict 字典方法
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
@@ -23,31 +22,41 @@
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
- #if ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
+ #if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
+
+
+
+ #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
#elseif($column.htmlType == "imageUpload")## 图片上传
- #set ($hasImageUploadColumn = true)
-
+
#elseif($column.htmlType == "fileUpload")## 文件上传
- #set ($hasFileUploadColumn = true)
-
+
#elseif($column.htmlType == "editor")## 文本编辑器
-
+
#elseif($column.htmlType == "select")## 下拉框
#if ("" != $dictType)## 有数据字典
- #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import
- #set($ignore = $dictMethods.add($dictMethod) )
- #end
#if ("" != $dictType)## 有数据字典
- #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import
- #set($ignore = $dictMethods.add($dictMethod) )
- #end
#if ("" != $dictType)## 有数据字典
- #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import
- #set($ignore = $dictMethods.add($dictMethod) )
- #end
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 10 || $table.templateType == 12 )
+
+
+ #foreach ($subTable in $subTables)
+ #set ($index = $foreach.count - 1)
+ #set ($subClassNameVar = $subClassNameVars.get($index))
+ #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+ #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+
+ <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
+
+ #end
+
+#end
确 定
取 消
@@ -121,10 +139,18 @@
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+
+/** 获得${table.classComment}树 */
+const get${simpleClassName}Tree = async () => {
+ ${classNameVar}Tree.value = []
+ const data = await ${simpleClassName}Api.get${simpleClassName}List()
+ const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }
+ root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
+ ${classNameVar}Tree.value.push(root)
+}
+#end
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
index 477654a371..eab90c12a3 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
@@ -8,7 +8,6 @@
:inline="true"
label-width="68px"
>
- #set ($dictMethods = [])## 使用到的 dict 字典方法
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
@@ -36,20 +35,13 @@
#elseif ($column.htmlType == "select" || $column.htmlType == "radio")
- #if ($javaField.length() + $comment.length() > 8)
- #else
-
- #end
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
- #if (!$dictMethods.contains($dictMethod))## 如果不存在,则添加到 dictMethods 数组中,后续好 import
- #set($ignore = $dictMethods.add($dictMethod) )
- #end
搜索
重置
- #if ($permissionPrefix.length() <= 12)
-
- #else
- #end
新增
导出
+## 特殊:树表专属逻辑
+#if ( $table.templateType == 2 )
+
+ 展开/折叠
+
+#end
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
+
+## 特殊:树表专属逻辑
+#elseif ( $table.templateType == 2 )
+
+#else
+#end
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
+
+
+
+
+ #foreach ($subTable in $subTables)
+ #set ($index = $foreach.count - 1)
+ #set ($subClassNameVar = $subClassNameVars.get($index))
+ #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+ #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+
+ <${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />
+
+ #end
+
+
+
+#end
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
@@ -134,7 +170,7 @@
:formatter="dateFormatter"
width="180px"
/>
- #elseif("" != $column.dictType)## 数据字典
+ #elseif($column.dictType && "" != $column.dictType)## 数据字典
@@ -177,21 +213,41 @@
<${simpleClassName}Form ref="formRef" @success="getList" />
+## 特殊:主子表专属逻辑
+#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
+
+
+
+ #foreach ($subTable in $subTables)
+ #set ($index = $foreach.count - 1)
+ #set ($subClassNameVar = $subClassNameVars.get($index))
+ #set ($subSimpleClassName = $subSimpleClassNames.get($index))
+ #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
+
+ <${subSimpleClassName}List :${subJoinColumn_strikeCase}="currentRow.id" />
+
+ #end
+
+
+#end
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/data.ts.vm
index cbc6a17baa..ff4fa810a8 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/data.ts.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/data.ts.vm
@@ -1,10 +1,5 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
-#foreach ($column in $columns)
- #if ($column.listOperationResult && $column.htmlType == "datetime")
import { dateFormatter } from '@/utils/formatTime'
- #break
- #end
-#end
// 表单校验
export const rules = reactive({
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/form.vue.vm
index 45b8aa2606..52f20a2f58 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/form.vue.vm
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_schema/views/form.vue.vm
@@ -8,7 +8,7 @@
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentContactList
new file mode 100644
index 0000000000..eada66a759
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentContactList
@@ -0,0 +1,146 @@
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentForm
new file mode 100644
index 0000000000..2e3fc0387a
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentForm
@@ -0,0 +1,152 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherForm
new file mode 100644
index 0000000000..242fd57f59
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherForm
@@ -0,0 +1,155 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherList
new file mode 100644
index 0000000000..1eba0a3d0b
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/StudentTeacherList
@@ -0,0 +1,146 @@
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/index
new file mode 100644
index 0000000000..f1b5a27241
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/vue/index
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/test/TestDemoMapper.xml b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/xml/InfraStudentMapper
similarity index 68%
rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/test/TestDemoMapper.xml
rename to yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/xml/InfraStudentMapper
index 287f58d82e..155aa5c27a 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/test/TestDemoMapper.xml
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_erp/xml/InfraStudentMapper
@@ -1,6 +1,6 @@
-
+
-
-
-
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/assert.json
new file mode 100644
index 0000000000..0937ba9148
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000000..72162a8592
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentContactDO
new file mode 100644
index 0000000000..17c668eaac
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentContactMapper
new file mode 100644
index 0000000000..35bbd53c2d
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentController
new file mode 100644
index 0000000000..b9a587b448
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentDO
new file mode 100644
index 0000000000..b0d4bd2167
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentMapper
new file mode 100644
index 0000000000..34e70a0822
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentPageReqVO
new file mode 100644
index 0000000000..41a3730125
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentRespVO
new file mode 100644
index 0000000000..c41a5501fe
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000000..faa491dfb8
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentService
new file mode 100644
index 0000000000..afa7d22eb3
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImpl
new file mode 100644
index 0000000000..c57cba6137
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImplTest
new file mode 100644
index 0000000000..b5f4bf0ff2
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentTeacherDO
new file mode 100644
index 0000000000..c19cf9fab2
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/java/InfraStudentTeacherMapper
new file mode 100644
index 0000000000..0521bbaf45
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/sql/h2
new file mode 100644
index 0000000000..6c1875f601
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/sql/sql
new file mode 100644
index 0000000000..4551d0de6f
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/ts/index
new file mode 100644
index 0000000000..6afca54c3f
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactForm
new file mode 100644
index 0000000000..20f129e5bf
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactForm
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ —
+
+
+
+
+
+ + 添加学生联系人
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactList
new file mode 100644
index 0000000000..d0e89dac20
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentContactList
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentForm
new file mode 100644
index 0000000000..d77df42d9c
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentForm
@@ -0,0 +1,184 @@
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherForm
new file mode 100644
index 0000000000..6027db8823
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherForm
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ dict.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherList
new file mode 100644
index 0000000000..e510adcf45
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/StudentTeacherList
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/index
new file mode 100644
index 0000000000..e50091add8
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/vue/index
@@ -0,0 +1,267 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+ 新增
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/xml/InfraStudentMapper
new file mode 100644
index 0000000000..155aa5c27a
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_inner/xml/InfraStudentMapper
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/assert.json
new file mode 100644
index 0000000000..60e7f47675
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/ErrorCodeConstants_手动操作
new file mode 100644
index 0000000000..72162a8592
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentContactDO
new file mode 100644
index 0000000000..17c668eaac
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentContactMapper
new file mode 100644
index 0000000000..35bbd53c2d
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentController
new file mode 100644
index 0000000000..b9a587b448
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentDO
new file mode 100644
index 0000000000..b0d4bd2167
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentMapper
new file mode 100644
index 0000000000..34e70a0822
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentPageReqVO
new file mode 100644
index 0000000000..41a3730125
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentRespVO
new file mode 100644
index 0000000000..c41a5501fe
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentSaveReqVO
new file mode 100644
index 0000000000..faa491dfb8
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentService
new file mode 100644
index 0000000000..afa7d22eb3
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_master_normal/java/InfraStudentServiceImpl
new file mode 100644
index 0000000000..c57cba6137
--- /dev/null
+++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/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