Browse Source

bump: 发布1.8.0

Uncarbon 3 năm trước cách đây
mục cha
commit
99ec7e2e4c
54 tập tin đã thay đổi với 765 bổ sung414 xóa
  1. 6 0
      Dockerfile
  2. 10 0
      attachments/db/MySQL/helio_boot.sql
  3. 9 0
      attachments/db/MySQL/upgrade/1.7.3_to_1.8.0.sql
  4. 22 0
      attachments/db/PostgreSQL/helio_boot.sql
  5. 21 0
      attachments/db/PostgreSQL/upgrade/1.7.3_to_1.8.0.sql
  6. 3 0
      entrypoint.sh
  7. 11 3
      pom.xml
  8. 204 0
      src/main/java/cc/uncarbon/aspect/SysLogAspect.java
  9. 25 0
      src/main/java/cc/uncarbon/aspect/extension/SysLogAspectExtensionForSysUserLogin.java
  10. 2 2
      src/main/java/cc/uncarbon/config/CustomInterceptorConfiguration.java
  11. 3 4
      src/main/java/cc/uncarbon/helper/RolePermissionCacheHelper.java
  12. 27 14
      src/main/java/cc/uncarbon/interceptor/AdminSaTokenParseInterceptor.java
  13. 63 0
      src/main/java/cc/uncarbon/interceptor/DefaultSaTokenParseInterceptor.java
  14. 0 3
      src/main/java/cc/uncarbon/module/app/web/AppAuthController.java
  15. 1 6
      src/main/java/cc/uncarbon/module/oss/web/AdminOssFileInfoController.java
  16. 1 6
      src/main/java/cc/uncarbon/module/oss/web/AdminOssUploadDownloadController.java
  17. 37 5
      src/main/java/cc/uncarbon/module/sys/annotation/SysLog.java
  18. 0 165
      src/main/java/cc/uncarbon/module/sys/aspect/SysLogAspect.java
  19. 0 22
      src/main/java/cc/uncarbon/module/sys/aspect/extension/SysLogAspectExtension.java
  20. 0 31
      src/main/java/cc/uncarbon/module/sys/aspect/extension/SysLogAspectExtensionForSysUserLogin.java
  21. 0 8
      src/main/java/cc/uncarbon/module/sys/constant/SysConstant.java
  22. 25 2
      src/main/java/cc/uncarbon/module/sys/entity/SysLogEntity.java
  23. 34 0
      src/main/java/cc/uncarbon/module/sys/extension/SysLogAspectExtension.java
  24. 67 0
      src/main/java/cc/uncarbon/module/sys/extension/impl/DefaultSysLogAspectExtension.java
  25. 2 3
      src/main/java/cc/uncarbon/module/sys/facade/impl/SysTenantFacadeImpl.java
  26. 2 3
      src/main/java/cc/uncarbon/module/sys/mapper/SysLogMapper.java
  27. 19 2
      src/main/java/cc/uncarbon/module/sys/model/request/AdminInsertSysLogDTO.java
  28. 1 2
      src/main/java/cc/uncarbon/module/sys/model/request/AdminListSysLogDTO.java
  29. 0 3
      src/main/java/cc/uncarbon/module/sys/model/request/SysUserLoginDTO.java
  30. 53 0
      src/main/java/cc/uncarbon/module/sys/model/response/IPLocationBO.java
  31. 16 2
      src/main/java/cc/uncarbon/module/sys/model/response/SysLogBO.java
  32. 5 9
      src/main/java/cc/uncarbon/module/sys/service/SysDataDictService.java
  33. 0 4
      src/main/java/cc/uncarbon/module/sys/service/SysDeptService.java
  34. 13 3
      src/main/java/cc/uncarbon/module/sys/service/SysLogService.java
  35. 0 4
      src/main/java/cc/uncarbon/module/sys/service/SysMenuService.java
  36. 4 7
      src/main/java/cc/uncarbon/module/sys/service/SysParamService.java
  37. 0 7
      src/main/java/cc/uncarbon/module/sys/service/SysRoleService.java
  38. 0 3
      src/main/java/cc/uncarbon/module/sys/service/SysTenantService.java
  39. 0 6
      src/main/java/cc/uncarbon/module/sys/service/SysUserService.java
  40. 1 2
      src/main/java/cc/uncarbon/module/sys/util/AdminStpUtil.java
  41. 4 9
      src/main/java/cc/uncarbon/module/sys/web/auth/AdminAuthController.java
  42. 4 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysDataDictController.java
  43. 5 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysDeptController.java
  44. 1 5
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysLogController.java
  45. 4 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysMenuController.java
  46. 5 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysParamController.java
  47. 5 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysRoleController.java
  48. 4 3
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysTenantController.java
  49. 6 4
      src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysUserController.java
  50. 1 1
      src/main/resources/application-prod.yml
  51. 1 1
      src/main/resources/application-test.yml
  52. 2 2
      src/main/resources/application.yml
  53. 36 0
      src/test/java/cc/uncarbon/test/BasicUnitTest.java
  54. 0 38
      src/test/java/cc/uncarbon/test/InjectUnitTest.java

+ 6 - 0
Dockerfile

