UserLoginServiceImpl.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. package com.zhentao.user.service.impl;
  2. import cn.hutool.core.util.IdUtil;
  3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5. import com.zhentao.enums.ApiServerException;
  6. import com.zhentao.exception.AsynException;
  7. import com.zhentao.information.service.WebSocketService;
  8. import com.zhentao.tool.TokenUtils;
  9. import com.zhentao.user.domain.UserLogin;
  10. import com.zhentao.user.dto.*;
  11. import com.zhentao.user.service.UserLoginService;
  12. import com.zhentao.user.mapper.UserLoginMapper;
  13. import com.zhentao.vo.Result;
  14. import org.redisson.api.RLock;
  15. import org.redisson.api.RedissonClient;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.data.redis.core.StringRedisTemplate;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.util.DigestUtils;
  20. import org.springframework.util.StringUtils;
  21. import org.springframework.web.multipart.MultipartFile;
  22. import java.util.List;
  23. import java.util.Optional;
  24. import java.util.HashMap;
  25. import java.util.Map;
  26. import java.util.UUID;
  27. import java.util.concurrent.TimeUnit;
  28. /**
  29. * @author 86183
  30. * @description 针对表【user_login(用户)】的数据库操作Service实现
  31. * @createDate 2025-06-03 18:38:51
  32. */
  33. @Service
  34. public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin>
  35. implements UserLoginService{
  36. @Autowired
  37. private UserLoginMapper userLoginMapper;
  38. @Autowired
  39. private RedissonClient redissonClient;
  40. @Autowired
  41. private StringRedisTemplate stringRedisTemplate;
  42. @Autowired
  43. public WebSocketService webSocketService;
  44. //注册
  45. @Override
  46. public Result register(UserRegister userRegister) {
  47. //打印用户注册的信息
  48. System.err.println(userRegister);
  49. //使用redisson客户端获取分布式锁,确保并发情况下用户注册的安全性
  50. RLock lock = redissonClient.getLock(userRegister.getPhone() + userRegister.getPassword());
  51. try {
  52. boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
  53. if(b){
  54. //用来判断验证码是否正确
  55. String s = stringRedisTemplate.opsForValue().get(userRegister.getPhone());
  56. System.err.println("redis取出来的验证码"+s);
  57. //验证码不匹配就抛出异常
  58. if(!s.equals(userRegister.getCode())){
  59. throw new AsynException(ApiServerException.NOTE_ERROR);
  60. }
  61. //根据手机号查询信息
  62. QueryWrapper<UserLogin> queryWrapper=new QueryWrapper<>();
  63. queryWrapper.eq("user_mobile",userRegister.getPhone());
  64. UserLogin one = this.getOne(queryWrapper);
  65. if(one==null){
  66. //新用户注册的流程
  67. UserLogin userLogin=new UserLogin();
  68. userLogin.setUserMobile(userRegister.getPhone());
  69. userLogin.setUserUsername(userRegister.getUsername());
  70. //随机字符串
  71. String uuid = String.valueOf(UUID.randomUUID());
  72. userLogin.setSalt(uuid);
  73. //md5加密
  74. String s1 = DigestUtils.md5DigestAsHex((uuid + userRegister.getPassword()).getBytes());
  75. userLogin.setUserPassword(s1);
  76. //生成唯一Id
  77. long l = IdUtil.getSnowflake(1, 1).nextId();
  78. userLogin.setId(l);
  79. //进行注册
  80. System.err.println(userLogin);
  81. boolean save = this.save(userLogin);
  82. if(save){
  83. return Result.OK(save,"注册成功");
  84. }else{
  85. return Result.ERR(save,"注册失败");
  86. }
  87. }else{
  88. //老用户更新信息流程
  89. one.setUserUsername(userRegister.getUsername());
  90. //随机字符串
  91. String uuid = String.valueOf(UUID.randomUUID());
  92. one.setSalt(uuid);
  93. //md5加密
  94. String s1 = DigestUtils.md5DigestAsHex((uuid + userRegister.getPassword()).getBytes());
  95. one.setUserPassword(s1);
  96. //进行更新
  97. boolean b1 = this.updateById(one);
  98. if(b1){
  99. return Result.OK(b1,"注册成功");
  100. }else{
  101. return Result.ERR(b1,"注册失败");
  102. }
  103. }
  104. }
  105. }catch (InterruptedException e){
  106. Thread.currentThread().interrupt();
  107. }finally {
  108. //释放锁
  109. lock.unlock();
  110. }
  111. return null;
  112. }
  113. //验证码
  114. @Override
  115. public Result note(NoteDto noteDto) {
  116. //随机生成六位数
  117. int randomSixDigit=100000 + (int)(Math.random() * 900000);
  118. System.err.println("手机号:"+noteDto.getPhone());
  119. System.err.println("验证码:"+randomSixDigit);
  120. stringRedisTemplate.opsForValue().set(noteDto.getPhone(),randomSixDigit+"");
  121. return Result.OK(randomSixDigit,"发送成功");
  122. }
  123. //登录
  124. /**
  125. * 用户登录方法
  126. * 使用Redis分布式锁来防止并发登录
  127. * @param userLoginDto 用户登录信息,包含手机号和验证码
  128. * @return 登录结果,包括token和提示信息
  129. */
  130. @Override
  131. public Result login(UserLoginDto userLoginDto) {
  132. // 获取Redis锁,锁的键为用户手机号,防止并发登录
  133. RLock lock = redissonClient.getLock(userLoginDto.getPhone() + "phone");
  134. try {
  135. // 尝试获取锁,等待时间10秒,锁的过期时间为20秒
  136. boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
  137. if (b) {
  138. // 打印用户登录信息,用于调试
  139. System.err.println(userLoginDto);
  140. // 从Redis中获取验证码
  141. String s = stringRedisTemplate.opsForValue().get(userLoginDto.getPhone());
  142. // 打印从Redis中获取的验证码,用于调试
  143. System.err.println("redis取出来的验证码" + s);
  144. // 验证码不匹配则抛出异常
  145. if (!s.equals(userLoginDto.getCode())) {
  146. throw new AsynException(ApiServerException.NOTE_ERROR);
  147. }
  148. try {
  149. // 查询数据库中是否存在该用户
  150. QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
  151. queryWrapper.eq("user_mobile", userLoginDto.getPhone());
  152. UserLogin one = this.getOne(queryWrapper);
  153. // 生成JWT token
  154. String token = TokenUtils.generateToken(one.getId() + "");
  155. stringRedisTemplate.opsForValue().set(one.getId()+"",token);
  156. // 返回登录成功结果,包含tokens
  157. return Result.OK(token, "登录成功");
  158. } catch (NullPointerException e) {
  159. // 用户不存在时返回错误结果
  160. return Result.ERR("登录失败", "用户不存在");
  161. }
  162. }
  163. } catch (InterruptedException e) {
  164. // 中断当前线程,重新抛出自定义异常
  165. Thread.currentThread().interrupt();
  166. throw new AsynException(ApiServerException.NOTE_ERROR);
  167. } finally {
  168. // 释放锁
  169. lock.unlock();
  170. }
  171. return null;
  172. }
  173. //账号密码登录
  174. /**
  175. * 使用用户名和密码进行用户登录的方法
  176. * 该方法实现了用户认证和JWT令牌的生成
  177. *
  178. * @param userPassDto 包含用户名和密码的DTO对象
  179. * @return 登录结果,包括登录状态和JWT令牌
  180. */
  181. @Override
  182. public Result UserPassLogin(UserPassDto userPassDto) {
  183. System.err.println(userPassDto);
  184. // 获取Redisson客户端的锁对象,用于处理并发登录请求
  185. RLock lock = redissonClient.getLock("user"+userPassDto.getUsername());
  186. try {
  187. // 尝试获取锁,设置等待和持有时间
  188. boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
  189. if (b){
  190. // 查询用户信息,根据用户名
  191. QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
  192. queryWrapper.eq("user_username",userPassDto.getUsername());
  193. UserLogin one = this.getOne(queryWrapper);
  194. // 如果用户不存在,抛出异常
  195. if (one==null){
  196. throw new AsynException(ApiServerException.NULL_USERNAME);
  197. }
  198. // 获取用户盐值,用于密码加密
  199. String salt = one.getSalt();
  200. // 加密用户输入的密码,并与数据库中的密码进行比较
  201. String s = DigestUtils.md5DigestAsHex((salt + userPassDto.getPassword()).getBytes());
  202. if (!s.equals(one.getUserPassword())){
  203. throw new AsynException(ApiServerException.NULL_PASSWORD);
  204. }
  205. // 生成JWT令牌
  206. String jwtToken = TokenUtils.generateToken(one.getId()+"");
  207. stringRedisTemplate.opsForValue().set(one.getId().toString(),jwtToken);
  208. System.err.println(stringRedisTemplate.opsForValue().get(one.getId().toString()));
  209. // 返回登录成功结果和JWT令牌
  210. // 将用户ID和token存储到WebSocketService中
  211. webSocketService.storeUserToken(one.getId()+"", jwtToken);
  212. Map<String,Object> map = new HashMap<>();
  213. map.put("token",jwtToken);
  214. map.put("userId",one.getId()+"");
  215. map.put("image",one.getAvatar());
  216. return Result.OK(map,"登录成功");
  217. }else {
  218. // 如果获取锁超时,返回错误信息
  219. return Result.ERR("获取锁超时",null);
  220. }
  221. }catch (InterruptedException e){
  222. // 如果线程被中断,恢复中断状态,并返回错误信息
  223. System.err.println("所异常");
  224. Thread.currentThread().interrupt();
  225. return Result.ERR("线程被中断",null);
  226. }finally {
  227. // 释放锁
  228. lock.unlock();
  229. }
  230. }
  231. //忘记密码
  232. @Override
  233. public Result ForgetPass(ForgetPassDto forgetPassDto) {
  234. RLock lock = redissonClient.getLock(forgetPassDto.getPhone() + "Phone");
  235. try {
  236. boolean b = lock.tryLock(10, 20, TimeUnit.SECONDS);
  237. if (b) {
  238. QueryWrapper<UserLogin> queryWrapper = new QueryWrapper<>();
  239. queryWrapper.eq("user_mobile", forgetPassDto.getPhone());
  240. UserLogin one = this.getOne(queryWrapper);
  241. if (one == null) {
  242. return Result.ERR("用户不存在", null);
  243. }
  244. // 获取Redis中的验证码
  245. String s = stringRedisTemplate.opsForValue().get(forgetPassDto.getPhone());
  246. // 验证码不匹配则抛出异常
  247. if (!s.equals(forgetPassDto.getCode())) {
  248. throw new AsynException(ApiServerException.NOTE_ERROR);
  249. }
  250. // 获取用户信息,根据手机号
  251. String salt = one.getSalt();
  252. String s1 = DigestUtils.md5DigestAsHex((salt + forgetPassDto.getPassword()).getBytes());
  253. one.setUserPassword(s1);
  254. boolean b1 = this.updateById(one);
  255. if (b1){
  256. return Result.OK("修改成功", null);
  257. }else {
  258. return Result.ERR("修改失败", null);
  259. }
  260. }
  261. } catch (InterruptedException e) {
  262. throw new RuntimeException(e);
  263. }
  264. return null;
  265. }
  266. //获取用户信息
  267. @Override
  268. public UserLogin getUserById(Long id) {
  269. UserLogin userLogin = userLoginMapper.selectById(id);
  270. return userLogin;
  271. }
  272. @Override
  273. public List<UserLogin> searchFriends(String keyword) {
  274. if(!StringUtils.hasLength(keyword))
  275. return null;
  276. //根据昵称、账号进行模糊查询
  277. List<UserLogin> list=this.query()
  278. .like("user_username",keyword)
  279. .or()
  280. .like("nick_name",keyword)
  281. .list();
  282. return list;
  283. }
  284. @Override
  285. public Optional<UserLogin> findById(Long userId) {
  286. return Optional.ofNullable(userLoginMapper.selectById(userId));
  287. }
  288. }