瀏覽代碼

【新增】system user 和 role 接入新操作日志

YunaiV 11 月之前
父節點
當前提交
87c7b3fac2
共有 14 個文件被更改,包括 232 次插入108 次删除
  1. 20 0
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java
  2. 33 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java
  3. 2 10
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java
  4. 8 3
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java
  5. 0 23
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java
  6. 15 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java
  7. 5 4
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java
  8. 2 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java
  9. 6 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java
  10. 46 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java
  11. 3 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java
  12. 0 8
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java
  13. 38 29
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
  14. 54 23
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java

+ 20 - 0
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/util/DataPermissionUtils.java

@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
 import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
 import lombok.SneakyThrows;
 
+import java.util.concurrent.Callable;
+
 /**
  * 数据权限 Util
  *
@@ -40,4 +42,22 @@ public class DataPermissionUtils {
         }
     }
 
+    /**
+     * 忽略数据权限,执行对应的逻辑
+     *
+     * @param callable 逻辑
+     * @return 执行结果
+     */
+    @SneakyThrows
+    public static <T> T executeIgnore(Callable<T> callable) {
+        DataPermission dataPermission = getDisableDataPermissionDisable();
+        DataPermissionContextHolder.add(dataPermission);
+        try {
+            // 执行 callable
+            return callable.call();
+        } finally {
+            DataPermissionContextHolder.remove();
+        }
+    }
+
 }

+ 33 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.system.enums;
+
+/**
+ * System 操作日志枚举
+ * 目的:统一管理,也减少 Service 里各种“复杂”字符串
+ *
+ * @author 芋道源码
+ */
+public interface LogRecordConstants {
+
+    // ======================= SYSTEM_USER 用户 =======================
+
+    String SYSTEM_USER_TYPE = "SYSTEM 用户";
+    String SYSTEM_USER_CREATE_SUB_TYPE = "创建用户";
+    String SYSTEM_USER_CREATE_SUCCESS = "创建了用户【{{#user.nickname}}】";
+    String SYSTEM_USER_UPDATE_SUB_TYPE = "更新用户";
+    String SYSTEM_USER_UPDATE_SUCCESS = "更新了用户【{{#user.nickname}}】: {_DIFF{#updateReqVO}}";
+    String SYSTEM_USER_DELETE_SUB_TYPE = "删除用户";
+    String SYSTEM_USER_DELETE_SUCCESS = "删除了用户【{{#user.nickname}}】";
+    String SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE = "重置用户密码";
+    String SYSTEM_USER_UPDATE_PASSWORD_SUCCESS = "将用户【{{#user.nickname}}】的密码从【{{#user.password}}】重置为【{{#newPassword}}】";
+
+    // ======================= SYSTEM_ROLE 角色 =======================
+
+    String SYSTEM_ROLE_TYPE = "SYSTEM 角色";
+    String SYSTEM_ROLE_CREATE_SUB_TYPE = "创建角色";
+    String SYSTEM_ROLE_CREATE_SUCCESS = "创建了角色【{{#role.name}}】";
+    String SYSTEM_ROLE_UPDATE_SUB_TYPE = "更新角色";
+    String SYSTEM_ROLE_UPDATE_SUCCESS = "更新了角色【{{#role.name}}】: {_DIFF{#updateReqVO}}";
+    String SYSTEM_ROLE_DELETE_SUB_TYPE = "删除角色";
+    String SYSTEM_ROLE_DELETE_SUCCESS = "删除了角色【{{#role.name}}】";
+
+}

+ 2 - 10
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java

@@ -52,14 +52,6 @@ public class RoleController {
         return success(true);
     }
 
-    @PutMapping("/update-status")
-    @Operation(summary = "修改角色状态")
-    @PreAuthorize("@ss.hasPermission('system:role:update')")
-    public CommonResult<Boolean> updateRoleStatus(@Valid @RequestBody RoleUpdateStatusReqVO reqVO) {
-        roleService.updateRoleStatus(reqVO.getId(), reqVO.getStatus());
-        return success(true);
-    }
-
     @DeleteMapping("/delete")
     @Operation(summary = "删除角色")
     @Parameter(name = "id", description = "角色编号", required = true, example = "1024")
