package com.tcm.training.admin.auth.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tcm.training.admin.auth.dto.ChangePasswordRequest; import com.tcm.training.admin.auth.dto.LoginRequest; import com.tcm.training.admin.auth.dto.ProfileUpdateRequest; import com.tcm.training.admin.auth.vo.LoginResponse; import com.tcm.training.admin.auth.vo.UserVO; import com.tcm.training.admin.user.entity.SysUser; import com.tcm.training.admin.user.service.RbacService; import com.tcm.training.admin.user.service.SysUserService; import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpUtil; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; import java.util.regex.Pattern; @Service public class AuthService { private static final Pattern PASSWORD_PATTERN = Pattern.compile( "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z\\d]).{12,64}$" ); private final SysUserService sysUserService; private final BCryptPasswordEncoder passwordEncoder; private final CaptchaService captchaService; private final RbacService rbacService; public AuthService( SysUserService sysUserService, BCryptPasswordEncoder passwordEncoder, CaptchaService captchaService, RbacService rbacService ) { this.sysUserService = sysUserService; this.passwordEncoder = passwordEncoder; this.captchaService = captchaService; this.rbacService = rbacService; } public LoginResponse login(LoginRequest req) { captchaService.validate(req.getCaptchaId(), req.getCaptchaCode()); SysUser user = sysUserService.getOne(new LambdaQueryWrapper() .eq(SysUser::getUsername, req.getUsername()) .eq(SysUser::getDeleted, 0) .last("limit 1")); if (user == null) { throw new IllegalArgumentException("账号或密码错误"); } if (user.getStatus() != null && user.getStatus() != 1) { throw new IllegalArgumentException("账号已被禁用"); } if (!passwordEncoder.matches(req.getPassword(), user.getPassword())) { throw new IllegalArgumentException("账号或密码错误"); } StpUtil.login(user.getId()); SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); user.setLastLoginAt(LocalDateTime.now()); sysUserService.updateById(user); UserVO userVO = buildUserVO(user); return new LoginResponse(tokenInfo.getTokenValue(), "Bearer", tokenInfo.getTokenTimeout(), userVO); } public void logout() { StpUtil.logout(); } public UserVO me() { return buildUserVO(requireCurrentUser()); } @Transactional public UserVO updateProfile(ProfileUpdateRequest req) { SysUser user = requireCurrentUser(); user.setNickname(normalizeRequired(req.getNickname(), "请输入用户名")); user.setAvatar(trimToNull(req.getAvatar())); user.setUpdatedAt(LocalDateTime.now()); sysUserService.updateById(user); return buildUserVO(user); } @Transactional public void changePassword(ChangePasswordRequest req) { SysUser user = requireCurrentUser(); if (!passwordEncoder.matches(req.getOldPassword(), user.getPassword())) { throw new IllegalArgumentException("原密码错误"); } if (!req.getNewPassword().equals(req.getConfirmPassword())) { throw new IllegalArgumentException("两次密码输入不一致"); } validatePassword(req.getNewPassword()); user.setPassword(passwordEncoder.encode(req.getNewPassword())); user.setUpdatedAt(LocalDateTime.now()); sysUserService.updateById(user); StpUtil.logout(); } private UserVO buildUserVO(SysUser user) { List roleCodes = rbacService.getRoleCodesByUserId(user.getId()); List permissionKeys = rbacService.getPermissionKeysByUserId(user.getId()); return new UserVO( user.getId(), user.getUsername(), user.getNickname(), user.getRealName(), user.getPhone(), user.getAvatar(), roleCodes, permissionKeys ); } private SysUser requireCurrentUser() { Long userId = StpUtil.getLoginIdAsLong(); SysUser user = sysUserService.getById(userId); if (user == null || (user.getDeleted() != null && user.getDeleted() == 1)) { throw new IllegalArgumentException("用户不存在"); } return user; } private void validatePassword(String password) { if (!PASSWORD_PATTERN.matcher(password).matches()) { throw new IllegalArgumentException("密码需至少12位,且包含大小写字母、数字和特殊字符"); } } private String normalizeRequired(String value, String message) { String normalized = trimToNull(value); if (normalized == null) { throw new IllegalArgumentException(message); } return normalized; } private String trimToNull(String value) { if (value == null) { return null; } String trimmed = value.trim(); return trimmed.isEmpty() ? null : trimmed; } }