/* * 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 register(@Valid @RequestBody UserRegisterParam userRegisterParam) { public ServerResponseEntity register(@Valid @RequestBody UserRegisterParam userRegisterParam) { // 目前注册都是发验证码去注册的,看看有没有校验验证码成功的标识 // userService.validate(userRegisterParam, CHECK_REGISTER_SMS_FLAG + userRegisterParam.getCheckRegisterSmsFlag()); // 正在进行申请注册 if (userService.count(new LambdaQueryWrapper().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().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().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 getUserInviteCode(@RequestParam String mobile) { User user = userService.getOne(new LambdaQueryWrapper().eq(User::getUserMobile, mobile)); if (user != null) { return ServerResponseEntity.success(true); } return ServerResponseEntity.success(false); } @PutMapping("/sendRegisterSms") @Operation(summary = "发送注册验证码", description = "发送注册验证码") public ServerResponseEntity register(@Valid @RequestBody SendSmsParam sendSmsParam) { if (userService.count(new LambdaQueryWrapper().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 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 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 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().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 updatePwd(@Valid @RequestBody UserPwdUpdateParam userPwdUpdateParam) { User user = userService.getOne(new LambdaQueryWrapper().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 page = new Page<>(1, 500); // 每页500条记录 List marsHandCardList; do { // 获取当前页的数据 IPage currentPage = marsHandCardService.page(page, Wrappers.lambdaQuery(MarsHandCard.class)); marsHandCardList = currentPage.getRecords(); // 并发处理数据 List> 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 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 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); } } }