@@ -0,0 +1,6 @@
+# https://hub.docker.com/_/eclipse-temurin/tags?page=1&name=alpine
+FROM eclipse-temurin:17.0.7_7-jre-alpine
+COPY ./target/*.jar ./app.jar
+COPY entrypoint.sh /
+RUN chmod +x ./entrypoint.sh
+ENTRYPOINT ["./entrypoint.sh"]

+ 10 - 0
attachments/db/MySQL/helio_boot.sql

@@ -489,3 +489,13 @@ INSERT INTO `sys_menu` (`id`, `tenant_id`, `revision`, `del_flag`, `created_at`,
                         `external_link`)
 VALUES (20220922152718, NULL, 1, 0, '2022-09-22 15:27:14', 'helio-generator', '2022-12-26 22:02:09', 'admin', '删除',
         20220922152714, 2, 'OssFileInfo:delete', NULL, 2, 1, NULL, '');
+
+-- v1.8.0 - Uncarbon - 新增错误原因堆栈、用户UA、IP属地字段;表注释更新
+ALTER TABLE sys_log
+    ADD COLUMN error_stacktrace varchar(3000) NOT NULL DEFAULT '' COMMENT '错误原因堆栈' AFTER status,
+    ADD COLUMN user_agent varchar(255) NOT NULL DEFAULT '' COMMENT '用户UA' AFTER error_stacktrace,
+    ADD COLUMN ip_location_region_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-国家或地区名' AFTER user_agent,
+    ADD COLUMN ip_location_province_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-省级行政区名' AFTER ip_location_region_name,
+    ADD COLUMN ip_location_city_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-市级行政区名' AFTER ip_location_province_name,
+    ADD COLUMN ip_location_district_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-县级行政区名' AFTER ip_location_city_name;
+ALTER TABLE sys_log COMMENT = '系统日志';

+ 9 - 0
attachments/db/MySQL/upgrade/1.7.3_to_1.8.0.sql

@@ -0,0 +1,9 @@
+-- v1.8.0 - Uncarbon - 新增错误原因堆栈、用户UA、IP属地字段;表注释更新
+ALTER TABLE sys_log
+    ADD COLUMN error_stacktrace varchar(3000) NOT NULL DEFAULT '' COMMENT '错误原因堆栈' AFTER status,
+    ADD COLUMN user_agent varchar(255) NOT NULL DEFAULT '' COMMENT '用户UA' AFTER error_stacktrace,
+    ADD COLUMN ip_location_region_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-国家或地区名' AFTER user_agent,
+    ADD COLUMN ip_location_province_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-省级行政区名' AFTER ip_location_region_name,
+    ADD COLUMN ip_location_city_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-市级行政区名' AFTER ip_location_province_name,
+    ADD COLUMN ip_location_district_name varchar(100) NOT NULL DEFAULT '' COMMENT 'IP地址属地-县级行政区名' AFTER ip_location_city_name;
+ALTER TABLE sys_log COMMENT = '系统日志';

+ 22 - 0
attachments/db/PostgreSQL/helio_boot.sql

@@ -969,3 +969,25 @@ INSERT INTO "sys_menu"("id", "tenant_id", "revision", "del_flag", "created_at",
                        "external_link")
 VALUES (20220922152718, NULL, 1, 0, '2022-09-22 15:27:14', 'helio-generator', '2022-09-22 15:36:03', 'admin',
         '删除', 1572848462892752896, 2, 'OssFileInfo:delete', NULL, 2, 1, NULL, '');
+
+-- v1.8.0 - Uncarbon - 新增错误原因堆栈、用户UA、IP属地字段;表注释更新
+ALTER TABLE sys_log
+    ADD COLUMN error_stacktrace varchar(3000) NOT NULL DEFAULT '',
+    ADD COLUMN user_agent varchar(255) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_region_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_province_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_city_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_district_name varchar(100) NOT NULL DEFAULT '';
+
+COMMENT ON COLUMN sys_log.error_stacktrace IS '错误原因堆栈';
+COMMENT ON COLUMN sys_log.user_agent IS '用户UA';
+COMMENT ON COLUMN sys_log.ip_location_region_name IS 'IP地址属地-国家或地区名';
+COMMENT ON COLUMN sys_log.ip_location_province_name IS 'IP地址属地-省级行政区名';
+COMMENT ON COLUMN sys_log.ip_location_city_name IS 'IP地址属地-市级行政区名';
+COMMENT ON COLUMN sys_log.ip_location_district_name IS 'IP地址属地-县级行政区名';
+
+COMMENT ON TABLE sys_log IS '系统日志';
+
+-- v1.8.0 - Uncarbon - 订正系统菜单-权限串缺少默认值问题
+ALTER TABLE "sys_menu"
+    ALTER COLUMN "permission" SET DEFAULT '';

+ 21 - 0
attachments/db/PostgreSQL/upgrade/1.7.3_to_1.8.0.sql

@@ -0,0 +1,21 @@
+-- v1.8.0 - Uncarbon - 新增错误原因堆栈、用户UA、IP属地字段;表注释更新
+ALTER TABLE sys_log
+    ADD COLUMN error_stacktrace varchar(3000) NOT NULL DEFAULT '',
+    ADD COLUMN user_agent varchar(255) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_region_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_province_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_city_name varchar(100) NOT NULL DEFAULT '',
+    ADD COLUMN ip_location_district_name varchar(100) NOT NULL DEFAULT '';
+
+COMMENT ON COLUMN sys_log.error_stacktrace IS '错误原因堆栈';
+COMMENT ON COLUMN sys_log.user_agent IS '用户UA';
+COMMENT ON COLUMN sys_log.ip_location_region_name IS 'IP地址属地-国家或地区名';
+COMMENT ON COLUMN sys_log.ip_location_province_name IS 'IP地址属地-省级行政区名';
+COMMENT ON COLUMN sys_log.ip_location_city_name IS 'IP地址属地-市级行政区名';
+COMMENT ON COLUMN sys_log.ip_location_district_name IS 'IP地址属地-县级行政区名';
+
+COMMENT ON TABLE sys_log IS '系统日志';
+
+-- v1.8.0 - Uncarbon - 订正系统菜单-权限串缺少默认值问题
+ALTER TABLE "sys_menu"
+    ALTER COLUMN "permission" SET DEFAULT '';

+ 3 - 0
entrypoint.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "The application will start ..."
+exec java ${JAVA_OPTS} ${APP_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -jar app.jar  "$@"

+ 11 - 3
pom.xml

@@ -8,12 +8,12 @@
     <parent>
         <groupId>cc.uncarbon.framework</groupId>
         <artifactId>helio-starters</artifactId>
-        <version>1.7.3</version>
+        <version>1.8.0</version>
     </parent>
 
     <artifactId>helio-boot</artifactId>
-    <!-- 程序版本号,建议每次打包发布时都调整以作区分 -->
-    <version>1.7.3</version>
+    <!-- 制品版本号,建议每次打包发布时都调整以作区分 -->
+    <version>1.8.0</version>
 
     <properties>
         <!-- 这里写其他第三方依赖 -->
@@ -114,9 +114,17 @@
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
                 <configuration>
                     <fork>true</fork>
                 </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
             </plugin>
 
             <!-- 编译时,跳过单元测试 -->

+ 204 - 0
src/main/java/cc/uncarbon/aspect/SysLogAspect.java

@@ -0,0 +1,204 @@
+package cc.uncarbon.aspect;
+
+import cc.uncarbon.framework.core.context.UserContextHolder;
+import cc.uncarbon.framework.web.util.IPUtil;
+import cc.uncarbon.module.sys.annotation.SysLog;
+import cc.uncarbon.module.sys.constant.SysConstant;
+import cc.uncarbon.module.sys.enums.SysLogStatusEnum;
+import cc.uncarbon.module.sys.extension.SysLogAspectExtension;
+import cc.uncarbon.module.sys.extension.impl.DefaultSysLogAspectExtension;
+import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
+import cc.uncarbon.module.sys.model.response.IPLocationBO;
+import cc.uncarbon.module.sys.service.SysLogService;
+import cn.dev33.satoken.spring.SpringMVCUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.text.StrPool;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.http.HttpHeaders;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+/**
+ * SysLog 切面实现类
+ *
+ * @author Uncarbon
+ * @author ruoyi
+ */
+@Aspect
+@Component
+@Slf4j
+@RequiredArgsConstructor
+public class SysLogAspect {
+
+    /**
+     * 用于数据脱敏的Bean复制选项
+     */
+    private static final CopyOptions MASKING_COPY_OPTIONS = new CopyOptions()
+            .setIgnoreNullValue(false)
+            .setIgnoreProperties(SysConstant.SENSITIVE_FIELDS);
+
+    /**
+     * 最大数据文本保存长度
+     */
+    private static final int MAX_STRING_SAVE_LENGTH = 3000;
+
+    private static final SysLogAspectExtension DEFAULT_SYS_LOG_ASPECT_EXTENSION = new DefaultSysLogAspectExtension();
+
+    private final SysLogService sysLogService;
+
+
+    /**
+     * 顺利运行
+     */
+    @AfterReturning(pointcut = "@annotation(annotation)", returning = "ret")
+    public void returning(JoinPoint joinPoint, SysLog annotation, Object ret) {
+        if (!ArrayUtil.contains(annotation.when(), SysLog.When.SUCCESS)) {
+            // 系统日志保存时机不包含“成功时”
+            return;
+        }
+
+        if (!annotation.syncSave()) {
+            // 异步保存
+            saveSysLogAsync(joinPoint, annotation, null, ret);
+            return;
+        }
+
+        saveSysLog(joinPoint, annotation, null, ret);
+    }
+
+    /**
+     * 出现异常
+     */
+    @AfterThrowing(value = "@annotation(annotation)", throwing = "e")
+    public void throwing(JoinPoint joinPoint, SysLog annotation, Throwable e) {
+        if (!ArrayUtil.contains(annotation.when(), SysLog.When.FAILED)) {
+            // 系统日志保存时机不包含“失败时”
+            return;
+        }
+
+        if (!annotation.syncSave()) {
+            // 异步保存
+            saveSysLogAsync(joinPoint, annotation, e, null);
+            return;
+        }
+
+        saveSysLog(joinPoint, annotation, e, null);
+    }
+
+    /**
+     * 同步保存系统日志
+     */
+    private void saveSysLog(final JoinPoint joinPoint, SysLog annotation, final Throwable e, Object ret) {
+        // 当前线程HTTP请求
+        HttpServletRequest request = SpringMVCUtil.getRequest();
+
+        // SysLog 切面实现类扩展
+        SysLogAspectExtension extensionInstance = DEFAULT_SYS_LOG_ASPECT_EXTENSION;
+        Class<? extends SysLogAspectExtension> extensionClazz = annotation.extension();
+        if (extensionClazz != null && extensionClazz != DefaultSysLogAspectExtension.class) {
+            // 有自定义扩展点
+            extensionInstance = ReflectUtil.newInstance(extensionClazz);
+        }
+
+        AdminInsertSysLogDTO dto = new AdminInsertSysLogDTO()
+                // 记录操作人
+                .setUserId(UserContextHolder.getUserId())
+                .setUsername(UserContextHolder.getUserName())
+                // 记录请求方法
+                .setMethod(StrUtil.builder(
+                        joinPoint.getTarget().getClass().getName(),
+                        "#",
+                        joinPoint.getSignature().getName()
+                ).toString())
+                // 记录操作内容
+                .setOperation(annotation.value())
+                // 默认置为成功
+                .setStatus(SysLogStatusEnum.SUCCESS)
+                ;
+
+        /*
+        记录请求参数
+         */
+        HashMap<Object, Object> afterMasked = new HashMap<>(16, 1);
+        String params = Arrays.stream(joinPoint.getArgs()).map(
+                item -> {
+                    if (ClassUtil.isBasicType(item.getClass())) {
+                        // 基元类型 OR 其包装类型,保存在DB时保持原样
+                        return StrUtil.toStringOrNull(item);
+                    }
+
+                    // 先去除敏感字段后再入库
+                    afterMasked.clear();
+                    BeanUtil.copyProperties(item, afterMasked, MASKING_COPY_OPTIONS);
+                    return JSONUtil.toJsonStr(afterMasked);
+                }
+        ).collect(Collectors.joining(StrPool.LF));
+        if (StrUtil.length(params) > MAX_STRING_SAVE_LENGTH) {
+            params = StrUtil.subPre(params, MAX_STRING_SAVE_LENGTH);
+        }
+        dto.setParams(params);
+
+        /*
+        记录IP地址
+        https://gitee.com/uncarbon97/helio-boot/issues/I5KN1X
+         */
+        String ip = UserContextHolder.getClientIP();
+        if (StrUtil.isEmpty(ip)) {
+            // 兜底处理,直接从当前线程的请求头中拿
+            ip = IPUtil.getClientIPAddress(request, 0);
+        }
+        dto.setIp(ip);
+
+        if (e != null) {
+            // 异常不为空,置状态为失败
+            dto.setStatus(SysLogStatusEnum.FAILED);
+
+            // 错误原因堆栈
+            dto.setErrorStacktrace(ExceptionUtil.stacktraceToString(e, MAX_STRING_SAVE_LENGTH));
+        }
+
+        // UA;Spring框架已经做了简单的防注入过滤
+        dto.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
+
+        if (annotation.queryIPLocation()) {
+            // 查询IP属地
+            IPLocationBO ipLocation = extensionInstance.queryIPLocation(ip);
+            dto
+                    .setIpLocationRegionName(ipLocation.getRegionName())
+                    .setIpLocationProvinceName(ipLocation.getProvinceName())
+                    .setIpLocationCityName(ipLocation.getCityName())
+                    .setIpLocationDistrictName(ipLocation.getDistrictName());
+        }
+
+        // 扩展:保存到 DB 前
+        extensionInstance.beforeSaving(dto, joinPoint, annotation, e, ret);
+
+        // 保存系统日志
+        sysLogService.adminInsert(dto);
+    }
+
+    /**
+     * 异步保存系统日志
+     */
+    @Async(value = "taskExecutor")
+    public void saveSysLogAsync(final JoinPoint joinPoint, SysLog annotation, final Throwable e, Object ret) {
+        this.saveSysLog(joinPoint, annotation, e, ret);
+    }
+}

