源本科技 | 码上会

后端业务开发 - 用户管理

2026/05/08
58
0

请求响应实体

  • com.lusifer.crmeb.pojo.request.SysUserRequest

package com.lusifer.crmeb.pojo.request;

import com.lusifer.crmeb.rule.pojo.request.BaseRequest;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Data
public class SysUserRequest extends BaseRequest {

  @NotNull(
      message = "主键不能为空",
      groups = {edit.class, delete.class, updateStatus.class})
  private Long userId;

  /** 用户名 */
  @NotBlank(
      message = "用户名不能为空",
      groups = {add.class, edit.class})
  private String username;

  /** 密码 */
  @NotBlank(
      message = "密码不能为空",
      groups = {add.class})
  private String password;

  /** 所属部门 */
  private Long deptId;

  /** 头像 */
  private String icon;

  /** 邮箱 */
  private String email;

  /** 手机号 */
  private String phone;

  /** 昵称 */
  private String nickName;

  /** 性别,0:男 1:女 2:未知 */
  private String sex;

  /** 备注信息 */
  private String note;

  /** 状态:1-启用,2-禁用 */
  private Character statusFlag;
}
  • com.lusifer.crmeb.pojo.response.SysUserVo

package com.lusifer.crmeb.pojo.response;

import com.lusifer.crmeb.entity.SysUser;
import lombok.Data;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
@Data
public class SysUserVo extends SysUser {}

业务逻辑处理

  • 修改 com.lusifer.crmeb.service.SysUserService 追加业务接口

package com.lusifer.crmeb.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.lusifer.crmeb.auth.response.SysUserLoginVo;
import com.lusifer.crmeb.auth.response.UserInfoResult;
import com.lusifer.crmeb.entity.SysUser;
import com.lusifer.crmeb.pojo.request.SysUserRequest;
import com.lusifer.crmeb.rule.pojo.page.PageResult;

/**
 * 用户表 服务类
 *
 * @author Lusifer
 * @since 2026-05-04
 */
public interface SysUserService extends IService<SysUser> {
  /**
   * 登录
   *
   * @param username 账号
   * @param password 密码
   * @return 用户登录视图对象
   */
  SysUserLoginVo login(String username, String password);

  /**
   * 获取用户信息
   *
   * @return 用户信息视图对象
   */
  UserInfoResult getInfo();

  /** 新增 */
  void add(SysUserRequest request);

  /** 删除 */
  void del(SysUserRequest request);

  /** 编辑 */
  void edit(SysUserRequest request);

  /** 详情 */
  SysUser detail(SysUserRequest request);

  /** 分页 */
  PageResult<SysUser> findPage(SysUserRequest request);
}
  • 修改 com.lusifer.crmeb.service.impl.SysUserServiceImpl 追加业务实现

