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

This commit is contained in:
YunaiV 2024-04-04 09:48:29 +08:00
parent 188f98eaa2
commit 911790ad16
14 changed files with 233 additions and 109 deletions

View File

@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder; import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import java.util.concurrent.Callable;
/** /**
* 数据权限 Util * 数据权限 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();
}
}
} }

View File

@ -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}}】";
}

View File

@ -52,14 +52,6 @@ public class RoleController {
return success(true); 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") @DeleteMapping("/delete")
@Operation(summary = "删除角色") @Operation(summary = "删除角色")
@Parameter(name = "id", description = "角色编号", required = true, example = "1024") @Parameter(name = "id", description = "角色编号", required = true, example = "1024")
@ -87,10 +79,10 @@ public class RoleController {
@GetMapping({"/list-all-simple", "/simple-list"}) @GetMapping({"/list-all-simple", "/simple-list"})
@Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项") @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项")
public CommonResult<List<RoleSimpleRespVO>> getSimpleRoleList() { public CommonResult<List<RoleRespVO>> getSimpleRoleList() {
List<RoleDO> list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); List<RoleDO> list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus()));
list.sort(Comparator.comparing(RoleDO::getSort)); list.sort(Comparator.comparing(RoleDO::getSort));
return success(BeanUtils.toBean(list, RoleSimpleRespVO.class)); return success(BeanUtils.toBean(list, RoleRespVO.class));
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}

View File

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

View File

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

View File

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

View File

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