Browse Source

添加接口日志记录

xudm 2 months ago
parent
commit
7217955a59
26 changed files with 577 additions and 189 deletions
  1. 26 0
      src/main/java/com/xs/core/common/annotation/OperationLog.java
  2. 64 0
      src/main/java/com/xs/core/common/aspect/OperationLogAspect.java
  3. 10 1
      src/main/java/com/xs/core/controller/coin/GoldCoinProductController.java
  4. 3 0
      src/main/java/com/xs/core/controller/competition/CompetitionController.java
  5. 3 0
      src/main/java/com/xs/core/controller/task/TaskInfoController.java
  6. 2 0
      src/main/java/com/xs/core/controller/team/DistributionController.java
  7. 8 0
      src/main/java/com/xs/core/controller/team/TeamShareController.java
  8. 2 0
      src/main/java/com/xs/core/controller/user/LoginController.java
  9. 2 0
      src/main/java/com/xs/core/controller/wallet/WalletController.java
  10. 53 3
      src/main/java/com/xs/core/filter/GlobalExceptionHandler.java
  11. 0 16
      src/main/java/com/xs/core/mapper/log/ApiRequestLogMapper.java
  12. 16 0
      src/main/java/com/xs/core/mapper/log/SysAppLogMapper.java
  13. 0 72
      src/main/java/com/xs/core/model/log/entity/ApiRequestLog.java
  14. 134 0
      src/main/java/com/xs/core/model/log/entity/SysAppLog.java
  15. 0 20
      src/main/java/com/xs/core/service/Impl/log/ApiRequestLogServiceImpl.java
  16. 103 0
      src/main/java/com/xs/core/service/Impl/log/SysAppLogServiceImpl.java
  17. 1 52
      src/main/java/com/xs/core/service/Impl/user/AppLoginServiceImpl.java
  18. 70 0
      src/main/java/com/xs/core/service/Impl/user/UserServiceImpl.java
  19. 0 16
      src/main/java/com/xs/core/service/log/IApiRequestLogService.java
  20. 28 0
      src/main/java/com/xs/core/service/log/ISysAppLogService.java
  21. 0 8
      src/main/java/com/xs/core/service/user/AppLoginService.java
  22. 9 0
      src/main/java/com/xs/core/service/user/IUserService.java
  23. 1 1
      src/main/java/com/xs/core/utils/MyGenerator.java
  24. 9 0
      src/main/resources/application-dev.yml
  25. 9 0
      src/main/resources/application-prod.yml
  26. 24 0
      src/main/resources/mapper/SysAppLogMapper.xml

+ 26 - 0
src/main/java/com/xs/core/common/annotation/OperationLog.java

@@ -0,0 +1,26 @@
+package com.xs.core.common.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 操作日志注解
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface OperationLog {
+    /**
+     * 操作描述
+     */
+    String description() default "";
+
+    /**
+     * 操作类型(如:添加、修改、删除、查询等)
+     */
+    String operationType() default "";
+
+    /**
+     * 模块名称
+     */
+    String module() default "";
+}

+ 64 - 0
src/main/java/com/xs/core/common/aspect/OperationLogAspect.java

@@ -0,0 +1,64 @@
+package com.xs.core.common.aspect;
+
+import com.xs.core.common.annotation.OperationLog;
+import com.xs.core.service.log.ISysAppLogService;
+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.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+@Aspect
+@Component
+public class OperationLogAspect {
+    @Autowired
+    private ISysAppLogService sysLogService;
+
+    @Pointcut("@annotation(com.xs.core.common.annotation.OperationLog)")
+    public void logPointCut() {
+    }
+
+    @Around("logPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        long beginTime = System.currentTimeMillis();
+        Object result = null;
+        try {
+            // 执行方法
+            result = point.proceed();
+            // 执行时长(毫秒)
+            long time = System.currentTimeMillis() - beginTime;
+            // 保存日志
+            saveLog(point, time, result, null);
+        } catch (Exception e) {
+            // 保存异常日志
+            saveLog(point, System.currentTimeMillis() - beginTime, null, e);
+            throw e;
+        }
+        return result;
+    }
+
+    private void saveLog(ProceedingJoinPoint joinPoint, long time, Object result, Exception e) {
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+
+        // 获取注解信息
+        OperationLog logAnnotation = method.getAnnotation(OperationLog.class);
+        String description = null;
+        String operationType = null;
+        String module = null;
+        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
+        if (logAnnotation != null) {
+            description = logAnnotation.description();
+            operationType = logAnnotation.operationType();
+            module = logAnnotation.module();
+        }
+
+        sysLogService.saveAppLog(description, operationType, module, methodName, joinPoint.getArgs(), result, time, e);
+    }
+
+}

+ 10 - 1
src/main/java/com/xs/core/controller/coin/GoldCoinProductController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.coin;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.model.ResponseResult;
 import com.xs.core.model.coin.entity.CoinSpeedUpgradesRules;
 import com.xs.core.model.coin.entity.GoldCoinProdState;
