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