@@ -87,10 +79,10 @@ public class RoleController {
 
     @GetMapping({"/list-all-simple", "/simple-list"})
     @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项")
-    public CommonResult<List<RoleSimpleRespVO>> getSimpleRoleList() {
+    public CommonResult<List<RoleRespVO>> getSimpleRoleList() {
         List<RoleDO> list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus()));
         list.sort(Comparator.comparing(RoleDO::getSort));
-        return success(BeanUtils.toBean(list, RoleSimpleRespVO.class));
+        return success(BeanUtils.toBean(list, RoleRespVO.class));
     }
 
     @GetMapping("/export-excel")

+ 8 - 3
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role;
 
+import com.mzt.logapi.starter.annotation.DiffLogField;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
@@ -7,7 +8,7 @@ import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
-@Schema(description = "管理后台 - 角色创建 Request VO")
+@Schema(description = "管理后台 - 角色创建/更新 Request VO")
 @Data
 public class RoleSaveReqVO {
 
@@ -16,19 +17,23 @@ public class RoleSaveReqVO {
 
     @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员")
     @NotBlank(message = "角色名称不能为空")
-    @Size(max = 30, message = "角色名称长度不能超过30个字符")
+    @Size(max = 30, message = "角色名称长度不能超过 30 个字符")
+    @DiffLogField(name = "角色名称")
     private String name;
 
     @NotBlank(message = "角色标志不能为空")
-    @Size(max = 100, message = "角色标志长度不能超过100个字符")
+    @Size(max = 100, message = "角色标志长度不能超过 100 个字符")
     @Schema(description = "角色编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ADMIN")
+    @DiffLogField(name = "角色标志")
     private String code;
 
     @Schema(description = "显示顺序不能为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @NotNull(message = "显示顺序不能为空")
+    @DiffLogField(name = "显示顺序")
     private Integer sort;
 
     @Schema(description = "备注", example = "我是一个角色")
+    @DiffLogField(name = "备注")
     private String remark;
 
 }

+ 0 - 23
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleUpdateStatusReqVO.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import jakarta.validation.constraints.NotNull;
-
-@Schema(description = "管理后台 - 角色更新状态 Request VO")
-@Data
-public class RoleUpdateStatusReqVO {
-
-    @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
-    @NotNull(message = "角色编号不能为空")
-    private Long id;
-
-    @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @NotNull(message = "状态不能为空")
-    @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
-    private Integer status;
-
-}

+ 15 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java

@@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.validation.Mobile;
+import cn.iocoder.yudao.module.system.framework.operatelog.core.DeptParseFunction;
+import cn.iocoder.yudao.module.system.framework.operatelog.core.PostParseFunction;
+import cn.iocoder.yudao.module.system.framework.operatelog.core.SexParseFunction;
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.mzt.logapi.starter.annotation.DiffLogField;
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.*;
 import lombok.Data;
 import org.hibernate.validator.constraints.Length;
 
-import jakarta.validation.constraints.*;
 import java.util.Set;
 
 @Schema(description = "管理后台 - 用户创建/修改 Request VO")
@@ -21,34 +25,43 @@ public class UserSaveReqVO {
     @NotBlank(message = "用户账号不能为空")
     @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成")
     @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符")
+    @DiffLogField(name = "用户账号")
     private String username;
 
     @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
     @Size(max = 30, message = "用户昵称长度不能超过30个字符")
+    @DiffLogField(name = "用户昵称")
     private String nickname;
 
     @Schema(description = "备注", example = "我是一个用户")
+    @DiffLogField(name = "备注")
     private String remark;
 
-    @Schema(description = "部门ID", example = "我是一个用户")
+    @Schema(description = "部门编号", example = "我是一个用户")
+    @DiffLogField(name = "部门", function = DeptParseFunction.NAME)
     private Long deptId;
 
     @Schema(description = "岗位编号数组", example = "1")
+    @DiffLogField(name = "岗位", function = PostParseFunction.NAME)
     private Set<Long> postIds;
 
     @Schema(description = "用户邮箱", example = "yudao@iocoder.cn")
     @Email(message = "邮箱格式不正确")
     @Size(max = 50, message = "邮箱长度不能超过 50 个字符")
+    @DiffLogField(name = "用户邮箱")
     private String email;
 
     @Schema(description = "手机号码", example = "15601691300")
     @Mobile
+    @DiffLogField(name = "手机号码")
     private String mobile;
 
     @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1")
+    @DiffLogField(name = "用户性别", function = SexParseFunction.NAME)
     private Integer sex;
 
     @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
+    @DiffLogField(name = "用户头像")
     private String avatar;
 
     // ========== 仅【创建】时,需要传递的字段 ==========

+ 5 - 4
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java

@@ -1,8 +1,9 @@
 package cn.iocoder.yudao.module.system.framework.operatelog.core;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import com.mzt.logapi.service.IParseFunction;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
@@ -20,7 +21,7 @@ public class AdminUserParseFunction implements IParseFunction {
     public static final String NAME = "getAdminUserById";
 
     @Resource
-    private AdminUserApi adminUserApi;
+    private AdminUserService adminUserService;
 
     @Override
     public String functionName() {
@@ -34,7 +35,7 @@ public class AdminUserParseFunction implements IParseFunction {
         }
 
         // 获取用户信息
-        AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
+        AdminUserDO user = adminUserService.getUser(Convert.toLong(value));
         if (user == null) {
             log.warn("[apply][获取用户{{}}为空", value);
             return "";

+ 2 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.system.framework.operatelog.core;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
 import com.mzt.logapi.service.IParseFunction;
@@ -32,7 +33,7 @@ public class AreaParseFunction implements IParseFunction {
         if (StrUtil.isEmptyIfStr(value)) {
             return "";
         }
-        return AreaUtils.format(Integer.parseInt(value.toString()));
+        return AreaUtils.format(Convert.toInt(value));
     }
 
 }

+ 6 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java

@@ -1,15 +1,16 @@
 package cn.iocoder.yudao.module.system.framework.operatelog.core;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.system.api.dept.DeptApi;
-import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import com.mzt.logapi.service.IParseFunction;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
 /**
- * 管理员名字的 {@link IParseFunction} 实现类
+ * 部门名字的 {@link IParseFunction} 实现类
  *
  * @author HUIHUI
  */
@@ -20,7 +21,7 @@ public class DeptParseFunction implements IParseFunction {
     public static final String NAME = "getDeptById";
 
     @Resource
-    private DeptApi deptApi;
+    private DeptService deptService;
 
     @Override
     public String functionName() {
@@ -34,7 +35,7 @@ public class DeptParseFunction implements IParseFunction {
         }
 
         // 获取部门信息
-        DeptRespDTO dept = deptApi.getDept(Long.parseLong(value.toString()));
+        DeptDO dept = deptService.getDept(Convert.toLong(value));
         if (dept == null) {
             log.warn("[apply][获取部门{{}}为空", value);
             return "";

+ 46 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.system.framework.operatelog.core;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
+import cn.iocoder.yudao.module.system.service.dept.PostService;
+import com.mzt.logapi.service.IParseFunction;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 岗位名字的 {@link IParseFunction} 实现类
+ *
+ * @author HUIHUI
+ */
+@Slf4j
+@Component
+public class PostParseFunction implements IParseFunction {
+
+    public static final String NAME = "getPostById";
+
+    @Resource
+    private PostService postService;
+
+    @Override
+    public String functionName() {
+        return NAME;
+    }
+
+    @Override
+    public String apply(Object value) {
+        if (StrUtil.isEmptyIfStr(value)) {
+            return "";
+        }
+
+        // 获取岗位信息
+        PostDO post = postService.getPost(Convert.toLong(value));
+        if (post == null) {
+            log.warn("[apply][获取岗位{{}}为空", value);
+            return "";
+        }
+        return post.getName();
+    }
+
+}

+ 3 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java

@@ -1 +1,4 @@
+/**
+ * 占位文件,避免文件夹缩进
+ */
 package cn.iocoder.yudao.module.system.framework.operatelog;

+ 0 - 8
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java

@@ -40,14 +40,6 @@ public interface RoleService {
      */
     void deleteRole(Long id);
 
-    /**
-     * 更新角色状态
-     *
-     * @param id 角色编号
-     * @param status 状态
-     */
-    void updateRoleStatus(Long id, Integer status);
-
     /**
      * 设置角色的数据权限
      *

+ 38 - 29
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java

@@ -17,6 +17,8 @@ import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
 import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
 import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
 import com.google.common.annotations.VisibleForTesting;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.starter.annotation.LogRecord;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
@@ -30,6 +32,7 @@ import java.util.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 
 /**
  * 角色 Service 实现类
@@ -48,41 +51,40 @@ public class RoleServiceImpl implements RoleService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}",
+            success = SYSTEM_ROLE_CREATE_SUCCESS)
     public Long createRole(RoleSaveReqVO createReqVO, Integer type) {
-        // 校验角色
+        // 1. 校验角色
         validateRoleDuplicate(createReqVO.getName(), createReqVO.getCode(), null);
-        // 插入到数据库
-        RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class);
-        role.setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType()));
-        role.setStatus(CommonStatusEnum.ENABLE.getStatus());
-        role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限
+
+        // 2. 插入到数据库
+        RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class)
+                .setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType()))
+                .setStatus(CommonStatusEnum.ENABLE.getStatus())
+                .setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限
         roleMapper.insert(role);
-        // 返回
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("role", role);
         return role.getId();
     }
 
     @Override
     @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id")
+    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
+            success = SYSTEM_ROLE_UPDATE_SUCCESS)
     public void updateRole(RoleSaveReqVO updateReqVO) {
-        // 校验是否可以更新
-        validateRoleForUpdate(updateReqVO.getId());
-        // 校验角色的唯一字段是否重复
+        // 1.1 校验是否可以更新
+        RoleDO role = validateRoleForUpdate(updateReqVO.getId());
+        // 1.2 校验角色的唯一字段是否重复
         validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getId());
 
-        // 更新到数据库
+        // 2. 更新到数据库
         RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class);
         roleMapper.updateById(updateObj);
-    }
-
-    @Override
-    @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
-    public void updateRoleStatus(Long id, Integer status) {
-        // 校验是否可以更新
-        validateRoleForUpdate(id);
 
-        // 更新状态
-        RoleDO updateObj = new RoleDO().setId(id).setStatus(status);
-        roleMapper.updateById(updateObj);
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("role", role);
     }
 
     @Override
@@ -102,13 +104,19 @@ public class RoleServiceImpl implements RoleService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id")
+    @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}",
+            success = SYSTEM_ROLE_DELETE_SUCCESS)
     public void deleteRole(Long id) {
-        // 校验是否可以更新
-        validateRoleForUpdate(id);
-        // 标记删除
+        // 1. 校验是否可以更新
+        RoleDO role = validateRoleForUpdate(id);
+
+        // 2.1 标记删除
         roleMapper.deleteById(id);
-        // 删除相关数据
+        // 2.2 删除相关数据
         permissionService.processRoleDeleted(id);
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("role", role);
     }
 
     /**
@@ -149,15 +157,16 @@ public class RoleServiceImpl implements RoleService {
      * @param id 角色编号
      */
     @VisibleForTesting
-    void validateRoleForUpdate(Long id) {
-        RoleDO roleDO = roleMapper.selectById(id);
-        if (roleDO == null) {
+    RoleDO validateRoleForUpdate(Long id) {
+        RoleDO role = roleMapper.selectById(id);
+        if (role == null) {
             throw exception(ROLE_NOT_EXISTS);
         }
         // 内置角色,不允许删除
-        if (RoleTypeEnum.SYSTEM.getType().equals(roleDO.getType())) {
+        if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) {
             throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
         }
+        return role;
     }
 
     @Override

+ 54 - 23
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java

@@ -27,6 +27,10 @@ import cn.iocoder.yudao.module.system.service.dept.PostService;
 import cn.iocoder.yudao.module.system.service.permission.PermissionService;
 import cn.iocoder.yudao.module.system.service.tenant.TenantService;
 import com.google.common.annotations.VisibleForTesting;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Lazy;
@@ -34,7 +38,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import jakarta.annotation.Resource;
 import java.io.InputStream;
 import java.time.LocalDateTime;
 import java.util.*;
@@ -43,6 +46,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 
 /**
  * 后台用户 Service 实现类
@@ -79,42 +83,54 @@ public class AdminUserServiceImpl implements AdminUserService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}",
+            success = SYSTEM_USER_CREATE_SUCCESS)
     public Long createUser(UserSaveReqVO createReqVO) {
-        // 校验账户配合
+        // 1.1 校验账户配合
         tenantService.handleTenantInfo(tenant -> {
             long count = userMapper.selectCount();
             if (count >= tenant.getAccountCount()) {
                 throw exception(USER_COUNT_MAX, tenant.getAccountCount());
             }
         });
-        // 校验正确性
+        // 1.2 校验正确性
         validateUserForCreateOrUpdate(null, createReqVO.getUsername(),
                 createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds());
-        // 插入用户
+        // 2.1 插入用户
         AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class);
         user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
         user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
         userMapper.insert(user);
-        // 插入关联岗位
+        // 2.2 插入关联岗位
         if (CollectionUtil.isNotEmpty(user.getPostIds())) {
             userPostMapper.insertBatch(convertList(user.getPostIds(),
                     postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId)));
         }
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("user", user);
         return user.getId();
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
+            success = SYSTEM_USER_UPDATE_SUCCESS)
     public void updateUser(UserSaveReqVO updateReqVO) {
         updateReqVO.setPassword(null); // 特殊:此处不更新密码
-        // 校验正确性
-        validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
+        // 1. 校验正确性
+        AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
                 updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds());
-        // 更新用户
+
+        // 2.1 更新用户
         AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
         userMapper.updateById(updateObj);
-        // 更新岗位
+        // 2.2 更新岗位
         updateUserPost(updateReqVO, updateObj);
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class));
+        LogRecordContext.putVariable("user", oldUser);
     }
 
     private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) {
@@ -124,7 +140,7 @@ public class AdminUserServiceImpl implements AdminUserService {
         Set<Long> postIds = CollUtil.emptyIfNull(updateObj.getPostIds());
         Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
         Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);
-        // 执行新增和删除。对于已经授权的菜单,不用做任何处理
+        // 执行新增和删除。对于已经授权的岗位,不用做任何处理
         if (!CollectionUtil.isEmpty(createPostIds)) {
             userPostMapper.insertBatch(convertList(createPostIds,
                     postId -> new UserPostDO().setUserId(userId).setPostId(postId)));
@@ -173,14 +189,21 @@ public class AdminUserServiceImpl implements AdminUserService {
     }
 
     @Override
+    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}",
+            success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS)
     public void updateUserPassword(Long id, String password) {
-        // 校验用户存在
-        validateUserExists(id);
-        // 更新密码
+        // 1. 校验用户存在
+        AdminUserDO user = validateUserExists(id);
+
+        // 2. 更新密码
         AdminUserDO updateObj = new AdminUserDO();
         updateObj.setId(id);
         updateObj.setPassword(encodePassword(password)); // 加密密码
         userMapper.updateById(updateObj);
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("user", user);
+        LogRecordContext.putVariable("newPassword", updateObj.getPassword());
     }
 
     @Override
@@ -196,15 +219,21 @@ public class AdminUserServiceImpl implements AdminUserService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
+            success = SYSTEM_USER_DELETE_SUCCESS)
     public void deleteUser(Long id) {
-        // 校验用户存在
-        validateUserExists(id);
-        // 删除用户
+        // 1. 校验用户存在
+        AdminUserDO user = validateUserExists(id);
+
+        // 2.1 删除用户
         userMapper.deleteById(id);
-        // 删除用户关联数据
+        // 2.2 删除用户关联数据
         permissionService.processUserDeleted(id);
-        // 删除用户岗位
+        // 2.2 删除用户岗位
         userPostMapper.deleteByUserId(id);
+
+        // 3. 记录操作日志上下文
+        LogRecordContext.putVariable("user", user);
     }
 
     @Override
@@ -294,12 +323,12 @@ public class AdminUserServiceImpl implements AdminUserService {
         return deptIds;
     }
 
-    private void validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
+    private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
                                                Long deptId, Set<Long> postIds) {
         // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
-        DataPermissionUtils.executeIgnore(() -> {
+        return DataPermissionUtils.executeIgnore(() -> {
             // 校验用户存在
-            validateUserExists(id);
+            AdminUserDO user = validateUserExists(id);
             // 校验用户名唯一
             validateUsernameUnique(id, username);
             // 校验手机号唯一
@@ -310,18 +339,20 @@ public class AdminUserServiceImpl implements AdminUserService {
             deptService.validateDeptList(CollectionUtils.singleton(deptId));
             // 校验岗位处于开启状态
             postService.validatePostList(postIds);
+            return user;
         });
     }
 
     @VisibleForTesting
-    void validateUserExists(Long id) {
+    AdminUserDO validateUserExists(Long id) {
         if (id == null) {
-            return;
+            return null;
         }
         AdminUserDO user = userMapper.selectById(id);
         if (user == null) {
             throw exception(USER_NOT_EXISTS);
         }
+        return user;
     }
 
     @VisibleForTesting