@@ -26,6 +27,7 @@ public class GoldCoinProductController {
     @Autowired
     private GoldCoinService goldCoinService;
 
+    @OperationLog(description = "开始产出金币", operationType = "开始产出金币", module = "金币产出相关api")
     @Operation(description = "开始产出金币", summary = "开始产出金币")
     @PostMapping("/start")
     public ResponseResult<?> startGoldCoinProduct() {
@@ -33,8 +35,10 @@ public class GoldCoinProductController {
         return ResponseResult.success();
     }
 
+
     @PostMapping("/getGoldCoinProductState")
     @Operation(description = "获取金币产出状态", summary = "获取金币产出状态")
+    @OperationLog(description = "获取金币产出状态", operationType = "获取金币产出状态", module = "金币产出相关api")
     public ResponseResult<GoldCoinProdStateResp> getGoldCoinProductState() {
         GoldCoinProdStateResp goldCoinProdState = goldCoinService.getGoldCoinProdState();
         return ResponseResult.success(goldCoinProdState);
@@ -43,6 +47,7 @@ public class GoldCoinProductController {
 
     @PostMapping("/updateGoldCoinProductRate")
     @Operation(description = "提升金币产出永久速率", summary = "提升金币产出永久速率")
+    @OperationLog(description = "提升金币产出永久速率", operationType = "提升金币产出永久速率", module = "金币产出相关api")
     public ResponseResult<?> updateGoldCoinProductPermanentRate(@RequestBody CoinSpeedUpgradesRulesReq req) {
         goldCoinService.permanentBoostProductGoldCoinRate(req);
         return ResponseResult.success();
@@ -50,6 +55,7 @@ public class GoldCoinProductController {
 
     @PostMapping("/getRateUpgradesRules")
     @Operation(description = "获取金币产出速率升级规则", summary = "获取金币产出速率升级规则")
+    @OperationLog(description = "获取金币产出速率升级规则", operationType = "获取金币产出速率升级规则", module = "金币产出相关api")
     public ResponseResult<CoinSpeedUpgradesRulesResp> getRateUpgradesRules() {
         CoinSpeedUpgradesRulesResp rules = goldCoinService.getRateUpgradesRulesByUser();
         return ResponseResult.success(rules);
@@ -57,6 +63,7 @@ public class GoldCoinProductController {
 
     @Operation(description = "更新临时速率", summary = "更新临时速率")
     @PostMapping("/updateGoldCoinProductTemporaryRate")
+    @OperationLog(description = "更新临时速率", operationType = "更新临时速率", module = "金币产出相关api")
     public ResponseResult<?> updateGoldCoinProductTemporaryRate(@RequestBody TemporaryRateReq req) {
         goldCoinService.temporaryBoostProductGoldCoinRate(req);
         return ResponseResult.success();
@@ -64,13 +71,15 @@ public class GoldCoinProductController {
 
     @Operation(description = "收取产出的币", summary = "收取产出的币")
     @PostMapping("/collectGoldCoin")
+    @OperationLog(description = "收取产出的币", operationType = "收取产出的币", module = "金币产出相关api")
     public ResponseResult<?> collectGoldCoin() {
         goldCoinService.collectGoldCoin();
         return ResponseResult.success();
     }
 
-    @Operation(description = "加成剩余时间", summary = "加成剩余时间")
+    @Operation(description = "获取加成剩余时间", summary = "获取加成剩余时间")
     @PostMapping("/getBoostResidueTimes")
+    @OperationLog(description = "获取加成剩余时间", operationType = "获取加成剩余时间", module = "金币产出相关api")
     public ResponseResult<BoostResidueTimesResp> getBoostResidueTimes() {
         BoostResidueTimesResp boostResidueTimesResp = goldCoinService.getBoostResidueTimes();
         return ResponseResult.success(boostResidueTimesResp);

+ 3 - 0
src/main/java/com/xs/core/controller/competition/CompetitionController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.competition;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
@@ -28,6 +29,7 @@ public class CompetitionController {
 
     @Operation(summary = "个人金币排行榜", description = "个人金币排行榜")
     @PostMapping("/goldCoinCompetitionRanking")
+    @OperationLog(description = "个人金币排行榜", operationType = "个人金币排行榜", module = "竞赛排行榜")
     public ResponseResult<GoldCoinLeaderboardInfo> goldCoinCompetitionRanking(@RequestBody GoldCoinCompetitionRankingReq query) {
         UserContext userContext = UserContextHolder.getContext();
         CompletableFuture<List<UserCompetitionResp>> listCompletableFuture = CompletableFuture.supplyAsync(() -> competitionService.individualGoldCoinCompetitionRanking(query.getLimit()));
@@ -39,6 +41,7 @@ public class CompetitionController {
 
     @Operation(summary = "团队邀请排行榜", description = "团队邀请排行榜")
     @PostMapping("/teamInviteCompetitionRanking")
+    @OperationLog(description = "团队邀请排行榜", operationType = "团队邀请排行榜", module = "竞赛排行榜")
     public ResponseResult<List<TeamCompetitionResp>> teamInviteCompetitionRanking(@RequestBody GoldCoinCompetitionRankingReq query) {
         List<TeamCompetitionResp> teamCompetitionResp = competitionService.teamInviteCompetitionRanking(query.getLimit(), 500);
         return ResponseResult.success(teamCompetitionResp);

+ 3 - 0
src/main/java/com/xs/core/controller/task/TaskInfoController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.task;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
@@ -29,6 +30,7 @@ public class TaskInfoController {
 
     @Operation(description = "任务信息查询")
     @PostMapping("/getTaskInfoList")
+    @OperationLog(description = "任务信息查询", operationType = "任务信息查询", module = "任务信息")
     public ResponseResult<List<TaskInfoResp>> getTaskInfoList(@RequestBody TaskInfoReq req) {
         Locale locale = LocaleContextHolder.getLocale();
         String languageTag = locale.toLanguageTag();
@@ -39,6 +41,7 @@ public class TaskInfoController {
 
     @Operation(description = "处理任务")
     @PostMapping("/handleTask")
+    @OperationLog(description = "处理任务", operationType = "处理任务", module = "任务信息")
     public ResponseResult<?> handleTask(@RequestBody TaskInfoReq req) {
         UserContext context = UserContextHolder.getContext();
         taskService.handleTask(req, context.getId());

+ 2 - 0
src/main/java/com/xs/core/controller/team/DistributionController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.team;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.model.ResponseResult;
 import com.xs.core.service.user.IUserService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -18,6 +19,7 @@ public class DistributionController {
 
     @PostMapping("/getShareCode")
     @Operation(summary = "获取邀请码", description = "获取邀请码")
+    @OperationLog(description = "获取邀请码", operationType = "获取邀请码", module = "团队邀请模块")
     public ResponseResult<?> getShareCode() {
         String inviteCode = userService.getInviteCode();
         return ResponseResult.success(inviteCode);

+ 8 - 0
src/main/java/com/xs/core/controller/team/TeamShareController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.team;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
@@ -28,6 +29,7 @@ public class TeamShareController {
 
     @Operation(summary = "获取我的团队信息", description = "获取我的团队信息")
     @PostMapping("/getMyTeamsInfo")
+    @OperationLog(description = "获取我的团队信息", operationType = "获取我的团队信息", module = "团队邀请")
     public ResponseResult<TeamsInfoResp> getMyTeamsInfo() {
         UserContext userContext = UserContextHolder.getContext();
         TeamsInfoResp teamsInfoResp = teamShareService.getMyTeamsInfo(userContext.getId());
@@ -36,6 +38,7 @@ public class TeamShareController {
 
     @Operation(summary = "获取团队产出奖励信息", description = "获取团队产出奖励信息")
     @PostMapping("/getTeamShareUserListByType")
+    @OperationLog(description = "获取团队产出奖励信息", operationType = "获取团队产出奖励信息", module = "团队邀请")
     public ResponseResult<TeamShareInfoResp> getTeamShareUserListByType(@Validated @RequestBody TeamShareReq req) {
         UserContext userContext = UserContextHolder.getContext();
         TeamShareInfoResp teamShareUserInfo = teamShareService.getTeamShareUserInfoByType(userContext.getId(), req);
@@ -44,6 +47,7 @@ public class TeamShareController {
 
     @Operation(summary = "收取团队奖励", description = "收取团队奖励")
     @PostMapping("/claimTeamShareReward")
+    @OperationLog(description = "收取团队奖励", operationType = "收取团队奖励", module = "团队邀请")
     public ResponseResult<List<TeamShareUserResp>> claimTeamShareReward(@Validated @RequestBody TeamShareReq req) {
         UserContext userContext = UserContextHolder.getContext();
         teamShareService.claimTeamShareReward(userContext.getId(), req);
@@ -52,6 +56,7 @@ public class TeamShareController {
 
     @Operation(summary = "获取团队邀请固定奖励列表", description = "获取团队邀请固定奖励列表(邀请一人奖励1000金币)")
     @PostMapping("/getTeamInviteRewardsList")
+    @OperationLog(description = "获取团队邀请固定奖励列表", operationType = "获取团队邀请固定奖励列表", module = "团队邀请")
     public ResponseResult<List<TeamInviteRewardsResp>> getTeamInviteRewardsList() {
         UserContext userContext = UserContextHolder.getContext();
         List<TeamInviteRewardsResp> teamInviteRewardsList = teamShareService.getTeamInviteRewardsList(userContext.getId());
@@ -60,6 +65,7 @@ public class TeamShareController {
 
     @Operation(summary = "收取团队邀请固定奖励", description = "收取团队邀请固定奖励")
     @PostMapping("/claimTeamInviteReward")
+    @OperationLog(description = "收取团队邀请固定奖励", operationType = "收取团队邀请固定奖励", module = "团队邀请")
     public ResponseResult<?> claimTeamInviteReward() {
         UserContext userContext = UserContextHolder.getContext();
         teamShareService.claimTeamInviteReward(userContext.getId());
@@ -68,6 +74,7 @@ public class TeamShareController {
 
     @Operation(summary = "获取团队邀请任务可领取奖励", description = "获取团队邀请任务可领取奖励")
     @PostMapping("/getTeamInviteRewardsRules")
+    @OperationLog(description = "获取团队邀请任务可领取奖励", operationType = "获取团队邀请任务可领取奖励", module = "团队邀请")
     public ResponseResult<TeamInviteAvailableRewardsResp> getTeamInviteAvailableRewardsRules() {
         UserContext userContext = UserContextHolder.getContext();
         TeamInviteAvailableRewardsResp resp = teamShareService.getTeamInviteAvailableRewardsRules(userContext.getId());
@@ -76,6 +83,7 @@ public class TeamShareController {
 
     @Operation(summary = "领取团队邀请任务奖励", description = "领取团队邀请任务奖励")
     @PostMapping("/claimTeamInviteTaskReward")
+    @OperationLog(description = "领取团队邀请任务奖励", operationType = "领取团队邀请任务奖励", module = "团队邀请")
     public ResponseResult<?> claimTeamInviteTaskReward(@RequestBody ClaimTeamInviteTaskRewardReq req) {
         UserContext userContext = UserContextHolder.getContext();
         teamShareService.claimTeamInviteTaskReward(userContext.getId(), req.getRuleId());

+ 2 - 0
src/main/java/com/xs/core/controller/user/LoginController.java

@@ -2,6 +2,7 @@ package com.xs.core.controller.user;
 
 import cn.dev33.satoken.annotation.SaIgnore;
 import cn.dev33.satoken.stp.SaTokenInfo;
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.model.ResponseResult;
 import com.xs.core.model.user.req.AppLoginReq;
 import com.xs.core.service.user.AppLoginService;
@@ -29,6 +30,7 @@ public class LoginController {
 
     @PostMapping("/coinLogin")
     @Operation(summary = "tg账号登录", description = "根据tg用户id进行登录")
+    @OperationLog(description = "tg账号登录", operationType = "登录", module = "登录")
     public ResponseResult<SaTokenInfo> coinLogin(@Validated @RequestBody AppLoginReq req) {
         SaTokenInfo saTokenInfo = loginService.tgLogin(req);
         return ResponseResult.success(saTokenInfo);

+ 2 - 0
src/main/java/com/xs/core/controller/wallet/WalletController.java

@@ -1,5 +1,6 @@
 package com.xs.core.controller.wallet;
 
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.common.content.UserContext;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.model.ResponseResult;
@@ -22,6 +23,7 @@ public class WalletController {
 
     @Operation(summary = "获取钱包信息", description = "获取钱包信息")
     @PostMapping("/getWalletInfo")
+    @OperationLog(description = "获取钱包信息", operationType = "获取钱包信息", module = "钱包")
     public ResponseResult<WalletInfoResp> getWalletInfo() {
         WalletInfoResp walletInfo = walletService.getWalletInfo();
         return ResponseResult.success(walletInfo);

+ 53 - 3
src/main/java/com/xs/core/filter/GlobalExceptionHandler.java

@@ -4,11 +4,15 @@ import cn.dev33.satoken.exception.NotLoginException;
 import cn.dev33.satoken.exception.NotPermissionException;
 import cn.dev33.satoken.jwt.exception.SaJwtException;
 import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.util.ReflectUtil;
+import com.xs.core.common.annotation.OperationLog;
 import com.xs.core.common.exception.BusinessException;
 import com.xs.core.model.ResponseResult;
+import com.xs.core.service.log.ISysAppLogService;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.ConstraintViolationException;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.util.CollectionUtils;
@@ -20,19 +24,25 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.multipart.MaxUploadSizeExceededException;
 import org.springframework.web.multipart.support.MissingServletRequestPartException;
+import org.springframework.web.servlet.HandlerMapping;
 import org.springframework.web.servlet.resource.NoResourceFoundException;
 
+import java.lang.reflect.Method;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @RestControllerAdvice
 @Slf4j
+@AllArgsConstructor
 public class GlobalExceptionHandler {
+    private final ISysAppLogService sysLogService;
+
     @ExceptionHandler({Exception.class, NotLoginException.class, HttpMessageNotReadableException.class,
             HttpMediaTypeNotSupportedException.class, MaxUploadSizeExceededException.class, NotPermissionException.class,
-            HttpRequestMethodNotSupportedException.class, MethodArgumentNotValidException.class, BindException.class,
-            ConstraintViolationException.class, MissingServletRequestPartException.class, SaJwtException.class, NoResourceFoundException.class, BusinessException.class})
+            HttpRequestMethodNotSupportedException.class, ConstraintViolationException.class, MissingServletRequestPartException.class, SaJwtException.class, NoResourceFoundException.class, BusinessException.class})
     @ResponseBody
     ResponseResult<?> serverExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
         if (e instanceof NotLoginException || e instanceof SaJwtException) {
@@ -53,7 +63,7 @@ public class GlobalExceptionHandler {
                         String field = fieldError.getField();
                         String defaultMessage = fieldError.getDefaultMessage();
                         // 尝试从消息源获取国际化消息,如果没有找到,则使用默认消息
-                        return ResponseResult.failed(defaultMessage,null);
+                        return ResponseResult.failed(defaultMessage, null);
                     }
                 }
             }
@@ -72,4 +82,44 @@ public class GlobalExceptionHandler {
             return ResponseResult.serverError(null);
         }
     }
+
+    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
+    public ResponseResult<?> handleValidationException(Exception ex, HttpServletRequest request) {
+        // 获取方法上的日志注解
+        HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(
+                HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);
+        OperationLog operationLog = handlerMethod.getMethodAnnotation(OperationLog.class);
+
+        Method method = handlerMethod.getMethod();
+        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
+
+        ResponseResult<Object> failed = ResponseResult.failed("response.validation.failed", null);
+        List<FieldError> fieldErrorList = ((BindException) ex).getFieldErrors();
+        if (!CollectionUtils.isEmpty(fieldErrorList)) {
+            for (FieldError fieldError : fieldErrorList) {
+                if (fieldError != null && fieldError.getDefaultMessage() != null) {
+                    String defaultMessage = fieldError.getDefaultMessage();
+                    // 尝试从消息源获取国际化消息,如果没有找到,则使用默认消息
+                    failed = ResponseResult.failed(defaultMessage, null);
+                    break;
+                }
+            }
+        }
+
+        // 记录日志
+        if (operationLog != null) {
+            sysLogService.saveAppLog(
+                    operationLog.description(),
+                    operationLog.operationType(),
+                    operationLog.module(),
+                    methodName,
+                    request.getParameterMap(),
+                    failed,
+                    0,
+                    ex
+            );
+        }
+        // 返回错误信息
+        return failed;
+    }
 }

+ 0 - 16
src/main/java/com/xs/core/mapper/log/ApiRequestLogMapper.java

@@ -1,16 +0,0 @@
-package com.xs.core.mapper.log;
-
-import com.xs.core.model.log.entity.ApiRequestLog;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-
-/**
- * <p>
- * 接口请求日志表 Mapper 接口
- * </p>
- *
- * @author xudm
- * @since 2024-12-23
- */
-public interface ApiRequestLogMapper extends BaseMapper<ApiRequestLog> {
-
-}

+ 16 - 0
src/main/java/com/xs/core/mapper/log/SysAppLogMapper.java

@@ -0,0 +1,16 @@
+package com.xs.core.mapper.log;
+
+import com.xs.core.model.log.entity.SysAppLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * app端系统操作日志表 Mapper 接口
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-24
+ */
+public interface SysAppLogMapper extends BaseMapper<SysAppLog> {
+
+}

+ 0 - 72
src/main/java/com/xs/core/model/log/entity/ApiRequestLog.java

@@ -1,72 +0,0 @@
-package com.xs.core.model.log.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.activerecord.Model;
-import java.io.Serializable;
-import java.time.LocalDateTime;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * <p>
- * 接口请求日志表
- * </p>
- *
- * @author xudm
- * @since 2024-12-23
- */
-@Getter
-@Setter
-@TableName("b_api_request_log")
-public class ApiRequestLog extends Model<ApiRequestLog> {
-
-    @TableId(value = "id", type = IdType.ASSIGN_ID)
-    private Long id;
-
-    @TableField("request_time")
-    private LocalDateTime requestTime;
-
-    @TableField("request_method")
-    private String requestMethod;
-
-    @TableField("request_url")
-    private String requestUrl;
-
-    @TableField("request_params")
-    private String requestParams;
-
-    @TableField("request_body")
-    private String requestBody;
-
-    @TableField("response_status")
-    private Integer responseStatus;
-
-    @TableField("response_body")
-    private String responseBody;
-
-    @TableField("ip_address")
-    private String ipAddress;
-
-    @TableField("user_agent")
-    private String userAgent;
-
-    @TableField("execution_time")
-    private Long executionTime;
-
-    @TableField("user_id")
-    private Long userId;
-
-    @TableField("error_message")
-    private String errorMessage;
-
-    @TableField("created_at")
-    private LocalDateTime createdAt;
-
-    @Override
-    public Serializable pkVal() {
-        return this.id;
-    }
-}

+ 134 - 0
src/main/java/com/xs/core/model/log/entity/SysAppLog.java

@@ -0,0 +1,134 @@
+package com.xs.core.model.log.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * app端系统操作日志表
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-24
+ */
+@Getter
+@Setter
+@TableName("b_sys_app_log")
+public class SysAppLog extends Model<SysAppLog> {
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    /**
+     * 操作用户
+     */
+    @TableField("user_id")
+    private String userId;
+
+    /**
+     * 操作描述
+     */
+    @TableField("operation")
+    private String operation;
+
+    /**
+     * 请求方法
+     */
+    @TableField("method")
+    private String method;
+
+    /**
+     * 请求参数
+     */
+    @TableField("params")
+    private String params;
+
+    /**
+     * 操作IP地址
+     */
+    @TableField("ip")
+    private String ip;
+
+    /**
+     * 创建时间
+     */
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    /**
+     * 执行时长(毫秒)
+     */
+    @TableField("execution_time")
+    private Long executionTime;
+
+    /**
+     * 操作类型
+     */
+    @TableField("operation_type")
+    private String operationType;
+
+    /**
+     * 模块名称
+     */
+    @TableField("module")
+    private String module;
+
+    /**
+     * 操作结果
+     */
+    @TableField("result")
+    private String result;
+
+    /**
+     * 状态(1:成功,0:失败)
+     */
+    @TableField("status")
+    private Integer status;
+
+    /**
+     * 错误消息
+     */
+    @TableField("error_msg")
+    private String errorMsg;
+
+    /**
+     * 请求URL
+     */
+    @TableField("request_url")
+    private String requestUrl;
+
+    /**
+     * 浏览器类型
+     */
+    @TableField("browser")
+    private String browser;
+
+    /**
+     * 操作系统
+     */
+    @TableField("os")
+    private String os;
+
+    /**
+     * 用户代理
+     */
+    @TableField("user_agent")
+    private String userAgent;
+
+    @Override
+    public Serializable pkVal() {
+        return this.id;
+    }
+}

+ 0 - 20
src/main/java/com/xs/core/service/Impl/log/ApiRequestLogServiceImpl.java

@@ -1,20 +0,0 @@
-package com.xs.core.service.Impl.log;
-
-import com.xs.core.model.log.entity.ApiRequestLog;
-import com.xs.core.mapper.log.ApiRequestLogMapper;
-import com.xs.core.service.log.IApiRequestLogService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.stereotype.Service;
-
-/**
- * <p>
- * 接口请求日志表 服务实现类
- * </p>
- *
- * @author xudm
- * @since 2024-12-23
- */
-@Service
-public class ApiRequestLogServiceImpl extends ServiceImpl<ApiRequestLogMapper, ApiRequestLog> implements IApiRequestLogService {
-
-}

+ 103 - 0
src/main/java/com/xs/core/service/Impl/log/SysAppLogServiceImpl.java

@@ -0,0 +1,103 @@
+package com.xs.core.service.Impl.log;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.xs.core.model.log.entity.SysAppLog;
+import com.xs.core.mapper.log.SysAppLogMapper;
+import com.xs.core.service.log.ISysAppLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.time.LocalDateTime;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * <p>
+ * app端系统操作日志表 服务实现类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-24
+ */
+@Service
+@Slf4j
+public class SysAppLogServiceImpl extends ServiceImpl<SysAppLogMapper, SysAppLog> implements ISysAppLogService {
+
+    @Value("${interfaceLog.openInterfaceLog}")
+    private boolean openInterfaceLog;
+
+    @Override
+    public void saveAppLog(String operation, String operationType, String module, String method, Object params, Object result, long time, Exception e) {
+        if (!openInterfaceLog) {
+            return;
+        }
+        SysAppLog sysLog = new SysAppLog();
+        // 设置基本信息
+        sysLog.setOperation(operation);
+        sysLog.setOperationType(operationType);
+        sysLog.setModule(module);
+        sysLog.setMethod(method);
+        sysLog.setCreateTime(LocalDateTime.now());
+        sysLog.setExecutionTime(time);
+
+        // 获取请求信息
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (attributes != null) {
+            HttpServletRequest request = attributes.getRequest();
+            sysLog.setIp(getIpAddress(request));
+            sysLog.setRequestUrl(request.getRequestURI());
+            sysLog.setUserAgent(request.getHeader("User-Agent"));
+        }
+
+        // 设置参数
+        if (params != null) {
+            sysLog.setParams(JSON.toJSONString(params));
+        }
+
+        // 设置结果
+        sysLog.setResult(JSON.toJSONString(result));
+        // 设置异常信息
+        // 设置操作结果
+        if (e != null) {
+            sysLog.setStatus(0);
+            sysLog.setErrorMsg(ExceptionUtil.stacktraceToString(e));
+        }
+
+        // 设置用户信息
+        if (StpUtil.isLogin()) {
+            sysLog.setUserId(StpUtil.getLoginIdAsString());
+        }
+
+        // 异步保存日志
+        CompletableFuture.runAsync(() -> {
+            try {
+                save(sysLog);
+            } catch (Exception ex) {
+                // 处理保存日志失败的情况
+                log.error("异步保存日志失败", ex);
+            }
+        });
+    }
+
+    // 获取IP地址
+    private String getIpAddress(HttpServletRequest request) {
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+}

+ 1 - 52
src/main/java/com/xs/core/service/Impl/user/AppLoginServiceImpl.java

@@ -38,9 +38,6 @@ import java.util.concurrent.CompletableFuture;
 public class AppLoginServiceImpl implements AppLoginService {
     private final IUserService userService;
 
-    private final TeamShareService teamShareService;
-
-    private final IUserWalletService walletService;
 
     @Override
     public SaTokenInfo tgLogin(AppLoginReq loginReq) {
@@ -48,61 +45,13 @@ public class AppLoginServiceImpl implements AppLoginService {
         queryWrapper.eq(User::getTgId, loginReq.getId());
         User user = userService.getOne(queryWrapper);
         if (ObjUtil.isNull(user)) {
-            user = registerToSys(loginReq);
+            user = userService.registerToSys(loginReq);
         }
         //校验是否被禁用
         checkUserStatus(user);
         return this.login(user);
     }
 
-    @Override
-    @Transactional
-    public User registerToSys(AppLoginReq loginReq) {
-        User user = User.builder()
-                .id(IdWorker.getId())
-                .tgAccount(loginReq.getUsername())
-                .tgId(loginReq.getId())
-                .avatar(StrUtil.isNotBlank(loginReq.getPhotoUrl()) ? URLDecoder.decode(loginReq.getPhotoUrl(), StandardCharsets.UTF_8) : "")
-                .firstName(loginReq.getFirstName())
-                .lastName(loginReq.getLastName())
-                //注册时指定用户的唯一邀请码
-                .inviteCode(InviteCodeGenerator.generateInviteCode())
-                .languageCode(loginReq.getLanguageCode())
-                .createdTime(LocalDateTime.now())
-                .updatedTime(LocalDateTime.now())
-                .build();
-        if (StrUtil.isNotBlank(loginReq.getAge_limit())) {
-            user.setAgeLimit(Integer.parseInt(loginReq.getAge_limit()));
-        }
-        User inviteUser;
-        if (StrUtil.isNotBlank(loginReq.getShare_code())) {
-            //如邀请码不为空 则写入邀请人信息
-            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
-            queryWrapper.eq(User::getInviteCode, loginReq.getShare_code());
-            inviteUser = userService.getOne(queryWrapper);
-            if (ObjUtil.isNotNull(inviteUser)) {
-                user.setReferrerId(String.valueOf(inviteUser.getId()));
-                user.setPassengerFlowWay(2);
-                user.setId(IdWorker.getId());
-            }
-        } else {
-            inviteUser = null;
-        }
-        //初始化钱包信息
-        UserWallet userWallet = new UserWallet();
-        userWallet.setUserId(user.getId());
-        userWallet.setUpdatedTime(LocalDateTime.now());
-        userService.save(user);
-        walletService.save(userWallet);
-        CompletableFuture.runAsync(() -> {
-            //注册成功后发放邀请奖励
-            if (ObjUtil.isNotNull(inviteUser)) {
-                teamShareService.teamInviteGoldCoinRewardGeneral(inviteUser.getId(), user.getId());
-            }
-        });
-        return user;
-    }
-
     /**
      * 检查用户状态
      *

+ 70 - 0
src/main/java/com/xs/core/service/Impl/user/UserServiceImpl.java

@@ -1,19 +1,33 @@
 package com.xs.core.service.Impl.user;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.xs.core.common.content.UserContextHolder;
 import com.xs.core.common.validation.CheckUtils;
 import com.xs.core.model.team.resp.TeamUserResp;
 import com.xs.core.model.user.entity.User;
 import com.xs.core.mapper.user.UserMapper;
+import com.xs.core.model.user.req.AppLoginReq;
+import com.xs.core.model.wallet.entity.UserWallet;
+import com.xs.core.service.team.TeamShareService;
 import com.xs.core.service.user.IUserService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.xs.core.service.wallet.IUserWalletService;
 import com.xs.core.utils.InviteCodeGenerator;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.io.Serializable;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * <p>
@@ -24,8 +38,16 @@ import java.util.List;
  * @since 2024-12-22
  */
 @Service
+
 public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
 
+    @Autowired
+    private IUserWalletService walletService;
+
+    @Autowired
+    @Lazy
+    private TeamShareService teamShareService;
+
     @Override
     public User getInviterByUserId(Long userId) {
         return getBaseMapper().getInviterByUserId(userId);
@@ -57,4 +79,52 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IU
         queryWrapper.eq(User::getReferrerId, id);
         return count(queryWrapper);
     }
+
+    @Override
+    @Transactional
+    public User registerToSys(AppLoginReq loginReq) {
+        User user = User.builder()
+                .id(IdWorker.getId())
+                .tgAccount(loginReq.getUsername())
+                .tgId(loginReq.getId())
+                .avatar(StrUtil.isNotBlank(loginReq.getPhotoUrl()) ? URLDecoder.decode(loginReq.getPhotoUrl(), StandardCharsets.UTF_8) : "")
+                .firstName(loginReq.getFirstName())
+                .lastName(loginReq.getLastName())
+                //注册时指定用户的唯一邀请码
+                .inviteCode(InviteCodeGenerator.generateInviteCode())
+                .languageCode(loginReq.getLanguageCode())
+                .createdTime(LocalDateTime.now())
+                .updatedTime(LocalDateTime.now())
+                .build();
+        if (StrUtil.isNotBlank(loginReq.getAge_limit())) {
+            user.setAgeLimit(Integer.parseInt(loginReq.getAge_limit()));
+        }
+        User inviteUser;
+        if (StrUtil.isNotBlank(loginReq.getShare_code())) {
+            //如邀请码不为空 则写入邀请人信息
+            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(User::getInviteCode, loginReq.getShare_code());
+            inviteUser = getOne(queryWrapper);
+            if (ObjUtil.isNotNull(inviteUser)) {
+                user.setReferrerId(String.valueOf(inviteUser.getId()));
+                user.setPassengerFlowWay(2);
+                user.setId(IdWorker.getId());
+            }
+        } else {
+            inviteUser = null;
+        }
+        //初始化钱包信息
+        UserWallet userWallet = new UserWallet();
+        userWallet.setUserId(user.getId());
+        userWallet.setUpdatedTime(LocalDateTime.now());
+        save(user);
+        walletService.save(userWallet);
+        CompletableFuture.runAsync(() -> {
+            //注册成功后发放邀请奖励
+            if (ObjUtil.isNotNull(inviteUser)) {
+                teamShareService.teamInviteGoldCoinRewardGeneral(inviteUser.getId(), user.getId());
+            }
+        });
+        return user;
+    }
 }

+ 0 - 16
src/main/java/com/xs/core/service/log/IApiRequestLogService.java

@@ -1,16 +0,0 @@
-package com.xs.core.service.log;
-
-import com.xs.core.model.log.entity.ApiRequestLog;
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * <p>
- * 接口请求日志表 服务类
- * </p>
- *
- * @author xudm
- * @since 2024-12-23
- */
-public interface IApiRequestLogService extends IService<ApiRequestLog> {
-
-}

+ 28 - 0
src/main/java/com/xs/core/service/log/ISysAppLogService.java

@@ -0,0 +1,28 @@
+package com.xs.core.service.log;
+
+import com.xs.core.model.log.entity.SysAppLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * app端系统操作日志表 服务类
+ * </p>
+ *
+ * @author xudm
+ * @since 2024-12-24
+ */
+public interface ISysAppLogService extends IService<SysAppLog> {
+    /**
+     * 保存app端操作日志
+     *
+     * @param operation     操作描述
+     * @param operationType 操作类型
+     * @param module        模块
+     * @param method        方法
+     * @param params        参数
+     * @param result        结果
+     * @param e             异常
+     */
+    void saveAppLog(String operation, String operationType, String module,
+                    String method, Object params, Object result, long time, Exception e);
+}

+ 0 - 8
src/main/java/com/xs/core/service/user/AppLoginService.java

@@ -12,12 +12,4 @@ public interface AppLoginService {
      * @return
      */
     SaTokenInfo tgLogin(AppLoginReq loginReq);
-
-    /**
-     * 注册tg用户到到系统
-     *
-     * @param loginReq
-     * @return
-     */
-    User registerToSys(AppLoginReq loginReq);
 }

+ 9 - 0
src/main/java/com/xs/core/service/user/IUserService.java

@@ -5,6 +5,7 @@ import com.xs.core.common.enums.CoinTransactionTypeEnum;
 import com.xs.core.model.team.resp.TeamUserResp;
 import com.xs.core.model.user.entity.User;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.xs.core.model.user.req.AppLoginReq;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -43,4 +44,12 @@ public interface IUserService extends IService<User> {
      * @return
      */
     Long queryInviterCount(Long id);
+
+    /**
+     * 注册tg用户到到系统
+     *
+     * @param loginReq
+     * @return
+     */
+    User registerToSys(AppLoginReq loginReq);
 }

+ 1 - 1
src/main/java/com/xs/core/utils/MyGenerator.java

@@ -57,7 +57,7 @@ public class MyGenerator {
 
     public static void main(String[] args) {
         // 项目名 例如app  web
-        generatorByBusinessModule("log", new String[]{"b_api_request_log"});
+        generatorByBusinessModule("log", new String[]{"b_sys_app_log"});
     }
 
     /**

+ 9 - 0
src/main/resources/application-dev.yml

@@ -60,6 +60,9 @@ spring:
         initial-interval: 1000
         max-attempts: 3
         multiplier: 2
+  aop:
+    auto: true
+    proxy-target-class: true
 
 #myabtis-plus
 mybatis-plus:
@@ -148,3 +151,9 @@ xssFilter:
   isOpenXssFilter: false
   # 不拦截路径,多个用“,”分隔,例如/userBusiness/*,/sysRole/*
   xssFilterWhileUrl: /process/*
+
+
+# 接口日志配置
+interfaceLog:
+  # 是否开启接口日志
+  openInterfaceLog: false

+ 9 - 0
src/main/resources/application-prod.yml

@@ -60,6 +60,9 @@ spring:
         initial-interval: 1000
         max-attempts: 3
         multiplier: 2
+  aop:
+    auto: true
+    proxy-target-class: true
 
 #myabtis-plus
 mybatis-plus:
@@ -148,3 +151,9 @@ xssFilter:
   isOpenXssFilter: false
   # 不拦截路径,多个用“,”分隔,例如/userBusiness/*,/sysRole/*
   xssFilterWhileUrl: /process/*
+
+
+# 接口日志配置
+interfaceLog:
+  # 是否开启接口日志
+  openInterfaceLog: false

+ 24 - 0
src/main/resources/mapper/SysAppLogMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.xs.core.mapper.log.SysAppLogMapper">
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.xs.core.model.log.entity.SysAppLog">
+        <id column="id" property="id"/>
+        <result column="user_id" property="userId"/>
+        <result column="operation" property="operation"/>
+        <result column="method" property="method"/>
+        <result column="params" property="params"/>
+        <result column="ip" property="ip"/>
+        <result column="create_time" property="createTime"/>
+        <result column="execution_time" property="executionTime"/>
+        <result column="operation_type" property="operationType"/>
+        <result column="module" property="module"/>
+        <result column="result" property="result"/>
+        <result column="status" property="status"/>
+        <result column="error_msg" property="errorMsg"/>
+        <result column="request_url" property="requestUrl"/>
+        <result column="browser" property="browser"/>
+        <result column="os" property="os"/>
+        <result column="user_agent" property="userAgent"/>
+    </resultMap>
+</mapper>