/*
|
* 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);
|
}
|
}
|
}
|