Commit f6963021 by fanjiaxin

新加入实名认证相关代码

parent f7b14568
Pipeline #75475 passed with stages
in 1 minute 11 seconds
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
<yanxuan-log.version>1.0.2</yanxuan-log.version> <yanxuan-log.version>1.0.2</yanxuan-log.version>
<tracer.version>1.0.8-RELEASE</tracer.version> <tracer.version>1.0.8-RELEASE</tracer.version>
<shadow.version>1.0.6-RELEASE</shadow.version> <shadow.version>1.0.6-RELEASE</shadow.version>
<user-info-client.version>1.0.14-RELEASE</user-info-client.version>
<netease.ddb.version>4.6.0.4</netease.ddb.version> <netease.ddb.version>4.6.0.4</netease.ddb.version>
<!-- store-db --> <!-- store-db -->
...@@ -184,6 +185,12 @@ ...@@ -184,6 +185,12 @@
<version>${shadow.version}</version> <version>${shadow.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.netease.yanxuan</groupId>
<artifactId>user-info-client</artifactId>
<version>${user-info-client.version}</version>
</dependency>
<!-- store-db --> <!-- store-db -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
......
package com.netease.yanxuan.wx.store.sharer.biz.meta.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* @Description 短信场景枚举
* @Author fanjiaxin
* @Date 2025/5/14 13:12
*/
@Getter
@RequiredArgsConstructor
public enum SmsScenesEnum {
REAL_NAME_AUTHENTICATED("实名认证");
private final String desc;
/**
* 检查场景标识是否存在
*
* @param scenes 场景标识
* @return true:存在,false: 不存在
*/
public static boolean checkScenes(String scenes) {
for (SmsScenesEnum scenesEnum : SmsScenesEnum.values()) {
if (scenesEnum.name().equals(scenes)) {
return true;
}
}
return false;
}
}
\ No newline at end of file
package com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @Description 实名认证-业务对象
* @Author fanjiaxin
* @Date 2025/3/9 13:44
*/
@Data
public class RealNameAuthenticatedBO {
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空")
private String username;
/**
* 身份证号
*/
@NotBlank(message = "身份证号不能为空")
private String idCardNumber;
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
private String mobilePhone;
/**
* 验证码
*/
@NotBlank(message = "验证码不能为空")
private String smsCode;
}
package com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @Description 发送短信验证码-业务对象
* @Author fanjiaxin
* @Date 2025/3/9 13:44
*/
@Data
public class SmsCodeBO {
/**
* 场景
*/
@NotBlank(message = "场景不能为空")
private String scenes;
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
private String mobilePhone;
}
...@@ -49,6 +49,11 @@ public class SharerInfoVO { ...@@ -49,6 +49,11 @@ public class SharerInfoVO {
* 绑定时需要的queryString参数 * 绑定时需要的queryString参数
*/ */
private String bindQueryString; private String bindQueryString;
/**
* 是否实名认证
*/
private Boolean isRealNameAuthenticated;
@Getter @Getter
@RequiredArgsConstructor @RequiredArgsConstructor
......
package com.netease.yanxuan.wx.store.sharer.biz.service;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.SmsCodeBO;
/**
* @Description 短信-业务层
* @Author fanjiaxin
* @Date 2025/3/10 12:28
*/
public interface ISmsService {
/**
* 发送短信验证码
*/
void sendSmsCode(SmsCodeBO bo);
/**
* 验证短信验证码
*/
void verifySmsCode(String scenes, String phone, String code);
}
package com.netease.yanxuan.wx.store.sharer.biz.service; package com.netease.yanxuan.wx.store.sharer.biz.service;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.RealNameAuthenticatedBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO;
...@@ -30,4 +31,9 @@ public interface IUserService { ...@@ -30,4 +31,9 @@ public interface IUserService {
* 查询平台默认的分佣比例 * 查询平台默认的分佣比例
*/ */
UserCommissionRatioVO getCommissionRatioDefault(); UserCommissionRatioVO getCommissionRatioDefault();
/**
* 实名认证
*/
void realNameAuthenticated(RealNameAuthenticatedBO bo);
} }
package com.netease.yanxuan.wx.store.sharer.biz.service.impl;
import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserContextHolder;
import com.netease.yanxuan.wx.store.sharer.biz.meta.enums.SmsScenesEnum;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.SmsCodeBO;
import com.netease.yanxuan.wx.store.sharer.biz.service.ISmsService;
import com.netease.yanxuan.wx.store.sharer.common.exception.BizException;
import com.netease.yanxuan.wx.store.sharer.common.handler.RedisClient;
import com.netease.yanxuan.wx.store.sharer.common.util.PhoneCheckUtils;
import com.netease.yanxuan.wx.store.sharer.common.util.RandomUtils;
import com.netease.yanxuan.wx.store.sharer.integration.client.IUasClient;
import com.netease.yanxuan.wx.store.sharer.integration.config.SmsConfig;
import com.netease.yanxuan.wx.store.sharer.integration.constant.SmsConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
/**
* @Description 短信-业务实现类
* @Author fanjiaxin
* @Date 2025/3/10 12:31
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SmsServiceImpl implements ISmsService {
private final SmsConfig smsConfig;
private final IUasClient iUasClient;
private final RedisClient redisClient;
@Override
public void sendSmsCode(SmsCodeBO bo) {
log.error("[op:sendSmsCode] begin...");
// 前置校验
validateSendCode(bo.getScenes(), bo.getMobilePhone());
boolean success = sendCode(bo.getScenes(), bo.getMobilePhone());
if (!success) {
log.error("[op:sendSmsCode] is fail...");
throw new BizException("短信验证码发送失败");
}
}
@Override
public void verifySmsCode(String scenes, String phone, String code) {
// 检查每日校验次数上限
if (!validateVerifyCodeVerifyCount(scenes, phone)) {
throw new BizException("验证码校验达到次数上限");
}
// 对比验证码
String codeKey = String.format(SmsConstant.SMS_CODE_KEY, scenes, phone);
String originCode = redisClient.getStr(codeKey);
if (StringUtils.isBlank(originCode) || !originCode.equals(code)) {
throw new BizException("验证码有误");
}
// 验证成功后清除验证码
redisClient.delete(codeKey);
}
/**
* 发送短信
*/
private Boolean sendCode(String scenes, String mobilePhone) {
log.error("[op:sendSmsCode] sendCode...");
// 生成6位随机数字
String code = RandomUtils.randomNumber(6);
// uas发送验证码
boolean success = iUasClient.sendSmsCode(LoginUserContextHolder.get().getOpenId(), mobilePhone, code, scenes);
if (!success) {
return false;
}
int sendIntervalSeconds = smsConfig.getSendIntervalSeconds();
int codeValidSeconds = smsConfig.getCodeValidSeconds();
// 标记验证码已发送
String sendCodeKey = String.format(SmsConstant.SMS_SEND_INTERVAL_KEY, scenes, mobilePhone);
redisClient.setStr(sendCodeKey, String.valueOf(System.currentTimeMillis()), sendIntervalSeconds);
// 验证码放进缓存
String codeKey = String.format(SmsConstant.SMS_CODE_KEY, scenes, mobilePhone);
redisClient.setStr(codeKey, code, codeValidSeconds);
return true;
}
/**
* 手机格式校验以及验证码相关校验
*
* @param scenes 场景
* @param mobilePhone 手机号
*/
private void validateSendCode(String scenes, String mobilePhone) {
log.error("[op:sendSmsCode] validate...");
// 场景标识检查
if (!SmsScenesEnum.checkScenes(scenes)) {
throw new BizException("场景标识错误");
}
// 手机格式校验
if (!PhoneCheckUtils.checkPhoneFormat(mobilePhone)) {
throw new BizException("手机号码格式错误");
}
// 验证码发送标记缓存
if (!validateVerifyCodeSend(scenes, mobilePhone)) {
throw new BizException("验证码发送过于频繁,请稍后重试");
}
// 验证码每日发送次数上限缓存
if (!validateVerifyCodeSendCount(scenes, mobilePhone)) {
throw new BizException("验证码发送达到次数上限");
}
}
/**
* 验证码发送标记
*
* @param scenes 场景
* @param mobilePhone 手机号
* @return true: 标记不存在,false:标记存在
*/
private Boolean validateVerifyCodeSend(String scenes, String mobilePhone) {
String key = String.format(SmsConstant.SMS_SEND_INTERVAL_KEY, scenes, mobilePhone);
return Boolean.FALSE.equals(redisClient.hasKey(key));
}
/**
* 验证码每日发送次数上限
*
* @param scenes 场景
* @param mobilePhone 手机号
* @return true: 未达上限,false: 到了上限
*/
private Boolean validateVerifyCodeSendCount(String scenes, String mobilePhone) {
String key = String.format(SmsConstant.SMS_SEND_COUNT_KEY, scenes, mobilePhone, getToday());
BoundValueOperations<String, Object> operations = redisClient.boundValueOps(key);
Long sendCount = operations.increment();
operations.expire(1, TimeUnit.DAYS);
return smsConfig.getSendMaxCount() >= (sendCount == null ? 1 : sendCount);
}
/**
* 验证码每日校验次数上限
*
* @param scenes 场景
* @param mobilePhone 手机号
* @return true: 未达上限,false: 到了上限
*/
public Boolean validateVerifyCodeVerifyCount(String scenes, String mobilePhone) {
String key = String.format(SmsConstant.SMS_VERIFY_COUNT_KEY, scenes, mobilePhone, getToday());
BoundValueOperations<String, Object> operations = redisClient.boundValueOps(key);
Long verifyCount = operations.increment();
operations.expire(1, TimeUnit.DAYS);
return smsConfig.getVerifyCodeMaxCount() >= (verifyCount == null ? 1 : verifyCount);
}
private String getToday() {
// 获取当前日期
LocalDate today = LocalDate.now();
// 定义日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
// 格式化日期
return today.format(formatter);
}
}
package com.netease.yanxuan.wx.store.sharer.biz.service.impl; package com.netease.yanxuan.wx.store.sharer.biz.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.netease.yanxuan.wx.store.sharer.biz.config.DrmSharerConfig; import com.netease.yanxuan.wx.store.sharer.biz.config.DrmSharerConfig;
import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserContextHolder; import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserContextHolder;
import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserHelper; import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserHelper;
import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserInfo; import com.netease.yanxuan.wx.store.sharer.biz.core.LoginUserInfo;
import com.netease.yanxuan.wx.store.sharer.biz.meta.enums.SmsScenesEnum;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.RealNameAuthenticatedBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO;
import com.netease.yanxuan.wx.store.sharer.biz.service.ISmsService;
import com.netease.yanxuan.wx.store.sharer.biz.service.IUserService; import com.netease.yanxuan.wx.store.sharer.biz.service.IUserService;
import com.netease.yanxuan.wx.store.sharer.common.exception.BizException;
import com.netease.yanxuan.wx.store.sharer.common.exception.WeChatException; import com.netease.yanxuan.wx.store.sharer.common.exception.WeChatException;
import com.netease.yanxuan.wx.store.sharer.dal.mapper.SharerInfoMapper; import com.netease.yanxuan.wx.store.sharer.dal.mapper.SharerInfoMapper;
import com.netease.yanxuan.wx.store.sharer.dal.meta.model.po.SharerInfo; import com.netease.yanxuan.wx.store.sharer.dal.meta.model.po.SharerInfo;
import com.netease.yanxuan.wx.store.sharer.integration.client.IUserInfoClient;
import com.netease.yanxuan.wx.store.sharer.integration.constant.WeChatApi; import com.netease.yanxuan.wx.store.sharer.integration.constant.WeChatApi;
import com.netease.yanxuan.wx.store.sharer.integration.handler.impl.WeChatShopSetSharerCommissionRequest; import com.netease.yanxuan.wx.store.sharer.integration.handler.impl.WeChatShopSetSharerCommissionRequest;
import com.netease.yanxuan.wx.store.sharer.integration.handler.impl.WeChatShopSharerListRequest; import com.netease.yanxuan.wx.store.sharer.integration.handler.impl.WeChatShopSharerListRequest;
...@@ -52,6 +58,8 @@ public class UserServiceImpl implements IUserService { ...@@ -52,6 +58,8 @@ public class UserServiceImpl implements IUserService {
private final WeChatShopSharerListRequest weChatShopSharerListRequest; private final WeChatShopSharerListRequest weChatShopSharerListRequest;
private final WeChatShopSetSharerCommissionRequest weChatShopSetSharerCommissionRequest; private final WeChatShopSetSharerCommissionRequest weChatShopSetSharerCommissionRequest;
private final DrmSharerConfig drmSharerConfig; private final DrmSharerConfig drmSharerConfig;
private final IUserInfoClient iUserInfoClient;
private final ISmsService iSmsService;
@Override @Override
...@@ -160,6 +168,8 @@ public class UserServiceImpl implements IUserService { ...@@ -160,6 +168,8 @@ public class UserServiceImpl implements IUserService {
} else { } else {
sharerInfoMapper.updateById(sharerInfo); sharerInfoMapper.updateById(sharerInfo);
} }
// 身份证号码不为空证明已经实名认证过
result.setIsRealNameAuthenticated(StringUtils.isNotBlank(sharerInfo.getIdCardNumber()));
return result; return result;
} }
...@@ -173,9 +183,13 @@ public class UserServiceImpl implements IUserService { ...@@ -173,9 +183,13 @@ public class UserServiceImpl implements IUserService {
sharerInfoLqw.last("LIMIT 1"); sharerInfoLqw.last("LIMIT 1");
SharerInfo sharerInfo = sharerInfoMapper.selectOne(sharerInfoLqw); SharerInfo sharerInfo = sharerInfoMapper.selectOne(sharerInfoLqw);
// 已经绑定 // 已经绑定
if (null != sharerInfo && StringUtils.isNotBlank(sharerInfo.getSharerAppid())) { if (null != sharerInfo) {
if(StringUtils.isNotBlank(sharerInfo.getSharerAppid())){
result.setSharerAppId(sharerInfo.getSharerAppid()); result.setSharerAppId(sharerInfo.getSharerAppid());
} }
// 身份证号码不为空证明已经实名认证过
result.setIsRealNameAuthenticated(StringUtils.isNotBlank(sharerInfo.getIdCardNumber()));
}
return result; return result;
} }
...@@ -185,6 +199,27 @@ public class UserServiceImpl implements IUserService { ...@@ -185,6 +199,27 @@ public class UserServiceImpl implements IUserService {
return UserCommissionRatioVO.builder().commissionRatio(defaultCommissionRatio).build(); return UserCommissionRatioVO.builder().commissionRatio(defaultCommissionRatio).build();
} }
@Override
public void realNameAuthenticated(RealNameAuthenticatedBO bo) {
// 验证身份信息
Boolean isVerifySuccess = iUserInfoClient.verifyByIdentityNo(bo.getUsername(), bo.getIdCardNumber());
if(null == isVerifySuccess || !isVerifySuccess){
throw new BizException("实名认证不通过");
}
// 验证码校验
iSmsService.verifySmsCode(SmsScenesEnum.REAL_NAME_AUTHENTICATED.name(),
bo.getMobilePhone(), bo.getSmsCode());
// 验证通过后更新实名认证信息
LoginUserInfo loginUserInfo = LoginUserContextHolder.get();
LambdaUpdateWrapper<SharerInfo> sharerInfoLuw = Wrappers.lambdaUpdate();
sharerInfoLuw.set(SharerInfo::getUsername, bo.getUsername());
sharerInfoLuw.set(SharerInfo::getIdCardNumber, bo.getIdCardNumber());
sharerInfoLuw.set(SharerInfo::getMobilePhone, bo.getMobilePhone());
sharerInfoLuw.set(SharerInfo::getUpdateTime, new Date());
sharerInfoLuw.eq(SharerInfo::getOpenId, loginUserInfo.getOpenId());
sharerInfoMapper.update(null, sharerInfoLuw);
}
private SharerInfoVO getSharerRegisterBindInfo(String openId) { private SharerInfoVO getSharerRegisterBindInfo(String openId) {
// 获取推客注册与绑定信息 // 获取推客注册与绑定信息
WeChatSharerRegisterBindVO sharerRegisterBindVO = weChatShopSharerRegisterBindRequest.handle(openId); WeChatSharerRegisterBindVO sharerRegisterBindVO = weChatShopSharerRegisterBindRequest.handle(openId);
...@@ -196,6 +231,7 @@ public class UserServiceImpl implements IUserService { ...@@ -196,6 +231,7 @@ public class UserServiceImpl implements IUserService {
result.setRegisterQueryString(sharerRegisterBindVO.getRegister_query_string()); result.setRegisterQueryString(sharerRegisterBindVO.getRegister_query_string());
result.setBindBusinessType(sharerRegisterBindVO.getBind_business_type()); result.setBindBusinessType(sharerRegisterBindVO.getBind_business_type());
result.setBindQueryString(sharerRegisterBindVO.getBind_query_string()); result.setBindQueryString(sharerRegisterBindVO.getBind_query_string());
result.setIsRealNameAuthenticated(false);
return result; return result;
} }
} }
package com.netease.yanxuan.wx.store.sharer.common.handler; package com.netease.yanxuan.wx.store.sharer.common.handler;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -49,4 +50,12 @@ public class RedisClient { ...@@ -49,4 +50,12 @@ public class RedisClient {
public Boolean delete(String key) { public Boolean delete(String key) {
return redisTemplate.delete(key); return redisTemplate.delete(key);
} }
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
public BoundValueOperations<String, Object> boundValueOps(String key) {
return redisTemplate.boundValueOps(key);
}
} }
\ No newline at end of file
/**
* @(#)PhoneCheckUtils.java, 2022/7/25.
* <p/>
* Copyright 2022 Netease, Inc. All rights reserved.
* NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.netease.yanxuan.wx.store.sharer.common.util;
import org.apache.commons.lang3.StringUtils;
import java.util.regex.Pattern;
/**
* @author 张艳(wb.zhangyan12 @ mesg.corp.netease.com)
*/
public class PhoneCheckUtils {
private static final String regex = "^[1][0-9]{10}$";
/**
* 手机号码格式验证
*
* @param phone 手机号
*/
public static boolean checkPhoneFormat(String phone) {
if (StringUtils.isBlank(phone)) {
return false;
}
if (phone.length() != 11) {
return false;
} else {
return Pattern.compile(regex).matcher(phone).matches();
}
}
}
\ No newline at end of file
/**
* @(#)RandomUtils.java, 2022/7/25.
* <p/>
* Copyright 2022 Netease, Inc. All rights reserved.
* NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.netease.yanxuan.wx.store.sharer.common.util;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author 张艳(wb.zhangyan12 @ mesg.corp.netease.com)
*/
public class RandomUtils {
private static final String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final String NUMBER = "0123456789";
/**
* 获取随机字符串
*
* @param length 生成的随机字符串长度
* @return 返回随机字符串
*/
public static String randomString(int length) {
return randomString(CHARS, length);
}
/**
* 获取随机数字字符串
*
* @param length
* @return
*/
public static String randomNumber(int length) {
return randomString(NUMBER, length);
}
/**
* 获取随机字符串
*
* @param sampleString 样本字符串
* @param length 生成的随机字符串长度
* @return 返回随机字符串
*/
public static String randomString(String sampleString, int length) {
if (sampleString == null || sampleString.length() == 0) {
return "";
}
if (length < 1) {
length = 1;
}
final StringBuilder sb = new StringBuilder(length);
int baseLength = sampleString.length();
while (sb.length() < length) {
int number = ThreadLocalRandom.current().nextInt(baseLength);
sb.append(sampleString.charAt(number));
}
return sb.toString();
}
public static String genUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
}
\ No newline at end of file
...@@ -49,6 +49,18 @@ public class SharerInfo implements Serializable { ...@@ -49,6 +49,18 @@ public class SharerInfo implements Serializable {
*/ */
private Integer commissionType; private Integer commissionType;
/** /**
* 姓名
*/
private String username;
/**
* 身份证号
*/
private String idCardNumber;
/**
* 手机号
*/
private String mobilePhone;
/**
* 创建时间 * 创建时间
*/ */
private Date createTime; private Date createTime;
......
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
<groupId>com.netease.yanxuan</groupId> <groupId>com.netease.yanxuan</groupId>
<artifactId>yanxuan-wx-store-sharer-common</artifactId> <artifactId>yanxuan-wx-store-sharer-common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.netease.yanxuan</groupId>
<artifactId>user-info-client</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
package com.netease.yanxuan.wx.store.sharer.integration.client;
/**
* @Description Uas服务
* @Author fanjiaxin
* @Date 2025/5/14 13:47
*/
public interface IUasClient {
/**
* 发送短信
*/
boolean sendSmsCode(String uid, String phone, String code, String scenes);
}
package com.netease.yanxuan.wx.store.sharer.integration.client;
/**
* @Description UserInfo服务
* @Author fanjiaxin
* @Date 2025/5/14 13:47
*/
public interface IUserInfoClient {
/**
* 实名认证
*/
Boolean verifyByIdentityNo(String realName, String identityNo);
}
/**
* @(#)UasClientImpl.java, 2022/7/27.
* <p/>
* Copyright 2022 Netease, Inc. All rights reserved.
* NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.netease.yanxuan.wx.store.sharer.integration.client.impl;
import com.alibaba.fastjson.JSONObject;
import com.netease.yanxuan.wx.store.sharer.integration.client.IUasClient;
import com.netease.yanxuan.wx.store.sharer.integration.config.RpcConfig;
import com.netease.yanxuan.wx.store.sharer.integration.config.SmsConfig;
import com.netease.yanxuan.wx.store.sharer.integration.handler.RestTemplateHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
@Service
public class UasClientImpl implements IUasClient {
private final SmsConfig smsConfig;
private final RpcConfig rpcConfig;
private final RestTemplateHandler restTemplateHandler;
private static final String PRODUCT = "yanxuan-wx-store-sharer";
private static final String SMS_SEND_PATH = "/push/sms/";
private static final String TOPIC_FORMAT = "verify_%s";
/**
* 发送验证码
*
* @param phone 手机号码
* @param code 验证码
* @param scenes 业务场景标识
* @return
*/
@Override
public boolean sendSmsCode(String uid, String phone, String code, String scenes) {
Map<String, Object> params = assembleSmsArg(uid, phone, code, scenes);
String httpUrl = rpcConfig.getUasUrl() + SMS_SEND_PATH + PRODUCT;
try {
JSONObject rpcResult = restTemplateHandler.execute(httpUrl, HttpMethod.POST, params, JSONObject.class);
return rpcResult.getIntValue("code") == 200;
} catch (Exception e) {
log.error("[SMS_SEND_CODE] send code error:", e);
return false;
}
}
private Map<String, Object> assembleSmsArg(String uid, String phone, String code, String scenes) {
Map<String, Object> params = new HashMap<>();
params.put("phone", phone);
params.put("message", String.format(smsConfig.getMessageFormat(), code, smsConfig.getCodeValidSeconds()));
params.put("level", 1);
params.put("uid", uid);
params.put("topic", String.format(TOPIC_FORMAT, scenes));
return params;
}
}
/**
* @(#)UasClientImpl.java, 2022/7/27.
* <p/>
* Copyright 2022 Netease, Inc. All rights reserved.
* NETEASE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.netease.yanxuan.wx.store.sharer.integration.client.impl;
import com.netease.yanxuan.userinfo.client.meta.CommonResult;
import com.netease.yanxuan.wx.store.sharer.integration.client.IUserInfoClient;
import com.netease.yanxuan.wx.store.sharer.integration.facade.UserInfoFacade;
import com.netease.yanxuan.wx.store.sharer.integration.meta.model.bo.UserInfoVerifyByIdentityNoBO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class UserInfoClientImpl implements IUserInfoClient {
private final UserInfoFacade userInfoFacade;
@Override
public Boolean verifyByIdentityNo(String realName, String identityNo) {
log.info("[op:verifyByIdentityNo] realName:{}, identityNo:{}", realName, identityNo);
try {
UserInfoVerifyByIdentityNoBO bo = UserInfoVerifyByIdentityNoBO.builder().realName(realName).identityNo(identityNo).build();
CommonResult<Boolean> result = userInfoFacade.verifyByIdentityNo(bo);
if (HttpStatus.OK.value() == result.getCode()) {
return result.getResult();
}
} catch (Exception e) {
log.error("[op:verifyByIdentityNo] error", e);
}
return false;
}
}
package com.netease.yanxuan.wx.store.sharer.integration.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "rpc")
@Getter
@Setter
public class RpcConfig {
/**
* uas
*/
private String uasUrl;
}
package com.netease.yanxuan.wx.store.sharer.integration.config;
import com.ctrip.framework.apollo.spring.annotation.EnableAutoUpdateApolloConfig;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @Description 应用配置
* @Author fanjiaxin
* @Date 2025/3/10 17:26
*/
@Data
@Component
@EnableAutoUpdateApolloConfig
public class SmsConfig {
/**
* 验证码发送最大次数
*/
@Value("${sms.send.max.count:10}")
private Integer sendMaxCount;
/**
* 验证码发送间隔时间,秒
*/
@Value("${sms.send.interval.seconds:60}")
private Integer sendIntervalSeconds;
/**
* 验证码有效时间,单位秒
*/
@Value("${sms.code.valid.seconds:300}")
private Integer codeValidSeconds;
/**
* 验证码短信格式
*/
@Value("${sms.message.format:}")
private String messageFormat;
/**
* 验证码每日校验次数上限
*/
@Value("${sms.verify.code.max.count:10}")
private Integer verifyCodeMaxCount;
}
\ No newline at end of file
package com.netease.yanxuan.wx.store.sharer.integration.constant;
/**
* @Description 短信常量类
* @Author fanjiaxin
* @Date 2025/3/11 11:59
*/
public class SmsConstant {
/**
* 短信验证码缓存key,公共前缀
*/
public static final String SMS_VERIFY_CODE_COMMON_PREFIX = "act_mobile_verify_";
/**
* 验证码发送标记缓存
* 验证码使用业务场景标识+手机号
*/
public static final String SMS_SEND_INTERVAL_KEY = SMS_VERIFY_CODE_COMMON_PREFIX + "send_interval_%s_%s";
/**
* 用户当前验证码缓存
* 验证码使用业务场景标识+手机号
*/
public static final String SMS_CODE_KEY = SMS_VERIFY_CODE_COMMON_PREFIX + "code_%s_%s";
/**
* 验证码每日发送次数上限缓存
* 验证码使用业务场景标识+手机号+今日0点时间戳毫秒
*/
public static final String SMS_SEND_COUNT_KEY = SMS_VERIFY_CODE_COMMON_PREFIX + "send_count_%s_%s_%s";
/**
* 验证码每日校验次数上限缓存
* 验证码使用业务场景标识+手机号+今日0点时间戳毫秒
*/
public static final String SMS_VERIFY_COUNT_KEY = SMS_VERIFY_CODE_COMMON_PREFIX + "verify_count_%s_%s_%s";
}
package com.netease.yanxuan.wx.store.sharer.integration.facade;
import com.netease.yanxuan.missa.client.annotation.MissaClient;
import com.netease.yanxuan.userinfo.client.meta.CommonResult;
import com.netease.yanxuan.wx.store.sharer.integration.meta.model.bo.UserInfoVerifyByIdentityNoBO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@MissaClient(serviceCode = "user-info")
public interface UserInfoFacade {
@RequestMapping(value = "/user/realName/verifyByIdentityNo", method = RequestMethod.POST)
CommonResult<Boolean> verifyByIdentityNo(@RequestBody UserInfoVerifyByIdentityNoBO bo);
}
package com.netease.yanxuan.wx.store.sharer.integration.handler;
import com.alibaba.fastjson.JSON;
import com.netease.yanxuan.wx.store.sharer.common.exception.BizException;
import com.netease.yanxuan.wx.store.sharer.common.handler.RestTemplateClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
/**
* @Description RestTemplateClient
* @Author fanjiaxin
* @Date 2025/3/11 17:27
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RestTemplateHandler {
private final RestTemplateClient restTemplateClient;
/**
* 执行操作
*/
public <T, R> R execute(String url, HttpMethod method, T params, Class<R> resType) {
try {
String resultJson = restTemplateClient.execute(url, method, params);
return JSON.parseObject(resultJson, resType);
} catch (Exception e) {
throw new BizException(e);
}
}
}
package com.netease.yanxuan.wx.store.sharer.integration.meta.model.bo;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* @Description 用户实名认证-业务对象
* @Author fanjiaxin
* @Date 2025/3/11 18:43
*/
@Data
@Builder
public class UserInfoVerifyByIdentityNoBO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 姓名
*/
private String realName;
/**
* 身份证号
*/
private String identityNo;
}
package com.netease.yanxuan.wx.store.sharer.web.controller;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.SmsCodeBO;
import com.netease.yanxuan.wx.store.sharer.biz.service.ISmsService;
import com.netease.yanxuan.wx.store.sharer.common.core.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 短信-控制器
* @Author fanjiaxin
* @Date 2025/3/9 13:10
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/sharer/sms")
@Slf4j
public class SmsController extends BaseController {
private final ISmsService iSmsService;
/**
* 发送短信验证码
*/
@PostMapping("/sendSmsCode")
public Result<Boolean> sendSmsCode(@Validated @RequestBody SmsCodeBO bo) {
iSmsService.sendSmsCode(bo);
return Result.ok(true);
}
}
package com.netease.yanxuan.wx.store.sharer.web.controller; package com.netease.yanxuan.wx.store.sharer.web.controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.LoginBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.bo.RealNameAuthenticatedBO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.SharerInfoVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserCommissionRatioVO;
import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO; import com.netease.yanxuan.wx.store.sharer.biz.meta.model.vo.UserTokenVO;
import com.netease.yanxuan.wx.store.sharer.biz.service.IUserService; import com.netease.yanxuan.wx.store.sharer.biz.service.IUserService;
import com.netease.yanxuan.wx.store.sharer.common.core.Result; import com.netease.yanxuan.wx.store.sharer.common.core.Result;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* @Description 用户-控制器 * @Description 用户-控制器
...@@ -63,4 +62,13 @@ public class UserController extends BaseController { ...@@ -63,4 +62,13 @@ public class UserController extends BaseController {
UserCommissionRatioVO result = iUserService.getCommissionRatioDefault(); UserCommissionRatioVO result = iUserService.getCommissionRatioDefault();
return Result.ok(result); return Result.ok(result);
} }
/**
* 实名认证
*/
@PostMapping("/realNameAuthenticated")
public Result<Boolean> realNameAuthenticated(@Validated @RequestBody RealNameAuthenticatedBO bo) {
iUserService.realNameAuthenticated(bo);
return Result.ok(true);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment