Просмотр исходного кода

运营后台和分开管理优化

liuchengsen 1 месяц назад
Родитель
Сommit
0828dacda8
16 измененных файлов с 104 добавлено и 217 удалено
  1. 1 1
      zhijiayun-gateway/src/main/resources/db/schema.sql
  2. 5 14
      zhijiayun-invite/src/main/java/com/xuekairui/invite/controller/AdminInviteController.java
  3. 3 2
      zhijiayun-user/src/main/java/com/xuekairui/user/config/SecurityConfig.java
  4. 5 10
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminAccountController.java
  5. 40 0
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminAuthController.java
  6. 1 7
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminLicenseController.java
  7. 1 1
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminMembershipController.java
  8. 5 12
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminUserController.java
  9. 2 8
      zhijiayun-user/src/main/java/com/xuekairui/user/controller/CouponController.java
  10. 0 5
      zhijiayun-user/src/main/java/com/xuekairui/user/entity/User.java
  11. 0 8
      zhijiayun-user/src/main/java/com/xuekairui/user/enums/OperationType.java
  12. 0 46
      zhijiayun-user/src/main/java/com/xuekairui/user/enums/UserRole.java
  13. 12 22
      zhijiayun-user/src/main/java/com/xuekairui/user/service/AuthService.java
  14. 8 0
      zhijiayun-user/src/main/java/com/xuekairui/user/service/CrawlerService.java
  15. 5 4
      zhijiayun-user/src/main/java/com/xuekairui/user/service/MembershipService.java
  16. 16 77
      zhijiayun-user/src/main/java/com/xuekairui/user/service/OperationAuditLogService.java

+ 1 - 1
zhijiayun-gateway/src/main/resources/db/schema.sql