package com.lusifer.crmeb.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lusifer.crmeb.auth.response.SysUserInfoVo;
import com.lusifer.crmeb.auth.response.SysUserLoginVo;
import com.lusifer.crmeb.auth.response.UserInfoResult;
import com.lusifer.crmeb.component.security.model.SysUserDetails;
import com.lusifer.crmeb.component.security.util.JwtUtils;
import com.lusifer.crmeb.component.security.util.SecurityUtils;
import com.lusifer.crmeb.config.properties.SecurityProperties;
import com.lusifer.crmeb.entity.SysUser;
import com.lusifer.crmeb.exception.base.ServiceException;
import com.lusifer.crmeb.exception.enums.SysExceptionEnum;
import com.lusifer.crmeb.mapper.SysUserMapper;
import com.lusifer.crmeb.pojo.request.SysUserRequest;
import com.lusifer.crmeb.rule.constants.SecurityConstants;
import com.lusifer.crmeb.rule.pojo.page.PageFactory;
import com.lusifer.crmeb.rule.pojo.page.PageResult;
import com.lusifer.crmeb.rule.pojo.page.PageResultFactory;
import com.lusifer.crmeb.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * 用户表 服务实现类
 *
 * @author Lusifer
 * @since 2026-05-04
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
    implements SysUserService {

  private final SecurityProperties securityProperties;
  private final AuthenticationManager authenticationManager;
  private final JwtUtils jwtUtils;

  private final PasswordEncoder passwordEncoder;

  @Override
  public SysUserLoginVo login(String username, String password) {

    SysUserLoginVo res = new SysUserLoginVo();
    String accessToken;
    Long expiration = securityProperties.getJwt().getTtl();
    // 密码需要客户端加密后传递
    try {
      // 该方法会去调用 UserDetailsServiceImpl.loadUserByUsername
      Authentication authenticate =
          authenticationManager.authenticate(
              new UsernamePasswordAuthenticationToken(username, password));
      SecurityContextHolder.getContext().setAuthentication(authenticate);

      // 认证成功后生成 JWT 令牌
      accessToken = jwtUtils.createToken(authenticate);

      res.setToken(accessToken);
      res.setExpiration(expiration);
      res.setTokenType(CharSequenceUtil.trim(SecurityConstants.JWT_TOKEN_PREFIX));

      return res;
    } catch (Exception e) {
      log.error("登录异常:{}", e.getMessage());
      throw new ServiceException(SysExceptionEnum.ERROR_USERNAME_OR_PASSWORD);
    }
  }

  @Override
  public UserInfoResult getInfo() {
    SysUserDetails sysUserDetails = SecurityUtils.getSysUserDetails();
    SysUser user = sysUserDetails.getSysUser();
    UserInfoResult result = new UserInfoResult();
    result.setUsername(user.getUsername());
    result.setPermissions(sysUserDetails.getPermissions());
    result.setRoles(sysUserDetails.getRoles());
    result.setUserInfo(BeanUtil.copyProperties(user, SysUserInfoVo.class));
    return result;
  }

  @Override
  public void add(SysUserRequest request) {
    SysUser sysUser = new SysUser();
    BeanUtil.copyProperties(request, sysUser);

    // 密码加密
    String password = passwordEncoder.encode(request.getPassword());
    sysUser.setPassword(password);

    this.save(sysUser);
  }

  @Override
  public void del(SysUserRequest request) {
    SysUser sysUser = this.querySysUser(request);
    this.removeById(sysUser.getUserId());
  }

  @Override
  public void edit(SysUserRequest request) {
    SysUser sysUser = this.querySysUser(request);
    BeanUtil.copyProperties(request, sysUser);

    // 只有当密码不为空时才进行加密和更新
    if (ObjectUtil.isNotEmpty(request.getPassword())) {
      String password = passwordEncoder.encode(request.getPassword());
      sysUser.setPassword(password);
    }

    this.updateById(sysUser);
  }

  @Override
  public SysUser detail(SysUserRequest request) {
    return this.querySysUser(request);
  }

  @Override
  public PageResult<SysUser> findPage(SysUserRequest request) {
    LambdaQueryWrapper<SysUser> wrapper = createWrapper(request);
    Page<SysUser> page = this.page(PageFactory.defaultPage(), wrapper);
    return PageResultFactory.createPageResult(page);
  }

  /** 获取详情 */
  private SysUser querySysUser(SysUserRequest request) {
    SysUser sysUser = this.getById(request.getUserId());
    if (ObjectUtil.isEmpty(sysUser)) {
      throw new ServiceException("SysUser", "0001", "用户不存在");
    }
    return sysUser;
  }

  /** 构造查询条件 */
  private LambdaQueryWrapper<SysUser> createWrapper(SysUserRequest request) {
    LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();

    // 根据查询条件搜索
    String searchText = request.getSearchText();
    if (ObjectUtil.isNotEmpty(searchText)){
      queryWrapper.like(SysUser::getUsername, searchText);
      queryWrapper.or().like(SysUser::getNickName, searchText);
    }

    // 排序条件
    queryWrapper.orderByDesc(SysUser::getCreateTime);

    return queryWrapper;
  }
}

编写控制器代码

  • 修改 com.lusifer.crmeb.controller.SysUserController

package com.lusifer.crmeb.controller;

import com.lusifer.crmeb.entity.SysUser;
import com.lusifer.crmeb.pojo.request.SysUserRequest;
import com.lusifer.crmeb.rule.pojo.page.PageResult;
import com.lusifer.crmeb.rule.pojo.request.BaseRequest;
import com.lusifer.crmeb.rule.pojo.response.ResponseData;
import com.lusifer.crmeb.rule.pojo.response.SuccessResponseData;
import com.lusifer.crmeb.service.SysUserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

/**
 * 用户表 前端控制器
 *
 * @author Lusifer
 * @since 2026-05-04
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/sys/user")
@Tag(name = "用户管理")
public class SysUserController {

  private final SysUserService sysUserService;

  /** 新增用户 */
  @PostMapping(name = "新增用户", path = "/add")
  public ResponseData<?> add(
      @RequestBody @Validated(BaseRequest.add.class) SysUserRequest request) {
    sysUserService.add(request);
    return new SuccessResponseData<>();
  }

  /** 删除用户 */
  @PostMapping(name = "删除用户", path = "/delete")
  public ResponseData<?> delete(
      @RequestBody @Validated(BaseRequest.delete.class) SysUserRequest request) {
    sysUserService.del(request);
    return new SuccessResponseData<>();
  }

  /** 编辑用户 */
  @PostMapping(name = "编辑用户", path = "/edit")
  public ResponseData<?> edit(
      @RequestBody @Validated(BaseRequest.edit.class) SysUserRequest request) {
    sysUserService.edit(request);
    return new SuccessResponseData<>();
  }

  /** 查看 */
  @PostMapping(name = "查看用户", path = "/detail")
  public ResponseData<SysUser> detail(
      @RequestBody @Validated(BaseRequest.detail.class) SysUserRequest request) {
    return new SuccessResponseData<>(sysUserService.detail(request));
  }

  /** 分页 */
  @GetMapping(name = "用户分页", path = "/page")
  public ResponseData<PageResult<SysUser>> page(SysUserRequest request) {
    return new SuccessResponseData<>(sysUserService.findPage(request));
  }
}

追加自动填充

注意:上述代码开发完成后运行会报错,因为通用字段的值没有填充,放在此时编写是因为我们需要开发完认证授权后获取到当前登录用户的信息以自动填充 create_userupdate_user 字段

  • com.lusifer.crmeb.rule.constants.DbFieldConstants

package com.lusifer.crmeb.rule.constants;

/** 数据库常用字段的枚举 */
public interface DbFieldConstants {

  /** 主键id的字段名 */
  String ID = "id";

  /** 创建用户的字段名 */
  String CREATE_USER = "createUser";

  /** 创建时间的字段名 */
  String CREATE_TIME = "createTime";

  /** 更新用户的字段名 */
  String UPDATE_USER = "updateUser";

  /** 更新时间的字段名 */
  String UPDATE_TIME = "updateTime";

  /** 删除标记的字段名 */
  String DEL_FLAG = "delFlag";

  /** 数据状态的字段 状态:1-启用,2-禁用 */
  String STATUS_FLAG = "statusFlag";

  /** 乐观锁版本,从0开始 */
  String VERSION_FLAG = "versionFlag";
}
  • com.lusifer.crmeb.config.CustomMetaObjectHandler

package com.lusifer.crmeb.config;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.lusifer.crmeb.component.security.util.SecurityUtils;
import com.lusifer.crmeb.rule.constants.DbFieldConstants;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;

/** 字段自动填充工具,在 mybatis-plus 执行更新和新增操作时候,会对指定字段进行自动填充,例如 create_time 等字段 */
@Slf4j
public class CustomMetaObjectHandler implements MetaObjectHandler {

  @Override
  public void insertFill(MetaObject metaObject) {
    // 设置 createUser
    setValue(metaObject, DbFieldConstants.CREATE_USER, SecurityUtils.getUserId());

    // 设置 createTime
    setValue(metaObject, DbFieldConstants.CREATE_TIME, new Date());

    // 设置删除标记,默认 N 表示未删除
    setValue(metaObject, DbFieldConstants.DEL_FLAG, "N");

    // 设置状态字段 默认 1 表示启用
    setValue(metaObject, DbFieldConstants.STATUS_FLAG, '1');

    // 设置乐观锁字段,从 0 开始
    setValue(metaObject, DbFieldConstants.VERSION_FLAG, 0L);
  }

  @Override
  public void updateFill(MetaObject metaObject) {
    // 设置 updateUser
    if (metaObject.hasSetter(DbFieldConstants.UPDATE_USER)) {
      metaObject.setValue(DbFieldConstants.UPDATE_USER, SecurityUtils.getUserId());
    }

    // 设置 updateTime
    if (metaObject.hasSetter(DbFieldConstants.UPDATE_TIME)) {
      metaObject.setValue(DbFieldConstants.UPDATE_TIME, new Date());
    }
  }

  /** 设置属性 */
  protected void setValue(MetaObject metaObject, String fieldName, Object value) {
    Object originalAttr = getFieldValByName(fieldName, metaObject);
    if (ObjectUtil.isEmpty(originalAttr)) {
      setFieldValByName(fieldName, value, metaObject);
    }
  }
}
  • 修改 com.lusifer.crmeb.config.ProjectMyBatisPlusAutoConfiguration 注入自动填充

package com.lusifer.crmeb.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/** MyBatis Plus 插件配置 */
@Configuration
@AutoConfigureBefore(MybatisPlusAutoConfiguration.class)
public class ProjectMyBatisPlusAutoConfiguration {

  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

    // 使用分页插插件
    interceptor.addInnerInterceptor(paginationInterceptor());

    // 使用乐观锁插件
    interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());

    return interceptor;
  }

  /** 分页插件 */
  @Bean
  public PaginationInnerInterceptor paginationInterceptor() {
    return new PaginationInnerInterceptor(DbType.MYSQL);
  }

  /** 乐观锁插件 */
  @Bean
  public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
    return new OptimisticLockerInnerInterceptor();
  }

  /** 公共字段填充插件 */
  @Bean
  @ConditionalOnMissingBean(MetaObjectHandler.class)
  public MetaObjectHandler metaObjectHandler() {
    return new CustomMetaObjectHandler();
  }
}

测试运行

上述操作完成后通过 ApiPost 进行测试