package com.sinata.appointment.service;
|
|
import cn.dev33.satoken.stp.StpUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.sinata.appointment.constants.BizErrorCode;
|
import com.sinata.appointment.dto.ResetPasswordRequest;
|
import com.sinata.appointment.dto.SysUserCreateRequest;
|
import com.sinata.appointment.dto.SysUserDetailVo;
|
import com.sinata.appointment.dto.SysUserListVo;
|
import com.sinata.appointment.dto.SysUserUpdateRequest;
|
import com.sinata.appointment.dto.UserRoleLabelRow;
|
import com.sinata.appointment.dto.UserStatusPatchRequest;
|
import com.sinata.appointment.entity.SysRole;
|
import com.sinata.appointment.entity.SysUser;
|
import com.sinata.appointment.entity.SysUserRole;
|
import com.sinata.appointment.exception.BizException;
|
import com.sinata.appointment.mapper.SysRoleMapper;
|
import com.sinata.appointment.mapper.SysUserMapper;
|
import com.sinata.appointment.mapper.SysUserRoleMapper;
|
import com.sinata.appointment.util.MobileMaskUtil;
|
import com.sinata.appointment.util.UuidAssert;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.stream.Collectors;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
@Service
|
@RequiredArgsConstructor
|
public class SysUserApplicationService {
|
|
private final SysUserMapper sysUserMapper;
|
private final SysRoleMapper sysRoleMapper;
|
private final SysUserRoleMapper sysUserRoleMapper;
|
private final BCryptPasswordEncoder bcrypt;
|
|
public IPage<SysUserListVo> pageUsers(
|
long page,
|
long size,
|
String realName,
|
String mobile,
|
Long roleId,
|
Integer status) {
|
LambdaQueryWrapper<SysUser> q = new LambdaQueryWrapper<>();
|
q.eq(SysUser::getDelFlag, 0);
|
if (StringUtils.hasText(realName)) {
|
q.like(SysUser::getRealName, realName.trim());
|
}
|
if (StringUtils.hasText(mobile)) {
|
q.like(SysUser::getMobile, mobile.trim());
|
}
|
if (roleId != null) {
|
q.apply(
|
"id IN (SELECT user_id FROM sys_user_role WHERE role_id = {0} AND del_flag = 0)",
|
roleId);
|
}
|
if (status != null) {
|
q.eq(SysUser::getStatus, status);
|
}
|
q.orderByDesc(SysUser::getCreateTime);
|
Page<SysUser> p = sysUserMapper.selectPage(new Page<>(page, size), q);
|
|
List<Long> userIds = p.getRecords().stream().map(SysUser::getId).toList();
|
Map<Long, String> roleLine = roleDisplayByUserIds(userIds);
|
|
return p.convert(
|
u -> {
|
SysUserListVo vo = new SysUserListVo();
|
vo.setId(u.getId());
|
vo.setUuid(u.getUuid());
|
vo.setUsername(u.getUsername());
|
vo.setRealName(u.getRealName());
|
vo.setMobileMasked(MobileMaskUtil.mask(u.getMobile()));
|
vo.setRolesDisplay(roleLine.getOrDefault(u.getId(), "—"));
|
vo.setStatus(u.getStatus());
|
vo.setCreateTime(u.getCreateTime());
|
return vo;
|
});
|
}
|
|
private Map<Long, String> roleDisplayByUserIds(List<Long> userIds) {
|
if (userIds.isEmpty()) {
|
return Map.of();
|
}
|
List<UserRoleLabelRow> rows = sysUserMapper.listRoleLabelsByUserIds(userIds);
|
return rows.stream()
|
.collect(
|
Collectors.groupingBy(
|
UserRoleLabelRow::getUserId,
|
Collectors.mapping(
|
UserRoleLabelRow::getRoleName,
|
Collectors.joining("、"))));
|
}
|
|
public SysUserDetailVo getDetail(Long id, String uuid) {
|
SysUser u = loadActiveWithUuid(id, uuid);
|
SysUserDetailVo vo = new SysUserDetailVo();
|
vo.setId(u.getId());
|
vo.setUuid(u.getUuid());
|
vo.setUsername(u.getUsername());
|
vo.setRealName(u.getRealName());
|
vo.setMobile(u.getMobile());
|
vo.setAvatarUrl(u.getAvatarUrl());
|
vo.setStatus(u.getStatus());
|
vo.setRoleIds(listRoleIds(id));
|
return vo;
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public Map<String, Object> createUser(SysUserCreateRequest req) {
|
long exists =
|
sysUserMapper.selectCount(
|
new LambdaQueryWrapper<SysUser>()
|
.eq(SysUser::getUsername, req.getUsername().trim())
|
.eq(SysUser::getDelFlag, 0));
|
if (exists > 0) {
|
throw new BizException(BizErrorCode.DUPLICATE_USERNAME, "登录账号已存在");
|
}
|
validateRoleIds(req.getRoleIds());
|
|
SysUser u = new SysUser();
|
u.setUsername(req.getUsername().trim());
|
u.setPasswordHash(bcrypt.encode(req.getPassword()));
|
u.setRealName(trimToNull(req.getRealName()));
|
u.setMobile(trimToNull(req.getMobile()));
|
u.setAvatarUrl(trimToNull(req.getAvatarUrl()));
|
u.setStatus(1);
|
sysUserMapper.insert(u);
|
SysUser fresh = sysUserMapper.selectById(u.getId());
|
if (fresh == null || !StringUtils.hasText(fresh.getUuid())) {
|
throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "创建后读取用户失败");
|
}
|
replaceRoles(fresh.getId(), req.getRoleIds());
|
Map<String, Object> m = new HashMap<>();
|
m.put("id", fresh.getId());
|
m.put("uuid", fresh.getUuid());
|
return m;
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void updateUser(Long id, SysUserUpdateRequest req) {
|
SysUser u = loadActiveWithUuid(id, req.getUuid());
|
validateRoleIds(req.getRoleIds());
|
u.setRealName(trimToNull(req.getRealName()));
|
u.setMobile(trimToNull(req.getMobile()));
|
u.setAvatarUrl(trimToNull(req.getAvatarUrl()));
|
sysUserMapper.updateById(u);
|
replaceRoles(id, req.getRoleIds());
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void resetPassword(Long id, ResetPasswordRequest req) {
|
if (!Objects.equals(req.getNewPassword(), req.getNewPasswordConfirm())) {
|
throw new BizException(BizErrorCode.PASSWORD_CONFIRM_MISMATCH, "两次输入的新密码不一致");
|
}
|
SysUser u = loadActiveWithUuid(id, req.getUuid());
|
u.setPasswordHash(bcrypt.encode(req.getNewPassword()));
|
sysUserMapper.updateById(u);
|
kickSysUserOffline(id);
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void patchStatus(Long id, UserStatusPatchRequest req, long operatorUserId) {
|
int st = req.getStatus();
|
if (st != 0 && st != 1) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "状态无效");
|
}
|
if (st == 0 && id == operatorUserId) {
|
throw new BizException(BizErrorCode.USER_SELF_FORBIDDEN, "不能禁用自己的账号");
|
}
|
SysUser u = loadActiveWithUuid(id, req.getUuid());
|
u.setStatus(st);
|
sysUserMapper.updateById(u);
|
if (st == 0) {
|
kickSysUserOffline(id);
|
}
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteUser(Long id, String uuid, long operatorUserId) {
|
if (id == operatorUserId) {
|
throw new BizException(BizErrorCode.USER_SELF_FORBIDDEN, "不能删除自己的账号");
|
}
|
SysUser u = loadActiveWithUuid(id, uuid);
|
sysUserRoleMapper.delete(
|
new LambdaQueryWrapper<SysUserRole>()
|
.eq(SysUserRole::getUserId, id)
|
.eq(SysUserRole::getDelFlag, 0));
|
int affected = sysUserMapper.deleteById(u.getId());
|
if (affected <= 0) {
|
throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "删除失败,请刷新后重试");
|
}
|
kickSysUserOffline(id);
|
}
|
|
private static void kickSysUserOffline(Long sysUserId) {
|
StpUtil.kickout(sysUserId);
|
}
|
|
private SysUser loadActiveWithUuid(Long id, String uuid) {
|
SysUser u = sysUserMapper.selectById(id);
|
if (u == null || u.getDelFlag() != null && u.getDelFlag() == 1) {
|
throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "资源不存在或无权访问");
|
}
|
UuidAssert.match(u, uuid);
|
return u;
|
}
|
|
private void validateRoleIds(List<Long> roleIds) {
|
if (roleIds == null || roleIds.isEmpty()) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "至少选择一个角色");
|
}
|
for (Long rid : roleIds) {
|
SysRole r = sysRoleMapper.selectById(rid);
|
if (r == null || r.getDelFlag() != null && r.getDelFlag() == 1) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "角色不存在: " + rid);
|
}
|
}
|
}
|
|
private List<Long> listRoleIds(Long userId) {
|
return sysUserRoleMapper
|
.selectList(
|
new LambdaQueryWrapper<SysUserRole>()
|
.eq(SysUserRole::getUserId, userId)
|
.eq(SysUserRole::getDelFlag, 0))
|
.stream()
|
.map(SysUserRole::getRoleId)
|
.toList();
|
}
|
|
private void replaceRoles(Long userId, List<Long> roleIds) {
|
sysUserRoleMapper.delete(
|
new LambdaQueryWrapper<SysUserRole>()
|
.eq(SysUserRole::getUserId, userId)
|
.eq(SysUserRole::getDelFlag, 0));
|
for (Long rid : roleIds) {
|
SysUserRole ur = new SysUserRole();
|
ur.setUserId(userId);
|
ur.setRoleId(rid);
|
sysUserRoleMapper.insert(ur);
|
}
|
}
|
|
private static String trimToNull(String s) {
|
if (!StringUtils.hasText(s)) {
|
return null;
|
}
|
String t = s.trim();
|
return t.isEmpty() ? null : t;
|
}
|
}
|