实名认证 + 人脸识别

This commit is contained in:
墨大叔 2024-09-23 17:59:52 +08:00
parent 4554d18f7b
commit 344c0f3853
27 changed files with 853 additions and 54 deletions

View File

@ -3,8 +3,12 @@ package com.ruoyi.common.auth.wx;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.TimeUnit;
@ -18,7 +22,10 @@ public class AccessTokenUtil {
@SneakyThrows
public static String getToken() {
if (isTokenExpired()) {
RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
String token = redisCache.getCacheObject(CacheConstants.WX_ACCESS_TOKEN);
if (StringUtils.isBlank(token)) {
String APPID = SpringUtils.getRequiredProperty("wx.appid");
String APPSECRET = SpringUtils.getRequiredProperty("wx.appSecret");
WxMaService wxMaService = new WxMaServiceImpl();
@ -26,13 +33,11 @@ public class AccessTokenUtil {
config.setAppid(APPID);
config.setSecret(APPSECRET);
wxMaService.setWxMaConfig(config);
String accessToken = wxMaService.getAccessToken();
cachedToken = accessToken;
// 更新 token 过期时间
tokenExpirationTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(7200L);
return cachedToken;
token = wxMaService.getAccessToken();
redisCache.setCacheObject(CacheConstants.WX_ACCESS_TOKEN, token, 5400, TimeUnit.SECONDS);
}
return cachedToken;
return token;
}
/**判断token是否过期*/

View File

@ -60,4 +60,9 @@ public class CacheConstants
* 活体检测Token
*/
public static final String LIVENESS_TOKEN = "liveness:";
/**
* 微信access_token
*/
public static final String WX_ACCESS_TOKEN = "wx_access_token";
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.common.valid.face;
import lombok.Data;
/**
* @author wjh
* 2024/9/23
*/
@Data
public class FaceResponseBody {
private String msg;
private Boolean success;
private String code;
private FaceResponseData data;
}

View File

@ -8,8 +8,11 @@ import com.ruoyi.common.valid.liveness.LivenessResponseBody;
import com.ruoyi.common.valid.liveness.LivenessResponseTokenData;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.asn1.ocsp.Request;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
@ -25,26 +28,21 @@ public class FaceUtils {
/**
* 获取活体检测token
* @param returnUrl 前端跳转地址
* @param colorLiveParam 自定义颜色逗号分隔
* @param actionLiveParam 自定义动作逗号分隔
* 人证比对
*/
public static LivenessResponseTokenData getToken(String returnUrl, String colorLiveParam, String actionLiveParam) {
String host = "https://smkjhtjc.market.alicloudapi.com";
String path = "/liveness/h5/token";
String method = "GET";
public static FaceResponseData getFaceIdb(String idcard, String name, String image, String url) {
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + APP_CODE);
Map<String, String> querys = new HashMap<String, String>();
querys.put("returnUrl", returnUrl);
querys.put("colorLiveParam", colorLiveParam);
querys.put("actionLiveParam", actionLiveParam);
Map<String, String> bodys = new HashMap<>();
bodys.put("idcard", idcard);
bodys.put("name", name);
bodys.put("url", url);
bodys.put("image", image);
try {
HttpResponse res = AliHttpUtils.doGet(host, path, method, headers, querys);
LivenessResponseBody<LivenessResponseTokenData> body = JSON.parseObject(EntityUtils.toString(res.getEntity()), LivenessResponseBody.class);
HttpResponse res = AliHttpUtils.doPost("https://faceidcardb.shumaidata.com", "/getfaceidb", "POST", headers, null, bodys);
FaceResponseBody body = JSON.parseObject(EntityUtils.toString(res.getEntity()), FaceResponseBody.class);
ServiceUtil.assertion(body.getCode() == null || !body.getCode().equals("200"), body.getMsg());
return body.getData();
} catch (Exception e) {

View File

@ -0,0 +1,49 @@
package com.ruoyi.common.valid.face.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author wjh
* 2024/9/23
*/
@Getter
@AllArgsConstructor
public enum FaceIncorrect {
/**
* 100 比对成功还要根据score的值来判断是否同一人
* 101 身份证号码姓名不一致
* 102 库中无此号
* 103 身份核验成功数据非法公安已经进行了身份核验姓名身份证号码一致公安返回的错误: 除库中无照片109特征提取失败110检测到多于一张人脸(111)人脸识别系统异常(106)图片不合法(112)以外的情况
* 104 数据非法公安未进行身份核验前图片校验公安返回的错误: 除照片质量不合格(107)上传图片文件过大(108)人像比对服务异常(113)以外的情况
* 106 身份核验成功人脸识别系统异常
* 107 照片质量不合格
* 108 上传图片文件过大
* 109 身份核验成功库中无照片
* 110 身份核验成功特征提取失败
* 111 身份核验成功检测到多于一张人脸
* 112 身份核验成功图片不合法
* 113 人像比对服务异常
* 103 身份核验成功数据非法
*/
SUCCESS(100, "比对成功"),
ID_NAME_NOT_CONSISTENT(101, "身份证号码姓名不一致"),
NO_THIS_ID(102, "库中无此号"),
ILLEGAL_DATA(103, "数据非法,公安已经进行了身份核验"),
ILLEGAL_DATA_BEFORE_VERIFY(104, "数据非法,公安未进行身份核验前图片校验"),
FACE_RECOGNITION_SYSTEM_ERROR(106, "身份核验成功,人脸识别系统异常"),
PHOTO_QUALITY_NOT_GOOD(107, "照片质量不合格"),
UPLOAD_FILE_TOO_LARGE(108, "上传图片文件过大"),
NO_PHOTO(109, "身份核验成功,库中无照片"),
FEATURE_EXTRACTION_FAILED(110, "身份核验成功,特征提取失败"),
MORE_THAN_ONE_FACE(111, "身份核验成功,检测到多于一张人脸"),
ILLEGAL_PICTURE(112, "身份核验成功,图片不合法"),
FACE_RECOGNITION_SERVICE_EXCEPTION(113, "人像比对服务异常");
private final Integer code;
private final String msg;
public static final boolean isSuccess(Integer code ) {
return SUCCESS.getCode().equals(code);
}
}

View File

@ -0,0 +1,45 @@
package com.ruoyi.common.valid.face.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import java.math.BigDecimal;
/**
* @author wjh
* 2024/9/23
*/
@Getter
@AllArgsConstructor
public enum FaceScoreResult {
NOT_SAME("0", "不是同一个人"),
UNKNOWN("1", "不确定是否为同一个人"),
SAME("2", "同一个人");
private final String code;
private final String msg;
public static final boolean isSame(FaceScoreResult result) {
return SAME.equals(result);
}
public static final boolean isSame(BigDecimal score) {
return isSame(getScoreCodeResult(score));
}
private static FaceScoreResult getScoreCodeResult(BigDecimal score) {
if (score == null) {
return NOT_SAME;
}
if (new BigDecimal("0.40").compareTo(score) > 0) {
return NOT_SAME;
}
if (new BigDecimal("0.45").compareTo(score) > 0) {
return UNKNOWN;
}
return SAME;
}
}

View File

@ -0,0 +1,22 @@
package com.ruoyi.common.valid.liveness.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author wjh
* 2024/9/23
*/
@Getter
@AllArgsConstructor
public enum LivenessQueryResult {
PASS(0, "已通过"),
FAIL(1, "未通过"),
NOT_FOUND(2, "未找到活体检测结果");
private final Integer result;
private final String msg;
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.ss.device.domain.vo;
import lombok.Data;
/**
* @author wjh
* 2024/9/23
*/
@Data
public class DeviceMacSnVO {
private String mac;
private String sn;
}

View File

@ -4,6 +4,7 @@ import com.ruoyi.ss.device.domain.Device;
import com.ruoyi.ss.device.domain.DeviceCountVO;
import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceBatchUpdateModelDTO;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import org.apache.ibatis.annotations.Param;
@ -167,7 +168,7 @@ public interface DeviceMapper
/**
* 获取所有MAC
*/
List<String> selectMacList(DeviceQuery query);
List<DeviceMacSnVO> selectMacSnList(DeviceQuery query);
/**
* 查询一个

View File

@ -233,8 +233,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where sd.device_no = #{deviceNo} and sd.deleted = false
</select>
<select id="selectMacList" resultType="java.lang.String">
select distinct sd.mac
<select id="selectMacSnList" resultType="DeviceMacSnVO">
select
sd.mac,
sd.device_no as sn
from sm_device sd
<where>
<include refid="searchCondition"/>

View File

@ -7,6 +7,7 @@ import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceBatchUpdateModelDTO;
import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import java.math.BigDecimal;
@ -235,7 +236,7 @@ public interface DeviceService
/**
* 获取所有MAC
*/
List<String> selectMacList(DeviceQuery query);
List<DeviceMacSnVO> selectMacSnList(DeviceQuery query);
/**
* 查询一个

View File

@ -21,6 +21,7 @@ import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.enums.DeviceStatus;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.mapper.DeviceMapper;
import com.ruoyi.ss.device.service.DeviceAssembler;
@ -335,8 +336,8 @@ public class DeviceServiceImpl implements DeviceService
}
@Override
public List<String> selectMacList(DeviceQuery query) {
return deviceMapper.selectMacList(query);
public List<DeviceMacSnVO> selectMacSnList(DeviceQuery query) {
return deviceMapper.selectMacSnList(query);
}
@Override

View File

@ -0,0 +1,54 @@
package com.ruoyi.ss.realName.domain;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.enums.DesensitizedType;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* 实名认证对象 ss_real_name
*
* @author ruoyi
* @date 2024-09-23
*/
@Data
public class RealName extends BaseEntity
{
private static final long serialVersionUID = 1L;
private Long id;
@Excel(name = "用户ID")
@ApiModelProperty("用户ID")
private Long userId;
@Excel(name = "姓名")
@ApiModelProperty("姓名")
@Sensitive(desensitizedType = DesensitizedType.USERNAME)
private String name;
@Excel(name = "身份证")
@ApiModelProperty("身份证")
@Sensitive(desensitizedType = DesensitizedType.ID_CARD)
private String idCard;
@Excel(name = "手机号")
@ApiModelProperty("手机号")
@Sensitive(desensitizedType = DesensitizedType.PHONE)
private String mobile;
@Excel(name = "比对分数")
@ApiModelProperty("比对分数")
private BigDecimal score;
@Excel(name = "人脸图像")
@ApiModelProperty("人脸图像")
private String faceImage;
}

View File

@ -0,0 +1,11 @@
package com.ruoyi.ss.realName.domain;
import lombok.Data;
/**
* @author wjh
* 2024/9/23
*/
@Data
public class RealNameQuery extends RealNameVO {
}

View File

@ -0,0 +1,14 @@
package com.ruoyi.ss.realName.domain;
import lombok.Data;
/**
* @author wjh
* 2024/9/23
*/
@Data
public class RealNameVO extends RealName {
private String userName;
}

View File

@ -0,0 +1,64 @@
package com.ruoyi.ss.realName.mapper;
import java.util.List;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.domain.RealNameVO;
import com.ruoyi.ss.realName.domain.RealNameQuery;
import org.apache.ibatis.annotations.Param;
/**
* 实名认证Mapper接口
*
* @author ruoyi
* @date 2024-09-23
*/
public interface RealNameMapper
{
/**
* 查询实名认证
*
* @param id 实名认证主键
* @return 实名认证
*/
public RealNameVO selectRealNameById(Long id);
/**
* 查询实名认证列表
*
* @param query 实名认证
* @return 实名认证集合
*/
public List<RealNameVO> selectRealNameList(@Param("query")RealNameQuery query);
/**
* 新增实名认证
*
* @param realName 实名认证
* @return 结果
*/
public int insertRealName(RealName realName);
/**
* 修改实名认证
*
* @param realName 实名认证
* @return 结果
*/
public int updateRealName(@Param("data") RealName realName);
/**
* 删除实名认证
*
* @param id 实名认证主键
* @return 结果
*/
public int deleteRealNameById(Long id);
/**
* 批量删除实名认证
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteRealNameByIds(Long[] ids);
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ss.realName.mapper.RealNameMapper">
<resultMap type="RealNameVO" id="RealNameResult" autoMapping="true"/>
<sql id="selectRealNameVo">
select
srn.id,
srn.user_id,
srn.name,
srn.id_card,
srn.mobile,
srn.create_time,
srn.score,
srn.face_image,
su.user_name as user_name
from ss_real_name srn
left join sm_user su on su.user_id = srn.user_id
</sql>
<sql id="searchCondition">
<if test="query.id != null "> and srn.id = #{query.id}</if>
<if test="query.userId != null "> and srn.user_id = #{query.userId}</if>
<if test="query.name != null and query.name != ''"> and srn.name like concat('%', #{query.name}, '%')</if>
<if test="query.idCard != null and query.idCard != ''"> and srn.id_card like concat('%', #{query.idCard}, '%')</if>
<if test="query.mobile != null and query.mobile != ''"> and srn.mobile like concat('%', #{query.mobile}, '%')</if>
<if test="query.userName != null and query.userName != ''"> and su.user_name like concat('%', #{query.userName}, '%')</if>
</sql>
<select id="selectRealNameList" parameterType="RealNameQuery" resultMap="RealNameResult">
<include refid="selectRealNameVo"/>
<where>
<include refid="searchCondition"/>
</where>
</select>
<select id="selectRealNameById" parameterType="Long" resultMap="RealNameResult">
<include refid="selectRealNameVo"/>
where srn.id = #{id}
</select>
<insert id="insertRealName" parameterType="RealName" useGeneratedKeys="true" keyProperty="id">
insert into ss_real_name
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="name != null">name,</if>
<if test="idCard != null">id_card,</if>
<if test="mobile != null">mobile,</if>
<if test="createTime != null">create_time,</if>
<if test="score != null">score,</if>
<if test="faceImage != null">face_image,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="name != null">#{name},</if>
<if test="idCard != null">#{idCard},</if>
<if test="mobile != null">#{mobile},</if>
<if test="createTime != null">#{createTime},</if>
<if test="score != null">#{score},</if>
<if test="faceImage != null">#{faceImage},</if>
</trim>
</insert>
<update id="updateRealName" parameterType="RealName">
update ss_real_name
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
where id = #{data.id}
</update>
<sql id="updateColumns">
<if test="data.userId != null">user_id = #{data.userId},</if>
<if test="data.name != null">name = #{data.name},</if>
<if test="data.idCard != null">id_card = #{data.idCard},</if>
<if test="data.mobile != null">mobile = #{data.mobile},</if>
<if test="data.createTime != null">create_time = #{data.createTime},</if>
<if test="data.score != null">score = #{data.score},</if>
<if test="data.faceImage != null">face_image = #{data.faceImage},</if>
</sql>
<delete id="deleteRealNameById" parameterType="Long">
delete from ss_real_name where id = #{id}
</delete>
<delete id="deleteRealNameByIds" parameterType="String">
delete from ss_real_name where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,63 @@
package com.ruoyi.ss.realName.service;
import java.util.List;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.domain.RealNameVO;
import com.ruoyi.ss.realName.domain.RealNameQuery;
/**
* 实名认证Service接口
*
* @author ruoyi
* @date 2024-09-23
*/
public interface IRealNameService
{
/**
* 查询实名认证
*
* @param id 实名认证主键
* @return 实名认证
*/
public RealNameVO selectRealNameById(Long id);
/**
* 查询实名认证列表
*
* @param realName 实名认证
* @return 实名认证集合
*/
public List<RealNameVO> selectRealNameList(RealNameQuery realName);
/**
* 新增实名认证
*
* @param realName 实名认证
* @return 结果
*/
public int insertRealName(RealName realName);
/**
* 修改实名认证
*
* @param realName 实名认证
* @return 结果
*/
public int updateRealName(RealName realName);
/**
* 批量删除实名认证
*
* @param ids 需要删除的实名认证主键集合
* @return 结果
*/
public int deleteRealNameByIds(Long[] ids);
/**
* 删除实名认证信息
*
* @param id 实名认证主键
* @return 结果
*/
public int deleteRealNameById(Long id);
}

View File

@ -0,0 +1,97 @@
package com.ruoyi.ss.realName.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.ss.realName.mapper.RealNameMapper;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.domain.RealNameVO;
import com.ruoyi.ss.realName.domain.RealNameQuery;
import com.ruoyi.ss.realName.service.IRealNameService;
/**
* 实名认证Service业务层处理
*
* @author ruoyi
* @date 2024-09-23
*/
@Service
public class RealNameServiceImpl implements IRealNameService
{
@Autowired
private RealNameMapper realNameMapper;
/**
* 查询实名认证
*
* @param id 实名认证主键
* @return 实名认证
*/
@Override
public RealNameVO selectRealNameById(Long id)
{
return realNameMapper.selectRealNameById(id);
}
/**
* 查询实名认证列表
*
* @param realName 实名认证
* @return 实名认证
*/
@Override
public List<RealNameVO> selectRealNameList(RealNameQuery realName)
{
return realNameMapper.selectRealNameList(realName);
}
/**
* 新增实名认证
*
* @param realName 实名认证
* @return 结果
*/
@Override
public int insertRealName(RealName realName)
{
realName.setCreateTime(DateUtils.getNowDate());
return realNameMapper.insertRealName(realName);
}
/**
* 修改实名认证
*
* @param realName 实名认证
* @return 结果
*/
@Override
public int updateRealName(RealName realName)
{
return realNameMapper.updateRealName(realName);
}
/**
* 批量删除实名认证
*
* @param ids 需要删除的实名认证主键
* @return 结果
*/
@Override
public int deleteRealNameByIds(Long[] ids)
{
return realNameMapper.deleteRealNameByIds(ids);
}
/**
* 删除实名认证信息
*
* @param id 实名认证主键
* @return 结果
*/
@Override
public int deleteRealNameById(Long id)
{
return realNameMapper.deleteRealNameById(id);
}
}

View File

@ -5,6 +5,7 @@ import lombok.Data;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
@ -17,21 +18,27 @@ public class WithdrawDTO {
@ApiModelProperty("提现金额")
@DecimalMin(value = "0.01", message = "提现金额不能小于0.01")
@NotNull(message = "提现金额不允许为空")
private BigDecimal money;
@ApiModelProperty("提现到账金额")
@NotNull(message = "到账金额不允许为空")
private BigDecimal arrivalAmount;
@ApiModelProperty("服务费")
@NotNull(message = "服务费不允许为空")
private BigDecimal serviceCharge;
@ApiModelProperty("服务费收取类型")
@NotNull(message = "服务费收取类型不允许为空")
private String serviceType;
@ApiModelProperty("服务费")
@NotNull(message = "服务费率不允许为空")
private BigDecimal serviceRate;
@ApiModelProperty("提现渠道ID")
@NotNull(message = "提现渠道ID不允许为空")
private Long channelId;
@ApiModelProperty("用户ID")

View File

@ -231,4 +231,11 @@ public interface ISmUserService
* 添加一次风险次数
*/
int addRiskCount(Long userId, int count);
/**
* 刷新实名认证结果
* @param userId 用户ID
* @return 认证结果是否成功
*/
int refreshRealName(Long userId);
}

View File

@ -9,13 +9,22 @@ import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.common.utils.qiniu.QiNiuUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.common.valid.face.FaceResponseData;
import com.ruoyi.common.valid.face.FaceUtils;
import com.ruoyi.common.valid.face.enums.FaceIncorrect;
import com.ruoyi.common.valid.face.enums.FaceScoreResult;
import com.ruoyi.common.valid.liveness.LivenessResponseQueryData;
import com.ruoyi.common.valid.liveness.LivenessResponseTokenData;
import com.ruoyi.common.valid.liveness.LivenessUtils;
import com.ruoyi.common.valid.liveness.enums.LivenessQueryResult;
import com.ruoyi.common.valid.realName.RealNameValidUtils;
import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.service.IRealNameService;
import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType;
import com.ruoyi.ss.recordBalance.service.RecordBalanceService;
import com.ruoyi.ss.store.domain.StoreQuery;
@ -69,6 +78,9 @@ public class SmUserServiceImpl implements ISmUserService
@Autowired
private RedisCache redisCache;
@Autowired
private IRealNameService realNameService;
// 活体检测返回地址
public static final String LIVENESS_RETURN_URL = SpringUtils.getRequiredProperty("liveness.returnUrl");
@ -107,8 +119,10 @@ public class SmUserServiceImpl implements ISmUserService
return;
}
list.forEach(item -> {
item.setPassword(null);
item.setPayPassword(null);
if (item != null) {
item.setPassword(null);
item.setPayPassword(null);
}
});
}
@ -297,18 +311,19 @@ public class SmUserServiceImpl implements ISmUserService
public UserRealNameVO realName(UserRealNameDTO dto) {
SmUserVo user = selectSmUserByUserId(dto.getUserId());
ServiceUtil.assertion(user == null, "用户信息不存在");
ServiceUtil.assertion(user.getIsReal() != null && user.getIsReal(), "您已进行过实名认证,无需再次认证");
// ServiceUtil.assertion(user.getIsReal() != null && user.getIsReal(), "您已进行过实名认证,无需再次认证");
// 调用第三方API三要素验证
boolean check = RealNameValidUtils.validMobile3Info(dto.getRealIdCard(), dto.getRealPhone(), dto.getRealName());
ServiceUtil.assertion(!check, "请输入正确的身份信息:姓名、身份证、手机号需要一致");
// 获取活体检测token
LivenessResponseTokenData tokenData = LivenessUtils.getToken(LIVENESS_RETURN_URL, null, null);
String colors = "#FFFFFF,#FFD9E3,#D8D6FF,#CFF9FF,#FFCEFD,#D8FFCD,#FFF4CB,#000000,#3B3C04,#183C03,#063C28,#063C3A,#060D3C,#3C0636";
LivenessResponseTokenData tokenData = LivenessUtils.getToken(LIVENESS_RETURN_URL, colors, null);
ServiceUtil.assertion(tokenData == null || StringUtils.isBlank(tokenData.getToken()), "获取活体检测token失败");
// 存入缓存:10分钟
String cacheKey = CacheConstants.LIVENESS_TOKEN + tokenData.getToken();
String cacheKey = CacheConstants.LIVENESS_TOKEN + dto.getUserId();
UserFaceDTO faceDto = new UserFaceDTO();
faceDto.setUserId(dto.getUserId());
faceDto.setToken(tokenData.getToken());
@ -323,14 +338,6 @@ public class SmUserServiceImpl implements ISmUserService
vo.setJumpUrl(LivenessUtils.JUMP_URL + "?token=" + tokenData.getToken());
return vo;
// 修改实名信息
// SmUser data = new SmUserQuery();
// data.setUserId(dto.getUserId());
// data.setRealName(dto.getRealName());
// data.setRealIdCard(dto.getRealIdCard());
// data.setRealPhone(dto.getRealPhone());
// data.setIsReal(true);
// return smUserMapper.updateSmUser(data);
}
@Override
@ -360,6 +367,63 @@ public class SmUserServiceImpl implements ISmUserService
return smUserMapper.addRiskCount(userId, count);
}
@Override
public int refreshRealName(Long userId) {
if (userId == null) {
return 0;
}
// 通过userId获取用户提交的信息
String cacheKey = CacheConstants.LIVENESS_TOKEN + userId;
UserFaceDTO face = redisCache.getCacheObject(cacheKey);
ServiceUtil.assertion(face == null, "token已过期");
// 获取活体图像及三要素信息
LivenessResponseQueryData livenessResult = LivenessUtils.queryResult(face.getToken());
ServiceUtil.assertion(livenessResult == null, "活体信息不存在");
ServiceUtil.assertion(!LivenessQueryResult.PASS.getResult().equals(livenessResult.getResult()), "活体检测未通过");
ServiceUtil.assertion(StringUtils.isBlank(livenessResult.getFaceImage()), "活体检测未通过");
// TODO 将Base64上传到七牛云中
// QiNiuUtils.upload(livenessResult.getFaceImage());
// 判断活体是否匹配
FaceResponseData faceIdb = FaceUtils.getFaceIdb(face.getIdCard(), face.getName(), livenessResult.getFaceImage(), null);
ServiceUtil.assertion(faceIdb == null, "人证比对数据不存在");
ServiceUtil.assertion(!FaceIncorrect.isSuccess(faceIdb.getIncorrect()), "人证比对未通过:" + faceIdb.getMsg());
ServiceUtil.assertion(!FaceScoreResult.isSame(faceIdb.getScore()), "人证比对未通过,分数:" + faceIdb.getScore());
// 通过修改实名认证信息用户表实名认证表
Integer result = transactionTemplate.execute(status -> {
// 修改用户实名信息
SmUser data = new SmUserQuery();
data.setUserId(face.getUserId());
data.setRealName(face.getName());
data.setRealIdCard(face.getIdCard());
data.setRealPhone(face.getMobile());
data.setIsReal(true);
int update = smUserMapper.updateSmUser(data);
ServiceUtil.assertion(update != 1, "更新用户信息失败");
// 插入实名认证表
RealName realName = new RealName();
realName.setUserId(userId);
realName.setName(face.getName());
realName.setIdCard(face.getIdCard());
realName.setMobile(face.getMobile());
realName.setScore(faceIdb.getScore());
realName.setFaceImage(livenessResult.getFaceImage());
int insert = realNameService.insertRealName(realName);
ServiceUtil.assertion(insert != 1, "新增实名认证失败");
// 删除缓存
redisCache.deleteObject(cacheKey);
return update;
});
return result == null ? 0 : result;
}
/**
* 逻辑删除前校验
* @param userIds

View File

@ -199,16 +199,16 @@ public class AppDeviceController extends BaseController {
@GetMapping("/listAllMac")
@DeviceAdminRequired
public AjaxResult getAllMac() {
return success(smDeviceService.selectMacList(new DeviceQuery()));
return success(smDeviceService.selectMacSnList(new DeviceQuery()));
}
@ApiOperation("管理员过滤已录入的设备MAC列表")
@ApiOperation("管理员过滤已录入的设备MAC和SN列表")
@DeviceAdminRequired
@GetMapping("/getExistMac/{mac}")
public AjaxResult getExistMac(@PathVariable @ApiParam("设备mac") List<String> mac) {
DeviceQuery query = new DeviceQuery();
query.setMacList(mac);
return success(smDeviceService.selectMacList(query));
return success(smDeviceService.selectMacSnList(query));
}
@ApiOperation("获取正在使用中的设备列表")

View File

@ -1,10 +1,12 @@
package com.ruoyi.web.controller.app;
import com.fasterxml.jackson.annotation.JsonView;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.JsonViewProfile;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.ss.user.domain.bo.SmUserBO;
import com.ruoyi.ss.user.domain.SmUserVo;
@ -47,6 +49,8 @@ public class AppUserController extends BaseController {
public AjaxResult userInfo() {
// 查询
SmUserVo user = userService.selectSmUserByUserId(getUserId());
ServiceUtil.assertion(user == null, "用户不存在", HttpStatus.UNAUTHORIZED);
List<SmUserVo> list = Collections.singletonList(user);
userService.desensitization(list);
userAssembler.assembleStoreCount(list);
@ -75,6 +79,12 @@ public class AppUserController extends BaseController {
return success(userService.realName(dto));
}
@ApiOperation("刷新用户实名认证结果")
@GetMapping("/refreshRealName")
public AjaxResult refreshRealName() {
return toAjax(userService.refreshRealName(getUserId()));
}
@ApiOperation("标记用户已读商户协议")
@PutMapping("/readMchLicence")
public AjaxResult readMchLicence() {

View File

@ -4,7 +4,10 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.PathParam;
import cn.binarywang.wx.miniapp.api.WxMaJsapiService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.domain.ValidGroup;
import com.ruoyi.common.utils.qiniu.QiNiuUtils;
@ -12,15 +15,15 @@ import com.ruoyi.system.domain.bo.VerificationCodeBO;
import com.ruoyi.system.domain.enums.verificationCode.CodeBusinessType;
import com.ruoyi.system.service.IVerificationCodeService;
import io.swagger.annotations.ApiOperation;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
@ -30,6 +33,8 @@ import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
import static com.ruoyi.common.core.domain.AjaxResult.success;
/**
* 通用请求处理
*
@ -49,6 +54,9 @@ public class CommonController
private static final String FILE_DELIMETER = ",";
@Autowired
private WxMaService wxMaService;
/**
* 通用下载请求
*
@ -94,7 +102,7 @@ public class CommonController
// 上传并返回新文件名称
String fileName = FileUploadUtils.uploadToQiNiu(file);
String url = QiNiuUtils.DOMAIN + "/" + fileName;
AjaxResult ajax = AjaxResult.success();
AjaxResult ajax = success();
ajax.put("url", url);
ajax.put("fileName", fileName);
ajax.put("newFileName", FileUtils.getName(fileName));
@ -131,7 +139,7 @@ public class CommonController
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
AjaxResult ajax = success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
@ -176,7 +184,7 @@ public class CommonController
@ApiOperation("获取七牛云上传信息")
@GetMapping("/qiniu/uploadInfo")
public AjaxResult getQiNiuToken() {
AjaxResult ajax = AjaxResult.success("操作成功");
AjaxResult ajax = success("操作成功");
ajax.put("token", QiNiuUtils.getToken());
ajax.put("domain", QiNiuUtils.DOMAIN);
return ajax;
@ -186,7 +194,14 @@ public class CommonController
@GetMapping("/verificationCode/mobile")
@Anonymous
public AjaxResult getVerificationMobileCode(@Validated({ValidGroup.FrontCreate.class}) VerificationCodeBO bo) {
return AjaxResult.success(verificationCodeService.sendMobileCode(bo.getMobile(), CodeBusinessType.parse(bo.getType())));
return success(verificationCodeService.sendMobileCode(bo.getMobile(), CodeBusinessType.parse(bo.getType())));
}
@ApiOperation("获取微信jssdk签名")
@GetMapping("/wx/js/sdk")
@Anonymous
public AjaxResult getWxJsSdk(@RequestParam String url) throws WxErrorException {
WxJsapiSignature jsapiSignature = wxMaService.getJsapiService().createJsapiSignature(url);
return success(jsapiSignature);
}
}

View File

@ -0,0 +1,107 @@
package com.ruoyi.web.controller.ss;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.ss.realName.domain.RealName;
import com.ruoyi.ss.realName.domain.RealNameVO;
import com.ruoyi.ss.realName.domain.RealNameQuery;
import com.ruoyi.ss.realName.service.IRealNameService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
* 实名认证Controller
*
* @author ruoyi
* @date 2024-09-23
*/
@RestController
@RequestMapping("/ss/realName")
public class RealNameController extends BaseController
{
@Autowired
private IRealNameService realNameService;
/**
* 查询实名认证列表
*/
@PreAuthorize("@ss.hasPermi('ss:realName:list')")
@GetMapping("/list")
public TableDataInfo list(RealNameQuery query)
{
startPage();
startOrderBy();
List<RealNameVO> list = realNameService.selectRealNameList(query);
return getDataTable(list);
}
/**
* 导出实名认证列表
*/
@PreAuthorize("@ss.hasPermi('ss:realName:export')")
@Log(title = "实名认证", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, RealNameQuery query)
{
List<RealNameVO> list = realNameService.selectRealNameList(query);
ExcelUtil<RealNameVO> util = new ExcelUtil<RealNameVO>(RealNameVO.class);
util.exportExcel(response, list, "实名认证数据");
}
/**
* 获取实名认证详细信息
*/
@PreAuthorize("@ss.hasPermi('ss:realName:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(realNameService.selectRealNameById(id));
}
/**
* 新增实名认证
*/
@PreAuthorize("@ss.hasPermi('ss:realName:add')")
@Log(title = "实名认证", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody RealName realName)
{
return toAjax(realNameService.insertRealName(realName));
}
/**
* 修改实名认证
*/
@PreAuthorize("@ss.hasPermi('ss:realName:edit')")
@Log(title = "实名认证", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody RealName realName)
{
return toAjax(realNameService.updateRealName(realName));
}
/**
* 删除实名认证
*/
@PreAuthorize("@ss.hasPermi('ss:realName:remove')")
@Log(title = "实名认证", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(realNameService.deleteRealNameByIds(ids));
}
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.web.core.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.ruoyi.common.config.WxConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author wjh
* 2024/9/23
*/
@Configuration
public class WxMaConfig {
@Autowired
private WxConfig wxConfig;
@Bean
public WxMaService wxMaService() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(wxConfig.getAppId());
config.setSecret(wxConfig.getAppSecret());
config.setMsgDataFormat("JSON");
WxMaService wxMaService = new WxMaServiceImpl();
wxMaService.setWxMaConfig(config);
return wxMaService;
}
}