+ 25 - 0
src/main/java/cc/uncarbon/aspect/extension/SysLogAspectExtensionForSysUserLogin.java

@@ -0,0 +1,25 @@
+package cc.uncarbon.aspect.extension;
+
+import cc.uncarbon.module.sys.annotation.SysLog;
+import cc.uncarbon.module.sys.extension.impl.DefaultSysLogAspectExtension;
+import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
+import cc.uncarbon.module.sys.model.request.SysUserLoginDTO;
+import lombok.NoArgsConstructor;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * SysLog 切面实现类扩展 for 登录后台用户
+ */
+@NoArgsConstructor
+public class SysLogAspectExtensionForSysUserLogin extends DefaultSysLogAspectExtension {
+
+    @Override
+    public void beforeSaving(AdminInsertSysLogDTO insertSysLogDTO, JoinPoint joinPoint, SysLog annotation, Throwable e, Object ret) {
+        for (Object arg : joinPoint.getArgs()) {
+            if (arg instanceof SysUserLoginDTO) {
+                SysUserLoginDTO argDTO = (SysUserLoginDTO) arg;
+                insertSysLogDTO.setUsername(argDTO.getUsername());
+            }
+        }
+    }
+}

+ 2 - 2
src/main/java/cc/uncarbon/config/CustomInterceptorConfiguration.java

@@ -1,8 +1,8 @@
 package cc.uncarbon.config;
 
 import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.satoken.interceptor.DefaultSaTokenParseInterceptor;
 import cc.uncarbon.interceptor.AdminSaTokenParseInterceptor;
+import cc.uncarbon.interceptor.DefaultSaTokenParseInterceptor;
 import cc.uncarbon.module.app.constant.AppConstant;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cn.dev33.satoken.interceptor.SaInterceptor;
