Commit bcb6a68b80110948aa345ebf8f2f6430edcda3e4

Authored by Lyon
2 parents 6163cfb4 d79bf795

Merge branch 'master' into feature_user-session-timeout

# Conflicts:
#	sql/ruoyi-vue-pro.sql
Showing 40 changed files with 625 additions and 252 deletions
README.md
@@ -43,11 +43,12 @@ @@ -43,11 +43,12 @@
43 1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题 43 1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题
44 1. 服务保障:基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 44 1. 服务保障:基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能
45 1. 日志服务:轻量级日志中心,查看远程服务器的日志 45 1. 日志服务:轻量级日志中心,查看远程服务器的日志
  46 +1. 单元测试:基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等
46 47
47 ### 研发工具 48 ### 研发工具
48 49
49 1. 表单构建:拖动表单元素生成相应的 HTML 代码 50 1. 表单构建:拖动表单元素生成相应的 HTML 代码
50 -1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载 51 +1. 代码生成:前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载
51 1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档 52 1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档
52 1. 数据库文档:基于 Screw 自动生成数据库文档 53 1. 数据库文档:基于 Screw 自动生成数据库文档
53 54
@@ -83,7 +84,9 @@ @@ -83,7 +84,9 @@
83 | [Spring Boot Admin](https://github.com/skywalking) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | 84 | [Spring Boot Admin](https://github.com/skywalking) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) |
84 | [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.11.4 | | 85 | [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.11.4 | |
85 | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | 86 | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) |
86 -| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码| 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | 87 +| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) |
  88 +| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.7.0 | - |
  89 +| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 3.6.28 | - |
87 90
88 **前端** 91 **前端**
89 92
@@ -125,7 +128,7 @@ @@ -125,7 +128,7 @@
125 </tr> 128 </tr>
126 <tr> 129 <tr>
127 <td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td> 130 <td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
128 - <td><img src="https://oscimg.oschina.net/oscnet/up-6d73c2140ce694e3de4c05035fdc1868d4c.png"/></td> 131 + <td> - </td>
129 </tr> 132 </tr>
130 </table> 133 </table>
131 134
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 - <modelVersion>4.0.0</modelVersion> 5 + <modelVersion>4.0.0</modelVersion>
6 6
7 <groupId>cn.iocoder</groupId> 7 <groupId>cn.iocoder</groupId>
8 <artifactId>dashboard</artifactId> 8 <artifactId>dashboard</artifactId>
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 <spring.boot.version>2.4.2</spring.boot.version> 25 <spring.boot.version>2.4.2</spring.boot.version>
26 <!-- Web 相关 --> 26 <!-- Web 相关 -->
27 <knife4j.version>3.0.2</knife4j.version> 27 <knife4j.version>3.0.2</knife4j.version>
  28 + <swagger-annotations.version>1.5.22</swagger-annotations.version>
28 <!-- DB 相关 --> 29 <!-- DB 相关 -->
29 <mysql-connector-java.version>5.1.46</mysql-connector-java.version> 30 <mysql-connector-java.version>5.1.46</mysql-connector-java.version>
30 <druid.version>1.2.4</druid.version> 31 <druid.version>1.2.4</druid.version>
@@ -104,8 +105,17 @@ @@ -104,8 +105,17 @@
104 <artifactId>guava</artifactId> 105 <artifactId>guava</artifactId>
105 <groupId>com.google.guava</groupId> 106 <groupId>com.google.guava</groupId>
106 </exclusion> 107 </exclusion>
  108 + <exclusion>
  109 + <artifactId>swagger-annotations</artifactId>
  110 + <groupId>io.swagger</groupId>
  111 + </exclusion>
107 </exclusions> 112 </exclusions>
108 </dependency> 113 </dependency>
  114 + <dependency>
  115 + <groupId>io.swagger</groupId>
  116 + <artifactId>swagger-annotations</artifactId>
  117 + <version>${swagger-annotations.version}</version>
  118 + </dependency>
109 119
110 <!-- DB 相关 --> 120 <!-- DB 相关 -->
111 <dependency> 121 <dependency>
sql/ruoyi-vue-pro.sql
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 Target Server Version : 50718 11 Target Server Version : 50718
12 File Encoding : 65001 12 File Encoding : 65001
13 13
14 - Date: 07/03/2021 00:43:34 14 + Date: 08/03/2021 00:50:29
15 */ 15 */
16 16
17 SET NAMES utf8mb4; 17 SET NAMES utf8mb4;
@@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` (
43 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 43 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
44 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 44 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
45 PRIMARY KEY (`id`) USING BTREE 45 PRIMARY KEY (`id`) USING BTREE
46 -) ENGINE=InnoDB AUTO_INCREMENT=1822 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; 46 +) ENGINE=InnoDB AUTO_INCREMENT=1850 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表';
47 47
48 -- ---------------------------- 48 -- ----------------------------
49 -- Records of inf_api_access_log 49 -- Records of inf_api_access_log
@@ -175,7 +175,7 @@ CREATE TABLE `inf_job_log` ( @@ -175,7 +175,7 @@ CREATE TABLE `inf_job_log` (
175 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 175 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
176 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 176 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
177 PRIMARY KEY (`id`) USING BTREE 177 PRIMARY KEY (`id`) USING BTREE
178 -) ENGINE=InnoDB AUTO_INCREMENT=4458 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; 178 +) ENGINE=InnoDB AUTO_INCREMENT=4477 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表';
179 179
180 -- ---------------------------- 180 -- ----------------------------
181 -- Records of inf_job_log 181 -- Records of inf_job_log
@@ -579,7 +579,7 @@ CREATE TABLE `sys_operate_log` ( @@ -579,7 +579,7 @@ CREATE TABLE `sys_operate_log` (
579 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 579 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
580 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 580 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
581 PRIMARY KEY (`id`) USING BTREE 581 PRIMARY KEY (`id`) USING BTREE
582 -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录'; 582 +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='æ“作日志记录';
583 583
584 -- ---------------------------- 584 -- ----------------------------
585 -- Records of sys_operate_log 585 -- Records of sys_operate_log
@@ -977,13 +977,14 @@ INSERT INTO `sys_user_session` VALUES (&#39;e3ad1ef8b9aa4b329855b29c7b372e8f&#39;, 1, &#39;1 @@ -977,13 +977,14 @@ INSERT INTO `sys_user_session` VALUES (&#39;e3ad1ef8b9aa4b329855b29c7b372e8f&#39;, 1, &#39;1
977 INSERT INTO `sys_user_session` VALUES ('ea0d48776db84da4ac0f4c2adf62c366', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '2021-02-08 07:02:03', '', '2021-02-08 07:02:03', '', '2021-03-07 14:58:55', b'0'); 977 INSERT INTO `sys_user_session` VALUES ('ea0d48776db84da4ac0f4c2adf62c366', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '2021-02-08 07:02:03', '', '2021-02-08 07:02:03', '', '2021-03-07 14:58:55', b'0');
978 INSERT INTO `sys_user_session` VALUES ('f881f7dc67d04cd29574657fdde32a62', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '2021-02-05 08:53:20', '', '2021-02-05 08:53:20', '', '2021-03-07 14:58:55', b'0'); 978 INSERT INTO `sys_user_session` VALUES ('f881f7dc67d04cd29574657fdde32a62', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '2021-02-05 08:53:20', '', '2021-02-05 08:53:20', '', '2021-03-07 14:58:55', b'0');
979 COMMIT; 979 COMMIT;
980 --- ----------------------------null,  
981 --- Table structure for tool_codegen_columnnull,  
982 --- ----------------------------null,  
983 -DROP TABLE IF EXISTS `tool_codegen_column`;null,  
984 -CREATE TABLE `tool_codegen_column` (null,  
985 - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·',null,  
986 - `table_id` bigint(20) NOT NULL COMMENT '表编å·',null, 980 +
  981 +-- ----------------------------
  982 +-- Table structure for tool_codegen_column
  983 +-- ----------------------------
  984 +DROP TABLE IF EXISTS `tool_codegen_column`;
  985 +CREATE TABLE `tool_codegen_column` (
  986 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼–å·',
  987 + `table_id` bigint(20) NOT NULL COMMENT '表编å·',
987 `column_name` varchar(200) NOT NULL COMMENT '字段å', 988 `column_name` varchar(200) NOT NULL COMMENT '字段å',
988 `column_type` varchar(100) NOT NULL COMMENT '字段类型', 989 `column_type` varchar(100) NOT NULL COMMENT '字段类型',
989 `column_comment` varchar(500) NOT NULL COMMENT '字段æè¿°', 990 `column_comment` varchar(500) NOT NULL COMMENT '字段æè¿°',
@@ -1007,7 +1008,7 @@ CREATE TABLE `tool_codegen_column` (null, @@ -1007,7 +1008,7 @@ CREATE TABLE `tool_codegen_column` (null,
1007 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 1008 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
1008 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 1009 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
1009 PRIMARY KEY (`id`) USING BTREE 1010 PRIMARY KEY (`id`) USING BTREE
1010 -) ENGINE=InnoDB AUTO_INCREMENT=369 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å­—段定义'; 1011 +) ENGINE=InnoDB AUTO_INCREMENT=381 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å­—段定义';
1011 1012
1012 -- ---------------------------- 1013 -- ----------------------------
1013 -- Records of tool_codegen_column 1014 -- Records of tool_codegen_column
@@ -1149,6 +1150,18 @@ INSERT INTO `tool_codegen_column` VALUES (365, 29, &#39;create_time&#39;, &#39;datetime&#39;, &#39;å @@ -1149,6 +1150,18 @@ INSERT INTO `tool_codegen_column` VALUES (365, 29, &#39;create_time&#39;, &#39;datetime&#39;, &#39;å
1149 INSERT INTO `tool_codegen_column` VALUES (366, 29, 'update_by', 'varchar(64)', '更新者', b'1', b'0', '0', 8, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); 1150 INSERT INTO `tool_codegen_column` VALUES (366, 29, 'update_by', 'varchar(64)', '更新者', b'1', b'0', '0', 8, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0');
1150 INSERT INTO `tool_codegen_column` VALUES (367, 29, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 9, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); 1151 INSERT INTO `tool_codegen_column` VALUES (367, 29, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 9, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0');
1151 INSERT INTO `tool_codegen_column` VALUES (368, 29, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 10, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); 1152 INSERT INTO `tool_codegen_column` VALUES (368, 29, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 10, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0');
  1153 +INSERT INTO `tool_codegen_column` VALUES (369, 30, 'id', 'bigint(20)', '字典编ç ', b'0', b'1', '1', 1, 'Long', 'id', '', NULL, b'0', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
  1154 +INSERT INTO `tool_codegen_column` VALUES (370, 30, 'sort', 'int(4)', '字典排åº', b'0', b'0', '0', 2, 'Integer', 'sort', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1155 +INSERT INTO `tool_codegen_column` VALUES (371, 30, 'label', 'varchar(100)', '字典标签', b'0', b'0', '0', 3, 'String', 'label', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1156 +INSERT INTO `tool_codegen_column` VALUES (372, 30, 'value', 'varchar(100)', '字典键值', b'0', b'0', '0', 4, 'String', 'value', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1157 +INSERT INTO `tool_codegen_column` VALUES (373, 30, 'dict_type', 'varchar(100)', '字典类型', b'0', b'0', '0', 5, 'String', 'dictType', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'select', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1158 +INSERT INTO `tool_codegen_column` VALUES (374, 30, 'status', 'tinyint(4)', '状æ€ï¼ˆ0正常 1åœç”¨ï¼‰', b'0', b'0', '0', 6, 'Integer', 'status', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'radio', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1159 +INSERT INTO `tool_codegen_column` VALUES (375, 30, 'remark', 'varchar(500)', '备注', b'1', b'0', '0', 7, 'String', 'remark', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0');
  1160 +INSERT INTO `tool_codegen_column` VALUES (376, 30, 'create_by', 'varchar(64)', '创建者', b'1', b'0', '0', 8, 'String', 'createBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
  1161 +INSERT INTO `tool_codegen_column` VALUES (377, 30, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 9, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
  1162 +INSERT INTO `tool_codegen_column` VALUES (378, 30, 'update_by', 'varchar(64)', '更新者', b'1', b'0', '0', 10, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
  1163 +INSERT INTO `tool_codegen_column` VALUES (379, 30, 'update_time', 'datetime', 'æ›´æ–°æ—¶é—´', b'0', b'0', '0', 11, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
  1164 +INSERT INTO `tool_codegen_column` VALUES (380, 30, 'deleted', 'bit(1)', '是å¦åˆ é™¤', b'0', b'0', '0', 12, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0');
1152 COMMIT; 1165 COMMIT;
1153 1166
1154 -- ---------------------------- 1167 -- ----------------------------
@@ -1174,7 +1187,7 @@ CREATE TABLE `tool_codegen_table` ( @@ -1174,7 +1187,7 @@ CREATE TABLE `tool_codegen_table` (
1174 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´', 1187 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ›´æ–°æ—¶é—´',
1175 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤', 1188 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是å¦åˆ é™¤',
1176 PRIMARY KEY (`id`) USING BTREE 1189 PRIMARY KEY (`id`) USING BTREE
1177 -) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å®šä¹‰'; 1190 +) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COMMENT='代ç ç”Ÿæˆè¡¨å®šä¹‰';
1178 1191
1179 -- ---------------------------- 1192 -- ----------------------------
1180 -- Records of tool_codegen_table 1193 -- Records of tool_codegen_table
@@ -1190,6 +1203,7 @@ INSERT INTO `tool_codegen_table` VALUES (26, 1, &#39;inf_api_access_log&#39;, &#39;API è®¿é— @@ -1190,6 +1203,7 @@ INSERT INTO `tool_codegen_table` VALUES (26, 1, &#39;inf_api_access_log&#39;, &#39;API 访é—
1190 INSERT INTO `tool_codegen_table` VALUES (27, 1, 'inf_api_error_log', 'API 错误日志', NULL, 'infra', 'apiErrorLog', 'InfApiErrorLog', 'API 错误日志', 'èŠ‹é“æºç ', 1, 1083, '', '2021-02-26 06:54:49', '', '2021-02-26 07:53:03', b'0'); 1203 INSERT INTO `tool_codegen_table` VALUES (27, 1, 'inf_api_error_log', 'API 错误日志', NULL, 'infra', 'apiErrorLog', 'InfApiErrorLog', 'API 错误日志', 'èŠ‹é“æºç ', 1, 1083, '', '2021-02-26 06:54:49', '', '2021-02-26 07:53:03', b'0');
1191 INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dictType', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:45:55', '', '2021-03-06 03:51:02', b'1'); 1204 INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dictType', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:45:55', '', '2021-03-06 03:51:02', b'1');
1192 INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dict', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); 1205 INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dict', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0');
  1206 +INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', '字典数æ®è¡¨', NULL, 'system', 'type', 'SysDictData', '字典数æ®', 'èŠ‹é“æºç ', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0');
1193 COMMIT; 1207 COMMIT;
1194 1208
1195 -- ---------------------------- 1209 -- ----------------------------
src/main/java/cn/iocoder/dashboard/framework/mybatis/config/MybatisConfiguration.java
@@ -13,7 +13,8 @@ import org.springframework.context.annotation.Configuration; @@ -13,7 +13,8 @@ import org.springframework.context.annotation.Configuration;
13 * @author 芋道源码 13 * @author 芋道源码
14 */ 14 */
15 @Configuration 15 @Configuration
16 -@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class) 16 +@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class,
  17 + lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试
17 public class MybatisConfiguration { 18 public class MybatisConfiguration {
18 19
19 @Bean 20 @Bean
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java
@@ -21,13 +21,13 @@ public class BaseDO implements Serializable { @@ -21,13 +21,13 @@ public class BaseDO implements Serializable {
21 */ 21 */
22 private Date updateTime; 22 private Date updateTime;
23 /** 23 /**
24 - * 创建者 TODO 芋艿:迁移成编号 24 + * 创建者
25 */ 25 */
26 - private String createBy; 26 + private String creator;
27 /** 27 /**
28 - * 更新者 TODO 芋艿:迁移成编号 28 + * 更新者
29 */ 29 */
30 - private String updateBy; 30 + private String updater;
31 /** 31 /**
32 * 是否删除 32 * 是否删除
33 */ 33 */
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/handle/DefaultDBFieldHandler.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.mybatis.core.handle;
  2 +
  3 +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.dashboard.framework.security.core.LoginUser;
  5 +import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils;
  6 +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
  7 +import org.apache.ibatis.reflection.MetaObject;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import java.util.Date;
  11 +import java.util.Objects;
  12 +
  13 +/**
  14 + * 通用参数填充实现类
  15 + *
  16 + * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
  17 + *
  18 + * @author hexiaowu
  19 + */
  20 +@Component
  21 +public class DefaultDBFieldHandler implements MetaObjectHandler {
  22 +
  23 + @Override
  24 + public void insertFill(MetaObject metaObject) {
  25 + if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
  26 + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
  27 + BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
  28 + Date current = new Date();
  29 +
  30 + // 创建时间为空,则以当前时间为插入时间
  31 + if (Objects.isNull(baseDO.getCreateTime())) {
  32 + baseDO.setCreateTime(current);
  33 + }
  34 + // 更新时间为空,则以当前时间为更新时间
  35 + if (Objects.isNull(baseDO.getUpdateTime())) {
  36 + baseDO.setUpdateTime(current);
  37 + }
  38 + // 当前登录用户不为空,创建人为空,则当前登录用户为创建人
  39 + if (Objects.nonNull(loginUser) && Objects.isNull(baseDO.getCreator())) {
  40 + baseDO.setCreator(loginUser.getId().toString());
  41 + }
  42 + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
  43 + if (Objects.nonNull(loginUser) && Objects.isNull(baseDO.getUpdater())) {
  44 + baseDO.setUpdater(loginUser.getId().toString());
  45 + }
  46 + }
  47 + }
  48 +
  49 + @Override
  50 + public void updateFill(MetaObject metaObject) {
  51 + Object modifyTime = getFieldValByName("updateTime", metaObject);
  52 + Object modifier = getFieldValByName("updater", metaObject);
  53 + // 获取登录用户信息
  54 + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
  55 +
  56 + // 更新时间为空,则以当前时间为更新时间
  57 + if (Objects.isNull(modifyTime)) {
  58 + setFieldValByName("updateTime", new Date(), metaObject);
  59 + }
  60 + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
  61 + if (Objects.nonNull(loginUser) && Objects.isNull(modifier)) {
  62 + setFieldValByName("updater", loginUser.getId(), metaObject);
  63 + }
  64 + }
  65 +}
src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java
@@ -28,6 +28,10 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; { @@ -28,6 +28,10 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; {
28 return selectOne(new QueryWrapper<T>().eq(field, value)); 28 return selectOne(new QueryWrapper<T>().eq(field, value));
29 } 29 }
30 30
  31 + default Integer selectCount(String field, Object value) {
  32 + return selectCount(new QueryWrapper<T>().eq(field, value));
  33 + }
  34 +
31 default List<T> selectList() { 35 default List<T> selectList() {
32 return selectList(new QueryWrapper<>()); 36 return selectList(new QueryWrapper<>());
33 } 37 }
src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 安全框架 Spring Security 入门》.md deleted
1 -<http://www.iocoder.cn/Spring-Boot/Spring-Security/?github>  
src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md 0 → 100644
  1 +<https://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao>
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
@@ -152,7 +152,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @@ -152,7 +152,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
152 .anyRequest().authenticated() 152 .anyRequest().authenticated()
153 .and() 153 .and()
154 .headers().frameOptions().disable(); 154 .headers().frameOptions().disable();
155 - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); 155 + httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler);
156 // 添加 JWT Filter 156 // 添加 JWT Filter
157 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 157 httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
158 } 158 }
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
1 package cn.iocoder.dashboard.framework.security.core.handler; 1 package cn.iocoder.dashboard.framework.security.core.handler;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.dashboard.common.pojo.CommonResult;
4 import cn.iocoder.dashboard.framework.security.config.SecurityProperties; 5 import cn.iocoder.dashboard.framework.security.config.SecurityProperties;
5 import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; 6 import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService;
6 import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; 7 import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils;
@@ -36,6 +37,6 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { @@ -36,6 +37,6 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
36 securityFrameworkService.logout(token); 37 securityFrameworkService.logout(token);
37 } 38 }
38 // 返回成功 39 // 返回成功
39 - ServletUtils.writeJSON(response, null); 40 + ServletUtils.writeJSON(response, CommonResult.success(null));
40 } 41 }
41 } 42 }
src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityFrameworkUtils.java
@@ -2,7 +2,10 @@ package cn.iocoder.dashboard.framework.security.core.util; @@ -2,7 +2,10 @@ package cn.iocoder.dashboard.framework.security.core.util;
2 2
3 import cn.iocoder.dashboard.framework.security.core.LoginUser; 3 import cn.iocoder.dashboard.framework.security.core.LoginUser;
4 import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils; 4 import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils;
  5 +import org.springframework.lang.Nullable;
5 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  7 +import org.springframework.security.core.Authentication;
  8 +import org.springframework.security.core.context.SecurityContext;
6 import org.springframework.security.core.context.SecurityContextHolder; 9 import org.springframework.security.core.context.SecurityContextHolder;
7 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 10 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
8 import org.springframework.util.StringUtils; 11 import org.springframework.util.StringUtils;
@@ -40,9 +43,20 @@ public class SecurityFrameworkUtils { @@ -40,9 +43,20 @@ public class SecurityFrameworkUtils {
40 43
41 /** 44 /**
42 * 获取当前用户 45 * 获取当前用户
  46 + *
  47 + * @return 当前用户
43 */ 48 */
  49 + @Nullable
44 public static LoginUser getLoginUser() { 50 public static LoginUser getLoginUser() {
45 - return (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 51 + SecurityContext context = SecurityContextHolder.getContext();
  52 + if (context == null) {
  53 + return null;
  54 + }
  55 + Authentication authentication = context.getAuthentication();
  56 + if (authentication == null) {
  57 + return null;
  58 + }
  59 + return (LoginUser) authentication.getPrincipal();
46 } 60 }
47 61
48 /** 62 /**
@@ -50,8 +64,10 @@ public class SecurityFrameworkUtils { @@ -50,8 +64,10 @@ public class SecurityFrameworkUtils {
50 * 64 *
51 * @return 用户编号 65 * @return 用户编号
52 */ 66 */
  67 + @Nullable
53 public static Long getLoginUserId() { 68 public static Long getLoginUserId() {
54 - return getLoginUser().getId(); 69 + LoginUser loginUser = getLoginUser();
  70 + return loginUser != null ? loginUser.getId() : null;
55 } 71 }
56 72
57 /** 73 /**
@@ -59,8 +75,10 @@ public class SecurityFrameworkUtils { @@ -59,8 +75,10 @@ public class SecurityFrameworkUtils {
59 * 75 *
60 * @return 角色编号数组 76 * @return 角色编号数组
61 */ 77 */
  78 + @Nullable
62 public static Set<Long> getLoginUserRoleIds() { 79 public static Set<Long> getLoginUserRoleIds() {
63 - return getLoginUser().getRoleIds(); 80 + LoginUser loginUser = getLoginUser();
  81 + return loginUser != null ? loginUser.getRoleIds() : null;
64 } 82 }
65 83
66 /** 84 /**
src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java
@@ -10,15 +10,17 @@ import org.springframework.context.annotation.Configuration; @@ -10,15 +10,17 @@ import org.springframework.context.annotation.Configuration;
10 import org.springframework.http.HttpHeaders; 10 import org.springframework.http.HttpHeaders;
11 import springfox.documentation.builders.ApiInfoBuilder; 11 import springfox.documentation.builders.ApiInfoBuilder;
12 import springfox.documentation.builders.PathSelectors; 12 import springfox.documentation.builders.PathSelectors;
13 -import springfox.documentation.service.*; 13 +import springfox.documentation.service.ApiInfo;
  14 +import springfox.documentation.service.ApiKey;
  15 +import springfox.documentation.service.AuthorizationScope;
  16 +import springfox.documentation.service.Contact;
  17 +import springfox.documentation.service.SecurityReference;
  18 +import springfox.documentation.service.SecurityScheme;
14 import springfox.documentation.spi.DocumentationType; 19 import springfox.documentation.spi.DocumentationType;
15 import springfox.documentation.spi.service.contexts.SecurityContext; 20 import springfox.documentation.spi.service.contexts.SecurityContext;
16 import springfox.documentation.spring.web.plugins.Docket; 21 import springfox.documentation.spring.web.plugins.Docket;
17 import springfox.documentation.swagger2.annotations.EnableSwagger2; 22 import springfox.documentation.swagger2.annotations.EnableSwagger2;
18 -import springfox.documentation.service.ApiKey;  
19 23
20 -import java.util.ArrayList;  
21 -import java.util.Arrays;  
22 import java.util.Collections; 24 import java.util.Collections;
23 import java.util.List; 25 import java.util.List;
24 26
src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java
@@ -27,9 +27,10 @@ public class WebConfiguration implements WebMvcConfigurer { @@ -27,9 +27,10 @@ public class WebConfiguration implements WebMvcConfigurer {
27 27
28 @Override 28 @Override
29 public void configurePathMatch(PathMatchConfigurer configurer) { 29 public void configurePathMatch(PathMatchConfigurer configurer) {
  30 + // 设置 API 前缀,仅仅匹配 controller 包下的
30 configurer.addPathPrefix(webProperties.getApiPrefix(), clazz -> 31 configurer.addPathPrefix(webProperties.getApiPrefix(), clazz ->
31 clazz.isAnnotationPresent(RestController.class) 32 clazz.isAnnotationPresent(RestController.class)
32 - && clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); 33 + && clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); // 仅仅匹配 controller 包
33 } 34 }
34 35
35 // ========== Filter 相关 ========== 36 // ========== Filter 相关 ==========
src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java
@@ -12,6 +12,7 @@ import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO; @@ -12,6 +12,7 @@ import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO;
12 import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum; 12 import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
13 import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer; 13 import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer;
14 import cn.iocoder.dashboard.modules.infra.service.config.InfConfigService; 14 import cn.iocoder.dashboard.modules.infra.service.config.InfConfigService;
  15 +import com.google.common.annotations.VisibleForTesting;
15 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
16 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
17 18
@@ -99,7 +100,8 @@ public class InfConfigServiceImpl implements InfConfigService { @@ -99,7 +100,8 @@ public class InfConfigServiceImpl implements InfConfigService {
99 checkConfigKeyUnique(id, key); 100 checkConfigKeyUnique(id, key);
100 } 101 }
101 102
102 - private InfConfigDO checkConfigExists(Long id) { 103 + @VisibleForTesting
  104 + public InfConfigDO checkConfigExists(Long id) {
103 if (id == null) { 105 if (id == null) {
104 return null; 106 return null;
105 } 107 }
@@ -110,7 +112,8 @@ public class InfConfigServiceImpl implements InfConfigService { @@ -110,7 +112,8 @@ public class InfConfigServiceImpl implements InfConfigService {
110 return config; 112 return config;
111 } 113 }
112 114
113 - private void checkConfigKeyUnique(Long id, String key) { 115 + @VisibleForTesting
  116 + public void checkConfigKeyUnique(Long id, String key) {
114 InfConfigDO config = configMapper.selectByKey(key); 117 InfConfigDO config = configMapper.selectByKey(key);
115 if (config == null) { 118 if (config == null) {
116 return; 119 return;
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/dept/SysDeptBaseVO.java
@@ -26,7 +26,7 @@ public class SysDeptBaseVO { @@ -26,7 +26,7 @@ public class SysDeptBaseVO {
26 26
27 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") 27 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024")
28 @NotBlank(message = "显示顺序不能为空") 28 @NotBlank(message = "显示顺序不能为空")
29 - private String sort; 29 + private Integer sort;
30 30
31 @ApiModelProperty(value = "负责人", example = "芋道") 31 @ApiModelProperty(value = "负责人", example = "芋道")
32 private String leader; 32 private String leader;
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostBaseVO.java
@@ -25,7 +25,7 @@ public class SysPostBaseVO { @@ -25,7 +25,7 @@ public class SysPostBaseVO {
25 25
26 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") 26 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024")
27 @NotBlank(message = "显示顺序不能为空") 27 @NotBlank(message = "显示顺序不能为空")
28 - private String sort; 28 + private Integer sort;
29 29
30 @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 SysCommonStatusEnum 枚举类") 30 @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 SysCommonStatusEnum 枚举类")
31 private Integer status; 31 private Integer status;
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java
@@ -23,7 +23,7 @@ public class SysPostExcelVO { @@ -23,7 +23,7 @@ public class SysPostExcelVO {
23 private String name; 23 private String name;
24 24
25 @ExcelProperty("岗位排序") 25 @ExcelProperty("岗位排序")
26 - private String sort; 26 + private Integer sort;
27 27
28 @ExcelProperty(value = "状态", converter = DictConvert.class) 28 @ExcelProperty(value = "状态", converter = DictConvert.class)
29 @DictFormat(SYS_COMMON_STATUS) 29 @DictFormat(SYS_COMMON_STATUS)
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/menu/SysMenuBaseVO.java
@@ -29,7 +29,7 @@ public class SysMenuBaseVO { @@ -29,7 +29,7 @@ public class SysMenuBaseVO {
29 29
30 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") 30 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024")
31 @NotBlank(message = "显示顺序不能为空") 31 @NotBlank(message = "显示顺序不能为空")
32 - private String sort; 32 + private Integer sort;
33 33
34 @ApiModelProperty(value = "父菜单 ID", required = true, example = "1024") 34 @ApiModelProperty(value = "父菜单 ID", required = true, example = "1024")
35 @NotNull(message = "父菜单 ID 不能为空") 35 @NotNull(message = "父菜单 ID 不能为空")
src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/role/SysRoleBaseVO.java
@@ -25,7 +25,7 @@ public class SysRoleBaseVO { @@ -25,7 +25,7 @@ public class SysRoleBaseVO {
25 25
26 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") 26 @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024")
27 @NotBlank(message = "显示顺序不能为空") 27 @NotBlank(message = "显示顺序不能为空")
28 - private String sort; 28 + private Integer sort;
29 29
30 @ApiModelProperty(value = "角色类型", required = true, example = "1", notes = "见 SysRoleTypeEnum 枚举") 30 @ApiModelProperty(value = "角色类型", required = true, example = "1", notes = "见 SysRoleTypeEnum 枚举")
31 private Integer type; 31 private Integer type;
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java
@@ -35,7 +35,7 @@ public class SysDeptDO extends BaseDO { @@ -35,7 +35,7 @@ public class SysDeptDO extends BaseDO {
35 /** 35 /**
36 * 显示顺序 36 * 显示顺序
37 */ 37 */
38 - private String sort; 38 + private Integer sort;
39 /** 39 /**
40 * 负责人 40 * 负责人
41 */ 41 */
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysPostDO.java
@@ -34,7 +34,7 @@ public class SysPostDO extends BaseDO { @@ -34,7 +34,7 @@ public class SysPostDO extends BaseDO {
34 /** 34 /**
35 * 岗位排序 35 * 岗位排序
36 */ 36 */
37 - private String sort; 37 + private Integer sort;
38 /** 38 /**
39 * 状态 39 * 状态
40 * 40 *
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/permission/SysMenuDO.java
@@ -49,7 +49,7 @@ public class SysMenuDO extends BaseDO { @@ -49,7 +49,7 @@ public class SysMenuDO extends BaseDO {
49 /** 49 /**
50 * 显示顺序 50 * 显示顺序
51 */ 51 */
52 - private String sort; 52 + private Integer sort;
53 /** 53 /**
54 * 父菜单ID 54 * 父菜单ID
55 */ 55 */
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java
@@ -15,13 +15,13 @@ import java.util.List; @@ -15,13 +15,13 @@ import java.util.List;
15 @Mapper 15 @Mapper
16 public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> { 16 public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> {
17 17
18 - default SysDictDataDO selectByDictTypeAndLabel(String dictType, String label) { 18 + default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) {
19 return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType) 19 return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)
20 - .eq("label", label)); 20 + .eq("value", value));
21 } 21 }
22 22
23 default int selectCountByDictType(String dictType) { 23 default int selectCountByDictType(String dictType) {
24 - return selectCount(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)); 24 + return selectCount("dict_type", dictType);
25 } 25 }
26 26
27 default PageResult<SysDictDataDO> selectPage(SysDictDataPageReqVO reqVO) { 27 default PageResult<SysDictDataDO> selectPage(SysDictDataPageReqVO reqVO) {
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -160,7 +160,26 @@ public class SysAuthServiceImpl implements SysAuthService { @@ -160,7 +160,26 @@ public class SysAuthServiceImpl implements SysAuthService {
160 160
161 @Override 161 @Override
162 public void logout(String token) { 162 public void logout(String token) {
163 -// AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); TODO 需要搞一搞 163 + // 查询用户信息
  164 + LoginUser loginUser = userSessionService.getLoginUser(token);
  165 + if (loginUser == null) {
  166 + return;
  167 + }
  168 + // 删除 session
  169 + userSessionService.deleteUserSession(token);
  170 + // 记录登出日子和
  171 + this.createLogoutLog(loginUser.getUsername());
  172 + }
  173 +
  174 + private void createLogoutLog(String username) {
  175 + SysLoginLogCreateReqVO reqVO = new SysLoginLogCreateReqVO();
  176 + reqVO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
  177 + reqVO.setTraceId(TracerUtils.getTraceId());
  178 + reqVO.setUsername(username);
  179 + reqVO.setUserAgent(ServletUtils.getUserAgent());
  180 + reqVO.setUserIp(ServletUtils.getClientIP());
  181 + reqVO.setResult(SysLoginResultEnum.SUCCESS.getResult());
  182 + loginLogService.createLoginLog(reqVO);
164 } 183 }
165 184
166 @Override 185 @Override
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java
@@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.dict.impl; @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.dict.impl;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
5 -import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;  
6 import cn.iocoder.dashboard.common.pojo.PageResult; 5 import cn.iocoder.dashboard.common.pojo.PageResult;
7 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; 6 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
8 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO; 7 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO;
@@ -10,12 +9,13 @@ import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataEx @@ -10,12 +9,13 @@ import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataEx
10 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataPageReqVO; 9 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataPageReqVO;
11 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO; 10 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO;
12 import cn.iocoder.dashboard.modules.system.convert.dict.SysDictDataConvert; 11 import cn.iocoder.dashboard.modules.system.convert.dict.SysDictDataConvert;
13 -import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper;  
14 import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictDataDO; 12 import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictDataDO;
15 import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; 13 import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
  14 +import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper;
16 import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; 15 import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer;
17 import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; 16 import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService;
18 import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; 17 import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService;
  18 +import com.google.common.annotations.VisibleForTesting;
19 import com.google.common.collect.ImmutableTable; 19 import com.google.common.collect.ImmutableTable;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.springframework.scheduling.annotation.Scheduled; 21 import org.springframework.scheduling.annotation.Scheduled;
@@ -28,6 +28,7 @@ import java.util.Comparator; @@ -28,6 +28,7 @@ import java.util.Comparator;
28 import java.util.Date; 28 import java.util.Date;
29 import java.util.List; 29 import java.util.List;
30 30
  31 +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
31 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; 32 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
32 33
33 /** 34 /**
@@ -156,7 +157,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { @@ -156,7 +157,7 @@ public class SysDictDataServiceImpl implements SysDictDataService {
156 @Override 157 @Override
157 public Long createDictData(SysDictDataCreateReqVO reqVO) { 158 public Long createDictData(SysDictDataCreateReqVO reqVO) {
158 // 校验正确性 159 // 校验正确性
159 - this.checkCreateOrUpdate(null, reqVO.getLabel(), reqVO.getDictType()); 160 + this.checkCreateOrUpdate(null, reqVO.getValue(), reqVO.getDictType());
160 // 插入字典类型 161 // 插入字典类型
161 SysDictDataDO dictData = SysDictDataConvert.INSTANCE.convert(reqVO); 162 SysDictDataDO dictData = SysDictDataConvert.INSTANCE.convert(reqVO);
162 dictDataMapper.insert(dictData); 163 dictDataMapper.insert(dictData);
@@ -168,7 +169,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { @@ -168,7 +169,7 @@ public class SysDictDataServiceImpl implements SysDictDataService {
168 @Override 169 @Override
169 public void updateDictData(SysDictDataUpdateReqVO reqVO) { 170 public void updateDictData(SysDictDataUpdateReqVO reqVO) {
170 // 校验正确性 171 // 校验正确性
171 - this.checkCreateOrUpdate(reqVO.getId(), reqVO.getLabel(), reqVO.getDictType()); 172 + this.checkCreateOrUpdate(reqVO.getId(), reqVO.getValue(), reqVO.getDictType());
172 // 更新字典类型 173 // 更新字典类型
173 SysDictDataDO updateObj = SysDictDataConvert.INSTANCE.convert(reqVO); 174 SysDictDataDO updateObj = SysDictDataConvert.INSTANCE.convert(reqVO);
174 dictDataMapper.updateById(updateObj); 175 dictDataMapper.updateById(updateObj);
@@ -191,46 +192,49 @@ public class SysDictDataServiceImpl implements SysDictDataService { @@ -191,46 +192,49 @@ public class SysDictDataServiceImpl implements SysDictDataService {
191 return dictDataMapper.selectCountByDictType(dictType); 192 return dictDataMapper.selectCountByDictType(dictType);
192 } 193 }
193 194
194 - private void checkCreateOrUpdate(Long id, String label, String dictType) { 195 + private void checkCreateOrUpdate(Long id, String value, String dictType) {
195 // 校验自己存在 196 // 校验自己存在
196 checkDictDataExists(id); 197 checkDictDataExists(id);
197 // 校验字典类型有效 198 // 校验字典类型有效
198 checkDictTypeValid(dictType); 199 checkDictTypeValid(dictType);
199 // 校验字典数据的值的唯一性 200 // 校验字典数据的值的唯一性
200 - checkDictDataValueUnique(id, dictType, label); 201 + checkDictDataValueUnique(id, dictType, value);
201 } 202 }
202 203
203 - private void checkDictDataValueUnique(Long id, String dictType, String label) {  
204 - SysDictDataDO dictData = dictDataMapper.selectByDictTypeAndLabel(dictType, label); 204 + @VisibleForTesting
  205 + public void checkDictDataValueUnique(Long id, String dictType, String value) {
  206 + SysDictDataDO dictData = dictDataMapper.selectByDictTypeAndValue(dictType, value);
205 if (dictData == null) { 207 if (dictData == null) {
206 return; 208 return;
207 } 209 }
208 // 如果 id 为空,说明不用比较是否为相同 id 的字典数据 210 // 如果 id 为空,说明不用比较是否为相同 id 的字典数据
209 if (id == null) { 211 if (id == null) {
210 - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); 212 + throw exception(DICT_DATA_VALUE_DUPLICATE);
211 } 213 }
212 if (!dictData.getId().equals(id)) { 214 if (!dictData.getId().equals(id)) {
213 - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); 215 + throw exception(DICT_DATA_VALUE_DUPLICATE);
214 } 216 }
215 } 217 }
216 218
217 - private void checkDictDataExists(Long id) { 219 + @VisibleForTesting
  220 + public void checkDictDataExists(Long id) {
218 if (id == null) { 221 if (id == null) {
219 return; 222 return;
220 } 223 }
221 SysDictDataDO dictData = dictDataMapper.selectById(id); 224 SysDictDataDO dictData = dictDataMapper.selectById(id);
222 if (dictData == null) { 225 if (dictData == null) {
223 - throw ServiceExceptionUtil.exception(DICT_DATA_NOT_EXISTS); 226 + throw exception(DICT_DATA_NOT_EXISTS);
224 } 227 }
225 } 228 }
226 229
227 - private void checkDictTypeValid(String type) { 230 + @VisibleForTesting
  231 + public void checkDictTypeValid(String type) {
228 SysDictTypeDO dictType = dictTypeService.getDictType(type); 232 SysDictTypeDO dictType = dictTypeService.getDictType(type);
229 if (dictType == null) { 233 if (dictType == null) {
230 - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_EXISTS); 234 + throw exception(DICT_TYPE_NOT_EXISTS);
231 } 235 }
232 if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) { 236 if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) {
233 - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_ENABLE); 237 + throw exception(DICT_TYPE_NOT_ENABLE);
234 } 238 }
235 } 239 }
236 240
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java
@@ -10,6 +10,7 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; @@ -10,6 +10,7 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
10 import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictTypeMapper; 10 import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictTypeMapper;
11 import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; 11 import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService;
12 import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; 12 import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService;
  13 +import com.google.common.annotations.VisibleForTesting;
13 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
14 15
15 import javax.annotation.Resource; 16 import javax.annotation.Resource;
@@ -97,8 +98,9 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { @@ -97,8 +98,9 @@ public class SysDictTypeServiceImpl implements SysDictTypeService {
97 checkDictTypeUnique(id, type); 98 checkDictTypeUnique(id, type);
98 } 99 }
99 100
100 - private void checkDictTypeNameUnique(Long id, String type) {  
101 - SysDictTypeDO dictType = dictTypeMapper.selectByName(type); 101 + @VisibleForTesting
  102 + public void checkDictTypeNameUnique(Long id, String name) {
  103 + SysDictTypeDO dictType = dictTypeMapper.selectByName(name);
102 if (dictType == null) { 104 if (dictType == null) {
103 return; 105 return;
104 } 106 }
@@ -111,7 +113,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { @@ -111,7 +113,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService {
111 } 113 }
112 } 114 }
113 115
114 - private void checkDictTypeUnique(Long id, String type) { 116 + @VisibleForTesting
  117 + public void checkDictTypeUnique(Long id, String type) {
115 SysDictTypeDO dictType = dictTypeMapper.selectByType(type); 118 SysDictTypeDO dictType = dictTypeMapper.selectByType(type);
116 if (dictType == null) { 119 if (dictType == null) {
117 return; 120 return;
@@ -125,7 +128,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { @@ -125,7 +128,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService {
125 } 128 }
126 } 129 }
127 130
128 - private SysDictTypeDO checkDictTypeExists(Long id) { 131 + @VisibleForTesting
  132 + public SysDictTypeDO checkDictTypeExists(Long id) {
129 if (id == null) { 133 if (id == null) {
130 return null; 134 return null;
131 } 135 }
src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java
@@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection;
2 2
3 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
4 4
  5 +import java.util.function.Consumer;
  6 +
5 /** 7 /**
6 * Array 工具类 8 * Array 工具类
7 * 9 *
@@ -18,11 +20,11 @@ public class ArrayUtils { @@ -18,11 +20,11 @@ public class ArrayUtils {
18 * @return 结果数组 20 * @return 结果数组
19 */ 21 */
20 @SafeVarargs 22 @SafeVarargs
21 - public static <T> T[] append(T object, T... newElements) { 23 + public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {
22 if (object == null) { 24 if (object == null) {
23 return newElements; 25 return newElements;
24 } 26 }
25 - T[] result = ArrayUtil.newArray(object.getClass(), 1 + newElements.length); 27 + Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length);
26 result[0] = object; 28 result[0] = object;
27 System.arraycopy(newElements, 0, result, 1, newElements.length); 29 System.arraycopy(newElements, 0, result, 1, newElements.length);
28 return result; 30 return result;
src/main/java/cn/iocoder/dashboard/util/object/ObjectUtils.java
@@ -19,4 +19,14 @@ public class ObjectUtils { @@ -19,4 +19,14 @@ public class ObjectUtils {
19 return result; 19 return result;
20 } 20 }
21 21
  22 + public static <T extends Comparable<T>> T max(T obj1, T obj2) {
  23 + if (obj1 == null) {
  24 + return obj2;
  25 + }
  26 + if (obj2 == null) {
  27 + return obj1;
  28 + }
  29 + return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
  30 + }
  31 +
22 } 32 }
src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java 0 → 100644
  1 +package cn.iocoder.dashboard;
  2 +
  3 +import cn.iocoder.dashboard.config.RedisTestConfiguration;
  4 +import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
  5 +import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
  6 +import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
  7 +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
  8 +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
  9 +import org.redisson.spring.starter.RedissonAutoConfiguration;
  10 +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
  11 +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  12 +import org.springframework.boot.test.context.SpringBootTest;
  13 +import org.springframework.context.annotation.Import;
  14 +import org.springframework.test.context.ActiveProfiles;
  15 +import org.springframework.test.context.jdbc.Sql;
  16 +
  17 +/**
  18 + * 依赖内存 DB 的单元测试
  19 + *
  20 + * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class)
  25 +@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
  26 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
  27 +public class BaseDbAndRedisUnitTest {
  28 +
  29 + @Import({
  30 + // DB 配置类
  31 + DataSourceConfiguration.class, // 自己的 DB 配置类
  32 + DataSourceAutoConfiguration.class, // Spring DB 自动配置类
  33 + DruidDataSourceAutoConfigure.class, // Druid 自动配置类
  34 + // MyBatis 配置类
  35 + MybatisConfiguration.class, // 自己的 MyBatis 配置类
  36 + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
  37 + // Redis 配置类
  38 + RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
  39 + RedisAutoConfiguration.class, // Spring Redis 自动配置类
  40 + RedisConfig.class, // 自己的 Redis 配置类
  41 + RedissonAutoConfiguration.class, // Redisson 自动高配置类
  42 + })
  43 + public static class Application {
  44 + }
  45 +
  46 +}
src/test/java/cn/iocoder/dashboard/BaseDbUnitTest.java 0 → 100644
  1 +package cn.iocoder.dashboard;
  2 +
  3 +import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration;
  4 +import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration;
  5 +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
  6 +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
  7 +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
  8 +import org.springframework.boot.test.context.SpringBootTest;
  9 +import org.springframework.context.annotation.Import;
  10 +import org.springframework.test.context.ActiveProfiles;
  11 +import org.springframework.test.context.jdbc.Sql;
  12 +
  13 +/**
  14 + * 依赖内存 DB 的单元测试
  15 + *
  16 + * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
  21 +@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
  22 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
  23 +public class BaseDbUnitTest {
  24 +
  25 + @Import({
  26 + // DB 配置类
  27 + DataSourceConfiguration.class, // 自己的 DB 配置类
  28 + DataSourceAutoConfiguration.class, // Spring DB 自动配置类
  29 + DruidDataSourceAutoConfigure.class, // Druid 自动配置类
  30 + // MyBatis 配置类
  31 + MybatisConfiguration.class, // 自己的 MyBatis 配置类
  32 + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
  33 + })
  34 + public static class Application {
  35 + }
  36 +
  37 +}
src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java
@@ -12,6 +12,7 @@ import javax.annotation.Resource; @@ -12,6 +12,7 @@ import javax.annotation.Resource;
12 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) 12 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
13 @ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 13 @ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
14 @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB 14 @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
  15 +@Deprecated
15 public class BaseSpringBootUnitTest { 16 public class BaseSpringBootUnitTest {
16 17
17 @Resource 18 @Resource
src/test/java/cn/iocoder/dashboard/config/RedisTestConfiguration.java
@@ -8,12 +8,10 @@ import org.springframework.boot.autoconfigure.data.redis.RedisProperties; @@ -8,12 +8,10 @@ import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
8 import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 import org.springframework.boot.context.properties.EnableConfigurationProperties;
9 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Bean;
10 import org.springframework.context.annotation.Configuration; 10 import org.springframework.context.annotation.Configuration;
11 -import org.springframework.context.annotation.Lazy;  
12 11
13 import java.io.IOException; 12 import java.io.IOException;
14 13
15 @Configuration(proxyBeanMethods = false) 14 @Configuration(proxyBeanMethods = false)
16 -@Lazy(false) // 禁用懒加载,因为需要保证 Redis Server 必须先启动  
17 @EnableConfigurationProperties(RedisProperties.class) 15 @EnableConfigurationProperties(RedisProperties.class)
18 @AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化 16 @AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化
19 public class RedisTestConfiguration { 17 public class RedisTestConfiguration {
src/test/java/cn/iocoder/dashboard/config/SecurityTestConfiguration.java deleted
1 -package cn.iocoder.dashboard.config;  
2 -  
3 -import org.mockito.Mockito;  
4 -import org.springframework.context.annotation.Bean;  
5 -import org.springframework.context.annotation.Configuration;  
6 -import org.springframework.security.authentication.AuthenticationManager;  
7 -  
8 -@Configuration  
9 -public class SecurityTestConfiguration {  
10 -  
11 - @Bean  
12 - public AuthenticationManager authenticationManager() {  
13 - return Mockito.mock(AuthenticationManager.class);  
14 - }  
15 -  
16 -}  
src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java
1 package cn.iocoder.dashboard.modules.infra.service.config; 1 package cn.iocoder.dashboard.modules.infra.service.config;
2 2
3 -import cn.iocoder.dashboard.BaseSpringBootUnitTest; 3 +import cn.iocoder.dashboard.BaseDbUnitTest;
4 import cn.iocoder.dashboard.common.pojo.PageResult; 4 import cn.iocoder.dashboard.common.pojo.PageResult;
5 import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO; 5 import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO;
6 import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigExportReqVO; 6 import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigExportReqVO;
@@ -15,6 +15,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils; @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils;
15 import cn.iocoder.dashboard.util.object.ObjectUtils; 15 import cn.iocoder.dashboard.util.object.ObjectUtils;
16 import org.junit.jupiter.api.Test; 16 import org.junit.jupiter.api.Test;
17 import org.springframework.boot.test.mock.mockito.MockBean; 17 import org.springframework.boot.test.mock.mockito.MockBean;
  18 +import org.springframework.context.annotation.Import;
18 19
19 import javax.annotation.Resource; 20 import javax.annotation.Resource;
20 import java.util.List; 21 import java.util.List;
@@ -24,8 +25,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle; @@ -24,8 +25,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
24 import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*; 25 import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*;
25 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; 26 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
26 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; 27 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
27 -import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;  
28 -import static cn.iocoder.dashboard.util.RandomUtils.randomPojo; 28 +import static cn.iocoder.dashboard.util.RandomUtils.*;
29 import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; 29 import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
30 import static org.junit.jupiter.api.Assertions.*; 30 import static org.junit.jupiter.api.Assertions.*;
31 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.times;
@@ -36,7 +36,8 @@ import static org.mockito.Mockito.verify; @@ -36,7 +36,8 @@ import static org.mockito.Mockito.verify;
36 * 36 *
37 * @author 芋道源码 37 * @author 芋道源码
38 */ 38 */
39 -public class InfConfigServiceTest extends BaseSpringBootUnitTest { 39 +@Import(InfConfigServiceImpl.class)
  40 +public class InfConfigServiceTest extends BaseDbUnitTest {
40 41
41 @Resource 42 @Resource
42 private InfConfigServiceImpl configService; 43 private InfConfigServiceImpl configService;
@@ -146,19 +147,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { @@ -146,19 +147,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
146 } 147 }
147 148
148 @Test 149 @Test
149 - public void testCreateConfig_keyDuplicate() {  
150 - // 准备参数  
151 - InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class);  
152 - // mock 数据  
153 - configMapper.insert(randomInfConfigDO(o -> { // @Sql  
154 - o.setKey(reqVO.getKey()); // 模拟 key 重复  
155 - }));  
156 -  
157 - // 调用, 并断言异常  
158 - assertServiceException(() -> configService.createConfig(reqVO), CONFIG_KEY_DUPLICATE);  
159 - }  
160 -  
161 - @Test  
162 public void testUpdateConfig_success() { 150 public void testUpdateConfig_success() {
163 // mock 数据 151 // mock 数据
164 InfConfigDO dbConfig = randomInfConfigDO(); 152 InfConfigDO dbConfig = randomInfConfigDO();
@@ -178,15 +166,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { @@ -178,15 +166,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
178 } 166 }
179 167
180 @Test 168 @Test
181 - public void testUpdateConfig_notExists() {  
182 - // 准备参数  
183 - InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class);  
184 -  
185 - // 调用, 并断言异常  
186 - assertServiceException(() -> configService.updateConfig(reqVO), CONFIG_NOT_EXISTS);  
187 - }  
188 -  
189 - @Test  
190 public void testDeleteConfig_success() { 169 public void testDeleteConfig_success() {
191 // mock 数据 170 // mock 数据
192 InfConfigDO dbConfig = randomInfConfigDO(o -> { 171 InfConfigDO dbConfig = randomInfConfigDO(o -> {
@@ -219,12 +198,49 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { @@ -219,12 +198,49 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest {
219 } 198 }
220 199
221 @Test 200 @Test
222 - public void testDeleteConfig_notExists() { 201 + public void testCheckConfigExists_success() {
  202 + // mock 数据
  203 + InfConfigDO dbConfigDO = randomInfConfigDO();
  204 + configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据
  205 +
  206 + // 调用成功
  207 + configService.checkConfigExists(dbConfigDO.getId());
  208 + }
  209 +
  210 + @Test
  211 + public void testCheckConfigExist_notExists() {
  212 + assertServiceException(() -> configService.checkConfigExists(randomLongId()), CONFIG_NOT_EXISTS);
  213 + }
  214 +
  215 + @Test
  216 + public void testCheckConfigKeyUnique_success() {
  217 + // 调用,成功
  218 + configService.checkConfigKeyUnique(randomLongId(), randomString());
  219 + }
  220 +
  221 + @Test
  222 + public void testCheckConfigKeyUnique_keyDuplicateForCreate() {
  223 + // 准备参数
  224 + String key = randomString();
  225 + // mock 数据
  226 + configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
  227 +
  228 + // 调用,校验异常
  229 + assertServiceException(() -> configService.checkConfigKeyUnique(null, key),
  230 + CONFIG_KEY_DUPLICATE);
  231 + }
  232 +
  233 + @Test
  234 + public void testCheckConfigKeyUnique_keyDuplicateForUpdate() {
223 // 准备参数 235 // 准备参数
224 Long id = randomLongId(); 236 Long id = randomLongId();
  237 + String key = randomString();
  238 + // mock 数据
  239 + configMapper.insert(randomInfConfigDO(o -> o.setKey(key)));
225 240
226 - // 调用, 并断言异常  
227 - assertServiceException(() -> configService.deleteConfig(id), CONFIG_NOT_EXISTS); 241 + // 调用,校验异常
  242 + assertServiceException(() -> configService.checkConfigKeyUnique(id, key),
  243 + CONFIG_KEY_DUPLICATE);
228 } 244 }
229 245
230 // ========== 随机对象 ========== 246 // ========== 随机对象 ==========
src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java
1 package cn.iocoder.dashboard.modules.system.service.auth; 1 package cn.iocoder.dashboard.modules.system.service.auth;
2 2
3 -import cn.iocoder.dashboard.BaseSpringBootUnitTest; 3 +import cn.iocoder.dashboard.BaseDbUnitTest;
4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
5 import cn.iocoder.dashboard.framework.security.core.LoginUser; 5 import cn.iocoder.dashboard.framework.security.core.LoginUser;
6 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; 6 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
7 import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl; 7 import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl;
  8 +import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService;
  9 +import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
8 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; 10 import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
9 import cn.iocoder.dashboard.modules.system.service.user.SysUserService; 11 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
10 import cn.iocoder.dashboard.util.AssertUtils; 12 import cn.iocoder.dashboard.util.AssertUtils;
11 import org.junit.jupiter.api.Test; 13 import org.junit.jupiter.api.Test;
12 import org.springframework.boot.test.mock.mockito.MockBean; 14 import org.springframework.boot.test.mock.mockito.MockBean;
  15 +import org.springframework.context.annotation.Import;
  16 +import org.springframework.security.authentication.AuthenticationManager;
13 import org.springframework.security.core.userdetails.UsernameNotFoundException; 17 import org.springframework.security.core.userdetails.UsernameNotFoundException;
14 18
15 import javax.annotation.Resource; 19 import javax.annotation.Resource;
@@ -21,7 +25,13 @@ import static org.junit.jupiter.api.Assertions.*; @@ -21,7 +25,13 @@ import static org.junit.jupiter.api.Assertions.*;
21 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.ArgumentMatchers.eq;
22 import static org.mockito.Mockito.when; 26 import static org.mockito.Mockito.when;
23 27
24 -public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { 28 +/**
  29 + * {@link SysAuthServiceImpl} 的单元测试
  30 + *
  31 + * @author 芋道源码
  32 + */
  33 +@Import(SysAuthServiceImpl.class)
  34 +public class SysAuthServiceImplTest extends BaseDbUnitTest {
25 35
26 @Resource 36 @Resource
27 private SysAuthServiceImpl authService; 37 private SysAuthServiceImpl authService;
@@ -30,6 +40,14 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { @@ -30,6 +40,14 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest {
30 private SysUserService userService; 40 private SysUserService userService;
31 @MockBean 41 @MockBean
32 private SysPermissionService permissionService; 42 private SysPermissionService permissionService;
  43 + @MockBean
  44 + private AuthenticationManager authenticationManager;
  45 + @MockBean
  46 + private SysCaptchaService captchaService;
  47 + @MockBean
  48 + private SysLoginLogService loginLogService;
  49 + @MockBean
  50 + private SysUserSessionService userSessionService;
33 51
34 @Test 52 @Test
35 public void testLoadUserByUsername_success() { 53 public void testLoadUserByUsername_success() {
src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java
1 package cn.iocoder.dashboard.modules.system.service.dict; 1 package cn.iocoder.dashboard.modules.system.service.dict;
2 2
3 -import cn.iocoder.dashboard.BaseSpringBootUnitTest; 3 +import cn.iocoder.dashboard.BaseDbUnitTest;
4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
5 import cn.iocoder.dashboard.common.pojo.PageResult; 5 import cn.iocoder.dashboard.common.pojo.PageResult;
6 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO;
@@ -12,14 +12,20 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; @@ -12,14 +12,20 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO;
12 import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; 12 import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper;
13 import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; 13 import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer;
14 import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl; 14 import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl;
  15 +import cn.iocoder.dashboard.util.collection.ArrayUtils;
15 import cn.iocoder.dashboard.util.object.ObjectUtils; 16 import cn.iocoder.dashboard.util.object.ObjectUtils;
  17 +import com.google.common.collect.ImmutableTable;
16 import org.junit.jupiter.api.Test; 18 import org.junit.jupiter.api.Test;
17 import org.springframework.boot.test.mock.mockito.MockBean; 19 import org.springframework.boot.test.mock.mockito.MockBean;
  20 +import org.springframework.context.annotation.Import;
18 21
19 import javax.annotation.Resource; 22 import javax.annotation.Resource;
  23 +import java.util.Date;
20 import java.util.List; 24 import java.util.List;
  25 +import java.util.function.Consumer;
21 26
22 -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS; 27 +import static cn.hutool.core.bean.BeanUtil.getFieldValue;
  28 +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
23 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; 29 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
24 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; 30 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
25 import static cn.iocoder.dashboard.util.RandomUtils.*; 31 import static cn.iocoder.dashboard.util.RandomUtils.*;
@@ -32,7 +38,8 @@ import static org.mockito.Mockito.*; @@ -32,7 +38,8 @@ import static org.mockito.Mockito.*;
32 * 38 *
33 * @author 芋道源码 39 * @author 芋道源码
34 */ 40 */
35 -public class SysDictDataServiceTest extends BaseSpringBootUnitTest { 41 +@Import(SysDictDataServiceImpl.class)
  42 +public class SysDictDataServiceTest extends BaseDbUnitTest {
36 43
37 @Resource 44 @Resource
38 private SysDictDataServiceImpl dictDataService; 45 private SysDictDataServiceImpl dictDataService;
@@ -44,6 +51,37 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { @@ -44,6 +51,37 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
44 @MockBean 51 @MockBean
45 private SysDictDataProducer dictDataProducer; 52 private SysDictDataProducer dictDataProducer;
46 53
  54 + /**
  55 + * 测试加载到新的字典数据的情况
  56 + */
  57 + @Test
  58 + @SuppressWarnings("unchecked")
  59 + public void testInitLocalCache() {
  60 + // mock 数据
  61 + SysDictDataDO dictData01 = randomDictDataDO();
  62 + dictDataMapper.insert(dictData01);
  63 + SysDictDataDO dictData02 = randomDictDataDO();
  64 + dictDataMapper.insert(dictData02);
  65 +
  66 + // 调用
  67 + dictDataService.initLocalCache();
  68 + // 断言 labelDictDataCache 缓存
  69 + ImmutableTable<String, String, SysDictDataDO> labelDictDataCache =
  70 + (ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "labelDictDataCache");
  71 + assertEquals(2, labelDictDataCache.size());
  72 + assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel()));
  73 + assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel()));
  74 + // 断言 valueDictDataCache 缓存
  75 + ImmutableTable<String, String, SysDictDataDO> valueDictDataCache =
  76 + (ImmutableTable<String, String, SysDictDataDO>) getFieldValue(dictDataService, "valueDictDataCache");
  77 + assertEquals(2, valueDictDataCache.size());
  78 + assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue()));
  79 + assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue()));
  80 + // 断言 maxUpdateTime 缓存
  81 + Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime");
  82 + assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime);
  83 + }
  84 +
47 @Test 85 @Test
48 public void testGetDictDataPage() { 86 public void testGetDictDataPage() {
49 // mock 数据 87 // mock 数据
@@ -107,8 +145,7 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { @@ -107,8 +145,7 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
107 SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, 145 SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class,
108 o -> o.setStatus(randomCommonStatus())); 146 o -> o.setStatus(randomCommonStatus()));
109 // mock 方法 147 // mock 方法
110 - when(dictTypeService.getDictType(eq(reqVO.getDictType())))  
111 - .thenReturn(randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); 148 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
112 149
113 // 调用 150 // 调用
114 Long dictDataId = dictDataService.createDictData(reqVO); 151 Long dictDataId = dictDataService.createDictData(reqVO);
@@ -124,50 +161,142 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { @@ -124,50 +161,142 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest {
124 @Test 161 @Test
125 public void testUpdateDictData_success() { 162 public void testUpdateDictData_success() {
126 // mock 数据 163 // mock 数据
127 - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); 164 + SysDictDataDO dbDictData = randomDictDataDO();
128 dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 165 dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
129 // 准备参数 166 // 准备参数
130 SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { 167 SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> {
131 o.setId(dbDictData.getId()); // 设置更新的 ID 168 o.setId(dbDictData.getId()); // 设置更新的 ID
  169 + o.setStatus(randomCommonStatus());
132 }); 170 });
  171 + // mock 方法,字典类型
  172 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
133 173
134 // 调用 174 // 调用
135 dictDataService.updateDictData(reqVO); 175 dictDataService.updateDictData(reqVO);
136 // 校验是否更新正确 176 // 校验是否更新正确
137 SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 177 SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
138 assertPojoEquals(reqVO, dictData); 178 assertPojoEquals(reqVO, dictData);
  179 + // 校验调用
  180 + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
139 } 181 }
140 182
141 @Test 183 @Test
142 - public void testUpdateDictData_notExists() { 184 + public void testDeleteDictData_success() {
  185 + // mock 数据
  186 + SysDictDataDO dbDictData = randomDictDataDO();
  187 + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
143 // 准备参数 188 // 准备参数
144 - SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class); 189 + Long id = dbDictData.getId();
145 190
146 - // 调用, 并断言异常  
147 - assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_NOT_EXISTS); 191 + // 调用
  192 + dictDataService.deleteDictData(id);
  193 + // 校验数据不存在了
  194 + assertNull(dictDataMapper.selectById(id));
  195 + // 校验调用
  196 + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage();
148 } 197 }
149 198
150 @Test 199 @Test
151 - public void testDeleteDictData_success() { 200 + public void testCheckDictDataExists_success() {
152 // mock 数据 201 // mock 数据
153 - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); 202 + SysDictDataDO dbDictData = randomDictDataDO();
154 dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 203 dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
  204 +
  205 + // 调用成功
  206 + dictDataService.checkDictDataExists(dbDictData.getId());
  207 + }
  208 +
  209 + @Test
  210 + public void testCheckDictDataExists_notExists() {
  211 + assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
  212 + }
  213 +
  214 + @Test
  215 + public void testCheckDictTypeValid_success() {
  216 + // mock 方法,数据类型被禁用
  217 + String type = randomString();
  218 + when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
  219 +
  220 + // 调用, 成功
  221 + dictDataService.checkDictTypeValid(type);
  222 + }
  223 +
  224 + @Test
  225 + public void testCheckDictTypeValid_notExists() {
  226 + assertServiceException(() -> dictDataService.checkDictTypeValid(randomString()), DICT_TYPE_NOT_EXISTS);
  227 + }
  228 +
  229 + @Test
  230 + public void testCheckDictTypeValid_notEnable() {
  231 + // mock 方法,数据类型被禁用
  232 + String dictType = randomString();
  233 + when(dictTypeService.getDictType(eq(dictType))).thenReturn(
  234 + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  235 +
  236 + // 调用, 并断言异常
  237 + assertServiceException(() -> dictDataService.checkDictTypeValid(dictType), DICT_TYPE_NOT_ENABLE);
  238 + }
  239 +
  240 + @Test
  241 + public void testCheckDictDataValueUnique_success() {
  242 + // 调用,成功
  243 + dictDataService.checkDictDataValueUnique(randomLongId(), randomString(), randomString());
  244 + }
  245 +
  246 + @Test
  247 + public void testCheckDictDataValueUnique_valueDuplicateForCreate() {
155 // 准备参数 248 // 准备参数
156 - Long id = dbDictData.getId(); 249 + String dictType = randomString();
  250 + String value = randomString();
  251 + // mock 数据
  252 + dictDataMapper.insert(randomDictDataDO(o -> {
  253 + o.setDictType(dictType);
  254 + o.setValue(value);
  255 + }));
157 256
158 - // 调用  
159 - dictDataService.deleteDictData(id);  
160 - // 校验数据不存在了  
161 - assertNull(dictDataMapper.selectById(id)); 257 + // 调用,校验异常
  258 + assertServiceException(() -> dictDataService.checkDictDataValueUnique(null, dictType, value),
  259 + DICT_DATA_VALUE_DUPLICATE);
162 } 260 }
163 261
164 @Test 262 @Test
165 - public void testDeleteDictData_notExists() { 263 + public void testCheckDictDataValueUnique_valueDuplicateForUpdate() {
166 // 准备参数 264 // 准备参数
167 Long id = randomLongId(); 265 Long id = randomLongId();
  266 + String dictType = randomString();
  267 + String value = randomString();
  268 + // mock 数据
  269 + dictDataMapper.insert(randomDictDataDO(o -> {
  270 + o.setDictType(dictType);
  271 + o.setValue(value);
  272 + }));
168 273
169 - // 调用, 并断言异常  
170 - assertServiceException(() -> dictDataService.deleteDictData(id), DICT_DATA_NOT_EXISTS); 274 + // 调用,校验异常
  275 + assertServiceException(() -> dictDataService.checkDictDataValueUnique(id, dictType, value),
  276 + DICT_DATA_VALUE_DUPLICATE);
  277 + }
  278 +
  279 + // ========== 随机对象 ==========
  280 +
  281 + @SafeVarargs
  282 + private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) {
  283 + Consumer<SysDictDataDO> consumer = (o) -> {
  284 + o.setStatus(randomCommonStatus()); // 保证 status 的范围
  285 + };
  286 + return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers));
  287 + }
  288 +
  289 + /**
  290 + * 生成一个有效的字典类型
  291 + *
  292 + * @param type 字典类型
  293 + * @return SysDictTypeDO 对象
  294 + */
  295 + private static SysDictTypeDO randomDictTypeDO(String type) {
  296 + return randomPojo(SysDictTypeDO.class, o -> {
  297 + o.setType(type);
  298 + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
  299 + });
171 } 300 }
172 301
173 } 302 }
src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java
1 package cn.iocoder.dashboard.modules.system.service.dict; 1 package cn.iocoder.dashboard.modules.system.service.dict;
2 2
3 -import cn.iocoder.dashboard.BaseSpringBootUnitTest; 3 +import cn.iocoder.dashboard.BaseDbUnitTest;
4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 4 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
5 import cn.iocoder.dashboard.common.pojo.PageResult; 5 import cn.iocoder.dashboard.common.pojo.PageResult;
6 import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO;
@@ -14,6 +14,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils; @@ -14,6 +14,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils;
14 import cn.iocoder.dashboard.util.object.ObjectUtils; 14 import cn.iocoder.dashboard.util.object.ObjectUtils;
15 import org.junit.jupiter.api.Test; 15 import org.junit.jupiter.api.Test;
16 import org.springframework.boot.test.mock.mockito.MockBean; 16 import org.springframework.boot.test.mock.mockito.MockBean;
  17 +import org.springframework.context.annotation.Import;
17 18
18 import javax.annotation.Resource; 19 import javax.annotation.Resource;
19 import java.util.List; 20 import java.util.List;
@@ -23,8 +24,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle; @@ -23,8 +24,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle;
23 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; 24 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
24 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; 25 import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
25 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; 26 import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
26 -import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;  
27 -import static cn.iocoder.dashboard.util.RandomUtils.randomPojo; 27 +import static cn.iocoder.dashboard.util.RandomUtils.*;
28 import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; 28 import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
29 import static org.junit.jupiter.api.Assertions.*; 29 import static org.junit.jupiter.api.Assertions.*;
30 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.ArgumentMatchers.eq;
@@ -35,7 +35,8 @@ import static org.mockito.Mockito.when; @@ -35,7 +35,8 @@ import static org.mockito.Mockito.when;
35 * 35 *
36 * @author 芋道源码 36 * @author 芋道源码
37 */ 37 */
38 -public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { 38 +@Import(SysDictTypeServiceImpl.class)
  39 +public class SysDictTypeServiceTest extends BaseDbUnitTest {
39 40
40 @Resource 41 @Resource
41 private SysDictTypeServiceImpl dictTypeService; 42 private SysDictTypeServiceImpl dictTypeService;
@@ -143,32 +144,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { @@ -143,32 +144,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
143 } 144 }
144 145
145 @Test 146 @Test
146 - public void testCreateDictType_nameDuplicate() {  
147 - // mock 数据  
148 - SysDictTypeDO dbDictType = randomDictTypeDO();  
149 - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据  
150 - // 准备参数  
151 - SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class,  
152 - o -> o.setName(dbDictType.getName())); // 模拟 name 重复  
153 -  
154 - // 调用, 并断言异常  
155 - assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_NAME_DUPLICATE);  
156 - }  
157 -  
158 - @Test  
159 - public void testCreateDictType_typeDuplicate() {  
160 - // mock 数据  
161 - SysDictTypeDO dbDictType = randomDictTypeDO();  
162 - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据  
163 - // 准备参数  
164 - SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class,  
165 - o -> o.setType(dbDictType.getType())); // 模拟 type 重复  
166 -  
167 - // 调用, 并断言异常  
168 - assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_TYPE_DUPLICATE);  
169 - }  
170 -  
171 - @Test  
172 public void testUpdateDictType_success() { 147 public void testUpdateDictType_success() {
173 // mock 数据 148 // mock 数据
174 SysDictTypeDO dbDictType = randomDictTypeDO(); 149 SysDictTypeDO dbDictType = randomDictTypeDO();
@@ -187,67 +162,108 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { @@ -187,67 +162,108 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest {
187 } 162 }
188 163
189 @Test 164 @Test
190 - public void testUpdateDictType_notExists() { 165 + public void testDeleteDictType_success() {
  166 + // mock 数据
  167 + SysDictTypeDO dbDictType = randomDictTypeDO();
  168 + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
191 // 准备参数 169 // 准备参数
192 - SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class); 170 + Long id = dbDictType.getId();
193 171
194 - // 调用, 并断言异常  
195 - assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NOT_EXISTS); 172 + // 调用
  173 + dictTypeService.deleteDictType(id);
  174 + // 校验数据不存在了
  175 + assertNull(dictTypeMapper.selectById(id));
196 } 176 }
197 177
198 @Test 178 @Test
199 - public void testUpdateDictType_nameDuplicate() {  
200 - // mock 数据,稍后更新它 179 + public void testDeleteDictType_hasChildren() {
  180 + // mock 数据
201 SysDictTypeDO dbDictType = randomDictTypeDO(); 181 SysDictTypeDO dbDictType = randomDictTypeDO();
202 - dictTypeMapper.insert(dbDictType);  
203 - // mock 数据,ks稍后模拟重复它的名字  
204 - SysDictTypeDO nameDictType = randomDictTypeDO();  
205 - dictTypeMapper.insert(nameDictType); 182 + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
206 // 准备参数 183 // 准备参数
207 - SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class, o -> {  
208 - o.setId(dbDictType.getId()); // 设置更新的 ID  
209 - o.setName(nameDictType.getName()); // 模拟 name 重复  
210 - }); 184 + Long id = dbDictType.getId();
  185 + // mock 方法
  186 + when(dictDataService.countByDictType(eq(dbDictType.getType()))).thenReturn(1);
211 187
212 // 调用, 并断言异常 188 // 调用, 并断言异常
213 - assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NAME_DUPLICATE); 189 + assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN);
214 } 190 }
215 191
216 @Test 192 @Test
217 - public void testDeleteDictType_success() { 193 + public void testCheckDictDataExists_success() {
218 // mock 数据 194 // mock 数据
219 SysDictTypeDO dbDictType = randomDictTypeDO(); 195 SysDictTypeDO dbDictType = randomDictTypeDO();
220 dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 196 dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
  197 +
  198 + // 调用成功
  199 + dictTypeService.checkDictTypeExists(dbDictType.getId());
  200 + }
  201 +
  202 + @Test
  203 + public void testCheckDictDataExists_notExists() {
  204 + assertServiceException(() -> dictTypeService.checkDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS);
  205 + }
  206 +
  207 + @Test
  208 + public void testCheckDictTypeUnique_success() {
  209 + // 调用,成功
  210 + dictTypeService.checkDictTypeUnique(randomLongId(), randomString());
  211 + }
  212 +
  213 + @Test
  214 + public void testCheckDictTypeUnique_valueDuplicateForCreate() {
221 // 准备参数 215 // 准备参数
222 - Long id = dbDictType.getId(); 216 + String type = randomString();
  217 + // mock 数据
  218 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
223 219
224 - // 调用  
225 - dictTypeService.deleteDictType(id);  
226 - // 校验数据不存在了  
227 - assertNull(dictTypeMapper.selectById(id)); 220 + // 调用,校验异常
  221 + assertServiceException(() -> dictTypeService.checkDictTypeUnique(null, type),
  222 + DICT_TYPE_TYPE_DUPLICATE);
228 } 223 }
229 224
230 @Test 225 @Test
231 - public void testDeleteDictType_notExists() { 226 + public void testCheckDictTypeUnique_valueDuplicateForUpdate() {
232 // 准备参数 227 // 准备参数
233 Long id = randomLongId(); 228 Long id = randomLongId();
  229 + String type = randomString();
  230 + // mock 数据
  231 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
234 232
235 - // 调用, 并断言异常  
236 - assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_NOT_EXISTS); 233 + // 调用,校验异常
  234 + assertServiceException(() -> dictTypeService.checkDictTypeUnique(id, type),
  235 + DICT_TYPE_TYPE_DUPLICATE);
237 } 236 }
238 237
239 @Test 238 @Test
240 - public void testDeleteDictType_hasChildren() { 239 + public void testCheckDictTypNameUnique_success() {
  240 + // 调用,成功
  241 + dictTypeService.checkDictTypeNameUnique(randomLongId(), randomString());
  242 + }
  243 +
  244 + @Test
  245 + public void testCheckDictTypeNameUnique_nameDuplicateForCreate() {
  246 + // 准备参数
  247 + String name = randomString();
241 // mock 数据 248 // mock 数据
242 - SysDictTypeDO dbDictType = randomDictTypeDO();  
243 - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 249 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
  250 +
  251 + // 调用,校验异常
  252 + assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name),
  253 + DICT_TYPE_NAME_DUPLICATE);
  254 + }
  255 +
  256 + @Test
  257 + public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() {
244 // 准备参数 258 // 准备参数
245 - Long id = dbDictType.getId();  
246 - // mock 方法  
247 - when(dictDataService.countByDictType(eq(dbDictType.getType()))).thenReturn(1); 259 + Long id = randomLongId();
  260 + String name = randomString();
  261 + // mock 数据
  262 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
248 263
249 - // 调用, 并断言异常  
250 - assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN); 264 + // 调用,校验异常
  265 + assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name),
  266 + DICT_TYPE_NAME_DUPLICATE);
251 } 267 }
252 268
253 // ========== 随机对象 ========== 269 // ========== 随机对象 ==========
src/test/resources/application-unit-test.yaml
@@ -3,21 +3,6 @@ spring: @@ -3,21 +3,6 @@ spring:
3 lazy-initialization: true # 开启懒加载,加快速度 3 lazy-initialization: true # 开启懒加载,加快速度
4 banner-mode: off # 单元测试,禁用 Banner 4 banner-mode: off # 单元测试,禁用 Banner
5 5
6 - # 去除的自动配置项  
7 - autoconfigure:  
8 - exclude:  
9 - - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration # 单元测试,禁用 SpringSecurity  
10 - - org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration # 单元测试,禁用 SpringSecurity  
11 - - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 单元测试,禁用 Quartz  
12 - - com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration # 单元测试,禁用 Lock4j 分布式锁  
13 - - org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration # 单元测试,禁用 Scheduler 定时任务  
14 - - org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration # 项目没有使用 Spring Data,所以禁用配置类,加速启动  
15 -  
16 -# Swagger 接口文档的自动配置(单元测试,禁用 Swagger)  
17 -springfox:  
18 - documentation:  
19 - auto-startup: false  
20 -  
21 --- #################### 数据库相关配置 #################### 6 --- #################### 数据库相关配置 ####################
22 7
23 spring: 8 spring:
@@ -29,6 +14,9 @@ spring: @@ -29,6 +14,9 @@ spring:
29 username: sa 14 username: sa
30 password: 15 password:
31 schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具 16 schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
  17 + druid:
  18 + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
  19 + initial-size: 1 # 单元测试,配置为 1,提升启动速度
32 20
33 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 21 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
34 redis: 22 redis:
@@ -36,17 +24,13 @@ spring: @@ -36,17 +24,13 @@ spring:
36 port: 16379 # 端口(单元测试,使用 16379 端口) 24 port: 16379 # 端口(单元测试,使用 16379 端口)
37 database: 0 # 数据库索引 25 database: 0 # 数据库索引
38 26
39 ---- #################### 定时任务相关配置 #################### 27 +mybatis:
  28 + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
40 29
41 -# Quartz 配置项,对应 QuartzProperties 配置类(单元测试,禁用 Quartz) 30 +--- #################### 定时任务相关配置 ####################
42 31
43 --- #################### 配置中心相关配置 #################### 32 --- #################### 配置中心相关配置 ####################
44 33
45 -# Apollo 配置中心  
46 -apollo:  
47 - bootstrap:  
48 - enabled: false # 单元测试,禁用配置中心  
49 -  
50 --- #################### 服务保障相关配置 #################### 34 --- #################### 服务保障相关配置 ####################
51 35
52 # Lock4j 配置项(单元测试,禁用 Lock4j) 36 # Lock4j 配置项(单元测试,禁用 Lock4j)
@@ -63,23 +47,6 @@ resilience4j: @@ -63,23 +47,6 @@ resilience4j:
63 47
64 --- #################### 监控相关配置 #################### 48 --- #################### 监控相关配置 ####################
65 49
66 -# Actuator 监控端点的配置项  
67 -management:  
68 - endpoints:  
69 - enabled-by-default: false  
70 -  
71 -# Spring Boot Admin 配置项  
72 -spring:  
73 - boot:  
74 - admin:  
75 - # Spring Boot Admin Client 客户端的相关配置  
76 - client:  
77 - enabled: false  
78 - # Spring Boot Admin Server 服务端的相关配置  
79 - context-path: /admin # 配置 Spring  
80 -  
81 -# 日志文件配置(不需要配置)  
82 -  
83 --- #################### 芋道相关配置 #################### 50 --- #################### 芋道相关配置 ####################
84 51
85 # 芋道配置项,设置当前项目所有自定义的配置 52 # 芋道配置项,设置当前项目所有自定义的配置
src/test/resources/sql/create_tables.sql
@@ -9,9 +9,9 @@ CREATE TABLE IF NOT EXISTS &quot;inf_config&quot; ( @@ -9,9 +9,9 @@ CREATE TABLE IF NOT EXISTS &quot;inf_config&quot; (
9 "value" varchar(500) NOT NULL DEFAULT '', 9 "value" varchar(500) NOT NULL DEFAULT '',
10 "sensitive" bit NOT NULL, 10 "sensitive" bit NOT NULL,
11 "remark" varchar(500) DEFAULT NULL, 11 "remark" varchar(500) DEFAULT NULL,
12 - "create_by" varchar(64) DEFAULT '', 12 + "creator" varchar(64) DEFAULT '',
13 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
14 - "update_by" varchar(64) DEFAULT '', 14 + "updater" varchar(64) DEFAULT '',
15 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 15 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
16 "deleted" bit NOT NULL DEFAULT FALSE, 16 "deleted" bit NOT NULL DEFAULT FALSE,
17 PRIMARY KEY ("id") 17 PRIMARY KEY ("id")
@@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_dept&quot; ( @@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_dept&quot; (
28 "phone" varchar(11) DEFAULT NULL, 28 "phone" varchar(11) DEFAULT NULL,
29 "email" varchar(50) DEFAULT NULL, 29 "email" varchar(50) DEFAULT NULL,
30 "status" tinyint NOT NULL, 30 "status" tinyint NOT NULL,
31 - "create_by" varchar(64) DEFAULT '', 31 + "creator" varchar(64) DEFAULT '',
32 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 32 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
33 - "update_by" varchar(64) DEFAULT '', 33 + "updater" varchar(64) DEFAULT '',
34 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 34 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
35 "deleted" bit NOT NULL DEFAULT FALSE, 35 "deleted" bit NOT NULL DEFAULT FALSE,
36 PRIMARY KEY ("id") 36 PRIMARY KEY ("id")
@@ -44,9 +44,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_dict_data&quot; ( @@ -44,9 +44,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_dict_data&quot; (
44 "dict_type" varchar(100) NOT NULL DEFAULT '', 44 "dict_type" varchar(100) NOT NULL DEFAULT '',
45 "status" tinyint NOT NULL DEFAULT '0', 45 "status" tinyint NOT NULL DEFAULT '0',
46 "remark" varchar(500) DEFAULT NULL, 46 "remark" varchar(500) DEFAULT NULL,
47 - "create_by" varchar(64) DEFAULT '', 47 + "creator" varchar(64) DEFAULT '',
48 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 48 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
49 - "update_by" varchar(64) DEFAULT '', 49 + "updater" varchar(64) DEFAULT '',
50 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 50 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
51 "deleted" bit NOT NULL DEFAULT FALSE, 51 "deleted" bit NOT NULL DEFAULT FALSE,
52 PRIMARY KEY ("id") 52 PRIMARY KEY ("id")
@@ -62,9 +62,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_role&quot; ( @@ -62,9 +62,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_role&quot; (
62 "status" tinyint NOT NULL, 62 "status" tinyint NOT NULL,
63 "type" tinyint NOT NULL, 63 "type" tinyint NOT NULL,
64 "remark" varchar(500) DEFAULT NULL, 64 "remark" varchar(500) DEFAULT NULL,
65 - "create_by" varchar(64) DEFAULT '', 65 + "creator" varchar(64) DEFAULT '',
66 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 66 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
67 - "update_by" varchar(64) DEFAULT '', 67 + "updater" varchar(64) DEFAULT '',
68 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 68 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
69 "deleted" bit NOT NULL DEFAULT FALSE, 69 "deleted" bit NOT NULL DEFAULT FALSE,
70 PRIMARY KEY ("id") 70 PRIMARY KEY ("id")
@@ -74,9 +74,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_role_menu&quot; ( @@ -74,9 +74,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_role_menu&quot; (
74 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, 74 "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
75 "role_id" bigint NOT NULL, 75 "role_id" bigint NOT NULL,
76 "menu_id" bigint NOT NULL, 76 "menu_id" bigint NOT NULL,
77 - "create_by" varchar(64) DEFAULT '', 77 + "creator" varchar(64) DEFAULT '',
78 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 78 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
79 - "update_by" varchar(64) DEFAULT '', 79 + "updater" varchar(64) DEFAULT '',
80 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 80 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
81 "deleted" bit NOT NULL DEFAULT FALSE, 81 "deleted" bit NOT NULL DEFAULT FALSE,
82 PRIMARY KEY ("id") 82 PRIMARY KEY ("id")
@@ -93,9 +93,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_menu&quot; ( @@ -93,9 +93,9 @@ CREATE TABLE IF NOT EXISTS &quot;sys_menu&quot; (
93 "icon" varchar(100) DEFAULT '#', 93 "icon" varchar(100) DEFAULT '#',
94 "component" varchar(255) DEFAULT NULL, 94 "component" varchar(255) DEFAULT NULL,
95 "status" tinyint NOT NULL DEFAULT '0', 95 "status" tinyint NOT NULL DEFAULT '0',
96 - "create_by" varchar(64) DEFAULT '', 96 + "creator" varchar(64) DEFAULT '',
97 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 97 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
98 - "update_by" varchar(64) DEFAULT '', 98 + "updater" varchar(64) DEFAULT '',
99 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 99 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
100 "deleted" bit NOT NULL DEFAULT FALSE, 100 "deleted" bit NOT NULL DEFAULT FALSE,
101 PRIMARY KEY ("id") 101 PRIMARY KEY ("id")
@@ -107,9 +107,9 @@ CREATE TABLE &quot;sys_dict_type&quot; ( @@ -107,9 +107,9 @@ CREATE TABLE &quot;sys_dict_type&quot; (
107 "type" varchar(100) NOT NULL DEFAULT '', 107 "type" varchar(100) NOT NULL DEFAULT '',
108 "status" tinyint NOT NULL DEFAULT '0', 108 "status" tinyint NOT NULL DEFAULT '0',
109 "remark" varchar(500) DEFAULT NULL, 109 "remark" varchar(500) DEFAULT NULL,
110 - "create_by" varchar(64) DEFAULT '', 110 + "creator" varchar(64) DEFAULT '',
111 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 111 "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
112 - "update_by" varchar(64) DEFAULT '', 112 + "updater" varchar(64) DEFAULT '',
113 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 113 "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
114 "deleted" bit NOT NULL DEFAULT FALSE, 114 "deleted" bit NOT NULL DEFAULT FALSE,
115 PRIMARY KEY ("id") 115 PRIMARY KEY ("id")