From efdb99f8cecc4afb592afad79c761081d5d5cf22 Mon Sep 17 00:00:00 2001 From: lee <4766465@qq.com> Date: Wed, 18 Dec 2024 13:27:00 +0800 Subject: [PATCH] init --- yami-shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java | 417 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 417 insertions(+), 0 deletions(-) diff --git a/yami-shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java b/yami-shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java new file mode 100644 index 0000000..8430897 --- /dev/null +++ b/yami-shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ +package com.yami.shop.api.controller; + + +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.binarywang.utils.qrcode.MatrixToImageWriter; +import com.google.common.collect.Maps; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.obs.services.ObsClient; +import com.yami.shop.bean.app.param.CheckRegisterSmsParam; +import com.yami.shop.bean.app.param.SendSmsParam; +import com.yami.shop.bean.app.param.UserPwdUpdateParam; +import com.yami.shop.bean.app.param.UserRegisterParam; +import com.yami.shop.bean.enums.SendType; +import com.yami.shop.bean.model.CdnTeamRelation; +import com.yami.shop.bean.model.MarsHandCard; +import com.yami.shop.bean.model.User; +import com.yami.shop.bean.param.im.ImConfigParam; +import com.yami.shop.common.bean.HuaWeiOss; +import com.yami.shop.common.exception.YamiShopBindException; +import com.yami.shop.common.response.ServerResponseEntity; +import com.yami.shop.common.util.PrincipalUtil; +import com.yami.shop.common.util.RedisUtil; +import com.yami.shop.security.common.enums.SysTypeEnum; +import com.yami.shop.security.common.manager.PasswordManager; +import com.yami.shop.security.common.manager.TokenStore; +import com.yami.shop.security.common.service.AppConnectService; +import com.yami.shop.security.common.service.ImTokenService; +import com.yami.shop.security.common.vo.TokenInfoVO; +import com.yami.shop.service.*; +import com.yami.shop.task.common.service.TaskInviteLogService; +import com.yami.shop.task.common.service.TaskUserRelationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import ma.glasnost.orika.MapperFacade; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 用户信息 + * + * @author LGH + */ +@RestController +@RequestMapping("/user") +@Tag(name = "用户注册相关接口") +@AllArgsConstructor +public class UserRegisterController { + + private final UserService userService; + private final MapperFacade mapperFacade; + + private final SmsLogService smsLogService; + + private final AppConnectService appConnectService; + + private final TokenStore tokenStore; + + private final PasswordEncoder passwordEncoder; + + private final PasswordManager passwordManager; + + private final TaskInviteLogService taskInviteLogService; + + private final TaskUserRelationService taskUserRelationService; + + private final MarsHandCardService marsHandCardService; + private final SysConfigService sysConfigService; + + + private final CdnTeamRelationService cdnTeamRelationService; + + private final ImTokenService imTokenService; + + public static final String CHECK_REGISTER_SMS_FLAG = "checkRegisterSmsFlag"; + + public static final String CHECK_UPDATE_PWD_SMS_FLAG = "updatePwdSmsFlag"; + + + /** + * 注册的头像昵称有几个值得注意的地方: + * 1. 如果是微信公众号 or 小程序注册,在注册之前,也就是在进入页面之前,需要调用SocialLoginController 这里面的尝试登录的接口,如果可以登录就直接登录了。 + * 2. 关于发送验证码 + * 1) 手机号注册,要发送验证码 + * 3. 当注册成功的时候,已经返回token,相对来说,已经登录了 + */ + @Operation(summary = "注册") + @PostMapping("/register") + @Transactional(rollbackFor = Exception.class) +// public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) { + public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) { + + // 目前注册都是发验证码去注册的,看看有没有校验验证码成功的标识 +// userService.validate(userRegisterParam, CHECK_REGISTER_SMS_FLAG + userRegisterParam.getCheckRegisterSmsFlag()); + // 正在进行申请注册 + if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUserMobile, userRegisterParam.getMobile())) > 0) { + // 该手机号已注册,无法重新注册 + throw new YamiShopBindException("yami.user.phone.exist"); + } + if (userRegisterParam.getMobile().length() != 11) { + // 该手机号已注册,无法重新注册 + throw new YamiShopBindException("手机号格式不对,应为11位!"); + } + String inviteCode = userRegisterParam.getInviteCode(); + //绑定推荐人关系 + if (!Objects.equals(inviteCode, "") && inviteCode != null) { + // 根据邀请码查询推荐人信息 + User u = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, inviteCode)); + if (u != null) { + // 注册并绑定用户 + User user = appConnectService.registerAndBindUserNick(userRegisterParam.getMobile(), userRegisterParam.getPassword(), null, userRegisterParam.getNickName()); + ImConfigParam imConfig = sysConfigService.getSysConfigObject("IM_CONFIG", ImConfigParam.class); + imTokenService.getImToken(imConfig,user,2); + + CdnTeamRelation teamRelation = cdnTeamRelationService.getOne(new LambdaQueryWrapper<CdnTeamRelation>().eq(CdnTeamRelation::getUserId, u.getUserId())); + CdnTeamRelation cdnTeamRelation = new CdnTeamRelation(); + cdnTeamRelation.setUserId(user.getUserId()); + cdnTeamRelation.setLevel(0); //初始等级为0 + cdnTeamRelation.setAgentLevelId(1);// 初始用户代理等级为会员 + cdnTeamRelation.setPid(u.getUserId()); // 推荐人id + cdnTeamRelation.setJoinTime(new Date()); + cdnTeamRelation.setCreateTime(new Date()); + if (teamRelation != null) { //不为空时推荐人合计拼接推荐人的推荐人合计,为空时推荐人合计只有推荐人 + if (teamRelation.getPids() != null && !teamRelation.getPids().equals("")) { + cdnTeamRelation.setPids(teamRelation.getPids() + (u.getUserId() + ",")); // 推荐人合计id + cdnTeamRelation.setSort(teamRelation.getSort() + 1); + } else { + cdnTeamRelation.setPid(u.getUserId()); // 推荐人id + cdnTeamRelation.setPids("," + u.getUserId() + ","); // 推荐人合计i + cdnTeamRelation.setSort(teamRelation.getSort() + 1); + } + } else { + cdnTeamRelation.setPid(u.getUserId()); // 推荐人id + cdnTeamRelation.setPids("," + u.getUserId() + ","); // 推荐人合计i + cdnTeamRelation.setSort(1); + } + cdnTeamRelationService.save(cdnTeamRelation); + } else { + return ServerResponseEntity.showFailMsg("该邀请码无效!"); + } + } else { + return ServerResponseEntity.showFailMsg("请输入邀请码!"); + } + return ServerResponseEntity.success(); + } + + @GetMapping("/getUserInviteCode") + @Operation(summary = "获取用户邀请码", description = "获取用户邀请码") + public ServerResponseEntity<Boolean> getUserInviteCode(@RequestParam String mobile) { + User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobile)); + if (user != null) { + return ServerResponseEntity.success(true); + } + return ServerResponseEntity.success(false); + } + + + @PutMapping("/sendRegisterSms") + @Operation(summary = "发送注册验证码", description = "发送注册验证码") + public ServerResponseEntity<Void> register(@Valid @RequestBody SendSmsParam sendSmsParam) { + if (userService.count(new LambdaQueryWrapper<User>().eq(User::getUserMobile, sendSmsParam.getMobile())) > 0) { + // 该手机号已注册,无法重新注册 + throw new YamiShopBindException("yami.user.phone.exist"); + } + // 每个手机号每分钟只能发十个注册的验证码,免得接口被利用 + smsLogService.sendSms(SendType.REGISTER, sendSmsParam.getMobile(), sendSmsParam.getMobile(), Maps.newHashMap()); + return ServerResponseEntity.success(); + } + + @PutMapping("/checkRegisterSms") + @Operation(summary = "校验验证码", description = "校验验证码返回校验成功的标识") + public ServerResponseEntity<String> register(@Valid @RequestBody CheckRegisterSmsParam checkRegisterSmsParam) { + // 每个ip每分钟只能发十个注册的验证码,免得接口被利用 + if (!smsLogService.checkValidCode(checkRegisterSmsParam.getMobile(), checkRegisterSmsParam.getValidCode(), SendType.REGISTER)) { + // 验证码有误或已过期 + throw new YamiShopBindException("yami.user.code.error"); + } + String checkRegisterSmsFlag = IdUtil.simpleUUID(); + RedisUtil.set(CHECK_REGISTER_SMS_FLAG + checkRegisterSmsFlag, checkRegisterSmsParam.getMobile(), 600); + return ServerResponseEntity.success(checkRegisterSmsFlag); + } + + @PutMapping("/sendBindSms") + @Operation(summary = "发送绑定验证码", description = "发送绑定验证码") + public ServerResponseEntity<Void> bindSms(@Valid @RequestBody SendSmsParam sendSmsParam) { + // 每个手机号每分钟只能发十个注册的验证码,免得接口被利用 + smsLogService.sendSms(SendType.VALID, sendSmsParam.getMobile(), sendSmsParam.getMobile(), Maps.newHashMap()); + return ServerResponseEntity.success(); + } + + + @PutMapping("/checkUpdatePwdSms") + @Operation(summary = "修改密码校验验证码", description = "校验验证码返回校验成功的标识") + public ServerResponseEntity<String> checkUpdatePwdSms(@RequestBody CheckRegisterSmsParam checkRegisterSmsParam) { + boolean isCheckPass = false; + if (Objects.nonNull(checkRegisterSmsParam) && Objects.nonNull(checkRegisterSmsParam.getMobile())) { + Matcher m = Pattern.compile(PrincipalUtil.MOBILE_REGEXP).matcher(checkRegisterSmsParam.getMobile()); + isCheckPass = m.matches(); + } + if (!isCheckPass) { + throw new YamiShopBindException("yami.user.err.phone"); + } + + User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, checkRegisterSmsParam.getMobile())); + if (user == null) { + // 此用户不存在,请先注册 + throw new YamiShopBindException("yami.user.account.no.exist"); + } + String defaultCode = sysConfigService.getValue("UPDATE_DEFAULT_CODE"); + // 验证码登录 + if(!defaultCode.equals(checkRegisterSmsParam.getValidCode())) { + if (!smsLogService.checkValidCode(user.getUserMobile(), checkRegisterSmsParam.getValidCode(), SendType.UPDATE_PASSWORD)) { + // 验证码有误或已过期 + throw new YamiShopBindException("yami.user.code.error"); + } + } + String checkRegisterSmsFlag = IdUtil.simpleUUID(); + RedisUtil.set(CHECK_UPDATE_PWD_SMS_FLAG + checkRegisterSmsFlag, checkRegisterSmsParam.getMobile(), 600); + return ServerResponseEntity.success(checkRegisterSmsFlag); + } + + @PutMapping("/updatePwd") + @Operation(summary = "修改密码", description = "修改密码") + public ServerResponseEntity<Void> updatePwd(@Valid @RequestBody UserPwdUpdateParam userPwdUpdateParam) { + User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, userPwdUpdateParam.getMobile())); + if (user == null) { + // 无法获取用户信息 + throw new YamiShopBindException("yami.user.no.exist"); + } + UserRegisterParam registerParam = mapperFacade.map(userPwdUpdateParam, UserRegisterParam.class); + // 看看有没有校验验证码成功的标识 + userService.validate(registerParam, CHECK_UPDATE_PWD_SMS_FLAG + userPwdUpdateParam.getCheckRegisterSmsFlag()); + String decryptPassword = passwordManager.decryptPassword(userPwdUpdateParam.getPassword()); + if (StrUtil.isBlank(decryptPassword)) { + // 新密码不能为空 + throw new YamiShopBindException("yami.user.password.no.exist"); + } + if (StrUtil.equals(passwordEncoder.encode(decryptPassword), user.getLoginPassword())) { + // 新密码不能与原密码相同! + throw new YamiShopBindException("yami.user.password.check"); + } + user.setModifyTime(new Date()); + user.setLoginPassword(passwordEncoder.encode(decryptPassword)); + userService.updateById(user); + tokenStore.deleteAllToken(SysTypeEnum.ORDINARY.value().toString(), user.getUserId()); + return ServerResponseEntity.success(); + } + + + @PostMapping("/test") + public ServerResponseEntity updateCardsWithThreadPool() { + // 创建线程池 + ExecutorService executorService = new ThreadPoolExecutor( + 4, // 核心线程数 + 8, // 最大线程数 + 60L, TimeUnit.SECONDS, // 空闲线程存活时间 + new ArrayBlockingQueue<>(100), // 阻塞队列容量 + new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 + ); + + // 分页查询数据 + Page<MarsHandCard> page = new Page<>(1, 500); // 每页500条记录 + List<MarsHandCard> marsHandCardList; + + do { + // 获取当前页的数据 + IPage<MarsHandCard> currentPage = marsHandCardService.page(page, Wrappers.lambdaQuery(MarsHandCard.class)); + marsHandCardList = currentPage.getRecords(); + + // 并发处理数据 + List<Future<?>> futures = new ArrayList<>(); + for (MarsHandCard marsHandCard : marsHandCardList) { + Future<?> future = executorService.submit(() -> { + String QRCodeName = marsHandCard.getCardNumber() + ".png"; + LocalDate now = LocalDate.now(); + String ossImagePath = "handCard/" + now.getYear() + "/" + now.getMonthValue() + "/" + QRCodeName; + + try { + uploadQRCode(marsHandCard.getCardNumber(), 300, 300); + String s = "https://hxsl.obs.cn-north-4.myhuaweicloud.com/" + ossImagePath; + marsHandCardService.update(Wrappers.lambdaUpdate(MarsHandCard.class) + .eq(MarsHandCard::getId, marsHandCard.getId()) + .set(MarsHandCard::getQrCode, s)); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + futures.add(future); + } + + // 等待所有任务完成 + CompletableFuture.allOf(futures.stream().map(CompletableFuture::completedFuture).toArray(CompletableFuture[]::new)).join(); + + // 准备下一页数据 + page.setCurrent(page.getCurrent() + 1); + } while (!marsHandCardList.isEmpty()); + + // 关闭线程池 + executorService.shutdown(); + + return ServerResponseEntity.success(); + } + + public static String uploadQRCode(String content, int width, int height) throws Exception { + String format = "png"; + // 定义二维码的参数 + HashMap<EncodeHintType, Object> hints = new HashMap<>(); + // 定义字符集编码格式 + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + // 纠错的等级 L > M > Q > H 纠错的能力越高可存储的越少,一般使用M + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + // 设置图片边距 + hints.put(EncodeHintType.MARGIN, 2); + + try { + // 最终生成 参数列表 (1.内容 2.格式 3.宽度 4.高度 5.二维码参数) + BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); + LocalDate now = LocalDate.now(); + String fullLocalImagePath = "C://" + now.getYear() + "/" + now.getMonthValue(); + Path path = Paths.get(fullLocalImagePath); + File directory = path.toFile(); + // 确保目录存在 + if (!directory.exists()) { + directory.mkdirs(); + } + // 生成文件名 + String fileName = content + "." + format; + Path filePath = path.resolve(fileName); + // 使用FileOutputStream或者Files.newOutputStream()将二维码写入文件 + try (OutputStream os = Files.newOutputStream(filePath)) { + MatrixToImageWriter.writeToStream(bitMatrix, format, os); + } + return ""; + } catch (IOException | WriterException e) { + throw new RuntimeException("Error generating or saving the QR code.", e); + } + } + + public static String uploadQRCodeToOSS(String content, int width, int height, String ossImagePath, HuaWeiOss aliOss) throws Exception { + String format = "png"; + // 定义二维码的参数 + HashMap<EncodeHintType, Object> hints = new HashMap<>(); + // 定义字符集编码格式 + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + // 纠错的等级 L > M > Q > H 纠错的能力越高可存储的越少,一般使用M + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + // 设置图片边距 + hints.put(EncodeHintType.MARGIN, 2); + + try { + // 最终生成 参数列表 (1.内容 2.格式 3.宽度 4.高度 5.二维码参数) + BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + MatrixToImageWriter.writeToStream(bitMatrix, format, outputStream); + byte[] imageBytes = outputStream.toByteArray(); + + if (Objects.nonNull(aliOss) && BooleanUtil.isTrue(aliOss.getIsOpen())){ + // 创建ObsClient实例 + ObsClient obsClient = new ObsClient(aliOss.getAccessKeyId(), aliOss.getSecretAccessKey(), aliOss.getEndpoint()); + InputStream input = null; + try { + input = new ByteArrayInputStream(imageBytes); + obsClient.putObject(aliOss.getBucketName(), ossImagePath, input); + } catch (Exception e) { + System.out.println("华为Oss上传文件错误"); + } finally { + obsClient.close(); + if (Objects.nonNull(input)) { + input.close(); + } + } + } + // 返回华为云OBS图片地址 + String bucketName = aliOss.getBucketName();//shjw2024 + String endpoint = aliOss.getEndpoint(); //https://oss-cn-shanghai.aliyuncs.com + String pre = endpoint.substring(0, 8); + String fix = endpoint.substring(8); + String path = pre + bucketName + "." + fix; + String img = path + "/" + ossImagePath; + return img; + } catch (IOException | WriterException e) { + throw new RuntimeException(e); + } + } +} -- Gitblit v1.9.3