map = Maps.newHashMap();
+ Class> clazz = obj.getClass();
+ for (Field field : clazz.getDeclaredFields()) {
+ field.setAccessible(true);
+ String fieldName = field.getName();
+ Object value = field.get(obj);
+ map.put(fieldName, value == null ? "" : value.toString());
+ }
+ return map;
+ }
+
+}
diff --git a/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyDateUtil.java b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyDateUtil.java
new file mode 100644
index 0000000..a530470
--- /dev/null
+++ b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyDateUtil.java
@@ -0,0 +1,421 @@
+package com.zhengqing.common.base.util;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.management.ManagementFactory;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * 时间工具类
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2019/8/18 12:57
+ */
+@Slf4j
+public class MyDateUtil {
+
+ public static final String GMT = "GMT+8";
+ public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ public static final String DATE_TIME_MS_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
+ public static final String DATE_TIME_START_FORMAT = "yyyy-MM-dd 00:00:00";
+ public static final String DATE_TIME_END_FORMAT = "yyyy-MM-dd 23:59:59";
+ public static final String MINUTE_FORMAT = "yyyy-MM-dd HH:mm";
+ public static final String HOUR_FORMAT = "yyyy-MM-dd HH";
+ public static final String DATE_FORMAT = "yyyy-MM-dd";
+ public static final String MONTH_FORMAT = "yyyy-MM";
+ public static final String YEAR_FORMAT = "yyyy";
+ public static final String MINUTE_ONLY_FORMAT = "mm";
+ public static final String HOUR_ONLY_FORMAT = "HH";
+ public static final String MINUTE_JAVA_AUTHOR_FORMAT = "yyyy/MM/dd HH:mm";
+ public static final String DAY_FORMAT = "yyyy/MM/dd";
+
+ /**
+ * 获取当前时间的字符串格式时间 -- 秒
+ *
+ * @return 字符串格式时间
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ public static String nowStr() {
+ return new SimpleDateFormat(DATE_TIME_FORMAT).format(new Date());
+ }
+
+ /**
+ * 获取当前时间的字符串格式时间 -- 毫秒
+ *
+ * @return 字符串格式时间
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ public static String nowMsStr() {
+ return new SimpleDateFormat(DATE_TIME_MS_FORMAT).format(new Date());
+ }
+
+ /**
+ * 获取当前时间字符串的指定格式时间
+ *
+ * @param format 时间格式
+ * @return 字符串格式时间
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ public static String nowStr(String format) {
+ return new SimpleDateFormat(format).format(new Date());
+ }
+
+ /**
+ * Date转Str
+ *
+ * @param date 时间
+ * @param format 时间格式
+ * @return 字符串时间类型
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ public static String dateToStr(Date date, String format) {
+ return new SimpleDateFormat(format).format(date);
+ }
+
+ /**
+ * Date转Str {@link MyDateUtil.DATE_TIME_FORMAT}
+ *
+ * @param date 时间
+ * @return 字符串时间类型
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ public static String dateToStr(Date date) {
+ return new SimpleDateFormat(DATE_TIME_FORMAT).format(date);
+ }
+
+ /**
+ * Date 时间开始处理 (ex: Sat Sep 04 10:11:25 CST 2021 -> 2021-09-04 00:00:00)
+ *
+ * @param date 时间
+ * @return 时间
+ * @author zhengqingya
+ * @date 2021/9/4 10:12
+ */
+ @SneakyThrows(Exception.class)
+ public static Date dateToStartTime(Date date) {
+ String dateTime = dateToStr(date, DATE_TIME_START_FORMAT);
+ return strToDate(dateTime, DATE_TIME_FORMAT);
+ }
+
+ /**
+ * Date 时间结束处理 (ex: Sat Sep 04 10:11:25 CST 2021 -> 2021-09-04 23:59:59)
+ *
+ * @param date 时间
+ * @return 时间
+ * @author zhengqingya
+ * @date 2021/9/4 10:12
+ */
+ @SneakyThrows(Exception.class)
+ public static Date dateToEndTime(Date date) {
+ String dateTime = dateToStr(date, DATE_TIME_END_FORMAT);
+ return strToDate(dateTime, DATE_TIME_FORMAT);
+ }
+
+ /**
+ * Str(yyyy-MM-dd HH:mm:ss)转DateTime
+ *
+ * @param dateStr 字符串时间
+ * @return Date时间类型
+ * @author zhengqingya
+ * @date 2021/8/20 9:41
+ */
+ @SneakyThrows(Exception.class)
+ public static Date strToDateTime(String dateStr) {
+ return new SimpleDateFormat(DATE_TIME_FORMAT).parse(dateStr);
+ }
+
+ /**
+ * Str转Date
+ *
+ * @param dateStr 字符串时间
+ * @param format 时间格式
+ * @return Date时间类型
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ @SneakyThrows(Exception.class)
+ public static Date strToDate(String dateStr, String format) {
+ return new SimpleDateFormat(format).parse(dateStr);
+ }
+
+ /**
+ * Str转long(10位-秒级别)
+ *
+ * @param dateStr 字符串时间
+ * @param format 时间格式
+ * @return 秒
+ * @author zhengqingya
+ * @date 2020/8/22 13:07
+ */
+ @SneakyThrows(Exception.class)
+ public static long strToLong(String dateStr, String format) {
+ return new SimpleDateFormat(format).parse(dateStr).getTime() / 1000;
+ }
+
+ /**
+ * 获取服务器启动时间
+ */
+ public static Date getServerStartDate() {
+ long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+ return new Date(time);
+ }
+
+ /**
+ * 判断结束时间是否大于等于开始时间
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @param format 时间格式
+ * @return 判断结果
+ * @author zhengqingya
+ * @date 2020/8/22 13:21
+ */
+ public static boolean verifyTime(String startTime, String endTime, String format) {
+ Date startDate = strToDate(startTime, format);
+ Date endDate = strToDate(endTime, format);
+ return endDate.getTime() >= startDate.getTime();
+ }
+
+ /**
+ * 获取今日开始时间
+ *
+ * @return 今日开始时间
+ * @author zhengqingya
+ * @date 2021/8/18 18:58
+ */
+ public static Date todayStartTime() {
+ LocalDateTime todayStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
+ return Date.from(todayStartTime.atZone(ZoneId.systemDefault()).toInstant());
+ }
+
+ /**
+ * 获取今日开始时间
+ *
+ * @return 今日开始时间
+ * @author zhengqingya
+ * @date 2021/8/18 18:58
+ */
+ public static String todayStartTimeStr() {
+ DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_TIME_START_FORMAT);
+ LocalDateTime time = LocalDateTime.now();
+ return df.format(time);
+ }
+
+ /**
+ * 获取今日结束时间
+ *
+ * @return 今日结束时间
+ * @author zhengqingya
+ * @date 2021/8/18 18:58
+ */
+ public static Date todayEndTime() {
+ LocalDateTime todayStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
+ return Date.from(todayStartTime.atZone(ZoneId.systemDefault()).toInstant());
+ }
+
+ /**
+ * 获取今日结束时间
+ *
+ * @return 今日结束时间
+ * @author zhengqingya
+ * @date 2021/8/18 18:58
+ */
+ public static String todayEndTimeStr() {
+ DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_TIME_END_FORMAT);
+ LocalDateTime time = LocalDateTime.now();
+ return df.format(time);
+ }
+
+ /**
+ * 计算两时间分钟差
+ *
+ * @param startTimeStr 开始时间 ex: 2020-09-09 10:00:10
+ * @param endTimeStr 结束时间 ex: 2020-09-09 10:30:10
+ * @return 分钟差 ex:30
+ * @author zhengqingya
+ * @date 2021/9/9 10:21
+ */
+ @SneakyThrows(Exception.class)
+ public static int diffMinute(String startTimeStr, String endTimeStr) {
+ SimpleDateFormat simpleFormat = new SimpleDateFormat(MINUTE_FORMAT);
+ long startTime = simpleFormat.parse(startTimeStr).getTime();
+ long endTime = simpleFormat.parse(endTimeStr).getTime();
+ return (int) ((endTime - startTime) / (1000 * 60));
+ }
+
+ /**
+ * 计算两时间毫秒差
+ *
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @return 毫秒差 ex:30
+ * @author zhengqingya
+ * @date 2021/9/9 10:21
+ */
+ @SneakyThrows(Exception.class)
+ public static long diffMillisecond(Date startTime, Date endTime) {
+ return endTime.getTime() - startTime.getTime();
+ }
+
+ /**
+ * 计算两个时间差
+ *
+ * @param startDate 开始时间
+ * @param endDate 结束时间
+ * @return 时间差
+ * @author zhengqingya
+ * @date 2021/7/23 10:17
+ */
+ public static String getDateDiffStr(Date startDate, Date endDate) {
+ long nd = 1000 * 24 * 60 * 60;
+ long nh = 1000 * 60 * 60;
+ long nm = 1000 * 60;
+ // long ns = 1000;
+ // 获得两个时间的毫秒时间差异
+ long diff = endDate.getTime() - startDate.getTime();
+ // 计算差多少天
+ long day = diff / nd;
+ // 计算差多少小时
+ long hour = diff % nd / nh;
+ // 计算差多少分钟
+ long min = diff % nd % nh / nm;
+ // 计算差多少秒//输出结果
+ // long sec = diff % nd % nh % nm / ns;
+ return day + "天" + hour + "小时" + min + "分钟";
+ }
+
+ /**
+ * 在当前时间上加指定时间
+ *
+ * @param timeUnit 时间单位
+ * @param time 时间
+ * @return 结果
+ * @author zhengqingya
+ * @date 2021/10/18 16:57
+ */
+ @SneakyThrows(Exception.class)
+ public static Date addTime(TimeUnit timeUnit, int time) {
+ return addAndSubTime(timeUnit, +time);
+ }
+
+ /**
+ * 在当前时间上减指定时间
+ *
+ * @param timeUnit 时间单位
+ * @param time 时间
+ * @return 结果
+ * @author zhengqingya
+ * @date 2021/10/18 16:57
+ */
+ @SneakyThrows(Exception.class)
+ public static Date subTime(TimeUnit timeUnit, int time) {
+ return addAndSubTime(timeUnit, -time);
+ }
+
+ /**
+ * 在当前时间上 加或减 指定时间
+ *
+ * @param timeUnit 时间单位
+ * @param time 时间
+ * @return 结果
+ * @author zhengqingya
+ * @date 2021/10/18 16:57
+ */
+ @SneakyThrows(Exception.class)
+ public static Date addAndSubTime(TimeUnit timeUnit, int time) {
+ return timeAddAndSubTime(new Date(), timeUnit, time);
+ }
+
+ /**
+ * 在指定时间上 加或减 指定时间
+ *
+ * @param timeUnit 时间单位
+ * @param time 时间
+ * @return 结果
+ * @author zhengqingya
+ * @date 2021/10/18 16:57
+ */
+ @SneakyThrows(Exception.class)
+ public static Date timeAddAndSubTime(Date sourceDate, TimeUnit timeUnit, int time) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(sourceDate);
+ switch (timeUnit) {
+ case SECONDS:
+ calendar.add(Calendar.SECOND, time);
+ break;
+ case MINUTES:
+ calendar.add(Calendar.MINUTE, time);
+ break;
+ case HOURS:
+ calendar.add(Calendar.HOUR, time);
+ break;
+ case DAYS:
+ calendar.add(Calendar.DATE, time);
+ break;
+ default:
+ throw new Exception("暂不支持该时间类型!");
+ }
+ return calendar.getTime();
+ }
+
+
+ public static void main(String[] args) {
+ Date nowDateTime = new Date();
+ String dateStr = dateToStr(nowDateTime, DATE_TIME_FORMAT);
+ System.out.println(nowStr(YEAR_FORMAT));
+ boolean result = verifyTime("2020-08-18 00:00:00", "2020-08-17 23:59:59", DATE_TIME_FORMAT);
+ System.out.println(result);
+
+ Date todayStartTime = todayStartTime();
+ Date todayEndTime = todayEndTime();
+
+ String todayStartTimeStr = todayStartTimeStr();
+ String todayEndTimeStr = todayEndTimeStr();
+
+ Date date = strToDateTime("2020-08-18 00:00:10");
+
+ Long aLong = strToLong("2020-08-18 00:00:10", DATE_TIME_FORMAT);
+
+ Date dateTimeStartFormat = dateToStartTime(nowDateTime);
+ Date dateTimeEndFormat = dateToEndTime(nowDateTime);
+
+ int diffMinute = diffMinute("2020-09-09 10:00:10", "2020-09-09 10:30:10");
+ long diffMillisecond = diffMillisecond(todayStartTime, todayEndTime);
+
+ log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.SECONDS, 20), DATE_TIME_FORMAT));
+ log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.MINUTES, 10), DATE_TIME_FORMAT));
+ log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.HOURS, 10), DATE_TIME_FORMAT));
+ log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.DAYS, 10), DATE_TIME_FORMAT));
+
+ log.info("--------------------------------");
+
+ log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.SECONDS, 20), DATE_TIME_FORMAT));
+ log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.MINUTES, 10), DATE_TIME_FORMAT));
+ log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.HOURS, 10), DATE_TIME_FORMAT));
+ log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.DAYS, 10), DATE_TIME_FORMAT));
+
+ log.info("--------------------------------");
+
+ log.info("nowTime:{} timeAddAndsubTime: {}", nowStr(), dateToStr(timeAddAndSubTime(todayStartTime, TimeUnit.MINUTES, 10), DATE_TIME_FORMAT));
+
+ log.info("当前时间到今天开始时间差:{}", MyDateUtil.getDateDiffStr(MyDateUtil.todayStartTime(), new Date()));
+ }
+
+}
diff --git a/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyFileUtil.java b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyFileUtil.java
new file mode 100644
index 0000000..a849096
--- /dev/null
+++ b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyFileUtil.java
@@ -0,0 +1,177 @@
+package com.zhengqing.common.base.util;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.ZipUtil;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ *
+ * 文件工具类
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2020/8/27 19:21
+ */
+@Slf4j
+public class MyFileUtil {
+
+ /**
+ * 多文件或目录压缩:将`srcPath`目录以及其目录下的所有文件目录打包到`zipPath`+`suffixFileName`文件中 【采用hutool工具类进行打包文件】
+ *
+ * @param srcPath 需打包的源目录
+ * @param zipPath 打包后的路径+文件后缀名
+ * @param isWithSrcDir 是否带目录显示 (true:表示带目录显示)
+ * @param isDeleteSrcZip 是否删除源目录
+ * @return java.lang.String
+ * @author zhengqingya
+ * @date 2020/8/27 19:25
+ */
+ public static File zip(String srcPath, String zipPath, boolean isWithSrcDir, boolean isDeleteSrcZip) {
+ log.debug("【压缩文件】 源目录路径: 【{}】 打包后的路径+文件后缀名: 【{}】", srcPath, zipPath);
+ File zipFile = ZipUtil.zip(srcPath, zipPath, isWithSrcDir);
+ // 删除目录 -> 保证下次生成文件的时候不会累计上次留下的文件
+ if (isDeleteSrcZip) {
+ MyFileUtil.deleteFileOrFolder(srcPath);
+ }
+ return zipFile;
+ }
+
+ /**
+ * 根据路径删除指定的目录或文件,无论存在与否
+ *
+ * @param fullFileOrDirPath 要删除的目录或文件
+ * @return 删除成功返回 true,否则返回 false
+ * @author zhengqingya
+ * @date 2020/9/5 20:56
+ */
+ public static boolean deleteFileOrFolder(String fullFileOrDirPath) {
+ return FileUtil.del(fullFileOrDirPath);
+ }
+
+ /**
+ * 根据路径创建文件
+ *
+ * @param fullFilePath 文件生成路径
+ * @return 文件信息
+ * @author zhengqingya
+ * @date 2020/9/8 21:41
+ */
+ public static File touch(String fullFilePath) {
+ return FileUtil.touch(fullFilePath);
+ }
+
+ /**
+ * 解压
+ *
+ * @param inputStream 流
+ * @param zipFilePath zip文件路径
+ * @param outFileDir 解压后的目录路径
+ * @param isDeleteZip 是否删除源zip文件
+ * @return 解压后的文件File信息
+ * @author zhengqingya
+ * @date 2020/9/5 20:50
+ */
+ @SneakyThrows(Exception.class)
+ public static File unzip(InputStream inputStream, String zipFilePath, String outFileDir, boolean isDeleteZip) {
+ log.debug("【解压文件】 zip文件路径: 【{}】 解压后的目录路径: 【{}】", zipFilePath, outFileDir);
+ // zip压缩文件
+// File zipFile = FileUtil.newFile(zipFilePath);
+ // 写入文件
+// FileUtils.copyInputStreamToFile(inputStream, zipFile);
+ // 编码方式 "UTF-8" 、"GBK" 【注: gbk编码才能解决报错: java.lang.IllegalArgumentException: MALFORMED】
+ File outFile = ZipUtil.unzip(zipFilePath, outFileDir, Charset.forName("GBK"));
+ // 删除zip -> 保证下次解压后的文件数据不会累计上次解压留下的文件
+ if (isDeleteZip) {
+ MyFileUtil.deleteFileOrFolder(zipFilePath);
+ }
+ return outFile;
+ }
+
+ /**
+ * 读取文件内容
+ *
+ * @param file 文件数据
+ * @return 文件内容
+ * @author zhengqingya
+ * @date 2020/9/5 23:00
+ */
+ public static String readFileContent(File file) {
+ return FileUtil.readUtf8String(file);
+ }
+
+ /**
+ * 读取文件内容
+ *
+ * @param filePath 文件路径
+ * @return 文件内容
+ * @author zhengqingya
+ * @date 2020/9/5 23:00
+ */
+ public static String readFileContent(String filePath) {
+ return FileUtil.readUtf8String(filePath);
+ }
+
+ /**
+ * 读取文件数据
+ *
+ * @param filePath 文件路径
+ * @return 文件字节码
+ * @author zhengqingya
+ * @date 2020/9/5 23:00
+ */
+ public static byte[] readBytes(String filePath) {
+ return FileUtil.readBytes(filePath);
+ }
+
+ /**
+ * 写入文件内容
+ *
+ * @param fileContent 文件内容
+ * @param filePath 文件路径
+ * @return 文件信息
+ * @author zhengqingya
+ * @date 2020/11/17 21:38
+ */
+ @SneakyThrows(Exception.class)
+ public static File writeFileContent(String fileContent, String filePath) {
+ return FileUtil.writeUtf8String(fileContent, filePath);
+ }
+
+ /**
+ * 字节码写入文件
+ *
+ * @param data 字节码
+ * @param filePath 文件路径
+ * @return 文件信息
+ * @author zhengqingya
+ * @date 2020/11/24 14:36
+ */
+ @SneakyThrows(Exception.class)
+ public static File writeFileContent(byte[] data, String filePath) {
+ return FileUtil.writeBytes(data, filePath);
+ }
+
+ public static void main(String[] args) {
+ try {
+ String filePath = "E:\\IT_zhengqing\\code\\me-workspace\\最新代码生成器\\code-api\\document\\import\\blog.zip";
+ String filePathX = "E:\\IT_zhengqing\\code\\me-workspace\\最新代码生成器\\code-api\\document\\import";
+ // File file =
+ // FileUtil.newFile(filePath);
+ // InputStream fileInputStream = new FileInputStream(file);
+ File unzip = ZipUtil.unzip(filePath, filePathX);
+ System.out.println(unzip);
+
+ String fileContent = FileUtil.readUtf8String(filePath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyStringUtil.java b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyStringUtil.java
new file mode 100644
index 0000000..7d39c6c
--- /dev/null
+++ b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/MyStringUtil.java
@@ -0,0 +1,118 @@
+package com.zhengqing.common.base.util;
+
+import com.google.common.base.CaseFormat;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.StringJoiner;
+
+/**
+ *
+ * 字符串工具类
+ *
+ *
+ * @author zhengqingya
+ * @date 2019/9/14 0014$ 19:49$
+ */
+public class MyStringUtil {
+
+ /**
+ * 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
+ * 例如:HelloWorld->HELLO_WORLD
+ *
+ * @param name 转换前的驼峰式命名的字符串
+ * @return 转换后下划线大写方式命名的字符串
+ */
+ public static String humpToMark(String name) {
+ StringBuilder result = new StringBuilder();
+ if (name != null && name.length() > 0) {
+ // 将第一个字符处理成大写
+ result.append(name.substring(0, 1).toUpperCase());
+ // 循环处理其余字符
+ for (int i = 1; i < name.length(); i++) {
+ String s = name.substring(i, i + 1);
+ // 在大写字母前添加下划线
+ if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
+ result.append("_");
+ }
+ // 其他字符直接转成大写
+ result.append(s.toUpperCase());
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * 将字符串转换为驼峰式
+ * 例如:HELLO.WORLD -> HelloWorld
+ *
+ * System.out.println(CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, "test-data"));//testData
+ * System.out.println(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "test_data"));//testData
+ * System.out.println(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "test_data"));//TestData
+ *
+ * System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "testdata"));//testdata
+ * System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "TestData"));//test_data
+ * System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, "testData"));//test-data
+ *
+ * @param str 转换前的字符串
+ * @param mark 以xx符号分割 ex: `,` `_` `-` `.`
+ * @return 转换后的驼峰式命名的字符串
+ */
+ public static String strToHumpByMark(String str, String mark) {
+ // 替换指定的符号mark为`_`
+ String replaceStr = StringUtils.replace(str, mark, "_");
+ // 再使用谷歌开发工具包转驼峰命名 ex:test_data -> TestData
+ String result = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, replaceStr);
+ return result;
+ }
+
+ /**
+ * 数据库表名/表字段 转 首字母小写驼峰命名 ex:test_data -> testData
+ *
+ * @param dbStr 数据库表名/表字段
+ * @return 驼峰命名字符串
+ * @author zhengqingya
+ * @date 2020/11/15 17:38
+ */
+ public static String dbStrToHumpLower(String dbStr) {
+ // 使用谷歌开发工具包转驼峰命名
+ return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, dbStr);
+ }
+
+ /**
+ * 数据库表名/表字段 转 首字母大写驼峰命名 ex:test_data -> TestData
+ *
+ * @param dbStr 数据库表名/表字段
+ * @return 驼峰命名字符串
+ * @author zhengqingya
+ * @date 2020/11/15 17:38
+ */
+ public static String dbStrToHumpUpper(String dbStr) {
+ // 使用谷歌开发工具包转驼峰命名
+ return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dbStr);
+ }
+
+ /**
+ * 获取表名缩写
+ *
+ * @param tableName 表名
+ * @return 缩写
+ * @author zhengqingya
+ * @date 2020/12/12 15:29
+ */
+ public static String tableNameToAbbr(String tableName) {
+ StringJoiner sj = new StringJoiner("");
+ String[] strArray = tableName.split("_");
+ for (String str : strArray) {
+ sj.add(str.substring(0, 1));
+ }
+ return sj.toString();
+ }
+
+ public static void main(String[] args) {
+ String str = dbStrToHumpLower("t_sys_user");
+ String str2 = dbStrToHumpUpper("t_sys_user");
+ String str3 = tableNameToAbbr("t_sys_user");
+ System.out.println(str3);
+ }
+
+}
diff --git a/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/SpringElUtil.java b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/SpringElUtil.java
new file mode 100644
index 0000000..a3f3732
--- /dev/null
+++ b/smallboot-api/common/base/src/main/java/com/zhengqing/common/base/util/SpringElUtil.java
@@ -0,0 +1,55 @@
+package com.zhengqing.common.base.util;
+
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.lang.reflect.Method;
+
+/**
+ * spring El表达式 工具类
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2020/4/12 0:40
+ */
+public class SpringElUtil {
+
+ /**
+ * 表达式解析器
+ */
+ private static final ExpressionParser PARSER = new SpelExpressionParser();
+
+ /**
+ * 获取方法的参数名
+ */
+ private static final LocalVariableTableParameterNameDiscoverer DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
+
+ /**
+ * 解析spring EL表达式
+ *
+ * @param key 表达式
+ * @param method 方法
+ * @param args 方法参数
+ * @return 结果
+ * @author zhengqingya
+ * @date 2022/1/18 14:35
+ */
+ public static String parse(String key, Method method, Object[] args) {
+ // 获取方法的参数名
+ String[] paramNameArray = DISCOVERER.getParameterNames(method);
+ if (paramNameArray == null) {
+ return "";
+ }
+ // 表达式的上下文
+ EvaluationContext context = new StandardEvaluationContext();
+ for (int i = 0; i < paramNameArray.length; i++) {
+ // 为了让表达式可以访问该对象, 先把对象放到上下文中
+ context.setVariable(paramNameArray[i], args[i]);
+ }
+ return PARSER.parseExpression(key).getValue(context, String.class);
+ }
+
+}
diff --git a/smallboot-api/common/base/src/main/resources/application-base.yml b/smallboot-api/common/base/src/main/resources/application-base.yml
new file mode 100644
index 0000000..e69de29
diff --git a/smallboot-api/common/base/src/main/resources/config.properties b/smallboot-api/common/base/src/main/resources/config.properties
new file mode 100644
index 0000000..e69de29
diff --git a/smallboot-api/common/core/pom.xml b/smallboot-api/common/core/pom.xml
new file mode 100644
index 0000000..10d4926
--- /dev/null
+++ b/smallboot-api/common/core/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+
+ common
+ com.zhengqing
+ ${revision}
+
+
+ core
+
+ ${project.artifactId}
+ ${revision}
+ jar
+
+
+
+
+
+
+ com.zhengqing
+ base
+
+
+
+ com.zhengqing
+ auth
+
+
+
+ com.zhengqing
+ log
+
+
+
+ com.zhengqing
+ swagger
+
+
+
+ com.zhengqing
+ web
+
+
+
+ com.zhengqing
+ db
+
+
+
+ com.zhengqing
+ redis
+
+
+
+
+ com.zhengqing
+ file
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/api/BaseController.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/api/BaseController.java
new file mode 100644
index 0000000..c29d94d
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/api/BaseController.java
@@ -0,0 +1,32 @@
+package com.zhengqing.common.core.api;
+
+import com.zhengqing.common.base.context.SysUserContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * Controller基类
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2019/8/17 0017 19:53
+ */
+@Slf4j
+@RestController
+//@Api(tags = "base接口")
+public class BaseController {
+
+ /**
+ * 获取当前登录人ID
+ *
+ * @return 当前登录人ID
+ * @author zhengqingya
+ * @date 2020/8/30 15:41
+ */
+ protected Integer appGetCurrentUserId() {
+ return SysUserContext.getUserId();
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ApiLogAspect.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ApiLogAspect.java
new file mode 100644
index 0000000..708f6bd
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ApiLogAspect.java
@@ -0,0 +1,89 @@
+package com.zhengqing.common.core.aspect;
+
+import cn.hutool.json.JSONUtil;
+import com.zhengqing.common.base.context.SysUserContext;
+import com.zhengqing.common.web.util.ServletUtil;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ *
+ * 日志切面
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2020/8/1 19:09
+ */
+@Slf4j
+@Aspect
+@Component
+@ConditionalOnProperty(
+ value = {"smallboot.api-log"},
+ havingValue = "true",
+ // true表示缺少此配置属性时也会加载该bean
+ matchIfMissing = true
+)
+public class ApiLogAspect {
+
+ /**
+ * 配置织入点
+ */
+ @Pointcut("execution(* com.zhengqing.*..*.*Controller.*(..))")
+ public void logPointCut() {
+ }
+
+ @Before("logPointCut()")
+ public void doAround(JoinPoint joinPoint) throws Throwable {
+ HttpServletRequest request = ServletUtil.getRequest();
+ if (request != null) {
+ Integer userId = SysUserContext.getUserId();
+ String username = SysUserContext.getUsername();
+
+ // 从切面织入点处通过反射机制获取织入点处的方法
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ // 获取切入点所在的方法
+ ApiOperation apiOperation = signature.getMethod().getAnnotation(ApiOperation.class);
+
+// log.debug("========================== ↓↓↓↓↓↓ 《ApiLogAspect》 Start... ↓↓↓↓↓↓ ==========================");
+// log.debug("《ApiLogAspect》 controller method: {}",
+// signature.getDeclaringTypeName() + "." + signature.getName());
+// log.debug("《ApiLogAspect》 controller method description: {}", apiOperation.value());
+// log.debug("《ApiLogAspect》 operatorId: {}", userId);
+// log.debug("《ApiLogAspect》 operatorName: {}", username);
+// log.debug("《ApiLogAspect》 request header: {}", request.getHeader(SwaggerConstant.REQUEST_HEADER_AUTHORIZATION));
+// log.debug("《ApiLogAspect》 request ip: {}", request.getRemoteAddr());
+// log.debug("《ApiLogAspect》 request url: {}", request.getRequestURL().toString());
+// log.debug("《ApiLogAspect》 request http method: {}", request.getMethod());
+// log.debug("《ApiLogAspect》 request params: {}", this.getRequestValue(request));
+// log.debug("========================== ↑↑↑↑↑↑ 《ApiLogAspect》 End... ↑↑↑↑↑↑ ==========================");
+
+ log.debug("[{}] [{}] 请求参数:{}",
+ request.getMethod(),
+ signature.getDeclaringTypeName() + "." + signature.getName(),
+ this.getRequestValue(request)
+ );
+ }
+ }
+
+ /**
+ * 获取请求的参数
+ */
+ private String getRequestValue(HttpServletRequest request) {
+ Map map = request.getParameterMap();
+ String params = JSONUtil.toJsonStr(map);
+ return StringUtils.substring(params, 0, 2000);
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ControllerAspect.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ControllerAspect.java
new file mode 100644
index 0000000..7dbf661
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/ControllerAspect.java
@@ -0,0 +1,60 @@
+package com.zhengqing.common.core.aspect;
+
+import com.zhengqing.common.base.context.SysUserContext;
+import com.zhengqing.common.base.model.dto.BaseDTO;
+import com.zhengqing.common.core.custom.parameter.ParamCheck;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Controller 切面
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2021/1/9 17:47
+ */
+@Slf4j
+@Aspect
+@Component
+public class ControllerAspect {
+
+ /**
+ * 配置织入点
+ */
+ @Pointcut("execution(* com.zhengqing.*..*.*Controller.*(..))")
+ public void controllerPointCut() {
+ }
+
+ /**
+ * Before增强:在目标方法被执行的时候织入增强
+ *
+ * Controller所有方法为切入点
+ */
+ @Before("controllerPointCut()")
+ public void controllerPointCut(JoinPoint joinPoint) {
+ // 参数处理
+ Object[] paramObjArray = joinPoint.getArgs();
+ // 遍历所有传入参数,赋值
+ for (Object paramObj : paramObjArray) {
+ // dto参数处理
+ if (paramObj instanceof BaseDTO) {
+ BaseDTO baseDTO = (BaseDTO) paramObj;
+ baseDTO.setCurrentUserId(SysUserContext.getUserId());
+ baseDTO.setCurrentUsername(SysUserContext.getUsername());
+ }
+
+ // 参数校验处理
+ if (paramObj instanceof ParamCheck) {
+ ParamCheck paramCheck = (ParamCheck) paramObj;
+ paramCheck.checkParam();
+ }
+ }
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/MapperAspect.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/MapperAspect.java
new file mode 100644
index 0000000..2c2804b
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/MapperAspect.java
@@ -0,0 +1,61 @@
+package com.zhengqing.common.core.aspect;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.zhengqing.common.base.exception.ParameterException;
+import com.zhengqing.common.swagger.constant.SwaggerConstant;
+import com.zhengqing.common.web.util.ServletUtil;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Mapper分页参数注入切面
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2020/8/1 18:18
+ */
+@Aspect
+@Order(2)
+@Component
+public class MapperAspect {
+
+ private static final int DEFAULT_PAGE_NUM = 1;
+ private static final int DEFAULT_PAGE_SIZE = 10;
+
+ /**
+ * 配置织入点
+ */
+ @Pointcut("execution(* com.zhengqing.*..*.*Mapper.*(..))")
+ public void mapperPointCut() {
+ }
+
+ /**
+ * Before增强:在目标方法被执行的时候织入增强
+ *
+ * Mapper所有方法作为切入点
+ */
+ @Before("mapperPointCut()")
+ public void mapperPointCut(JoinPoint joinPoint) {
+ Object[] paramObjArray = joinPoint.getArgs();
+ int pageNum = ServletUtil.getParameterToInt(SwaggerConstant.PAGE_NUM, DEFAULT_PAGE_NUM);
+ int pageSize = ServletUtil.getParameterToInt(SwaggerConstant.PAGE_SIZE, DEFAULT_PAGE_SIZE);
+ // 遍历所有传入参数,赋值
+ for (Object paramObj : paramObjArray) {
+ if (paramObj instanceof IPage) {
+ if (pageNum < 1 || pageSize < 1) {
+ throw new ParameterException("传递的分页参数有误!");
+ }
+ IPage> page = ((IPage>) paramObj);
+ page.setCurrent(pageNum);
+ page.setSize(pageSize);
+ }
+ }
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/BeanSelfAware.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/BeanSelfAware.java
new file mode 100644
index 0000000..741bbbf
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/BeanSelfAware.java
@@ -0,0 +1,14 @@
+package com.zhengqing.common.core.aspect.config;
+
+/**
+ *
+ * 定义BeanPostProcessor 需要使用的标识接口
+ *
+ *
+ * @author zhengqingya
+ * @description 即我们自定义的BeanPostProcessor (InjectBeanSelfProcessor)如果发现我们的Bean是实现了该标识接口就调用setSelf注入代理对象
+ * @date 2021/1/9 1:10
+ */
+public interface BeanSelfAware {
+ void setSelf(Object proxyBean);
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/InjectBeanSelfProcessor.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/InjectBeanSelfProcessor.java
new file mode 100644
index 0000000..d033b92
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/aspect/config/InjectBeanSelfProcessor.java
@@ -0,0 +1,59 @@
+package com.zhengqing.common.core.aspect.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * 通过BeanPostProcessor 在目标对象中注入代理对象 -> 解决Spring AOP不拦截对象内部调用方法问题
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2021/1/9 0:55
+ */
+@Slf4j
+@Component
+public class InjectBeanSelfProcessor implements BeanPostProcessor, ApplicationContextAware {
+
+ private ApplicationContext applicationContext;
+
+ /**
+ * 1、注入ApplicationContext
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ // 2、如果Bean没有实现BeanSelfAware标识接口 跳过
+ if (!(bean instanceof BeanSelfAware)) {
+ return bean;
+ }
+
+ log.debug("inject proxy:【{}】", bean.getClass());
+
+ if (AopUtils.isAopProxy(bean)) {
+ // 3、如果当前对象是AOP代理对象,直接注入
+ ((BeanSelfAware) bean).setSelf(bean);
+ } else {
+ // 4、如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
+ // 此种方式不适合解决prototype Bean的代理对象注入
+ ((BeanSelfAware) bean).setSelf(this.applicationContext.getBean(beanName));
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/AppCommonRunner.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/AppCommonRunner.java
new file mode 100644
index 0000000..66356be
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/AppCommonRunner.java
@@ -0,0 +1,26 @@
+package com.zhengqing.common.core.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * 服务初始化之后,执行方法
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2020/5/22 19:29
+ */
+@Slf4j
+@Component
+public abstract class AppCommonRunner implements CommandLineRunner {
+
+ public void appRun() {
+ log.info("《AppCommonRunner》: 服务初始化之后,执行方法 start...");
+
+ log.info("《AppCommonRunner》: 服务初始化之后,执行方法 end...");
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/CommonProperty.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/CommonProperty.java
new file mode 100644
index 0000000..a9473f4
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/CommonProperty.java
@@ -0,0 +1,41 @@
+package com.zhengqing.common.core.config;
+
+import lombok.Data;
+
+/**
+ *
+ * 公共基础配置参数
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2019/8/19 9:07
+ */
+@Data
+public class CommonProperty {
+
+ /**
+ * ip
+ */
+ private String ip;
+
+ /**
+ * MySQL参数
+ */
+ private Mysql mysql;
+
+ @Data
+ public static class Mysql {
+ private MysqlConn master;
+ private MysqlConn dbTest;
+ }
+
+ @Data
+ public static class MysqlConn {
+ private String ip;
+ private String port;
+ private String username;
+ private String password;
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/WebAppConfig.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/WebAppConfig.java
new file mode 100644
index 0000000..2712996
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/WebAppConfig.java
@@ -0,0 +1,26 @@
+package com.zhengqing.common.core.config;
+
+import com.zhengqing.common.core.config.interceptor.HandlerInterceptorForTokenUser;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ *
+ * 注册拦截器
+ *
+ *
+ * @author zhengqingya
+ * @description ex: 租户ID
+ * @date 2021/1/13 14:41
+ */
+@Configuration
+public class WebAppConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 可添加多个
+ registry.addInterceptor(new HandlerInterceptorForTokenUser()).addPathPatterns("/**");
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/interceptor/HandlerInterceptorForTokenUser.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/interceptor/HandlerInterceptorForTokenUser.java
new file mode 100644
index 0000000..74db3a3
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/interceptor/HandlerInterceptorForTokenUser.java
@@ -0,0 +1,63 @@
+package com.zhengqing.common.core.config.interceptor;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.json.JSONUtil;
+import com.zhengqing.common.auth.model.bo.JwtUserBO;
+import com.zhengqing.common.base.constant.AppConstant;
+import com.zhengqing.common.base.context.JwtCustomUserContext;
+import com.zhengqing.common.base.context.SysUserContext;
+import com.zhengqing.common.base.context.UmsUserContext;
+import com.zhengqing.common.core.config.WebAppConfig;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 拦截器 -- token用户信息
+ *
+ * @author zhengqingya
+ * @description 注册使用参考 {@link WebAppConfig}
+ * @date 2022/1/10 16:28
+ */
+public class HandlerInterceptorForTokenUser implements HandlerInterceptor {
+
+ /**
+ * 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理
+ * {@link com.zhengqing.gateway.filter.AuthFilter#filter }
+ */
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ String token = request.getHeader(AppConstant.REQUEST_HEADER_TOKEN);
+ if (StringUtils.isBlank(token)) {
+ return true;
+ }
+ JwtUserBO jwtUserBO = JSONUtil.toBean(StpUtil.getLoginId().toString(), JwtUserBO.class);
+ SysUserContext.setUserId(jwtUserBO.getUserId());
+ SysUserContext.setUsername(jwtUserBO.getUserName());
+ return true;
+ }
+
+ /**
+ * 在业务处理器处理请求执行完成后,生成视图之前执行。
+ * 后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView
+ */
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+ HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
+ }
+
+ /**
+ * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面)
+ */
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
+ SysUserContext.remove();
+ UmsUserContext.remove();
+ JwtCustomUserContext.remove();
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/NativeAsyncTaskExecutePool.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/NativeAsyncTaskExecutePool.java
new file mode 100644
index 0000000..0098491
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/NativeAsyncTaskExecutePool.java
@@ -0,0 +1,71 @@
+package com.zhengqing.common.core.config.threadpool;
+
+import com.zhengqing.common.base.constant.ThreadPoolConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 原生(Spring)异步任务线程池装配类
+ *
+ * @author zhengqingya
+ * @description 配置默认的线程池,重写spring默认线程池的方式使用的时候,只需要加@Async注解就可以,不用去声明线程池类。
+ * @date 2021/5/27 10:31
+ */
+@Slf4j
+@EnableAsync
+@Configuration
+public class NativeAsyncTaskExecutePool implements AsyncConfigurer {
+
+ @Override
+ public Executor getAsyncExecutor() {
+ ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
+ // 核心线程池大小
+ threadPoolTaskExecutor.setCorePoolSize(5);
+ // 最大线程数
+ threadPoolTaskExecutor.setMaxPoolSize(10);
+ // 队列容量
+ threadPoolTaskExecutor.setQueueCapacity(200);
+ // 活跃时间 60s
+ threadPoolTaskExecutor.setKeepAliveSeconds(60);
+ // 线程名字前缀
+ threadPoolTaskExecutor
+ .setThreadNamePrefix(ThreadPoolConstant.SPRING_DEFAULT_THREAD_NAME_PREFIX);
+ // 设置在关闭线程池时是否等待任务完成
+ threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
+ // 允许核心线程超时
+ threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
+ // 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
+ threadPoolTaskExecutor.setAwaitTerminationSeconds(60);
+ // 修改拒绝策略为使用当前线程执行
+ // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
+ // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
+ threadPoolTaskExecutor
+ .setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+ //初始化线程池
+ threadPoolTaskExecutor.initialize();
+ return threadPoolTaskExecutor;
+ }
+
+ /**
+ * 异步任务中异常处理 (注:只能捕获到@Async下无返回值的方法)
+ */
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return new AsyncUncaughtExceptionHandler() {
+ @Override
+ public void handleUncaughtException(Throwable throwable, Method method,
+ Object... objects) {
+ log.error("exception method: 【{}】", method);
+ log.error("exception msg: ", throwable);
+ }
+ };
+ }
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/SmallToolsThreadPoolConfig.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/SmallToolsThreadPoolConfig.java
new file mode 100644
index 0000000..9963476
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/config/threadpool/SmallToolsThreadPoolConfig.java
@@ -0,0 +1,51 @@
+package com.zhengqing.common.core.config.threadpool;
+
+import com.zhengqing.common.base.constant.ThreadPoolConstant;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 自定义线程池配置
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2021/5/27 10:03
+ */
+@Configuration
+public class SmallToolsThreadPoolConfig {
+
+ @Bean(ThreadPoolConstant.SMALL_TOOLS_THREAD_POOL)
+ public Executor threadPoolExecutor() {
+ ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
+ // 核心线程池大小
+ threadPoolTaskExecutor.setCorePoolSize(5);
+ // 最大线程数
+ threadPoolTaskExecutor.setMaxPoolSize(10);
+ // 队列容量
+ threadPoolTaskExecutor.setQueueCapacity(200);
+ // 活跃时间 60s
+ threadPoolTaskExecutor.setKeepAliveSeconds(60);
+ // 线程名字前缀
+ threadPoolTaskExecutor
+ .setThreadNamePrefix(ThreadPoolConstant.SMALL_TOOLS_THREAD_NAME_PREFIX);
+ // 设置在关闭线程池时是否等待任务完成
+ threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
+ // 允许核心线程超时
+ threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
+ // 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
+ threadPoolTaskExecutor.setAwaitTerminationSeconds(60);
+ // 修改拒绝策略为使用当前线程执行
+ // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
+ // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
+ threadPoolTaskExecutor
+ .setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+ //初始化线程池
+ threadPoolTaskExecutor.initialize();
+ return threadPoolTaskExecutor;
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidator.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidator.java
new file mode 100644
index 0000000..9190b90
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidator.java
@@ -0,0 +1,67 @@
+package com.zhengqing.common.core.custom.fieldrepeat;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+/**
+ *
+ * 自定义字段对应数据库内容重复校验 注解
+ *
+ *
+ * @author zhengqingya
+ * @description
+ * @date 2019/9/10 9:32
+ */
+// 元注解: 给其他普通的标签进行解释说明 【@Retention、@Documented、@Target、@Inherited、@Repeatable】
+@Documented
+/**
+ * 指明生命周期: RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
+ * RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
+ */
+@Retention(RetentionPolicy.RUNTIME)
+/**
+ * 指定注解运用的地方: ElementType.ANNOTATION_TYPE 可以给一个注解进行注解 ElementType.CONSTRUCTOR 可以给构造方法进行注解 ElementType.FIELD 可以给属性进行注解
+ * ElementType.LOCAL_VARIABLE 可以给局部变量进行注解 ElementType.METHOD 可以给方法进行注解 ElementType.PACKAGE 可以给一个包进行注解
+ * ElementType.PARAMETER 可以给一个方法内的参数进行注解 ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE})
+@Constraint(validatedBy = FieldRepeatValidatorClass.class)
+// @Repeatable(LinkVals.class)(可重复注解同一字段,或者类,java1.8后支持)
+public @interface FieldRepeatValidator {
+
+ /**
+ * 表名
+ */
+ String tableName();
+
+ /**
+ * 数据库主键id字段属性名 - 默认为id (该值可无)
+ */
+ String idDbName() default "id";
+
+ /**
+ * 注解属性 - 对应校验字段名组 ex: { "name", "code" } 【 注:第一个字段名为所校验的字段,其后的字段为辅助校验字段!!! 】
+ */
+ String[] fieldNames() default {};
+
+ /**
+ * 数据库校验字段属性名 - 对应数据库校验字段名组 ex: { "name", "code" }
+ */
+ String[] dbFieldNames() default {};
+
+ /**
+ * 校验字段组固定值 ex: { "", "1" }
+ */
+ String[] fieldFixedValues() default {};
+
+ /**
+ * 默认错误提示信息
+ */
+ String message() default "字段内容重复!";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorClass.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorClass.java
new file mode 100644
index 0000000..cb16cf5
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorClass.java
@@ -0,0 +1,46 @@
+package com.zhengqing.common.core.custom.fieldrepeat;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ *
+ * `@FieldRepeatValidator`注解接口实现类
+ *
+ *
+ * @author zhengqingya
+ * @description 技巧01:必须实现ConstraintValidator接口
+ * 技巧02:实现了ConstraintValidator接口后即使不进行Bean配置,spring也会将这个类进行Bean管理
+ * 技巧03:可以在实现了ConstraintValidator接口的类中依赖注入其它Bean
+ * 技巧04:实现了ConstraintValidator接口后必须重写 initialize 和 isValid 这两个方法
+ * initialize 方法主要来进行初始化,通常用来获取自定义注解的属性值;
+ * isValid 方法主要进行校验逻辑,返回true表示校验通过,返回false表示校验失败,通常根据注解属性值和实体类属性值进行校验判断 [Object:校验字段的属性值]
+ * @date 2019/9/10 9:22
+ */
+public class FieldRepeatValidatorClass implements ConstraintValidator {
+
+ private String tableName;
+ private String idDbName;
+ private String[] fieldNames;
+ private String[] dbFieldNames;
+ private String[] fieldFixedValues;
+ private String message;
+
+ @Override
+ public void initialize(FieldRepeatValidator fieldRepeatValidator) {
+ this.tableName = fieldRepeatValidator.tableName();
+ this.idDbName = fieldRepeatValidator.idDbName();
+ this.fieldNames = fieldRepeatValidator.fieldNames();
+ this.dbFieldNames = fieldRepeatValidator.dbFieldNames();
+ this.fieldFixedValues = fieldRepeatValidator.fieldFixedValues();
+ this.message = fieldRepeatValidator.message();
+ }
+
+ @Override
+ public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
+ FieldRepeatValidatorUtil fieldRepeatValidator =
+ new FieldRepeatValidatorUtil(this.tableName, this.idDbName, this.fieldNames, this.dbFieldNames, this.fieldFixedValues, o, this.message);
+ return fieldRepeatValidator.fieldRepeat();
+ }
+
+}
diff --git a/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorUtil.java b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorUtil.java
new file mode 100644
index 0000000..780e046
--- /dev/null
+++ b/smallboot-api/common/core/src/main/java/com/zhengqing/common/core/custom/fieldrepeat/FieldRepeatValidatorUtil.java
@@ -0,0 +1,210 @@
+package com.zhengqing.common.core.custom.fieldrepeat;
+
+import com.zhengqing.common.base.exception.MyException;
+import com.zhengqing.common.db.mapper.MyBaseMapper;
+import com.zhengqing.common.base.util.ApplicationContextUtil;
+import com.zhengqing.common.base.util.MyBeanUtil;
+import com.zhengqing.common.base.util.MyStringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 数据库字段内容重复判断处理工具类
+ *
+ *
+ * @author zhengqingya
+ * @date 2019/9/10 9:28
+ */
+@Slf4j
+public class FieldRepeatValidatorUtil {
+
+ /**
+ * 表名
+ */
+ private String TABLE_NAME;
+
+ /**
+ * 数据库对应实体类主键id字段属性名
+ */
+ private String idName;
+ /**
+ * 数据库主键id字段属性名
+ */
+ private String idDbName;
+
+ /**
+ * 数据库主键id字段属性值
+ */
+ private Integer idValue;
+
+ /**
+ * 校验字段名组
+ */
+ private String[] fieldNames;
+
+ /**
+ * 校验辅助字段组值 - 字符串、数字、对象...
+ */
+ private List