package vvpkassistant.User.service; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.temp.SaTempUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.lang.UUID; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.zxing.WriterException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import vvpkassistant.Data.WxChatParam; import vvpkassistant.Tools.BcryptUtils; import vvpkassistant.Tools.QRCodeUtil; import vvpkassistant.Tools.VVTools; import vvpkassistant.User.mapper.UserDao; import vvpkassistant.User.model.*; import vvpkassistant.User.model.DTO.LoginInfoDTO; import vvpkassistant.User.model.DTO.ScanInfoDTO; import vvpkassistant.User.model.DTO.UserModelDTO; import vvpkassistant.User.model.enumeration.LoginStatusEnum; import vvpkassistant.common.ErrorCode; import vvpkassistant.exception.BusinessException; import vvpkassistant.mail.service.MailService; import javax.annotation.Resource; import java.io.IOException; import java.util.Objects; import java.util.concurrent.TimeUnit; /* * @author: ziin * @date: 2025/8/4 16:19 */ @Service @Slf4j public class UserServiceImpl extends ServiceImpl implements UserService { @Resource private UserDao userDao; @Resource private WxChatParam wxChatParam; @Autowired private MailService mailService; private final Cache qrcodeCache = Caffeine.newBuilder() .expireAfterWrite(2, TimeUnit.MINUTES) .build(); @Override public UserModelVO loginWithMail(UserModelDTO model) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(UserModel::getEmail,model.getEmail()) .in(UserModel::getStatus, 0,2); UserModel userModel = userDao.selectOne(lambdaQueryWrapper); if (userModel == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } String password = userModel.getPassword(); UserModelVO userModelVO = BeanUtil.copyProperties(userModel, UserModelVO.class); if (BcryptUtils.matchPassword(password, model.getPassword())) { StpUtil.login(userModel.getId()); userModelVO.setToken(StpUtil.getTokenValue()); userModelVO.setChatInfo(wxChatParam); return userModelVO; }else { throw new BusinessException(ErrorCode.PASSWORD_ERROR); } } @Override public UserModelVO updateUserInfo(UserModelDTO userModelDTO) { UserModel userInfo = userDao.selectById(userModelDTO.getId()); if (userInfo == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } // 用户没有密码的情况下设置密码 if (userInfo.getPassword() == null && userModelDTO.getNewPassword() != null) { if (!userModelDTO.getNewPassword().isEmpty()){ if (userModelDTO.getNewPassword().length()<6){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位"); } userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getNewPassword())); } } if (userModelDTO.getEmail() != null) { mailService.sendVerificationMail(userModelDTO.getEmail(), userModelDTO.getId()); } // 用户有密码的情况下重新设置密码 if (userInfo.getPassword() != null && userModelDTO.getOldPassword() != null) { if (BcryptUtils.matchPassword(userModelDTO.getOldPassword(), userInfo.getPassword())) { userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getNewPassword())); }else { throw new BusinessException(ErrorCode.PASSWORD_ERROR,"旧密码不正确"); } } UserModel userModel = BeanUtil.copyProperties(userModelDTO, UserModel.class); int i = userDao.updateById(userModel); // 返回结果 UserModel afterUserInfo = userDao.selectById(userModel.getId()); UserModelVO userModelVO = BeanUtil.copyProperties(afterUserInfo, UserModelVO.class); userModelVO.setNewAccount(false); if (i == 1){ return userModelVO; }else { throw new BusinessException(ErrorCode.SYSTEM_ERROR); } } @Override public UserModelVO addUserWithMail(UserModelDTO userModelDTO) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(UserModel::getEmail,userModelDTO.getEmail()); UserModel userModel = userDao.selectOne(lambdaQueryWrapper); if (userModel != null) { throw new BusinessException(ErrorCode.MAIL_ALREADY_EXIST); } if (userModelDTO.getPassword().length() < 6 ){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位"); } userModelDTO.setPassword(BcryptUtils.encryptPassword(userModelDTO.getPassword())); userModelDTO.setCreateTime(VVTools.currentTimeStamp()); //设置状态为待验证 userModelDTO.setStatus(2); //设置积分为0 userModelDTO.setPoints(0); UserModel userModelEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class); userModelEntity.setMailVerification(1); if ( userDao.insert(userModelEntity) != 1){ throw new BusinessException(ErrorCode.ADD_FAILED,"用户注册失败"); } mailService.sendMail(userModelDTO.getEmail(),userModelEntity.getId()); // 判断用户是否为邀请用户 if (userModelDTO.getInviterId() != null) { UserModel oldUser = userDao.selectById(userModelDTO.getInviterId()); oldUser.setPoints(oldUser.getPoints() + 10); userDao.updateById(oldUser); } UserModelVO userModelVO = BeanUtil.copyProperties(userModelEntity, UserModelVO.class); userModelVO.setNewAccount(true); userModelVO.setChatInfo(wxChatParam); return userModelVO; } @Override public Boolean activateAccount(String token) { Integer userId = SaTempUtil.parseToken(token, Integer.class); UserModel userModel = userDao.selectById(userId); if (userModel == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } userModel.setStatus(0); userModel.setMailVerification(0); if (userDao.updateById(userModel) == 1){ return true; }else { throw new BusinessException(ErrorCode.UPDATE_FAILED,"激活失败"); } } @Override public Boolean verificationMail(String token) { Integer userId = SaTempUtil.parseToken(token, Integer.class); UserModel userModel = userDao.selectById(userId); userModel.setMailVerification(0); if (userDao.updateById(userModel) == 1){ return true; } throw new BusinessException(ErrorCode.SYSTEM_ERROR,"邮箱验证失败"); } @Override public QrcodeVO generatedQrcode() { String uuid = UUID.randomUUID().toString(); QrcodeEntity qrcodeEntity = new QrcodeEntity(); qrcodeEntity.setUuid(uuid); qrcodeEntity.setType("qrcdoe"); String base64QR = null; try { base64QR = QRCodeUtil.generateQRCode(JSONUtil.toJsonStr(qrcodeEntity), 200, 200); } catch (WriterException | IOException e) { log.error(e.getMessage()); throw new BusinessException(ErrorCode.SYSTEM_ERROR,"二维码生成失败"); } LoginInfoDTO loginInfoDTO = new LoginInfoDTO(); loginInfoDTO.setStatus(LoginStatusEnum.UNSCANNED.name()); loginInfoDTO.setUuid(uuid); // 二维码uuid绑定,存入缓存 qrcodeCache.put(uuid,loginInfoDTO); // 返回生成的二维码信息 QrcodeVO vo = QrcodeVO.builder().uuid(uuid).qrcode("data:image/png;base64," + base64QR).build(); log.info("-------生成二维码成功:{}-------", uuid); return vo; } @Override public Object checkQrcode(String uuid) { LoginInfoDTO loginInfoDTO = qrcodeCache.getIfPresent(uuid); if (loginInfoDTO == null) { throw new BusinessException(ErrorCode.QRCODE_EXPIRED); } if (Objects.equals(loginInfoDTO.getStatus(), LoginStatusEnum.SCANNED.name())) { return loginInfoDTO; } if (LoginStatusEnum.CONFIRMED.name().equals(loginInfoDTO.getStatus())) { UserModel userModel = userDao.selectById(loginInfoDTO.getUserId()); StpUtil.login(userModel.getId()); UserModelVO userModelVO = BeanUtil.copyProperties(userModel, UserModelVO.class); userModelVO.setToken(StpUtil.getTokenValue()); userModelVO.setChatInfo(wxChatParam); return userModelVO; } return null; } @Override public void scanQrcode(ScanInfoDTO scanInfoDTO) { LoginInfoDTO loginInfoDTO = qrcodeCache.getIfPresent(scanInfoDTO.getUuid()); if (loginInfoDTO != null) { loginInfoDTO.setStatus(LoginStatusEnum.SCANNED.name()); } if (loginInfoDTO != null) { qrcodeCache.put(scanInfoDTO.getUuid(),loginInfoDTO); } log.info("-------扫码成功uuid:{}-------", scanInfoDTO.getUuid()); } @Override public void confirm(ScanInfoDTO scanInfoDTO) { LoginInfoDTO loginInfoDTO = qrcodeCache.getIfPresent(scanInfoDTO.getUuid()); if (loginInfoDTO != null) { loginInfoDTO.setStatus(LoginStatusEnum.CONFIRMED.name()); loginInfoDTO.setUserId(scanInfoDTO.getUserId()); qrcodeCache.put(scanInfoDTO.getUuid(),loginInfoDTO); } log.info("-------确认登录成功uuid:{}-------", scanInfoDTO.getUuid()); } }