@@ -50,7 +50,7 @@ public class CustomInterceptorConfiguration implements WebMvcConfigurer {
         3. /app/** 路由拦截器, 使几乎所有接口都需要登录
         放行接口请在配置文件的 helio.security.exclude-routes 中设置
 
-        @see http://sa-token.dev33.cn/doc/index.html#/use/route-check
+        @see https://sa-token.cc/doc.html#/use/route-check
          */
         registry
                 .addInterceptor(new SaInterceptor(

+ 3 - 4
src/main/java/cc/uncarbon/helper/RolePermissionCacheHelper.java

@@ -2,16 +2,15 @@ package cc.uncarbon.helper;
 
 import cc.uncarbon.framework.core.context.UserContextHolder;
 import cn.hutool.core.collection.CollUtil;
-
-import java.util.*;
-
 import lombok.RequiredArgsConstructor;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
+import java.util.*;
+
 /**
  * 将角色对应权限,缓存至 Redis
- * 参考文章: https://sa-token.dev33.cn/doc/index.html#/fun/jur-cache
+ * 参考文章: https://sa-token.cc/doc.html#/fun/jur-cache
  *
  * @author Uncarbon
  */

+ 27 - 14
src/main/java/cc/uncarbon/interceptor/AdminSaTokenParseInterceptor.java

@@ -6,16 +6,18 @@ import cc.uncarbon.framework.core.context.UserContext;
 import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.web.util.IPUtil;
 import cc.uncarbon.module.sys.util.AdminStpUtil;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import cn.dev33.satoken.session.SaSession;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.servlet.AsyncHandlerInterceptor;
 import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 
 /**
- * 从请求头解析并赋值到用户上下文
+ * 从请求头解析并赋值到用户上下文,用于后台管理用户的鉴权
  * 其实就是"DefaultSaTokenParseInterceptor"改个名, 工具类换成"AdminStpUtil"
  * @author Uncarbon
  */
@@ -32,18 +34,9 @@ public class AdminSaTokenParseInterceptor implements AsyncHandlerInterceptor {
 
         // SA-Token 会自动从请求头中解析 token,所以这里可以直接拿到对应 session,从而取出业务字段
         if (AdminStpUtil.isLogin()) {
-            UserContext userContext = (UserContext) AdminStpUtil.getSession().get(UserContext.CAMEL_NAME);
-
-            // 获取用户公网IP
-            userContext.setClientIP(IPUtil.getClientIPAddress(request));
-            UserContextHolder.setUserContext(userContext);
-
-            log.debug("[SA-Token][Admin] 从请求头解析出用户上下文 >> {}", userContext);
-
-            // 赋值租户上下文
-            TenantContext tenantContext = (TenantContext) AdminStpUtil.getSession().get(TenantContext.CAMEL_NAME);
-            TenantContextHolder.setTenantContext(tenantContext);
+            setContextsFromSaSession(AdminStpUtil.getSession(), request);
 
+            log.debug("[SA-Token][Admin] 从请求头解析出用户上下文 >> {}", UserContextHolder.getUserContext());
         } else {
             UserContextHolder.clear();
             TenantContextHolder.clear();
@@ -52,4 +45,24 @@ public class AdminSaTokenParseInterceptor implements AsyncHandlerInterceptor {
         return true;
     }
 
+    /**
+     * 自 SaSession 更新当前线程用户、租户上下文
+     *
+     * @param session 会话对象
+     * @param request ServletRequest 对象
+     */
+    public static void setContextsFromSaSession(SaSession session, HttpServletRequest request) {
+        // 赋值用户上下文
+        UserContext userContext = (UserContext) session.get(UserContext.CAMEL_NAME);
+
+        // 获取用户公网IP
+        // 先按逗号分隔后,再取第index个IP地址(从0开始);兼容启用了云防护盾CDN的服务器(可能获取到的IP会带上中间代理节点的IP地址)
+        userContext.setClientIP(IPUtil.getClientIPAddress(request, 0));
+        UserContextHolder.setUserContext(userContext);
+
+        // 赋值租户上下文
+        TenantContext tenantContext = (TenantContext) session.get(TenantContext.CAMEL_NAME);
+        TenantContextHolder.setTenantContext(tenantContext);
+    }
+
 }

+ 63 - 0
src/main/java/cc/uncarbon/interceptor/DefaultSaTokenParseInterceptor.java

@@ -0,0 +1,63 @@
+package cc.uncarbon.interceptor;
+
+import cc.uncarbon.framework.core.context.TenantContext;
+import cc.uncarbon.framework.core.context.TenantContextHolder;
+import cc.uncarbon.framework.core.context.UserContext;
+import cc.uncarbon.framework.core.context.UserContextHolder;
+import cc.uncarbon.framework.web.util.IPUtil;
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpUtil;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 从请求头解析并赋值到用户上下文,默认用于C端用户的鉴权
+ * @author Uncarbon
+ */
+@Slf4j
+public class DefaultSaTokenParseInterceptor implements AsyncHandlerInterceptor {
+
+    @Override
+    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
+        if (handler instanceof ResourceHttpRequestHandler) {
+            // 直接放行静态资源
+            return true;
+        }
+
+        // SA-Token 会自动从请求头中解析 token,所以这里可以直接拿到对应 session,从而取出业务字段
+        if (StpUtil.isLogin()) {
+            setContextsFromSaSession(StpUtil.getSession(), request);
+            log.debug("[SA-Token] 从请求头解析出用户上下文 >> {}", UserContextHolder.getUserContext());
+        } else {
+            UserContextHolder.clear();
+            TenantContextHolder.clear();
+        }
+
+        return true;
+    }
+
+    /**
+     * 自 SaSession 更新当前线程用户、租户上下文
+     *
+     * @param session 会话对象
+     * @param request ServletRequest 对象
+     */
+    public static void setContextsFromSaSession(SaSession session, HttpServletRequest request) {
+        // 赋值用户上下文
+        UserContext userContext = (UserContext) session.get(UserContext.CAMEL_NAME);
+
+        // 获取用户公网IP
+        // 先按逗号分隔后,再取第index个IP地址(从0开始);兼容启用了云防护盾CDN的服务器(可能获取到的IP会带上中间代理节点的IP地址)
+        userContext.setClientIP(IPUtil.getClientIPAddress(request, 0));
+        UserContextHolder.setUserContext(userContext);
+
+        // 赋值租户上下文
+        TenantContext tenantContext = (TenantContext) session.get(TenantContext.CAMEL_NAME);
+        TenantContextHolder.setTenantContext(tenantContext);
+    }
+}

+ 0 - 3
src/main/java/cc/uncarbon/module/app/web/AppAuthController.java

@@ -13,9 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @Slf4j
 @Api(value = "APP鉴权接口", tags = {"APP鉴权接口"})

+ 1 - 6
src/main/java/cc/uncarbon/module/oss/web/AdminOssFileInfoController.java

@@ -21,15 +21,10 @@ import org.springframework.web.bind.annotation.*;
 import javax.validation.Valid;
 
 
-/**
- * 后台管理-上传文件信息管理接口
- *
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
-@Api(value = "上传文件信息管理接口", tags = {"上传文件信息管理接口"})
+@Api(value = "后台管理-上传文件信息管理接口", tags = {"后台管理-上传文件信息管理接口"})
 @RequestMapping(SysConstant.SYS_MODULE_CONTEXT_PATH + HelioConstant.Version.HTTP_API_VERSION_V1 + "/oss/file/infos")
 @RestController
 public class AdminOssFileInfoController {

+ 1 - 6
src/main/java/cc/uncarbon/module/oss/web/AdminOssUploadDownloadController.java

@@ -31,14 +31,9 @@ import java.io.IOException;
 import java.net.URLEncoder;
 
 
-/**
- * 后台管理专用 - 上传、下载文件接口
- *
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @Slf4j
-@Api(value = "【后台管理】上传、下载文件接口", tags = {"【后台管理】上传、下载文件接口"})
+@Api(value = "后台管理-上传、下载文件接口", tags = {"后台管理-上传、下载文件接口"})
 @RequestMapping(SysConstant.SYS_MODULE_CONTEXT_PATH + HelioConstant.Version.HTTP_API_VERSION_V1 + "/oss/files")
 @RestController
 public class AdminOssUploadDownloadController {

+ 37 - 5
src/main/java/cc/uncarbon/module/sys/annotation/SysLog.java

@@ -1,12 +1,14 @@
 package cc.uncarbon.module.sys.annotation;
 
+import cc.uncarbon.module.sys.extension.SysLogAspectExtension;
+import cc.uncarbon.module.sys.extension.impl.DefaultSysLogAspectExtension;
+
 import java.lang.annotation.*;
 
 
 /**
- * 放在方法上,可将操作记录至系统日志中
- *
- * @author Uncarbon
+ * 放在Controller方法上,可将操作记录至系统日志中
+ * 需同JVM中,存在对应的SysLogAspect切面类
  */
 @Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
@@ -17,13 +19,43 @@ public @interface SysLog {
     /**
      * 操作内容(如:新增部门)
      */
-    String value() default "";
+    String value();
 
     /**
      * 是否同步保存至系统日志数据表中
      * true = 同步保存:如果开启了事务/事务注解,若系统日志保存失败,则会抛出异常触发回滚,使得本次操作也失败
      * false = 异步保存:若系统日志保存失败,不影响本次操作
      */
-    boolean syncSaving() default false;
+    boolean syncSave() default false;
+
+    /**
+     * 保存系统日志至数据表的时机
+     * 默认为仅“成功时”
+     * 多个输入值间默认为“或”关系
+     */
+    When[] when() default When.SUCCESS;
+    enum When {
+        /**
+         * 成功时
+         */
+        SUCCESS,
+
+        /**
+         * 失败时
+         */
+        FAILED,
+    }
+
+    /**
+     * 扩展点,可以增强系统日志,在保存时的动作
+     * 示例见:SysLogAspectExtensionForSysUserLogin
+     */
+    Class<? extends SysLogAspectExtension> extension() default DefaultSysLogAspectExtension.class;
+
+    /**
+     * 是否查询IP地址属地
+     * 本功能可能存在网络开销,建议仅在关键处使用,并视情况决定是否搭配异步保存(即:syncSave = false)使用
+     */
+    boolean queryIPLocation() default false;
 
 }

+ 0 - 165
src/main/java/cc/uncarbon/module/sys/aspect/SysLogAspect.java

@@ -1,165 +0,0 @@
-package cc.uncarbon.module.sys.aspect;
-
-import cc.uncarbon.framework.core.context.UserContextHolder;
-import cc.uncarbon.framework.satoken.util.IPUtil;
-import cc.uncarbon.module.sys.annotation.SysLog;
-import cc.uncarbon.module.sys.aspect.extension.SysLogAspectExtension;
-import cc.uncarbon.module.sys.constant.SysConstant;
-import cc.uncarbon.module.sys.enums.SysLogStatusEnum;
-import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
-import cc.uncarbon.module.sys.service.SysLogService;
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.bean.copier.CopyOptions;
-import cn.hutool.core.text.StrPool;
-import cn.hutool.json.JSONUtil;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.stream.Collectors;
-
-/**
- * SysLog 切面实现类
- *
- * @author Uncarbon
- */
-@Aspect
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class SysLogAspect {
-
-    private final SysLogService sysLogService;
-    private final ObjectProvider<SysLogAspectExtension> extensions;
-    // Bean 属性复制配置项
-    private static final CopyOptions copyOptions4MaskingArgs = new CopyOptions();
-
-
-    static {
-        copyOptions4MaskingArgs.setIgnoreNullValue(false);
-        copyOptions4MaskingArgs.setIgnoreProperties(SysConstant.SENSITIVE_FIELDS);
-    }
-
-    @Pointcut("@annotation(cc.uncarbon.module.sys.annotation.SysLog)")
-    public void sysLogPointcut() {
-        // AOP Pointcut
-    }
-
-    @Around("sysLogPointcut()")
-    public Object sysLogAround(ProceedingJoinPoint point) throws Throwable {
-        // 切面点执行结果
-        Object executeResult = null;
-        // 切面点执行是否成功
-        boolean executeSuccessFlag = false;
-        // 切面点执行过程中抛出的异常
-        Exception executeFailedException = null;
-        try {
-            executeResult = point.proceed();
-            executeSuccessFlag = true;
-        } catch (Exception e) {
-            // store it
-            executeFailedException = e;
-        }
-
-        /*
-        记录对应系统日志
-         */
-        MethodSignature methodSignature = (MethodSignature) point.getSignature();
-        SysLog sysLogAnnotation = methodSignature.getMethod().getAnnotation(SysLog.class);
-
-        if (sysLogAnnotation.syncSaving()) {
-            this.sysLogSaving(point, methodSignature, sysLogAnnotation, executeSuccessFlag);
-        } else {
-            this.sysLogSavingAsync(point, methodSignature, sysLogAnnotation, executeSuccessFlag);
-        }
-
-        /*
-        如果执行过程中存在异常则抛出
-         */
-        if (executeFailedException != null) {
-            throw executeFailedException;
-        }
-
-        return executeResult;
-    }
-
-    /**
-     * 同步保存系统日志
-     */
-    private void sysLogSaving(ProceedingJoinPoint point,
-                              MethodSignature methodSignature,
-                              SysLog sysLogAnnotation,
-                              boolean executeSuccessFlag
-    ) {
-        AdminInsertSysLogDTO dto = new AdminInsertSysLogDTO()
-                // 记录操作人
-                .setUserId(UserContextHolder.getUserId())
-                .setUsername(UserContextHolder.getUserName())
-                // 记录请求方法
-                .setMethod(methodSignature.toString())
-                // 记录操作内容
-                .setOperation(sysLogAnnotation.value());
-
-        /*
-        记录请求参数
-         */
-        HashMap<Object, Object> afterMasked = new HashMap<>();
-        dto.setParams(Arrays.stream(point.getArgs()).map(
-                each -> {
-                    // 先去除敏感字段后再入库
-                    afterMasked.clear();
-                    BeanUtil.copyProperties(each, afterMasked, copyOptions4MaskingArgs);
-                    return JSONUtil.toJsonStr(afterMasked);
-                }
-        ).collect(Collectors.joining(StrPool.COMMA)));
-
-        /*
-        记录IP地址
-        https://gitee.com/uncarbon97/helio-boot/issues/I5KN1X
-         */
-        String ip = UserContextHolder.getClientIP();
-        if (ip == null) {
-            // 兜底处理,直接从当前线程的请求头中拿
-            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            if (requestAttributes != null) {
-                ip = IPUtil.getClientIPAddress(requestAttributes.getRequest());
-            }
-        }
-        dto.setIp(ip);
-
-        // 记录状态
-        dto.setStatus(executeSuccessFlag ? SysLogStatusEnum.SUCCESS : SysLogStatusEnum.FAILED);
-
-        // 执行扩展 - 保存到 DB 前
-        for (SysLogAspectExtension extension : extensions) {
-            extension.beforeSaving(sysLogAnnotation, point, dto);
-        }
-
-        // 保存系统日志
-        sysLogService.adminInsert(dto);
-    }
-
-    /**
-     * 异步保存系统日志
-     */
-    @Async(value = "taskExecutor")
-    public void sysLogSavingAsync(ProceedingJoinPoint point,
-                                  MethodSignature methodSignature,
-                                  SysLog sysLogAnnotation,
-                                  boolean executeSuccessFlag
-    ) {
-        this.sysLogSaving(point, methodSignature, sysLogAnnotation, executeSuccessFlag);
-    }
-
-}

+ 0 - 22
src/main/java/cc/uncarbon/module/sys/aspect/extension/SysLogAspectExtension.java

@@ -1,22 +0,0 @@
-package cc.uncarbon.module.sys.aspect.extension;
-
-import cc.uncarbon.module.sys.annotation.SysLog;
-import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
-import org.aspectj.lang.ProceedingJoinPoint;
-
-/**
- * SysLog 切面实现类扩展
- *
- * @author Uncarbon
- */
-public interface SysLogAspectExtension {
-
-    /**
-     * 保存到 DB 前
-     * @param sysLogAnnotation 注解
-     * @param point 切面 point
-     * @param insertSysLogDTO 日志请求体
-     */
-    void beforeSaving(SysLog sysLogAnnotation, ProceedingJoinPoint point, AdminInsertSysLogDTO insertSysLogDTO);
-
-}

+ 0 - 31
src/main/java/cc/uncarbon/module/sys/aspect/extension/SysLogAspectExtensionForSysUserLogin.java

@@ -1,31 +0,0 @@
-package cc.uncarbon.module.sys.aspect.extension;
-
-import cc.uncarbon.module.sys.annotation.SysLog;
-import cc.uncarbon.module.sys.constant.SysConstant;
-import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
-import cc.uncarbon.module.sys.model.request.SysUserLoginDTO;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.springframework.stereotype.Component;
-
-/**
- * SysLog 切面实现类扩展 for 登录后台用户
- *
- * @author Uncarbon
- */
-@Component
-public class SysLogAspectExtensionForSysUserLogin implements SysLogAspectExtension {
-
-    @Override
-    public void beforeSaving(SysLog sysLogAnnotation, ProceedingJoinPoint point, AdminInsertSysLogDTO insertSysLogDTO) {
-        if (SysConstant.SysLogOperation.SYS_USER_LOGIN.equals(sysLogAnnotation.value())) {
-            // 仅针对特定注解扩展
-            for (Object arg : point.getArgs()) {
-                if (arg instanceof SysUserLoginDTO) {
-                    SysUserLoginDTO dto = (SysUserLoginDTO) arg;
-                    insertSysLogDTO.setUsername(dto.getUsername());
-                    insertSysLogDTO.setIp(dto.getClientIP());
-                }
-            }
-        }
-    }
-}

+ 0 - 8
src/main/java/cc/uncarbon/module/sys/constant/SysConstant.java

@@ -33,12 +33,4 @@ public interface SysConstant {
      */
     String[] SENSITIVE_FIELDS = {"password", "oldPassword", "newPassword", "confirmNewPassword", "passwordOfNewUser", "randomPassword", "tenantAdminPassword"};
 
-    /**
-     * SysLog 系统日志操作内容
-     */
-    interface SysLogOperation {
-
-        String SYS_USER_LOGIN = "登录后台用户";
-
-    }
 }

+ 25 - 2
src/main/java/cc/uncarbon/module/sys/entity/SysLogEntity.java

@@ -14,8 +14,7 @@ import lombok.experimental.SuperBuilder;
 
 
 /**
- * 后台操作日志
- * @author Uncarbon
+ * 系统日志
  */
 @EqualsAndHashCode(callSuper = true)
 @Accessors(chain = true)
@@ -54,4 +53,28 @@ public class SysLogEntity extends HelioBaseEntity<Long> {
 	@TableField(value = "status")
 	private SysLogStatusEnum status;
 
+	@ApiModelProperty(value = "错误原因堆栈")
+	@TableField(value = "error_stacktrace")
+	private String errorStacktrace;
+
+	@ApiModelProperty(value = "用户UA")
+	@TableField(value = "user_agent")
+	private String userAgent;
+
+	@ApiModelProperty(value = "IP地址属地-国家或地区名")
+	@TableField(value = "ip_location_region_name")
+	private String ipLocationRegionName;
+
+	@ApiModelProperty(value = "IP地址属地-省级行政区名")
+	@TableField(value = "ip_location_province_name")
+	private String ipLocationProvinceName;
+
+	@ApiModelProperty(value = "IP地址属地-市级行政区名")
+	@TableField(value = "ip_location_city_name")
+	private String ipLocationCityName;
+
+	@ApiModelProperty(value = "IP地址属地-县级行政区名")
+	@TableField(value = "ip_location_district_name")
+	private String ipLocationDistrictName;
+
 }

+ 34 - 0
src/main/java/cc/uncarbon/module/sys/extension/SysLogAspectExtension.java

@@ -0,0 +1,34 @@
+package cc.uncarbon.module.sys.extension;
+
+import cc.uncarbon.module.sys.annotation.SysLog;
+import cc.uncarbon.module.sys.model.request.AdminInsertSysLogDTO;
+import cc.uncarbon.module.sys.model.response.IPLocationBO;
+import org.aspectj.lang.JoinPoint;
+
+/**
+ * SysLog 切面实现类扩展
+ *
+ */
+public interface SysLogAspectExtension {
+
+    /**
+     * 查询IP地址属地
+     * @param ip IPv4或IPv6地址
+     */
+    default IPLocationBO queryIPLocation(String ip) {
+        return IPLocationBO.unknown();
+    }
+
+    /**
+     * 保存到 DB 前
+     * @param insertSysLogDTO 新系统日志DTO
+     * @param joinPoint 切点
+     * @param annotation 注解实例
+     * @param e 异常实例;如果没有发生异常则值为null
+     * @param ret 原始方法过程,于顺利运行时的返回值
+     */
+    default void beforeSaving(final AdminInsertSysLogDTO insertSysLogDTO, final JoinPoint joinPoint, SysLog annotation, final Throwable e, Object ret) {
+
+    }
+
+}

+ 67 - 0
src/main/java/cc/uncarbon/module/sys/extension/impl/DefaultSysLogAspectExtension.java

@@ -0,0 +1,67 @@
+package cc.uncarbon.module.sys.extension.impl;
+
+import cc.uncarbon.module.sys.extension.SysLogAspectExtension;
+import cc.uncarbon.module.sys.model.response.IPLocationBO;
+import cn.hutool.core.net.NetUtil;
+import cn.hutool.core.text.StrPool;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class DefaultSysLogAspectExtension implements SysLogAspectExtension {
+
+    @Override
+    public IPLocationBO queryIPLocation(String ip) {
+        if (StrUtil.contains(ip, StrPool.COLON)) {
+            // 仅根据冒号简易判断;暂不支持IPv6地址
+            return IPLocationBO.unknown();
+        }
+
+        if (NetUtil.isInnerIP(ip)) {
+            return IPLocationBO.intranet();
+        }
+
+        // 该API主要支持中国内地
+        HttpRequest httpRequest = HttpRequest.get("http://whois.pconline.com.cn/ipJson.jsp")
+                .form("ip", ip)
+                .form("json", Boolean.TRUE.toString())
+                .charset(CharsetUtil.CHARSET_GBK)
+                .timeout(5000);
+        try (HttpResponse httpResponse = httpRequest.execute()) {
+            String repStr = httpResponse.body();
+            if (JSONUtil.isTypeJSONObject(repStr)) {
+                /*
+                返回文本是JSON对象
+                example1:
+                {"ip":"39.156.66.10","pro":"北京市","proCode":"110000","city":"北京市","cityCode":"110000","region":"","regionCode":"0","addr":"北京市 移通","regionNames":"","err":""}
+
+                example2:
+                {"ip":"1.1.1.1","pro":"","proCode":"999999","city":"","cityCode":"0","region":"","regionCode":"0","addr":" 美国APNIC&CloudFlare公共DNS服务器","regionNames":"","err":"noprovince"}
+
+                example3:
+                {"ip":"8.8.8.8","pro":"","proCode":"999999","city":"","cityCode":"0","region":"","regionCode":"0","addr":" 美国","regionNames":"","err":"noprovince"}
+                 */
+                JSONObject repJson = JSONUtil.parseObj(repStr);
+                String pro = repJson.getStr("pro");
+                String err = repJson.getStr("err");
+                if (StrUtil.isEmpty(pro) || "noprovince".equals(err)) {
+                    // 可能是非中国内地IP
+                    String regionName = repJson.getStr("addr");
+                    return IPLocationBO.unknown().setRegionName(regionName);
+                }
+                return IPLocationBO.inChina()
+                        .setProvinceName(pro)
+                        .setCityName(repJson.getStr("city"));
+            }
+        } catch (Exception e) {
+            log.error("[SysLog切面][查询IP地址属地异常] >> ip={}  \n", ip, e);
+        }
+
+        return IPLocationBO.unknown();
+    }
+}

+ 2 - 3
src/main/java/cc/uncarbon/module/sys/facade/impl/SysTenantFacadeImpl.java

@@ -1,7 +1,6 @@
 package cc.uncarbon.module.sys.facade.impl;
 
 import cc.uncarbon.framework.core.context.UserContextHolder;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.entity.SysTenantEntity;
 import cc.uncarbon.module.sys.entity.SysUserRoleRelationEntity;
 import cc.uncarbon.module.sys.facade.SysTenantFacade;
@@ -13,12 +12,13 @@ import cc.uncarbon.module.sys.service.SysTenantService;
 import cc.uncarbon.module.sys.service.SysUserRoleRelationService;
 import cc.uncarbon.module.sys.service.SysUserService;
 import cn.hutool.core.bean.BeanUtil;
-import java.time.LocalDateTime;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDateTime;
+
 /**
  * 系统租户防腐层,用于解决循环依赖
  *
@@ -39,7 +39,6 @@ public class SysTenantFacadeImpl implements SysTenantFacade {
 
 
     @Override
-    @SysLog(value = "新增系统租户")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertSysTenantDTO dto) {
         log.info("[后台管理-新增系统租户] >> 入参={}", dto);

+ 2 - 3
src/main/java/cc/uncarbon/module/sys/mapper/SysLogMapper.java

@@ -5,10 +5,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 /**
- * 后台操作日志
- * @author Uncarbon
+ * 系统日志
  */
 @Mapper
 public interface SysLogMapper extends BaseMapper<SysLogEntity> {
-	
+
 }

+ 19 - 2
src/main/java/cc/uncarbon/module/sys/model/request/AdminInsertSysLogDTO.java

@@ -11,8 +11,7 @@ import java.io.Serializable;
 
 
 /**
- * 后台管理-新增操作日志
- * @author Uncarbon
+ * 后台管理-新增系统日志
  */
 @Accessors(chain = true)
 @AllArgsConstructor
@@ -41,4 +40,22 @@ public class AdminInsertSysLogDTO implements Serializable {
     @ApiModelProperty(value = "状态")
     private SysLogStatusEnum status;
 
+    @ApiModelProperty(value = "错误原因堆栈")
+    private String errorStacktrace;
+
+    @ApiModelProperty(value = "用户UA")
+    private String userAgent;
+
+    @ApiModelProperty(value = "IP地址属地-国家或地区名")
+    private String ipLocationRegionName;
+
+    @ApiModelProperty(value = "IP地址属地-省级行政区名")
+    private String ipLocationProvinceName;
+
+    @ApiModelProperty(value = "IP地址属地-市级行政区名")
+    private String ipLocationCityName;
+
+    @ApiModelProperty(value = "IP地址属地-县级行政区名")
+    private String ipLocationDistrictName;
+
 }

+ 1 - 2
src/main/java/cc/uncarbon/module/sys/model/request/AdminListSysLogDTO.java

@@ -16,8 +16,7 @@ import java.time.LocalDateTime;
 
 
 /**
- * 后台管理-分页列表后台操作日志
- * @author Uncarbon
+ * 后台管理-分页列表系统日志
  */
 @Accessors(chain = true)
 @Builder

+ 0 - 3
src/main/java/cc/uncarbon/module/sys/model/request/SysUserLoginDTO.java

@@ -48,7 +48,4 @@ public class SysUserLoginDTO implements Serializable {
     @ApiModelProperty(value = "验证码答案(可选,需自行对接业务逻辑)")
     private String captchaAnswer;
 
-    @ApiModelProperty(value = "客户端IP地址", hidden = true)
-    private String clientIP;
-
 }

+ 53 - 0
src/main/java/cc/uncarbon/module/sys/model/response/IPLocationBO.java

@@ -0,0 +1,53 @@
+package cc.uncarbon.module.sys.model.response;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * IP地址属地
+ * 首用于系统日志
+ */
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class IPLocationBO implements Serializable {
+
+    @ApiModelProperty(value = "国家或地区名")
+    private String regionName;
+
+    @ApiModelProperty(value = "省级行政区名")
+    private String provinceName;
+
+    @ApiModelProperty(value = "市级行政区名")
+    private String cityName;
+
+    @ApiModelProperty(value = "县级行政区名")
+    private String districtName;
+
+    /**
+     * 未知属地
+     */
+    public static IPLocationBO unknown() {
+        return new IPLocationBO("未知", null, null, null);
+    }
+
+    /**
+     * 内网地址
+     */
+    public static IPLocationBO intranet() {
+        return new IPLocationBO("内网", null, null, null);
+    }
+
+    /**
+     * 位于中国内地
+     */
+    public static IPLocationBO inChina() {
+        return new IPLocationBO("中国", null, null, null);
+    }
+}

+ 16 - 2
src/main/java/cc/uncarbon/module/sys/model/response/SysLogBO.java

@@ -16,8 +16,7 @@ import java.time.LocalDateTime;
 
 
 /**
- * 后台操作日志BO
- * @author Uncarbon
+ * 系统日志BO
  */
 @Accessors(chain = true)
 @Builder
@@ -43,4 +42,19 @@ public class SysLogBO implements Serializable {
     @ApiModelProperty(value = "状态")
     private SysLogStatusEnum status;
 
+    @ApiModelProperty(value = "用户UA")
+    private String userAgent;
+
+    @ApiModelProperty(value = "IP地址属地-国家或地区名")
+    private String ipLocationRegionName;
+
+    @ApiModelProperty(value = "IP地址属地-省级行政区名")
+    private String ipLocationProvinceName;
+
+    @ApiModelProperty(value = "IP地址属地-市级行政区名")
+    private String ipLocationCityName;
+
+    @ApiModelProperty(value = "IP地址属地-县级行政区名")
+    private String ipLocationDistrictName;
+
 }

+ 5 - 9
src/main/java/cc/uncarbon/module/sys/service/SysDataDictService.java

@@ -5,28 +5,27 @@ import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.entity.SysDataDictEntity;
 import cc.uncarbon.module.sys.enums.SysErrorEnum;
 import cc.uncarbon.module.sys.mapper.SysDataDictMapper;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysDataDictDTO;
 import cc.uncarbon.module.sys.model.request.AdminListSysDataDictDTO;
 import cc.uncarbon.module.sys.model.response.SysDataDictBO;
-import cc.uncarbon.module.sys.model.response.SysParamBO;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
 
 /**
  * 数据字典
@@ -84,7 +83,6 @@ public class SysDataDictService extends HelioBaseServiceImpl<SysDataDictMapper,
     /**
      * 后台管理-新增
      */
-    @SysLog(value = "新增数据字典")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysDataDictDTO dto) {
         log.info("[后台管理-新增数据字典] >> 入参={}", dto);
@@ -102,7 +100,6 @@ public class SysDataDictService extends HelioBaseServiceImpl<SysDataDictMapper,
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑数据字典")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysDataDictDTO dto) {
         log.info("[后台管理-编辑数据字典] >> 入参={}", dto);
@@ -117,7 +114,6 @@ public class SysDataDictService extends HelioBaseServiceImpl<SysDataDictMapper,
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除数据字典")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除数据字典] >> 入参={}", ids);

+ 0 - 4
src/main/java/cc/uncarbon/module/sys/service/SysDeptService.java

@@ -3,7 +3,6 @@ package cc.uncarbon.module.sys.service;
 import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.entity.SysDeptEntity;
 import cc.uncarbon.module.sys.entity.SysUserDeptRelationEntity;
@@ -82,7 +81,6 @@ public class SysDeptService extends HelioBaseServiceImpl<SysDeptMapper, SysDeptE
     /**
      * 后台管理-新增
      */
-    @SysLog(value = "新增部门")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysDeptDTO dto) {
         log.info("[后台管理-新增部门] >> 入参={}", dto);
@@ -104,7 +102,6 @@ public class SysDeptService extends HelioBaseServiceImpl<SysDeptMapper, SysDeptE
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑部门")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysDeptDTO dto) {
         log.info("[后台管理-编辑部门] >> 入参={}", dto);
@@ -123,7 +120,6 @@ public class SysDeptService extends HelioBaseServiceImpl<SysDeptMapper, SysDeptE
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除部门")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除部门] >> 入参={}", ids);

+ 13 - 3
src/main/java/cc/uncarbon/module/sys/service/SysLogService.java

@@ -27,8 +27,7 @@ import java.util.List;
 
 
 /**
- * 后台操作日志
- * @author Uncarbon
+ * 系统日志
  */
 @Slf4j
 @Service
@@ -44,7 +43,10 @@ public class SysLogService extends HelioBaseServiceImpl<SysLogMapper, SysLogEnti
                 new QueryWrapper<SysLogEntity>()
                         .lambda()
                         // 仅返回给前端少量字段
-                        .select(SysLogEntity::getCreatedAt, SysLogEntity::getUsername, SysLogEntity::getOperation, SysLogEntity::getIp, SysLogEntity::getStatus)
+                        .select(SysLogEntity::getCreatedAt, SysLogEntity::getUsername, SysLogEntity::getOperation,
+                                SysLogEntity::getIp, SysLogEntity::getStatus, SysLogEntity::getUserAgent,
+                                SysLogEntity::getIpLocationRegionName, SysLogEntity::getIpLocationProvinceName,
+                                SysLogEntity::getIpLocationCityName, SysLogEntity::getIpLocationDistrictName)
                         // 用户账号
                         .like(StrUtil.isNotBlank(dto.getUsername()), SysLogEntity::getUsername, StrUtil.cleanBlank(dto.getUsername()))
                         // 操作内容
@@ -96,6 +98,14 @@ public class SysLogService extends HelioBaseServiceImpl<SysLogMapper, SysLogEnti
         SysLogEntity entity = new SysLogEntity();
         BeanUtil.copyProperties(dto, entity);
 
+        int USER_AGENT_MAX_LENGTH = 255;
+        if (StrUtil.length(entity.getUserAgent()) > USER_AGENT_MAX_LENGTH) {
+            // 超长度截断
+            entity.setUserAgent(
+                    StrUtil.subPre(entity.getUserAgent(), USER_AGENT_MAX_LENGTH)
+            );
+        }
+
         this.save(entity);
 
         return entity.getId();

+ 0 - 4
src/main/java/cc/uncarbon/module/sys/service/SysMenuService.java

@@ -5,7 +5,6 @@ import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.function.StreamFunction;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.entity.SysMenuEntity;
 import cc.uncarbon.module.sys.enums.GenericStatusEnum;
@@ -93,7 +92,6 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
      *
      * @return 主键ID
      */
-    @SysLog(value = "新增后台菜单")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysMenuDTO dto) {
         log.info("[后台管理-新增后台菜单] >> 入参={}", dto);
@@ -116,7 +114,6 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑后台菜单")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysMenuDTO dto) {
         log.info("[后台管理-编辑后台菜单] >> 入参={}", dto);
@@ -135,7 +132,6 @@ public class SysMenuService extends HelioBaseServiceImpl<SysMenuMapper, SysMenuE
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除后台菜单")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除后台菜单] >> 入参={}", ids);

