“mitao”
2026-04-07 4ba9020b7c67cc318448a8aeac741492b23b4189
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.tcm.training.admin.app.auth.service;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tcm.training.admin.app.auth.dto.AppSmsCodeSendRequest;
import com.tcm.training.admin.app.auth.dto.AppSmsLoginRequest;
import com.tcm.training.admin.app.auth.support.StpAppUserUtil;
import com.tcm.training.admin.app.auth.vo.AppLoginResponse;
import com.tcm.training.admin.app.auth.vo.AppLoginUserVO;
import com.tcm.training.admin.app.auth.vo.AppSmsCodeSendResponse;
import com.tcm.training.admin.member.entity.AppUser;
import com.tcm.training.admin.member.mapper.UserMapper;
import cn.dev33.satoken.stp.SaTokenInfo;
import org.springframework.stereotype.Service;
 
import java.util.regex.Pattern;
 
/**
 * APP 短信验证码登录服务。
 */
@Service
public class AppAuthService {
 
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$");
 
    private final UserMapper userMapper;
    private final AppSmsCodeService appSmsCodeService;
 
    /**
     * 注入 APP 用户查询能力与短信验证码服务。
     */
    public AppAuthService(UserMapper userMapper, AppSmsCodeService appSmsCodeService) {
        this.userMapper = userMapper;
        this.appSmsCodeService = appSmsCodeService;
    }
 
    /**
     * 为已注册 APP 用户生成登录验证码。
     */
    public AppSmsCodeSendResponse sendLoginCode(AppSmsCodeSendRequest request) {
        String phone = normalizePhone(request.getPhone());
        requireEnabledUserByPhone(phone);
        return appSmsCodeService.createLoginCode(phone);
    }
 
    /**
     * 使用短信验证码完成 APP 用户登录。
     */
    public AppLoginResponse smsLogin(AppSmsLoginRequest request) {
        String phone = normalizePhone(request.getPhone());
        AppUser user = requireEnabledUserByPhone(phone);
        appSmsCodeService.validateLoginCode(phone, normalizeCode(request.getCode()));
 
        StpAppUserUtil.login(user.getId());
        SaTokenInfo tokenInfo = StpAppUserUtil.getTokenInfo();
        return new AppLoginResponse(
                tokenInfo.getTokenValue(),
                "Bearer",
                tokenInfo.getTokenTimeout(),
                buildLoginUser(user)
        );
    }
 
    /**
     * 将 APP 用户实体转换为登录响应中的用户摘要。
     */
    private AppLoginUserVO buildLoginUser(AppUser user) {
        AppLoginUserVO loginUser = new AppLoginUserVO();
        loginUser.setId(user.getId());
        loginUser.setNickname(resolveNickname(user));
        loginUser.setPhone(user.getPhone());
        loginUser.setAvatar(user.getAvatar());
        loginUser.setType(user.getType());
        loginUser.setStatus(user.getStatus());
        loginUser.setClinicId(user.getClinicId());
        loginUser.setMemberExpireAt(user.getMemberExpireAt());
        return loginUser;
    }
 
    /**
     * 按手机号查询并校验 APP 用户状态。
     */
    private AppUser requireEnabledUserByPhone(String phone) {
        AppUser user = userMapper.selectOne(new LambdaQueryWrapper<AppUser>()
                .eq(AppUser::getPhone, phone)
                .eq(AppUser::getDeleted, 0)
                .last("limit 1"));
        if (user == null) {
            throw new IllegalArgumentException("账号不存在,请先注册");
        }
        if (user.getStatus() == null || user.getStatus() != 1) {
            throw new IllegalArgumentException("账号已被禁用");
        }
        return user;
    }
 
    /**
     * 归一化并校验手机号格式。
     */
    private String normalizePhone(String phone) {
        String normalized = trimToNull(phone);
        if (normalized == null || !PHONE_PATTERN.matcher(normalized).matches()) {
            throw new IllegalArgumentException("请输入正确的手机号");
        }
        return normalized;
    }
 
    /**
     * 归一化并校验验证码。
     */
    private String normalizeCode(String code) {
        String normalized = trimToNull(code);
        if (normalized == null) {
            throw new IllegalArgumentException("请输入验证码");
        }
        return normalized;
    }
 
    /**
     * 优先使用昵称,缺失时回退到手机号作为展示名称。
     */
    private String resolveNickname(AppUser user) {
        String nickname = trimToNull(user.getNickname());
        return nickname == null ? user.getPhone() : nickname;
    }
 
    /**
     * 将空白字符串归一化为 null。
     */
    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
}