peng
2026-03-18 e59a0201057ba67cad425fed804c82ff4ba0c6f1
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package com.tievd.cube.modules.system.service.impl;
 
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.math.Calculator;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tievd.cube.application.config.properties.CaptchaConfigProperties;
import com.tievd.cube.common.system.api.ISysBaseAPI;
import com.tievd.cube.commons.base.Result;
import com.tievd.cube.commons.constant.CacheConst;
import com.tievd.cube.commons.exception.CubeAppException;
import com.tievd.cube.commons.intf.ISmsSender;
import com.tievd.cube.commons.utils.CommonUtil;
import com.tievd.cube.commons.utils.SystemContextUtil;
import com.tievd.cube.commons.utils.captcha.CaptchaGenerator;
import com.tievd.cube.commons.utils.crypto.PasswordTransportUtil;
import com.tievd.cube.commons.utils.crypto.PasswordUtil;
import com.tievd.cube.commons.utils.spring.RedisUtil;
import com.tievd.cube.modules.system.entity.SysDepart;
import com.tievd.cube.modules.system.entity.SysUser;
import com.tievd.cube.modules.system.mapper.SysDepartMapper;
import com.tievd.cube.modules.system.mapper.SysLogMapper;
import com.tievd.cube.modules.system.mapper.SysUserMapper;
import com.tievd.cube.modules.system.model.SmsType;
import com.tievd.cube.modules.system.model.api.response.LogInfoResponse;
import com.tievd.cube.modules.system.model.api.response.LoginResponse;
import com.tievd.cube.modules.system.model.api.response.VisitsInfo;
import com.tievd.cube.modules.system.service.ILoginService;
import com.tievd.cube.modules.system.service.ISysDictService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
 
@Slf4j
@Service
public class LoginServiceImpl implements ILoginService {
 
    @Autowired
    private SysDepartMapper sysDepartMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysLogMapper sysLogMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ISysBaseAPI sysBaseAPI;
    @Autowired
    private ISysDictService sysDictService;
    @Autowired
    private CaptchaConfigProperties captchaConfigProperties;
    @Autowired
    private CaptchaGenerator captchaGenerator;
    @Autowired(required = false)
    private ISmsSender smsSender;
 
    @Override
    public LoginResponse getUserInfo(SysUser sysUser) {
        String username = sysUser.getUsername();
        // 获取用户部门信息
        LoginResponse loginResponse = new LoginResponse();
        List<SysDepart> departs = sysDepartMapper.queryUserDeparts(sysUser.getId());
        loginResponse.setDeparts(departs);
        if (departs.size() == 1) {
            SysDepart sysDepart = departs.get(0);
            sysUser.setOrgCode(sysDepart.getOrgCode());
            sysUser.setOrgCodeTxt(sysDepart.getDepartName());
            sysUserMapper.updateUserDepart(username, sysDepart.getOrgCode());
        }
        // 生成token
        StpUtil.login(username);
        SaTokenInfo saTokenInfo = StpUtil.getTokenInfo();
        loginResponse.setToken(saTokenInfo.getTokenValue());
        loginResponse.setTokenTimeout(saTokenInfo.getTokenTimeout());
        loginResponse.setUserInfo(sysUser);
        loginResponse.setUserRoles(sysBaseAPI.queryUserRoles(sysUser.getUsername()));
        loginResponse.setSysAllDictItems(sysDictService.queryAllDictItems());
        // 登录日志
        SystemContextUtil.log("登录成功,登录凭证:" + saTokenInfo.getTokenValue());
        // 删除原有用户信息缓存
        redisUtil.del(String.format("%s::%s", CacheConst.SYS_USERS_CACHE, username));
        return loginResponse;
    }
 
    @Override
    public LoginResponse loginWithPassword(String username, String password, String captcha, String checkKey) {
        //AES CBC 解密
        password = PasswordTransportUtil.getPlaintext(password);
        if (captchaConfigProperties.isEnable()) {
            if (StrUtil.isEmpty(captcha)) {
                throw new CubeAppException("请填写验证码!");
            }
            String lowerCaseCaptcha = captcha.toLowerCase();
            String realKey = SecureUtil.md5(lowerCaseCaptcha + checkKey);
            Object checkCode = redisUtil.get(realKey);
            //当进入登录页时,有一定几率出现验证码错误 #1714
            if (checkCode == null || !String.valueOf(checkCode).equals(lowerCaseCaptcha)) {
                throw new CubeAppException("验证码错误!");
            }
        }
        //1. 校验用户是否有效
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
        Result<?> userState = sysBaseAPI.checkUserState(sysUser);
        if (!userState.isSuccess()) {
            throw new CubeAppException(userState.getMessage());
        }
        //2. 校验用户名或密码是否正确
        String userInputPassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
        String userPassword = sysUser.getPassword();
        if (!userPassword.equals(userInputPassword)) {
            throw new CubeAppException("用户名或密码错误!");
        }
        return this.getUserInfo(sysUser);
    }
 
    @Override
    public LoginResponse loginWithCode(String phone, String code) {
        //校验用户有效性
        SysUser sysUser = sysUserMapper.getUserByPhone(phone);
        Result<?> userState = sysBaseAPI.checkUserState(sysUser);
        if (!userState.isSuccess()) {
            throw new CubeAppException(userState.getMessage());
        }
        Object verificationCode = redisUtil.get(phone);
        if (!code.equals(verificationCode)) {
            throw new CubeAppException("手机验证码错误!");
        }
        return this.getUserInfo(sysUser);
    }
 
    @Override
    public void logout() {
        // 删除用户的缓存信息(包括部门信息),例如sys:cache:user::<username>
        redisUtil.del(String.format("%s::%s", CacheConst.SYS_USERS_CACHE, StpUtil.getLoginIdAsString()));
        StpUtil.logout();
        SystemContextUtil.log("登出成功,登录凭证:" + StpUtil.getTokenInfo().getTokenValue());
    }
 
    @Override
    public LogInfoResponse visitsToday() {
        // 获取一天的开始和结束时间
        Calendar calendar = new GregorianCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date dayStart = calendar.getTime();
        calendar.add(Calendar.DATE, 1);
        Date dayEnd = calendar.getTime();
        // 获取系统访问记录
        Long totalVisitCount = sysLogMapper.findTotalVisitCount();
        Long todayVisitCount = sysLogMapper.findTodayVisitCount(dayStart, dayEnd);
        Long todayIp = sysLogMapper.findTodayIp(dayStart, dayEnd);
        return new LogInfoResponse(totalVisitCount, todayVisitCount, todayIp);
    }
 
    @Override
    public List<VisitsInfo> visitsSevenDay() {
        Calendar calendar = new GregorianCalendar();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date dayEnd = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, -7);
        Date dayStart = calendar.getTime();
        String dbType = CommonUtil.getDatabaseType();
        return sysLogMapper.findVisitCount(dayStart, dayEnd, dbType);
    }
 
    @Override
    public SysUser selectDepart(String orgCode) {
        String username = StpUtil.getLoginIdAsString();
        // 重新选择部门时删除原有缓存
        redisUtil.del(String.format("%s::%s", CacheConst.SYS_USERS_CACHE, username));
        if (StrUtil.isNotEmpty(orgCode)) {
            sysUserMapper.updateUserDepart(username, orgCode);
        }
        SysUser sysUser = sysUserMapper.getUserByName(username);
        String departName = sysBaseAPI.translateDictFromTable("sys_depart", "depart_name", "org_code", orgCode);
        sysUser.setOrgCodeTxt(departName);
        return sysUser;
    }
 
    @Override
    public void sendSms(String phone, String mode) {
        String captcha = RandomUtil.randomNumbers(6);
        SmsType smsType = SmsType.SECURITY_TEMPLATE;
        switch (mode) {
            case "0":
                smsType = SmsType.LOGIN_TEMPLATE;
                break;
            case "1":
                SysUser sysUser = sysUserMapper.getUserByPhone(phone);
                if (sysUser != null) {
                    throw new CubeAppException("手机号已经注册,请直接登录!");
                }
                break;
            case "2":
                smsType = SmsType.FORGET_PASSWORD_TEMPLATE;
                break;
        }
        redisUtil.set(phone, captcha, 600);
        if (smsSender != null) {
            JSONObject params = JSONUtil.createObj();
            params.set("code", captcha);
            smsSender.send(phone, smsType, params);
        }
        log.info("Send SMS template={} code={} phone={} !", smsType.name(), captcha, phone);
    }
 
    @Override
    public String randomImage(String key) {
        String base64 = captchaGenerator.generate();
        String lowerCaseCode = captchaGenerator.getCode().toLowerCase();
        if (captchaConfigProperties.isMath()) {
            int calculateResult = (int) Calculator.conversion(lowerCaseCode);
            lowerCaseCode = String.valueOf(calculateResult);
        }
        String realKey = SecureUtil.md5(lowerCaseCode + key);
        redisUtil.set(realKey, lowerCaseCode, 60 * 2);
        return base64;
    }
 
    @Override
    public void checkCaptcha(String captcha, String checkKey) {
        String lowerCaseCaptcha = captcha.toLowerCase();
        String realKey = SecureUtil.md5(lowerCaseCaptcha + checkKey);
        Object checkCode = redisUtil.get(realKey);
        if (checkCode == null || !checkCode.equals(lowerCaseCaptcha)) {
            throw new CubeAppException("验证码错误!");
        }
    }
}