+ 4 - 7
src/main/java/cc/uncarbon/module/sys/service/SysParamService.java

@@ -5,7 +5,6 @@ import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.entity.SysParamEntity;
 import cc.uncarbon.module.sys.enums.SysErrorEnum;
 import cc.uncarbon.module.sys.mapper.SysParamMapper;
@@ -17,13 +16,14 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 
 /**
@@ -83,7 +83,6 @@ public class SysParamService extends HelioBaseServiceImpl<SysParamMapper, SysPar
     /**
      * 后台管理-新增
      */
-    @SysLog(value = "新增系统参数")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysParamDTO dto) {
         log.info("[后台管理-新增系统参数] >> 入参={}", dto);
@@ -101,7 +100,6 @@ public class SysParamService extends HelioBaseServiceImpl<SysParamMapper, SysPar
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑系统参数")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysParamDTO dto) {
         log.info("[后台管理-编辑系统参数] >> 入参={}", dto);
@@ -116,7 +114,6 @@ public class SysParamService extends HelioBaseServiceImpl<SysParamMapper, SysPar
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除系统参数")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除系统参数] >> 入参={}", ids);

+ 0 - 7
src/main/java/cc/uncarbon/module/sys/service/SysRoleService.java

@@ -6,7 +6,6 @@ import cc.uncarbon.framework.core.function.StreamFunction;
 import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.entity.SysRoleEntity;
 import cc.uncarbon.module.sys.enums.SysErrorEnum;
 import cc.uncarbon.module.sys.mapper.SysRoleMapper;