@@ -32,7 +32,7 @@ CREATE TABLE IF NOT EXISTS `t_user` (
     `city`                  VARCHAR(50)  DEFAULT NULL COMMENT '市',
     `district`              VARCHAR(50)  DEFAULT NULL COMMENT '区',
     `status`                INT          NOT NULL DEFAULT 1 COMMENT '账号状态:0-禁用 1-正常',
-    `role`                  VARCHAR(20)  NOT NULL DEFAULT 'USER' COMMENT '用户角色:USER/ADMIN/SUPER_ADMIN',
+    `role`                  VARCHAR(20)  NOT NULL DEFAULT 'USER' COMMENT '用户角色(管理员角色已迁至 t_admin)',
     `membership_level`      VARCHAR(20)  DEFAULT NULL COMMENT '当前会员等级:PLUS/PRO/ULTRA',
     `last_login_time`       DATETIME     DEFAULT NULL COMMENT '最后登录时间',
     `last_login_ip`         VARCHAR(45)  DEFAULT NULL COMMENT '最后登录IP',

+ 5 - 14
zhijiayun-invite/src/main/java/com/xuekairui/invite/controller/AdminInviteController.java

@@ -58,9 +58,8 @@ public class AdminInviteController {
     public Result<InviteConfig> updateInviteConfig(@Valid @RequestBody InviteConfigRequest request,
                                                     HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         InviteConfig result = inviteConfigService.updateConfig(request);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
+        auditLogService.logAdmin(operatorId,
                 OperationType.INVITE_CONFIG_UPDATE, "INVITE_CONFIG", result.getId());
         return Result.success(result);
     }
@@ -93,9 +92,8 @@ public class AdminInviteController {
     public Result<InviteConfig> createInviteConfig(@Valid @RequestBody InviteConfigRequest request,
                                                     HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         InviteConfig result = inviteConfigService.createConfig(request);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
+        auditLogService.logAdmin(operatorId,
                 OperationType.INVITE_CONFIG_UPDATE, "INVITE_CONFIG_CREATE", result.getId());
         return Result.success(result);
     }
@@ -109,9 +107,8 @@ public class AdminInviteController {
                                                         @Valid @RequestBody InviteConfigRequest request,
                                                         HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         InviteConfig result = inviteConfigService.updateConfig(id, request);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
+        auditLogService.logAdmin(operatorId,
                 OperationType.INVITE_CONFIG_UPDATE, "INVITE_CONFIG_UPDATE:" + id, id);
         return Result.success(result);
     }
@@ -124,9 +121,8 @@ public class AdminInviteController {
     public Result<Void> deleteInviteConfig(@PathVariable Long id,
                                             HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         inviteConfigService.deleteConfig(id);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
+        auditLogService.logAdmin(operatorId,
                 OperationType.INVITE_CONFIG_UPDATE, "INVITE_CONFIG_DELETE:" + id, id);
         return Result.success();
     }
@@ -141,9 +137,8 @@ public class AdminInviteController {
     public Result<CrawlerQuotaGrant> grantCrawlerQuota(@Valid @RequestBody QuotaGrantRequest request,
                                                         HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         CrawlerQuotaGrant grant = inviteService.adminGrantQuota(request);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
+        auditLogService.logAdmin(operatorId,
                 OperationType.CRAWLER_QUOTA_GRANT, "CRAWLER_QUOTA:" + grant.getUserId(), grant.getUserId());
         return Result.success(grant);
     }
@@ -195,8 +190,4 @@ public class AdminInviteController {
         return Result.success(inviteService.getInviteConversionStats(userId, phone));
     }
 
-    private String getOperatorRole(HttpServletRequest request) {
-        String role = (String) request.getAttribute("role");
-        return role != null ? role : "ADMIN";
-    }
 }

+ 3 - 2
zhijiayun-user/src/main/java/com/xuekairui/user/config/SecurityConfig.java

@@ -34,8 +34,9 @@ public class SecurityConfig {
 
     /** 公开接口白名单 */
     private static final String[] PUBLIC_URLS = {
-            "/api/auth/sms/send",        // 发送验证码
-            "/api/auth/sms/login",       // 短信登录
+            "/api/auth/sms/send",
+            "/api/auth/sms/login",
+            "/api/admin/auth/login",       // 管理员登录(公开)
             "/api/auth/password/login",  // 密码登录
             "/api/auth/refresh",         // 刷新Token(无需认证)
             "/api/auth/wechat/**",       // 微信登录相关

+ 5 - 10
zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminAccountController.java

@@ -42,7 +42,7 @@ public class AdminAccountController {
                                                   HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         AdminUserResponse resp = adminUserService.createAdmin(request);
-        auditLogService.logSuccess(operatorId, "管理员", getOperatorRole(httpRequest),
+        auditLogService.logAdmin(operatorId,
                 OperationType.USER_UPDATE, "ADMIN_CREATE:" + resp.getId(), resp.getId());
         return Result.success(resp);
     }
@@ -53,7 +53,7 @@ public class AdminAccountController {
                                                   HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         AdminUserResponse resp = adminUserService.updateAdmin(id, request);
-        auditLogService.logSuccess(operatorId, "管理员", getOperatorRole(httpRequest),
+        auditLogService.logAdmin(operatorId,
                 OperationType.USER_UPDATE, "ADMIN_UPDATE:" + id, id);
         return Result.success(resp);
     }
@@ -63,7 +63,7 @@ public class AdminAccountController {
                                        HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         adminUserService.deleteAdmin(id);
-        auditLogService.logSuccess(operatorId, "管理员", getOperatorRole(httpRequest),
+        auditLogService.logAdmin(operatorId,
                 OperationType.USER_UPDATE, "ADMIN_DELETE:" + id, id);
         return Result.success("管理员已禁用");
     }
@@ -73,7 +73,7 @@ public class AdminAccountController {
                                          HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         String newPwd = adminAuthService.resetPassword(id);
-        auditLogService.logSuccess(operatorId, "管理员", getOperatorRole(httpRequest),
+        auditLogService.logAdmin(operatorId,
                 OperationType.USER_UPDATE, "ADMIN_RESET_PWD:" + id, id);
         return Result.success(newPwd);
     }
@@ -92,13 +92,8 @@ public class AdminAccountController {
                                      HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         adminUserService.kickAdmin(adminId, operatorId);
-        auditLogService.logSuccess(operatorId, "管理员", getOperatorRole(httpRequest),
+        auditLogService.logAdmin(operatorId,
                 OperationType.USER_UPDATE, "ADMIN_KICK:" + adminId, adminId);
         return Result.success("管理员已被强制退出");
     }
-
-    private String getOperatorRole(HttpServletRequest request) {
-        String role = (String) request.getAttribute("role");
-        return role != null ? role : "ADMIN";
-    }
 }

+ 40 - 0
zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminAuthController.java

@@ -0,0 +1,40 @@
+package com.xuekairui.user.controller;
+
+import com.xuekairui.common.Result;
+import com.xuekairui.user.dto.LoginResponse;
+import com.xuekairui.user.service.AdminAuthService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 管理员认证接口(运营端,操作 t_admin 表)
+ */
+@RestController
+@RequestMapping("/api/admin/auth")
+@RequiredArgsConstructor
+public class AdminAuthController {
+
+    private final AdminAuthService adminAuthService;
+
+    /**
+     * 管理员密码登录
+     * POST /api/admin/auth/login
+     */
+    @PostMapping("/login")
+    public Result<LoginResponse> login(@RequestBody Map<String, String> body,
+                                        HttpServletRequest request) {
+        String account = body.get("username");
+        if (account == null || account.isBlank()) {
+            account = body.get("phone");
+        }
+        String password = body.get("password");
+        String ip = request.getHeader("X-Forwarded-For");
+        if (ip == null || ip.isBlank()) {
+            ip = request.getRemoteAddr();
+        }
+        return Result.success(adminAuthService.passwordLogin(account, password, ip));
+    }
+}

+ 1 - 7
zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminLicenseController.java

@@ -49,7 +49,6 @@ public class AdminLicenseController {
             @Valid @RequestBody ReviewRequest request,
             HttpServletRequest httpRequest) {
         Long reviewerId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
 
         BusinessLicense license = businessLicenseMapper.selectById(request.getLicenseId());
         if (license == null) {
@@ -92,16 +91,11 @@ public class AdminLicenseController {
         // 记录审计日志
         OperationType opType = "APPROVED".equals(request.getAction())
                 ? OperationType.LICENSE_APPROVE : OperationType.LICENSE_REJECT;
-        auditLogService.logSuccess(reviewerId, "管理员", operatorRole, opType,
+        auditLogService.logAdmin(reviewerId, opType,
                 "LICENSE:" + license.getId(), license.getId());
         return Result.success();
     }
 
-    private String getOperatorRole(HttpServletRequest request) {
-        String role = (String) request.getAttribute("role");
-        return role != null ? role : "ADMIN";
-    }
-
     /**
      * 检查用户是否已有其他审核通过的营业执照(避免重复赠送会员)
      */

+ 1 - 1
zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminMembershipController.java

@@ -32,7 +32,7 @@ public class AdminMembershipController {
             HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
         String operatorRole = getOperatorRole(httpRequest);
-        return Result.success(membershipService.grantMembership(request, operatorId, operatorRole));
+        return Result.success(membershipService.grantMembership(request, operatorId));
     }
 
     /**

+ 5 - 12
zhijiayun-user/src/main/java/com/xuekairui/user/controller/AdminUserController.java

@@ -36,8 +36,7 @@ public class AdminUserController {
             @RequestParam(defaultValue = "20") Integer size,
             @RequestParam(required = false) Long userId,
             @RequestParam(required = false) String phone,
-            @RequestParam(required = false) String nickname,
-            @RequestParam(required = false) String role) {
+            @RequestParam(required = false) String nickname) {
         return Result.success(adminUserService.listUsers(page, size, userId, phone, nickname));
     }
 
@@ -50,10 +49,9 @@ public class AdminUserController {
             @PathVariable Long userId,
             HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         adminUserService.kickUser(userId, operatorId);
-        auditLogService.logSuccess(operatorId, "管理员", operatorRole,
-                OperationType.USER_UPDATE, "USER_KICK:" + userId, userId);
+        auditLogService.logAdmin(operatorId, OperationType.USER_UPDATE,
+                "USER_KICK:" + userId, userId);
         return Result.success("用户已被强制退出,下次请求将返回1304");
     }
 
@@ -67,15 +65,10 @@ public class AdminUserController {
             @Valid @RequestBody UpdateRoleRequest request,
             HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         AdminUserResponse response = adminUserService.updateUserRole(userId, request, operatorId);
-        auditLogService.logSuccess(operatorId, "管理员", operatorRole,
-                OperationType.USER_UPDATE, "USER_ROLE:" + userId + "->" + request.getRole(), userId);
+        auditLogService.logAdmin(operatorId, OperationType.USER_UPDATE,
+                "USER_ROLE:" + userId + "->" + request.getRole(), userId);
         return Result.success(response);
     }
 
-    private String getOperatorRole(HttpServletRequest request) {
-        String role = (String) request.getAttribute("role");
-        return role != null ? role : "ADMIN";
-    }
 }

+ 2 - 8
zhijiayun-user/src/main/java/com/xuekairui/user/controller/CouponController.java

@@ -34,18 +34,12 @@ public class CouponController {
     public Result<Integer> issueCoupon(@Valid @RequestBody CouponIssueRequest request,
                                        HttpServletRequest httpRequest) {
         Long operatorId = (Long) httpRequest.getAttribute("userId");
-        String operatorRole = getOperatorRole(httpRequest);
         int count = couponService.issueCoupon(request);
-        auditLogService.logSuccess(operatorId != null ? operatorId : 0L, "管理员", operatorRole,
-                OperationType.COUPON_ISSUE, "COUPON_ISSUE:" + count, null);
+        auditLogService.logAdmin(operatorId, OperationType.COUPON_ISSUE,
+                "COUPON_ISSUE:" + count, null);
         return Result.success(count);
     }
 
-    private String getOperatorRole(HttpServletRequest request) {
-        String role = (String) request.getAttribute("role");
-        return role != null ? role : "ADMIN";
-    }
-
     /**
      * 获取我的优惠券列表
      * GET /api/coupon/my?status=0

+ 0 - 5
zhijiayun-user/src/main/java/com/xuekairui/user/entity/User.java

@@ -2,7 +2,6 @@ package com.xuekairui.user.entity;
 
 import com.baomidou.mybatisplus.annotation.*;
 import com.xuekairui.user.enums.MembershipLevel;
-import com.xuekairui.user.enums.UserRole;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.AllArgsConstructor;
@@ -67,10 +66,6 @@ public class User {
     @Builder.Default
     private Integer status = 1;
 
-    /** 用户角色:USER/ADMIN/SUPER_ADMIN */
-    @Builder.Default
-    private UserRole role = UserRole.USER;
-
     /** 当前会员等级:PLUS/PRO/ULTRA */
     private MembershipLevel membershipLevel;
 

+ 0 - 8
zhijiayun-user/src/main/java/com/xuekairui/user/enums/OperationType.java

@@ -72,14 +72,6 @@ public enum OperationType {
     /** 作废优惠券 */
     COUPON_INVALIDATE("COUPON", "INVALIDATE", "作废优惠券", true),
 
-    // ==================== 体验配额配置模块 ====================
-    /** 创建体验配置 */
-    TRIAL_CONFIG_CREATE("TRIAL", "CONFIG_CREATE", "创建体验配额配置", true),
-    /** 更新体验配置 */
-    TRIAL_CONFIG_UPDATE("TRIAL", "CONFIG_UPDATE", "更新体验配额配置", true),
-    /** 删除体验配置 */
-    TRIAL_CONFIG_DELETE("TRIAL", "CONFIG_DELETE", "删除体验配额配置", true),
-
     // ==================== 爬虫配额模块 ====================
     /** 手动发放爬虫配额 */
     CRAWLER_QUOTA_GRANT("CRAWLER", "QUOTA_GRANT", "手动发放爬虫配额", true),

+ 0 - 46
zhijiayun-user/src/main/java/com/xuekairui/user/enums/UserRole.java

@@ -1,46 +0,0 @@
-package com.xuekairui.user.enums;
-
-import lombok.Getter;
-
-/**
- * 用户角色枚举
- */
-@Getter
-public enum UserRole {
-
-    /**
-     * 普通用户:仅可访问用户侧接口
-     */
-    USER("USER"),
-
-    /**
-     * 运营人员:可访问运营后台接口
-     */
-    ADMIN("ADMIN"),
-
-    /**
-     * 超级管理员:可管理用户角色及所有运营接口
-     */
-    SUPER_ADMIN("SUPER_ADMIN");
-
-    private final String code;
-
-    UserRole(String code) {
-        this.code = code;
-    }
-
-    /**
-     * 根据 code 获取枚举
-     */
-    public static UserRole fromCode(String code) {
-        if (code == null || code.isBlank()) {
-            return USER;
-        }
-        for (UserRole role : values()) {
-            if (role.code.equalsIgnoreCase(code)) {
-                return role;
-            }
-        }
-        return USER;
-    }
-}

+ 12 - 22
zhijiayun-user/src/main/java/com/xuekairui/user/service/AuthService.java

@@ -94,12 +94,11 @@ public class AuthService {
         response.setNewUser(isNewUser);
 
         // 记录审计日志
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
         if (isNewUser) {
-            auditLogService.logSuccess(user.getId(), user.getNickname(), roleCode,
+            auditLogService.logSuccess(user.getId(), user.getNickname(), "USER",
                     OperationType.USER_REGISTER, "USER:" + user.getId(), user.getId());
         }
-        auditLogService.logSuccess(user.getId(), user.getNickname(), roleCode,
+        auditLogService.logSuccess(user.getId(), user.getNickname(), "USER",
                 OperationType.USER_LOGIN, "USER:" + user.getId(), user.getId());
         return response;
     }
@@ -298,7 +297,7 @@ public class AuthService {
         // 只查 Refresh Token 校验所需的字段,减少DB开销
         User user = userMapper.selectOne(
                 new LambdaQueryWrapper<User>()
-                        .select(User::getId, User::getPhone, User::getRole,
+                        .select(User::getId, User::getPhone,
                                 User::getTokenVersion, User::getRefreshTokenHash,
                                 User::getRefreshTokenStatus, User::getStatus)
                         .eq(User::getId, userId));
@@ -331,7 +330,7 @@ public class AuthService {
 
         // 5. 旋转:标记旧 Refresh Token 为 USED,签发新的一对
         user.setRefreshTokenStatus("USED");
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
+        String roleCode = "USER";
         long currentVersion = user.getTokenVersion() != null ? user.getTokenVersion() : 0L;
 
         String newAccessToken = jwtUtil.generateAccessToken(
@@ -363,17 +362,11 @@ public class AuthService {
         // 登录/注册时注入 MDC uid,不依赖 token 解析
         MDC.put("uid", String.valueOf(user.getId()));
 
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
+        String roleCode = "USER";
 
         // 检测是否存在旧会话
         long oldVersion = user.getTokenVersion() != null ? user.getTokenVersion() : 0L;
-        boolean hadPreviousSession;
-        if ("ADMIN".equals(roleCode) || "SUPER_ADMIN".equals(roleCode)) {
-            // 管理员允许多设备:仅当旧版本号 ≥ 5 时说明有其他设备被挤出
-            hadPreviousSession = oldVersion >= 5;
-        } else {
-            hadPreviousSession = oldVersion > 0;
-        }
+        boolean hadPreviousSession = oldVersion > 0;
 
         // 递增 tokenVersion,使所有旧 Token 立即失效
         long newVersion = oldVersion + 1;
@@ -381,7 +374,7 @@ public class AuthService {
 
         // 生成 Token
         String accessToken = jwtUtil.generateAccessToken(
-                user.getId(), roleCode, newVersion);
+                user.getId(), "USER", newVersion);
         String refreshToken = jwtUtil.generateRefreshToken(user.getId());
 
         // 存储 Refresh Token 哈希(用于旋转+重用检测)
@@ -442,7 +435,7 @@ public class AuthService {
                 .province(user.getProvince())
                 .city(user.getCity())
                 .district(user.getDistrict())
-                .role(user.getRole() != null ? user.getRole().getCode() : "USER")
+                .role("USER")
                 .membershipLevel(levelInfo != null ? levelInfo.getLevelCode() : "PLUS")
                 .membershipLevelName(levelInfo != null ? levelInfo.getLevelName() : "普通会员")
                 .inviterId(inviterId)
@@ -501,8 +494,7 @@ public class AuthService {
 
         // 6. 生成Token
         LoginResponse response = buildLoginResponse(user);
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
-        auditLogService.logSuccess(user.getId(), user.getNickname(), roleCode,
+        auditLogService.logSuccess(user.getId(), user.getNickname(), "USER",
                 OperationType.USER_LOGIN, "USER:" + user.getId(), user.getId());
         return response;
     }
@@ -530,9 +522,8 @@ public class AuthService {
         user.setPassword(passwordUtil.encode(request.getNewPassword()));
         userMapper.updateById(user);
         log.info("用户{}设置/修改密码成功", userId);
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
-        auditLogService.logSuccess(userId, user.getNickname(), roleCode,
-                OperationType.USER_UPDATE, "PASSWORD:" + userId, userId);
+        auditLogService.logSuccess(userId, user.getNickname(), "USER",
+                OperationType.USER_UPDATE, "PHONE:" + userId, userId);
     }
 
     /**
@@ -560,8 +551,7 @@ public class AuthService {
         user.setPhone(request.getNewPhone());
         userMapper.updateById(user);
         log.info("用户{}修改手机号成功,旧手机号:{},新手机号:{}", userId, oldPhone, request.getNewPhone());
-        String roleCode = user.getRole() != null ? user.getRole().getCode() : "USER";
-        auditLogService.logSuccess(userId, user.getNickname(), roleCode,
+        auditLogService.logSuccess(userId, user.getNickname(), "USER",
                 OperationType.USER_UPDATE, "PHONE:" + userId, userId);
     }
 }

+ 8 - 0
zhijiayun-user/src/main/java/com/xuekairui/user/service/CrawlerService.java

@@ -3,6 +3,7 @@ package com.xuekairui.user.service;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.xuekairui.common.BusinessException;
 import com.xuekairui.common.ErrorCode;
+import com.xuekairui.user.enums.OperationType;
 import com.xuekairui.user.dto.CrawlerStatusResponse;
 import com.xuekairui.user.dto.QuotaBreakdownItem;
 import com.xuekairui.user.entity.Coupon;
@@ -42,6 +43,7 @@ public class CrawlerService {
     private final MembershipService membershipService;
     private final CouponService couponService;
     private final CrawlerQuotaGrantService crawlerQuotaGrantService;
+    private final OperationAuditLogService auditLogService;
 
     /**
      * 获取用户爬虫权益状态(统一展示等级配额 + 额外配额来源明细)
@@ -164,18 +166,24 @@ public class CrawlerService {
                 consumeFromQuota(userId, quotaCount);
             }
             recordUsage(userId, count, "MIXED", "优惠券+" + shortage + ",配额+" + quotaCount, platform);
+            auditLogService.logSuccess(userId, null, "USER",
+                    OperationType.CRAWLER_QUOTA_DEDUCT, "CRAWLER_CONSUME:" + count, userId);
             return true;
         }
 
         // 日配额充足
         if (useCouponFirst && consumeWithCoupon(userId, count)) {
             recordUsage(userId, count, "COUPON", "使用优惠券", platform);
+            auditLogService.logSuccess(userId, null, "USER",
+                    OperationType.CRAWLER_QUOTA_DEDUCT, "CRAWLER_CONSUME:" + count, userId);
             return true;
         }
 
         // 从配额中扣减(额外配额优先,快过期优先)
         consumeFromQuota(userId, count);
         recordUsage(userId, count, "QUOTA", null, platform);
+        auditLogService.logSuccess(userId, null, "USER",
+                OperationType.CRAWLER_QUOTA_DEDUCT, "CRAWLER_CONSUME:" + count, userId);
         return true;
     }
 

+ 5 - 4
zhijiayun-user/src/main/java/com/xuekairui/user/service/MembershipService.java

@@ -114,7 +114,7 @@ public class MembershipService {
      * 发放/调整用户会员等级(运营/超管)
      */
     @Transactional
-    public MembershipResponse grantMembership(GrantMembershipRequest request, Long operatorId, String operatorRole) {
+    public MembershipResponse grantMembership(GrantMembershipRequest request, Long operatorId) {
         User user;
         if (request.getUserId() != null) {
             user = userMapper.selectById(request.getUserId());
@@ -161,9 +161,8 @@ public class MembershipService {
         userMapper.updateById(user);
 
         // 审计日志
-        auditLogService.logSuccess(operatorId, "管理员", operatorRole,
-                OperationType.USER_UPDATE,
-                "MEMBERSHIP:" + request.getUserId() + "->" + level.getCode(), request.getUserId());
+        auditLogService.logAdmin(operatorId, OperationType.USER_UPDATE,
+                "MEMBERSHIP:" + user.getId() + "->" + level.getCode(), user.getId());
 
         log.info("运营人员{}为用户{}发放会员等级{}", operatorId, request.getUserId(), level.getCode());
         return toResponse(membership);
@@ -267,6 +266,8 @@ public class MembershipService {
     public void grantInviteRewardMembership(Long inviterId, Long inviteeId) {
         grantOrExtendMembership(inviterId, MembershipLevel.PRO, 30,
                 "INVITE", "邀请奖励:邀请用户" + inviteeId + "注册");
+        auditLogService.logSuccess(inviterId, null, "USER",
+                OperationType.INVITE_REWARD_GRANT, "INVITE_REWARD:" + inviterId, inviterId);
         log.info("邀请奖励:用户{}获得30天高级会员(PRO)", inviterId);
     }
 

+ 16 - 77
zhijiayun-user/src/main/java/com/xuekairui/user/service/OperationAuditLogService.java

@@ -27,6 +27,7 @@ import java.util.List;
 public class OperationAuditLogService {
 
     private final OperationAuditLogMapper auditLogMapper;
+    private final com.xuekairui.user.mapper.AdminMapper adminMapper;
     private final ObjectMapper objectMapper = new ObjectMapper();
 
     /**
@@ -35,10 +36,6 @@ public class OperationAuditLogService {
     @Async("auditLogExecutor")
     public void logOperation(OperationAuditLog auditLog) {
         try {
-            // 注意:异步线程中无法获取RequestContextHolder,需要在调用前填充
-            // fillRequestInfo(auditLog); // 已移除,改由调用方填充
-            
-            // 保存日志
             auditLogMapper.insert(auditLog);
             
             // 如果是敏感操作,额外记录警告日志
@@ -58,6 +55,16 @@ public class OperationAuditLogService {
         }
     }
 
+    /**
+     * 管理端专用:自动从 t_admin 解析操作人信息
+     */
+    public void logAdmin(Long operatorId, OperationType opType, String targetId, Long targetNum) {
+        var admin = operatorId != null ? adminMapper.selectById(operatorId) : null;
+        String name = admin != null ? admin.getUsername() : "管理员";
+        String role = admin != null ? admin.getRole() : "ADMIN";
+        logSuccess(operatorId != null ? operatorId : 0L, name, role, opType, targetId, targetNum);
+    }
+
     /**
      * 便捷方法:记录成功操作
      */
@@ -94,32 +101,11 @@ public class OperationAuditLogService {
             builder.afterData(toJson(afterData));
         }
 
-        logOperation(builder.build());
+        OperationAuditLog log = builder.build();
+        fillRequestInfo(log);
+        logOperation(log);
     }
 
-    /**
-     * 便捷方法:记录失败操作
-     */
-    public void logFailure(Long operatorId, String operatorName, String operatorRole,
-                          OperationType operationType, String targetIdentifier, Long targetId,
-                          String errorMessage) {
-        OperationAuditLog auditLog = AuditLogBuilder.builder()
-                .operatorId(operatorId)
-                .operatorName(operatorName)
-                .operatorRole(operatorRole)
-                .operationType(operationType.getCode())
-                .module(operationType.getModule())
-                .description(operationType.getDescription())
-                .targetIdentifier(targetIdentifier)
-                .targetId(targetId)
-                .targetType(getTargetType(operationType))
-                .result("FAILED")
-                .errorMessage(errorMessage)
-                .isSensitive(operationType.isSensitive())
-                .build();
-
-        logOperation(auditLog);
-    }
 
     /**
      * 查询用户的操作日志
@@ -296,10 +282,6 @@ public class OperationAuditLogService {
             return this;
         }
 
-        public AuditLogBuilder operationTime(LocalDateTime operationTime) {
-            this.operationTime = operationTime;
-            return this;
-        }
 
         public AuditLogBuilder module(String module) {
             this.module = module;
@@ -331,14 +313,12 @@ public class OperationAuditLogService {
             return this;
         }
 
-        public AuditLogBuilder beforeData(String beforeData) {
+        public void beforeData(String beforeData) {
             this.beforeData = beforeData;
-            return this;
         }
 
-        public AuditLogBuilder afterData(String afterData) {
+        public void afterData(String afterData) {
             this.afterData = afterData;
-            return this;
         }
 
         public AuditLogBuilder result(String result) {
@@ -346,47 +326,6 @@ public class OperationAuditLogService {
             return this;
         }
 
-        public AuditLogBuilder errorMessage(String errorMessage) {
-            this.errorMessage = errorMessage;
-            return this;
-        }
-
-        public AuditLogBuilder ipAddress(String ipAddress) {
-            this.ipAddress = ipAddress;
-            return this;
-        }
-
-        public AuditLogBuilder userAgent(String userAgent) {
-            this.userAgent = userAgent;
-            return this;
-        }
-
-        public AuditLogBuilder requestUrl(String requestUrl) {
-            this.requestUrl = requestUrl;
-            return this;
-        }
-
-        public AuditLogBuilder requestMethod(String requestMethod) {
-            this.requestMethod = requestMethod;
-            return this;
-        }
-
-        public AuditLogBuilder requestParams(String requestParams) {
-            this.requestParams = requestParams;
-            return this;
-        }
-
-        public AuditLogBuilder responseCode(Integer responseCode) {
-            this.responseCode = responseCode;
-            return this;
-        }
-
-        public AuditLogBuilder duration(Long duration) {
-            this.duration = duration;
-            return this;
-        }
-
-
         public AuditLogBuilder remark(String remark) {
             this.remark = remark;
             return this;