package com.xuekairui.user.service; import com.xuekairui.common.BusinessException; import com.xuekairui.common.ErrorCode; import com.xuekairui.common.RandomUtil; import com.xuekairui.user.dto.LoginResponse; import com.xuekairui.user.entity.Admin; import com.xuekairui.user.mapper.AdminMapper; import com.xuekairui.user.security.TokenVersionCache; import com.xuekairui.user.util.JwtUtil; import com.xuekairui.user.util.PasswordUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; /** * 管理员认证服务(操作 t_admin 表,与普通用户 AuthService 隔离) */ @Slf4j @Service @RequiredArgsConstructor public class AdminAuthService { private final AdminMapper adminMapper; private final JwtUtil jwtUtil; private final PasswordUtil passwordUtil; private final TokenVersionCache tokenVersionCache; private static final int RESET_LEN = 10; /** * 管理员密码登录(按用户名或手机号) */ @Transactional public LoginResponse passwordLogin(String account, String password, String ip) { Admin admin = adminMapper.selectByUsername(account); if (admin == null) { admin = adminMapper.selectByPhone(account); } if (admin == null) { throw new BusinessException(ErrorCode.USER_NOT_FOUND); } if (admin.getPassword() == null || admin.getPassword().isEmpty()) { throw new BusinessException(ErrorCode.PASSWORD_NOT_SET); } if (!passwordUtil.matches(password, admin.getPassword())) { throw new BusinessException(ErrorCode.PASSWORD_INCORRECT); } if (admin.getStatus() != null && admin.getStatus() == 0) { throw new BusinessException(ErrorCode.USER_DISABLED); } return buildLoginResponse(admin, ip); } /** * 构建管理员登录响应(递增 tokenVersion,签发 Token) */ private LoginResponse buildLoginResponse(Admin admin, String ip) { String roleCode = admin.getRole() != null ? admin.getRole() : "ADMIN"; // 管理员允许多设备:最多5台 long oldVersion = admin.getTokenVersion() != null ? admin.getTokenVersion() : 0L; long newVersion = oldVersion + 1; admin.setTokenVersion(newVersion); String accessToken = jwtUtil.generateAccessToken(admin.getId(), roleCode, "PC", newVersion); String refreshToken = jwtUtil.generateRefreshToken(admin.getId(), "PC"); admin.setRefreshTokenHash(JwtUtil.hashToken(refreshToken)); admin.setRefreshTokenStatus("ACTIVE"); admin.setLastLoginTime(LocalDateTime.now()); admin.setLastLoginIp(ip); adminMapper.updateById(admin); tokenVersionCache.putVersion(admin.getId(), true, newVersion); boolean hadPreviousSession = oldVersion >= 5; return LoginResponse.builder() .userId(admin.getId()) .accessToken(accessToken) .refreshToken(refreshToken) .expiresIn(jwtUtil.getExpirationInSeconds(roleCode)) .needBindPhone(false) .newUser(false) .role(roleCode) .kickedPreviousSession(hadPreviousSession) .build(); } /** * 管理员自行修改密码(需验旧密码) */ @Transactional public void setPassword(Long adminId, String oldPassword, String newPassword) { Admin admin = adminMapper.selectById(adminId); if (admin == null) { throw new BusinessException(ErrorCode.USER_NOT_FOUND); } if (admin.getPassword() != null && !admin.getPassword().isEmpty() && oldPassword != null && !passwordUtil.matches(oldPassword, admin.getPassword())) { throw new BusinessException(ErrorCode.OLD_PASSWORD_INCORRECT); } admin.setPassword(passwordUtil.encode(newPassword)); adminMapper.updateById(admin); } /** * 重置管理员密码为随机密码,返回明文 */ @Transactional public String resetPassword(Long adminId) { Admin admin = adminMapper.selectById(adminId); if (admin == null) { throw new BusinessException(ErrorCode.USER_NOT_FOUND); } String rawPwd = RandomUtil.generateCode(RESET_LEN); admin.setPassword(passwordUtil.encode(rawPwd)); adminMapper.updateById(admin); tokenVersionCache.putVersion(adminId, true, (admin.getTokenVersion() != null ? admin.getTokenVersion() : 0L) + 1); return rawPwd; } }