@@ -30,8 +29,6 @@ import java.util.stream.Collectors;
 
 /**
  * 后台角色
- *
- * @author Uncarbon
  */
 @Slf4j
 @Service
@@ -95,7 +92,6 @@ public class SysRoleService extends HelioBaseServiceImpl<SysRoleMapper, SysRoleE
      *
      * @return 主键ID
      */
-    @SysLog(value = "新增后台角色")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysRoleDTO dto) {
         log.info("[后台管理-新增后台角色] >> 入参={}", dto);
@@ -113,7 +109,6 @@ public class SysRoleService extends HelioBaseServiceImpl<SysRoleMapper, SysRoleE
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑后台角色")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysRoleDTO dto) {
         log.info("[后台管理-编辑后台角色] >> 入参={}", dto);
@@ -128,7 +123,6 @@ public class SysRoleService extends HelioBaseServiceImpl<SysRoleMapper, SysRoleE
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除后台角色")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除后台角色] >> 入参={}", ids);
@@ -140,7 +134,6 @@ public class SysRoleService extends HelioBaseServiceImpl<SysRoleMapper, SysRoleE
      *
      * @return 新菜单ID集合对应的权限名
      */
-    @SysLog(value = "绑定角色与菜单关联关系")
     @Transactional(rollbackFor = Exception.class)
     public Set<String> adminBindMenus(AdminBindRoleMenuRelationDTO dto) {
         Set<String> newPermissions = sysMenuService.listPermissionsByMenuIds(dto.getMenuIds());

+ 0 - 3
src/main/java/cc/uncarbon/module/sys/service/SysTenantService.java

@@ -5,7 +5,6 @@ import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.crud.service.impl.HelioBaseServiceImpl;
-import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.entity.SysTenantEntity;
 import cc.uncarbon.module.sys.enums.SysErrorEnum;
 import cc.uncarbon.module.sys.mapper.SysTenantMapper;
@@ -90,7 +89,6 @@ public class SysTenantService extends HelioBaseServiceImpl<SysTenantMapper, SysT
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑系统租户")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminUpdateSysTenantDTO dto) {
         log.info("[后台管理-编辑系统租户] >> 入参={}", dto);
@@ -105,7 +103,6 @@ public class SysTenantService extends HelioBaseServiceImpl<SysTenantMapper, SysT
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除系统租户")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除系统租户] >> 入参={}", ids);

