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.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import vvpkassistant.Data.WxChatParam; import vvpkassistant.Tools.BcryptUtils; import vvpkassistant.Tools.CacheHolder; 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.model.MailModel; 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) { if (model.getUserNameOrEmail().isEmpty()){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户名或邮箱不能为空"); } LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(UserModel::getEmail,model.getUserNameOrEmail()) .or() .eq(UserModel::getUserName,model.getUserNameOrEmail()) .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(userInfo.getPassword(),userModelDTO.getOldPassword())) { 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 usernameWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(UserModel::getEmail,userModelDTO.getEmail()); UserModel userModel = userDao.selectOne(lambdaQueryWrapper); UserModel usernameModel = userDao.selectOne(usernameWrapper .eq(UserModel::getUserName, userModelDTO.getUserName())); if (userModel != null) { throw new BusinessException(ErrorCode.MAIL_ALREADY_EXIST); } if (usernameModel != null) { throw new BusinessException(ErrorCode.USERNAME_ALREADY_EXIST); } if (userModelDTO.getPassword().length() < 6 ){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位"); } if (userModelDTO.getUserName().isEmpty()){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户名不能为空"); } 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); StpUtil.login(userModelVO.getId()); userModelVO.setToken(StpUtil.getTokenValue()); userModelVO.setHavaPassword(true); userModelVO.setNewAccount(true); userModelVO.setChatInfo(wxChatParam); log.info("用户{}注册,邮箱{},手机号{}",userModelVO.getId(),userModelVO.getEmail(),userModelVO.getPhoneNumber()); 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); } if (userModel.getStatus() == 0){ throw new BusinessException(ErrorCode.USER_HAS_ACTIVATED); } 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.getId()); StpUtil.login(userModel.getId()); UserModelVO userModelVO = BeanUtil.copyProperties(userModel, UserModelVO.class); userModelVO.setToken(StpUtil.getTokenValue()); userModelVO.setChatInfo(wxChatParam); return userModelVO; } return null; } @Override public LoginInfoDTO scanQrcode(ScanInfoDTO scanInfoDTO) { LoginInfoDTO loginInfoDTO = qrcodeCache.getIfPresent(scanInfoDTO.getUuid()); if (loginInfoDTO != null) { loginInfoDTO.setStatus(LoginStatusEnum.SCANNED.name()); qrcodeCache.put(scanInfoDTO.getUuid(),loginInfoDTO); } log.info("-------扫码成功uuid:{}-------", scanInfoDTO.getUuid()); return loginInfoDTO; } @Override public void confirm(ScanInfoDTO scanInfoDTO) { LoginInfoDTO loginInfoDTO = qrcodeCache.getIfPresent(scanInfoDTO.getUuid()); if (loginInfoDTO != null) { loginInfoDTO.setStatus(LoginStatusEnum.CONFIRMED.name()); loginInfoDTO.setId(scanInfoDTO.getId()); qrcodeCache.put(scanInfoDTO.getUuid(),loginInfoDTO); } log.info("-------确认登录成功uuid:{}-------", scanInfoDTO.getUuid()); } @Override public void logOut(Integer id) { StpUtil.logout(id); } @Override public boolean setPassWord(UserModelDTO userModelDTO) { UserModel userModel = userDao.selectById(userModelDTO.getId()); if (userModel == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } if (userModel.getPassword()!= null){ throw new BusinessException(ErrorCode.SYSTEM_ERROR,"已设置过密码"); } if (userModelDTO.getPassword().length()< 6 ){ throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码长度不能小于 6 位"); } if (!Objects.equals(userModelDTO.getPassword(), userModelDTO.getConfirmPassword())) { log.error("密码{},确认密码{}",userModelDTO.getPassword(),userModelDTO.getConfirmPassword()); throw new BusinessException(ErrorCode.PARAMS_ERROR,"两次密码输入不一致"); }else{ UserModel saveEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class); saveEntity.setPassword(BcryptUtils.encryptPassword(userModelDTO.getPassword())); return userDao.updateById(saveEntity) == 1 ; } } @Override public Object sendForgetPassWordMail(MailModel mailModel) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); UserModel userModel = userDao.selectOne(lambdaQueryWrapper .eq(UserModel::getEmail, mailModel.getMailAddress()) .eq(UserModel::getStatus, 0) .eq(UserModel::getMailVerification, 0)); if (userModel == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } mailService.sendForgetPassWordMail(mailModel.getMailAddress(),userModel.getId()); return true; } @Override public Object resetPassWord(UserModelDTO userModelDTO) { Integer i = SaTempUtil.parseToken(userModelDTO.getToken(), Integer.class); UserModel userModel = userDao.selectById(i); if (userModel == null) { throw new BusinessException(ErrorCode.USER_DOES_NOT_EXIST); } if (userModelDTO.getPassword().equals(userModelDTO.getConfirmPassword())) { UserModel updateEntity = BeanUtil.copyProperties(userModelDTO, UserModel.class); updateEntity.setPassword(BcryptUtils.encryptPassword(updateEntity.getPassword())); return userDao.updateById(updateEntity) == 1; } return false; } @Override public Boolean updateUserMail(MailModel mailModel) { String mail = CacheHolder.VERIFICATION_MAIL.getIfPresent(mailModel.getCode()); if (mail != null && mail.isEmpty()) { throw new BusinessException(ErrorCode.SYSTEM_ERROR,"验证码过期或验证码错误"); } LambdaQueryWrapper duplicateMailUserWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); UserModel duplicateMailUser = userDao.selectOne(duplicateMailUserWrapper .eq(UserModel::getEmail, mailModel.getMailAddress())); if (duplicateMailUser != null) { throw new BusinessException(ErrorCode.SYSTEM_ERROR,"邮箱地址已被使用"); } UserModel userModel = userDao.selectOne(lambdaQueryWrapper .eq(UserModel::getEmail, mail) .eq(UserModel::getMailVerification, 0 )); if (userModel == null) { throw new BusinessException(ErrorCode.USER_MAIL_NOT_VERIFICATION); } userModel.setEmail(mailModel.getMailAddress()); mailService.sendVerificationMail(mailModel.getMailAddress(),userModel.getId()); userModel.setMailVerification(1); return userDao.updateById(userModel) == 1; } @Override public Boolean checkUserName(UserModelDTO userModelDTO) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); UserModel userModel = userDao.selectOne(lambdaQueryWrapper .eq(UserModel::getUserName, userModelDTO.getUserName())); return userModel == null; } }