package com.tourguide.modules.user.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tourguide.common.exception.BusinessException;
|
import com.tourguide.modules.system.service.AdminPermissionService;
|
import com.tourguide.modules.user.domain.ClientType;
|
import com.tourguide.modules.user.domain.UserRole;
|
import com.tourguide.modules.user.dto.AuthResponse;
|
import com.tourguide.modules.user.dto.GuideUserResponse;
|
import com.tourguide.modules.user.dto.LoginRequest;
|
import com.tourguide.modules.user.dto.MiniRegisterRequest;
|
import com.tourguide.modules.user.dto.MiniUserResponse;
|
import com.tourguide.modules.user.dto.UserResponse;
|
import com.tourguide.modules.user.entity.AdminUser;
|
import com.tourguide.modules.user.entity.GuideUser;
|
import com.tourguide.modules.user.entity.MiniUser;
|
import com.tourguide.modules.user.mapper.MiniUserMapper;
|
import com.tourguide.security.JwtTokenService;
|
import com.tourguide.security.TokenPayload;
|
import com.tourguide.security.TokenSessionService;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.nio.charset.StandardCharsets;
|
import java.util.List;
|
import java.util.UUID;
|
|
/** 统一编排登录流程,但后台账号与小程序账号始终从不同数据表读取。 */
|
@Service
|
public class AuthService {
|
public static final int GUIDE_ACCOUNT_DISABLED_CODE = 40301;
|
|
private final AdminUserService adminUserService;
|
private final AdminPermissionService adminPermissionService;
|
private final MiniUserService miniUserService;
|
private final GuideUserService guideUserService;
|
private final GuideAutoDisableService guideAutoDisableService;
|
private final MiniUserMapper miniUserMapper;
|
private final PasswordEncoder passwordEncoder;
|
private final JwtTokenService jwtTokenService;
|
private final TokenSessionService tokenSessionService;
|
|
public AuthService(AdminUserService adminUserService, AdminPermissionService adminPermissionService,
|
MiniUserService miniUserService,
|
GuideUserService guideUserService, GuideAutoDisableService guideAutoDisableService,
|
MiniUserMapper miniUserMapper,
|
PasswordEncoder passwordEncoder, JwtTokenService jwtTokenService, TokenSessionService tokenSessionService) {
|
this.adminUserService = adminUserService;
|
this.adminPermissionService = adminPermissionService;
|
this.miniUserService = miniUserService;
|
this.guideUserService = guideUserService;
|
this.guideAutoDisableService = guideAutoDisableService;
|
this.miniUserMapper = miniUserMapper;
|
this.passwordEncoder = passwordEncoder;
|
this.jwtTokenService = jwtTokenService;
|
this.tokenSessionService = tokenSessionService;
|
}
|
|
public AuthResponse<UserResponse> loginAdmin(LoginRequest request) {
|
AdminUser user = adminUserService.findByUsername(request.getUsername());
|
if (user == null) {
|
throw new BusinessException(401, "用户名或密码错误");
|
}
|
if (user.getStatus() == null || user.getStatus() != 1) {
|
throw new BusinessException(403, "账号已被禁用,请联系管理员");
|
}
|
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
throw new BusinessException(401, "用户名或密码错误");
|
}
|
List<String> menuCodes = adminPermissionService.getMenuCodes(user.getRoleCode());
|
String token = issueToken(user.getId(), user.getUsername(), user.getRoleCode(), ClientType.ADMIN);
|
return new AuthResponse<UserResponse>(token, jwtTokenService.getExpirationSeconds(),
|
UserResponse.from(user, menuCodes));
|
}
|
|
public AuthResponse<MiniUserResponse> loginMini(LoginRequest request) {
|
MiniUser user = miniUserService.findByUsername(request.getUsername());
|
if (user == null || user.getStatus() == null || user.getStatus() != 1 || !passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
throw new BusinessException(401, "用户名或密码错误");
|
}
|
String token = issueToken(user.getId(), user.getUsername(), UserRole.MINI_USER.name(), ClientType.MINI);
|
return new AuthResponse<MiniUserResponse>(token, jwtTokenService.getExpirationSeconds(), MiniUserResponse.from(user));
|
}
|
|
@Transactional
|
public AuthResponse<MiniUserResponse> loginMiniWechat(String openid, String mobile) {
|
MiniUser user = miniUserMapper.selectOne(new LambdaQueryWrapper<MiniUser>()
|
.eq(MiniUser::getOpenid, openid)
|
.last("LIMIT 1"));
|
if (user == null) {
|
user = new MiniUser();
|
user.setUsername(buildWechatUsername(openid));
|
user.setPassword(passwordEncoder.encode(UUID.randomUUID().toString()));
|
user.setNickname("微信用户");
|
user.setMobile(mobile);
|
user.setOpenid(openid);
|
user.setStatus(1);
|
user.setDeleted(0);
|
miniUserMapper.insert(user);
|
} else {
|
if (user.getStatus() == null || user.getStatus() != 1) {
|
throw new BusinessException(401, "小程序用户已停用");
|
}
|
if (mobile != null && !mobile.equals(user.getMobile())) {
|
user.setMobile(mobile);
|
miniUserMapper.updateById(user);
|
}
|
}
|
String token = issueToken(user.getId(), user.getUsername(), UserRole.MINI_USER.name(), ClientType.MINI);
|
return new AuthResponse<MiniUserResponse>(token, jwtTokenService.getExpirationSeconds(), MiniUserResponse.from(user));
|
}
|
|
public AuthResponse<GuideUserResponse> loginGuide(LoginRequest request) {
|
GuideUser user = guideUserService.findByUsername(request.getUsername());
|
validateGuideLoginStatus(user, "用户名不存在");
|
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
throw new BusinessException(401, "用户名或密码错误");
|
}
|
guideAutoDisableService.disableGuideIfEligible(user.getId());
|
GuideUser currentUser = guideUserService.findByUsername(request.getUsername());
|
validateGuideLoginStatus(currentUser, "用户名不存在");
|
return issueGuideToken(currentUser);
|
}
|
|
public AuthResponse<GuideUserResponse> loginGuideByPhone(String phone) {
|
return issueGuideToken(findLoginableGuideByPhone(phone));
|
}
|
|
public void ensureGuidePhoneCanLogin(String phone) {
|
findLoginableGuideByPhone(phone);
|
}
|
|
private GuideUser findLoginableGuideByPhone(String phone) {
|
GuideUser user = guideUserService.findByPhone(phone);
|
validateGuideLoginStatus(user, "手机号未注册");
|
validateGuideLoginPhone(user, phone);
|
guideAutoDisableService.disableGuideIfEligible(user.getId());
|
GuideUser currentUser = guideUserService.findByPhone(phone);
|
validateGuideLoginStatus(currentUser, "手机号未注册");
|
validateGuideLoginPhone(currentUser, phone);
|
return currentUser;
|
}
|
|
private void validateGuideLoginPhone(GuideUser user, String phone) {
|
if (StringUtils.hasText(user.getMobile()) && !phone.equals(user.getMobile())) {
|
throw new BusinessException(401, "手机号未注册");
|
}
|
}
|
|
private void validateGuideLoginStatus(GuideUser user, String notFoundMessage) {
|
if (user == null) {
|
throw new BusinessException(401, notFoundMessage);
|
}
|
if (Integer.valueOf(GuideUserService.STATUS_DISABLED).equals(user.getStatus())) {
|
throw new BusinessException(GUIDE_ACCOUNT_DISABLED_CODE, "账号已被禁用,请联系后台工作人员");
|
}
|
if (Integer.valueOf(GuideUserService.STATUS_CANCELLED).equals(user.getStatus())) {
|
throw new BusinessException(403, "账号已注销,无法登录");
|
}
|
if (!Integer.valueOf(GuideUserService.STATUS_ENABLED).equals(user.getStatus())
|
&& !Integer.valueOf(GuideUserService.STATUS_PENDING).equals(user.getStatus())) {
|
throw new BusinessException(403, "账号状态异常,请联系后台工作人员");
|
}
|
}
|
|
private AuthResponse<GuideUserResponse> issueGuideToken(GuideUser user) {
|
String token = issueToken(user.getId(), user.getUsername(), UserRole.GUIDE_USER.name(), ClientType.GUIDE);
|
return new AuthResponse<GuideUserResponse>(token, jwtTokenService.getExpirationSeconds(), GuideUserResponse.from(user));
|
}
|
|
@Transactional
|
public AuthResponse<MiniUserResponse> registerMini(MiniRegisterRequest request) {
|
if (miniUserMapper.selectCount(new LambdaQueryWrapper<MiniUser>().eq(MiniUser::getUsername, request.getUsername())) > 0) {
|
throw new BusinessException(409, "小程序用户名已存在");
|
}
|
MiniUser user = new MiniUser();
|
user.setUsername(request.getUsername());
|
user.setPassword(passwordEncoder.encode(request.getPassword()));
|
user.setNickname(request.getNickname());
|
user.setMobile(request.getMobile());
|
user.setStatus(1);
|
user.setDeleted(0);
|
miniUserMapper.insert(user);
|
String token = issueToken(user.getId(), user.getUsername(), UserRole.MINI_USER.name(), ClientType.MINI);
|
return new AuthResponse<MiniUserResponse>(token, jwtTokenService.getExpirationSeconds(), MiniUserResponse.from(user));
|
}
|
|
public void logout(String token, ClientType expectedClientType) {
|
TokenPayload payload = jwtTokenService.parse(token);
|
if (payload.getClientType() != expectedClientType) { throw new BusinessException(401, "Token端类型不匹配"); }
|
tokenSessionService.remove(payload);
|
}
|
|
private String issueToken(Long userId, String username, String authority, ClientType clientType) {
|
String token = jwtTokenService.createToken(userId, username, authority, clientType);
|
tokenSessionService.save(jwtTokenService.parse(token), jwtTokenService.getExpirationSeconds());
|
return token;
|
}
|
|
private String buildWechatUsername(String openid) {
|
return "wx_" + UUID.nameUUIDFromBytes(openid.getBytes(StandardCharsets.UTF_8))
|
.toString().replace("-", "").substring(0, 29);
|
}
|
}
|