+ 0 - 6
src/main/java/cc/uncarbon/module/sys/service/SysUserService.java

@@ -120,7 +120,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
      *
      * @return 主键ID
      */
-    @SysLog(value = "新增后台用户")
     @Transactional(rollbackFor = Exception.class)
     public Long adminInsert(AdminInsertOrUpdateSysUserDTO dto) {
         log.info("[后台管理-新增后台用户] >> 入参={}", dto);
@@ -147,7 +146,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
     /**
      * 后台管理-编辑
      */
-    @SysLog(value = "编辑后台用户")
     @Transactional(rollbackFor = Exception.class)
     public void adminUpdate(AdminInsertOrUpdateSysUserDTO dto) {
         log.info("[后台管理-编辑后台用户] >> 入参={}", dto);
@@ -166,7 +164,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
     /**
      * 后台管理-删除
      */
-    @SysLog(value = "删除后台用户")
     @Transactional(rollbackFor = Exception.class)
     public void adminDelete(Collection<Long> ids) {
         log.info("[后台管理-删除后台用户] >> 入参={}", ids);
@@ -176,7 +173,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
     /**
      * 后台管理-登录
      */
-    @SysLog(value = SysConstant.SysLogOperation.SYS_USER_LOGIN)
     public SysUserLoginBO adminLogin(SysUserLoginDTO dto) {
         /*
         如果启用了多租户功能,并且前端指定了租户ID,则先查库确认租户是否有效
@@ -256,7 +252,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
     /**
      * 后台管理-重置某用户密码
      */
-    @SysLog(value = "重置某用户密码")
     public void adminResetUserPassword(AdminResetSysUserPasswordDTO dto) {
         SysUserEntity sysUserEntity = this.getById(dto.getUserId());
 
@@ -272,7 +267,6 @@ public class SysUserService extends HelioBaseServiceImpl<SysUserMapper, SysUserE
     /**
      * 后台管理-修改当前用户密码
      */
-    @SysLog(value = "修改当前用户密码")
     public void adminUpdateCurrentUserPassword(AdminUpdateCurrentSysUserPasswordDTO dto) {
         SysUserEntity sysUserEntity = this.getById(UserContextHolder.getUserId());
         if (sysUserEntity == null || !sysUserEntity.getPwd().equals(PwdUtil.encrypt(dto.getOldPassword(), sysUserEntity.getSalt()))) {

+ 1 - 2
src/main/java/cc/uncarbon/module/sys/util/AdminStpUtil.java

@@ -12,8 +12,7 @@ import java.util.List;
 
 /**
  * 后台管理专用 StpUtil
- * 文档见 SA-Token -> 多账户认证
- * https://sa-token.dev33.cn/doc/index.html#/up/many-account
+ * 文档见 SA-Token -> 多账户认证:https://sa-token.cc/doc.html#/up/many-account
  *
  * @author Uncarbon
  */

+ 4 - 9
src/main/java/cc/uncarbon/module/sys/web/auth/AdminAuthController.java

@@ -1,15 +1,16 @@
 package cc.uncarbon.module.sys.web.auth;
 
 
+import cc.uncarbon.aspect.extension.SysLogAspectExtensionForSysUserLogin;
 import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.core.context.TenantContext;
 import cc.uncarbon.framework.core.context.TenantContextHolder;
 import cc.uncarbon.framework.core.context.UserContext;
 import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.web.model.response.ApiResult;
-import cc.uncarbon.framework.web.util.IPUtil;
 import cc.uncarbon.helper.CaptchaHelper;
 import cc.uncarbon.helper.RolePermissionCacheHelper;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.enums.SysErrorEnum;
 import cc.uncarbon.module.sys.model.request.SysUserLoginDTO;
@@ -26,15 +27,11 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @Slf4j
 @Api(value = "SaaS后台管理鉴权接口", tags = {"SaaS后台管理鉴权接口"})
@@ -49,12 +46,10 @@ public class AdminAuthController {
     private final CaptchaHelper captchaHelper;
 
 
+    @SysLog(value = "登录后台用户", syncSave = true, extension = SysLogAspectExtensionForSysUserLogin.class, queryIPLocation = true)
     @ApiOperation(value = "登录")
     @PostMapping(value = "/login")
-    public ApiResult<SysUserLoginVO> login(HttpServletRequest request, @RequestBody @Valid SysUserLoginDTO dto) {
-        // 从请求中得到客户端IP地址
-        dto.setClientIP(IPUtil.getClientIPAddress(request));
-
+    public ApiResult<SysUserLoginVO> login(@RequestBody @Valid SysUserLoginDTO dto) {
         // RPC调用, 失败抛异常, 成功返回用户信息
         SysUserLoginBO userInfo = sysUserService.adminLogin(dto);
 

+ 4 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysDataDictController.java

@@ -5,6 +5,7 @@ import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysDataDictDTO;
 import cc.uncarbon.module.sys.model.request.AdminListSysDataDictDTO;
@@ -17,15 +18,11 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -53,6 +50,7 @@ public class AdminSysDataDictController {
         return ApiResult.data(sysDataDictService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增数据字典")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -62,6 +60,7 @@ public class AdminSysDataDictController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑数据字典")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -72,6 +71,7 @@ public class AdminSysDataDictController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除数据字典")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping

+ 5 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysDeptController.java

@@ -4,6 +4,7 @@ package cc.uncarbon.module.sys.web.sys;
 import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysDeptDTO;
 import cc.uncarbon.module.sys.model.response.SysDeptBO;
@@ -15,15 +16,12 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.util.List;
 
-/**
- * @author Uncarbon
- */
+
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -51,6 +49,7 @@ public class AdminSysDeptController {
         return ApiResult.data(sysDeptService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增部门")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -60,6 +59,7 @@ public class AdminSysDeptController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑部门")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -70,6 +70,7 @@ public class AdminSysDeptController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除部门")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping

+ 1 - 5
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysLogController.java

@@ -15,20 +15,16 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
-@Api(value = "后台操作日志管理接口", tags = {"后台操作日志管理接口"})
+@Api(value = "系统日志管理接口", tags = {"系统日志管理接口"})
 @RequestMapping(SysConstant.SYS_MODULE_CONTEXT_PATH + HelioConstant.Version.HTTP_API_VERSION_V1 + "/sys/logs")
 @RestController
 public class AdminSysLogController {

+ 4 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysMenuController.java

@@ -3,6 +3,7 @@ package cc.uncarbon.module.sys.web.sys;
 import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysMenuDTO;
 import cc.uncarbon.module.sys.model.response.SysMenuBO;
@@ -14,16 +15,12 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.util.List;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -51,6 +48,7 @@ public class AdminSysMenuController {
         return ApiResult.data(sysMenuService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增后台菜单")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -60,6 +58,7 @@ public class AdminSysMenuController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑后台菜单")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -70,6 +69,7 @@ public class AdminSysMenuController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除后台菜单")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping

+ 5 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysParamController.java

@@ -5,6 +5,7 @@ import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysParamDTO;
 import cc.uncarbon.module.sys.model.request.AdminListSysParamDTO;
@@ -17,14 +18,11 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 
-/**
- * @author Uncarbon
- */
+
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -52,6 +50,7 @@ public class AdminSysParamController {
         return ApiResult.data(sysParamService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增系统参数")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -61,6 +60,7 @@ public class AdminSysParamController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑系统参数")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -71,6 +71,7 @@ public class AdminSysParamController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除系统参数")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping

+ 5 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysRoleController.java

@@ -6,6 +6,7 @@ import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
 import cc.uncarbon.helper.RolePermissionCacheHelper;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.AdminBindRoleMenuRelationDTO;
 import cc.uncarbon.module.sys.model.request.AdminInsertOrUpdateSysRoleDTO;
@@ -19,16 +20,12 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.util.Set;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -58,6 +55,7 @@ public class AdminSysRoleController {
         return ApiResult.data(sysRoleService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增后台角色")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -67,6 +65,7 @@ public class AdminSysRoleController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑后台角色")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -77,6 +76,7 @@ public class AdminSysRoleController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除后台角色")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping
@@ -89,6 +89,7 @@ public class AdminSysRoleController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "绑定角色与菜单关联关系")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + "bindMenus")
     @ApiOperation(value = "绑定角色与菜单关联关系")
     @PutMapping(value = "/{id}/menus")

+ 4 - 3
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysTenantController.java

@@ -5,6 +5,7 @@ import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.facade.SysTenantFacade;
 import cc.uncarbon.module.sys.model.request.AdminInsertSysTenantDTO;
@@ -24,9 +25,6 @@ import org.springframework.web.bind.annotation.*;
 import javax.validation.Valid;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -56,6 +54,7 @@ public class AdminSysTenantController {
         return ApiResult.data(sysTenantService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增系统租户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -65,6 +64,7 @@ public class AdminSysTenantController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑系统租户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -75,6 +75,7 @@ public class AdminSysTenantController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除系统租户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping

+ 6 - 4
src/main/java/cc/uncarbon/module/sys/web/sys/AdminSysUserController.java

@@ -6,6 +6,7 @@ import cc.uncarbon.framework.core.page.PageParam;
 import cc.uncarbon.framework.core.page.PageResult;
 import cc.uncarbon.framework.web.model.request.IdsDTO;
 import cc.uncarbon.framework.web.model.response.ApiResult;
+import cc.uncarbon.module.sys.annotation.SysLog;
 import cc.uncarbon.module.sys.constant.SysConstant;
 import cc.uncarbon.module.sys.model.request.*;
 import cc.uncarbon.module.sys.model.response.SysUserBO;
@@ -19,16 +20,12 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.util.Set;
 
 
-/**
- * @author Uncarbon
- */
 @RequiredArgsConstructor
 @SaCheckLogin(type = AdminStpUtil.TYPE)
 @Slf4j
@@ -56,6 +53,7 @@ public class AdminSysUserController {
         return ApiResult.data(sysUserService.getOneById(id, true));
     }
 
+    @SysLog(value = "新增后台用户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.CREATE)
     @ApiOperation(value = "新增")
     @PostMapping
@@ -65,6 +63,7 @@ public class AdminSysUserController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "编辑后台用户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.UPDATE)
     @ApiOperation(value = "编辑")
     @PutMapping(value = "/{id}")
@@ -75,6 +74,7 @@ public class AdminSysUserController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "删除后台用户")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + HelioConstant.Permission.DELETE)
     @ApiOperation(value = "删除")
     @DeleteMapping
@@ -90,6 +90,7 @@ public class AdminSysUserController {
         return ApiResult.data(sysUserService.adminGetCurrentUserInfo());
     }
 
+    @SysLog(value = "重置某用户密码")
     @SaCheckPermission(type = AdminStpUtil.TYPE, value = PERMISSION_PREFIX + "resetPassword")
     @ApiOperation(value = "重置某用户密码")
     @PutMapping(value = "/{userId}/password")
@@ -103,6 +104,7 @@ public class AdminSysUserController {
         return ApiResult.success();
     }
 
+    @SysLog(value = "修改当前用户密码")
     @ApiOperation(value = "修改当前用户密码")
     @PostMapping(value = "/updatePassword")
     public ApiResult<?> updatePassword(@RequestBody @Valid AdminUpdateCurrentSysUserPasswordDTO dto) {

+ 1 - 1
src/main/resources/application-prod.yml

@@ -25,7 +25,7 @@ spring:
     host: 127.0.0.1
     # Redis连接端口
     port: 6379
-    # Redis登录密码
+    # Redis登录密码;如果没有设置密码,请注释掉下面这行,避免向 Redis-server 发送 AUTH 鉴权请求,引发程序启动失败
     password:
     # Redis数据库序号
     database: 1

+ 1 - 1
src/main/resources/application-test.yml

@@ -25,7 +25,7 @@ spring:
     host: 127.0.0.1
     # Redis连接端口
     port: 6379
-    # Redis登录密码
+    # Redis登录密码;如果没有设置密码,请注释掉下面这行,避免向 Redis-server 发送 AUTH 鉴权请求,引发程序启动失败
     password:
     # Redis数据库序号
     database: 1

+ 2 - 2
src/main/resources/application.yml

@@ -88,7 +88,7 @@ logging:
     # 指定日志级别,开发、测试环境建议为 DEBUG,生产环境建议为 INFO
     cc.uncarbon: DEBUG
 
-# 详细配置文档:https://sa-token.dev33.cn/doc/index.html#/use/config
+# 详细配置文档:https://sa-token.cc/doc.html#/use/config
 sa-token:
   # token名称
   token-name: Authorization
@@ -102,7 +102,7 @@ sa-token:
   is-read-body: false
   # 是否尝试从cookie里读取token
   is-read-cookie: false
-  # 有效期解释:https://sa-token.dev33.cn/doc/index.html#/fun/token-timeout
+  # 有效期解释:https://sa-token.cc/doc.html#/fun/token-timeout
   # token 有效期,单位=秒;默认30天,-1代表永不过期
   timeout: 2592000
   # token 临时有效期 (指定时间内无操作就视为token过期),单位=秒

+ 36 - 0
src/test/java/cc/uncarbon/test/BasicUnitTest.java

@@ -0,0 +1,36 @@
+package cc.uncarbon.test;
+
+import cc.uncarbon.HelioBootApplication;
+import cc.uncarbon.framework.i18n.util.I18nUtil;
+import cc.uncarbon.module.sys.enums.SysErrorEnum;
+import cn.hutool.core.lang.Assert;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.Locale;
+
+/**
+ * 基础单元测试
+ * @author Uncarbon
+ */
+@SpringBootTest(classes = HelioBootApplication.class)
+class BasicUnitTest {
+
+    @Test
+    void testI18nMessage() {
+        String msg = I18nUtil.messageOf(SysErrorEnum.INVALID_TENANT);
+        Assert.isTrue("所属租户无效".equals(msg));
+
+        msg = I18nUtil.messageOf(Locale.US, SysErrorEnum.INCORRECT_PIN_OR_PWD);
+        Assert.isTrue("Incorrect username or password".equals(msg));
+
+        msg = I18nUtil.messageOf(Locale.CHINA, SysErrorEnum.DISABLED_TENANT);
+        Assert.isTrue("所属租户已禁用".equals(msg));
+
+        msg = I18nUtil.messageOf(Locale.US, "INCORRECT_PIN_OR_PWD");
+        Assert.isTrue(msg == null);
+
+        msg = I18nUtil.messageOf(Locale.US, "SysErrorEnum.UUID_CANNOT_BE_BLANK");
+        Assert.isTrue("UUID cannot be blank".equals(msg));
+    }
+}

+ 0 - 38
src/test/java/cc/uncarbon/test/InjectUnitTest.java

@@ -1,38 +0,0 @@
-package cc.uncarbon.test;
-
-import cc.uncarbon.HelioBootApplication;
-import cc.uncarbon.framework.core.exception.BusinessException;
-import cc.uncarbon.framework.core.props.HelioProperties;
-import cc.uncarbon.framework.crud.support.TenantSupport;
-import cc.uncarbon.framework.crud.support.impl.DefaultTenantSupport;
-import javax.annotation.Resource;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.test.context.SpringBootTest;
-
-/**
- * @author Uncarbon
- */
-@SpringBootTest(classes = HelioBootApplication.class)
-class InjectUnitTest {
-
-    @Resource
-    private TenantSupport tenantSupport;
-
-    @Resource
-    private HelioProperties helioProperties;
-
-    @Value("${helio.tenant.enabled}")
-    private Boolean tenantEnabled;
-
-    @Test
-    void init() {
-        throw new BusinessException(500, "template test {} {} {} {} {}",
-                tenantEnabled,
-                helioProperties,
-                helioProperties.getTenant().getEnabled(),
-                helioProperties.getTenant().getIgnoredTables(),
-                tenantSupport.getClass().isAssignableFrom(DefaultTenantSupport.class)
-                );
-    }
-}