风控
This commit is contained in:
parent
fb736ec07a
commit
72c8a68c39
|
@ -152,17 +152,20 @@ public class SmUser extends BaseEntity
|
|||
|
||||
@Excel(name = "真实姓名")
|
||||
@ApiModelProperty("真实姓名")
|
||||
@JsonView(JsonViewProfile.AppMch.class)
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
@Sensitive(desensitizedType = DesensitizedType.USERNAME)
|
||||
private String realName;
|
||||
|
||||
@Excel(name = "身份证号")
|
||||
@ApiModelProperty("身份证号")
|
||||
@Sensitive(desensitizedType = DesensitizedType.ID_CARD)
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private String realIdCard;
|
||||
|
||||
@Excel(name = "实名认证手机号")
|
||||
@ApiModelProperty("实名认证手机号")
|
||||
@Sensitive(desensitizedType = DesensitizedType.PHONE)
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private String realPhone;
|
||||
|
||||
@ApiModelProperty("是否已经实名认证")
|
||||
|
|
|
@ -14,8 +14,10 @@ import lombok.Getter;
|
|||
public enum ServiceCode {
|
||||
|
||||
DEVICE_RECHARGE_IOT_ERROR(101001, "物联网设备充值失败"),
|
||||
DEVICE_RECHARGE_ERROR(101002, "设备充值失败");
|
||||
DEVICE_RECHARGE_ERROR(101002, "设备充值失败"),
|
||||
RISK_WITHDRAW(102001, "提现风险"),
|
||||
RISK_REFUND(102002, "退款风险");
|
||||
|
||||
private final Integer code;
|
||||
private final String msg;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,4 +49,8 @@ public class RealName extends BaseEntity
|
|||
@ApiModelProperty("人脸图像")
|
||||
private String faceImage;
|
||||
|
||||
@Excel(name = "认证类型", readConverterExp = "1=普通认证,2=风控认证")
|
||||
@ApiModelProperty("认证类型")
|
||||
private String type;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.ruoyi.ss.realName.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/10/21
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RealNameType {
|
||||
|
||||
NORMAL("1", "普通实名"),
|
||||
RISK("2", "风控实名");
|
||||
|
||||
private final String type;
|
||||
private final String msg;
|
||||
}
|
|
@ -16,6 +16,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
srn.create_time,
|
||||
srn.score,
|
||||
srn.face_image,
|
||||
srn.type,
|
||||
su.user_name as user_name
|
||||
from ss_real_name srn
|
||||
left join sm_user su on su.user_id = srn.user_id
|
||||
|
@ -28,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<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>
|
||||
<if test="query.type != null and query.type != ''"> and type = #{query.type}</if>
|
||||
</sql>
|
||||
|
||||
<select id="selectRealNameList" parameterType="RealNameQuery" resultMap="RealNameResult">
|
||||
|
@ -43,15 +45,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</select>
|
||||
|
||||
<insert id="insertRealName" parameterType="RealName" useGeneratedKeys="true" keyProperty="id">
|
||||
<selectKey keyProperty="id" order="AFTER" resultType="Long">
|
||||
SELECT LAST_INSERT_ID();
|
||||
</selectKey>
|
||||
insert into ss_real_name
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="name != null">name,</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>
|
||||
<if test="type != null and type != ''">type,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="userId != null">#{userId},</if>
|
||||
|
@ -61,6 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="score != null">#{score},</if>
|
||||
<if test="faceImage != null">#{faceImage},</if>
|
||||
<if test="type != null and type != ''">#{type},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
|
@ -74,12 +81,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
|
||||
<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.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>
|
||||
<if test="data.type != null and data.type != ''">type = #{data.type},</if>
|
||||
</sql>
|
||||
|
||||
<delete id="deleteRealNameById" parameterType="Long">
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package com.ruoyi.ss.risk.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
@ -28,7 +32,7 @@ public class Risk extends BaseEntity
|
|||
|
||||
@Excel(name = "类型")
|
||||
@ApiModelProperty("类型")
|
||||
private String type;
|
||||
private List<String> type;
|
||||
|
||||
@Excel(name = "风控原因")
|
||||
@ApiModelProperty("风控原因")
|
||||
|
@ -37,7 +41,7 @@ public class Risk extends BaseEntity
|
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("结束时间")
|
||||
private Date endTime;
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Excel(name = "实名认证ID")
|
||||
@ApiModelProperty("实名认证ID")
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package com.ruoyi.ss.risk.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/10/18
|
||||
*/
|
||||
@Data
|
||||
public class RiskQuery extends RiskVO{
|
||||
public class RiskQuery extends RiskVO {
|
||||
|
||||
@ApiModelProperty("包含类型")
|
||||
private String containsType;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.ss.risk.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -8,4 +9,11 @@ import lombok.Data;
|
|||
*/
|
||||
@Data
|
||||
public class RiskVO extends Risk{
|
||||
|
||||
@ApiModelProperty("是否结束")
|
||||
private Boolean isFinished;
|
||||
|
||||
@ApiModelProperty("用户名")
|
||||
private String userName;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.ruoyi.ss.risk.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/10/21
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RiskType {
|
||||
|
||||
WITHDRAW("1", "限制提现"),
|
||||
REFUND("2", "限制退款");
|
||||
|
||||
private final String type;
|
||||
private final String msg;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.ss.risk.mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import com.ruoyi.ss.risk.domain.Risk;
|
||||
import com.ruoyi.ss.risk.domain.RiskVO;
|
||||
|
@ -61,4 +62,14 @@ public interface RiskMapper
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteRiskByRiskIds(Long[] riskIds);
|
||||
|
||||
/**
|
||||
* 批量修改结束时间
|
||||
* @param riskIds ID列表
|
||||
* @param endTime 结束时间
|
||||
* @return
|
||||
*/
|
||||
int batchFinish(@Param("riskIds") List<Long> riskIds,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("realNameId") Long realNameId);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.ss.risk.mapper.RiskMapper">
|
||||
|
||||
<resultMap type="RiskVO" id="RiskResult" autoMapping="true"/>
|
||||
<resultMap type="RiskVO" id="RiskResult" autoMapping="true">
|
||||
<result column="type" property="type" typeHandler="com.ruoyi.system.mapper.typehandler.StringSplitListTypeHandler"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="isFinished">
|
||||
(sr.end_time is not null and sr.end_time <= now())
|
||||
</sql>
|
||||
|
||||
<sql id="selectRiskVo">
|
||||
select
|
||||
|
@ -14,16 +20,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
sr.reason,
|
||||
sr.end_time,
|
||||
sr.create_time,
|
||||
sr.real_name_id
|
||||
sr.real_name_id,
|
||||
<include refid="isFinished"/> as is_finished,
|
||||
su.user_name as user_name
|
||||
from ss_risk sr
|
||||
left join sm_user su on su.user_id = sr.user_id
|
||||
</sql>
|
||||
|
||||
<sql id="searchCondition">
|
||||
<if test="query.riskId != null "> and sr.risk_id = #{query.riskId}</if>
|
||||
<if test="query.userId != null "> and sr.user_id = #{query.userId}</if>
|
||||
<if test="query.type != null and query.type != ''"> and sr.type = #{query.type}</if>
|
||||
<if test="query.containsType != null and query.containsType != ''">
|
||||
and find_in_set(#{query.containsType}, sr.type)
|
||||
</if>
|
||||
<if test="query.reason != null and query.reason != ''"> and sr.reason like concat('%', #{query.reason}, '%')</if>
|
||||
<if test="query.userName != null and query.userName != ''"> and su.user_name like concat('%', #{query.userName}, '%')</if>
|
||||
<if test="query.realNameId != null "> and sr.real_name_id = #{query.realNameId}</if>
|
||||
<if test="query.isFinished != null">
|
||||
<if test="query.isFinished">and <include refid="isFinished"/></if>
|
||||
<if test="!query.isFinished">and !<include refid="isFinished"/></if>
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<select id="selectRiskList" parameterType="RiskQuery" resultMap="RiskResult">
|
||||
|
@ -42,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
insert into ss_risk
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="type != null and type != ''">type,</if>
|
||||
<if test="type != null and type.size() > 0">type,</if>
|
||||
<if test="reason != null">reason,</if>
|
||||
<if test="endTime != null">end_time,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
|
@ -50,7 +67,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="userId != null">#{userId},</if>
|
||||
<if test="type != null and type != ''">#{type},</if>
|
||||
<if test="type != null and type.size() > 0">#{type,typeHandler=com.ruoyi.system.mapper.typehandler.StringSplitListTypeHandler},</if>
|
||||
<if test="reason != null">#{reason},</if>
|
||||
<if test="endTime != null">#{endTime},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
|
@ -66,9 +83,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
where risk_id = #{data.riskId}
|
||||
</update>
|
||||
|
||||
<update id="batchFinish">
|
||||
update ss_risk
|
||||
set end_time = #{endTime},
|
||||
real_name_id = #{realNameId}
|
||||
where risk_id in
|
||||
<foreach item="item" collection="riskIds" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<sql id="updateColumns">
|
||||
<if test="data.userId != null">user_id = #{data.userId},</if>
|
||||
<if test="data.type != null and data.type != ''">type = #{data.type},</if>
|
||||
<if test="data.type != null and data.type.size() > 0">type = #{data.type,typeHandler=com.ruoyi.system.mapper.typehandler.StringSplitListTypeHandler},</if>
|
||||
<if test="data.reason != null">reason = #{data.reason},</if>
|
||||
<if test="data.endTime != null">end_time = #{data.endTime},</if>
|
||||
<if test="data.createTime != null">create_time = #{data.createTime},</if>
|
||||
|
|
|
@ -60,4 +60,21 @@ public interface IRiskService
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteRiskByRiskId(Long riskId);
|
||||
|
||||
/**
|
||||
* 查询未结束的风控列表
|
||||
*/
|
||||
List<RiskVO> selectNotFinishedList(RiskQuery query);
|
||||
|
||||
/**
|
||||
* 批量结束
|
||||
*/
|
||||
int batchFinish(List<Long> riskIds, Long realNameId);
|
||||
|
||||
/**
|
||||
* 限制提现
|
||||
* @param userId 用户ID
|
||||
* @param reason 限制原因
|
||||
*/
|
||||
int limitWithdraw(Long userId, String reason);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package com.ruoyi.ss.risk.service.impl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.collection.CollectionUtils;
|
||||
import com.ruoyi.ss.risk.domain.enums.RiskType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.ss.risk.mapper.RiskMapper;
|
||||
|
@ -94,4 +98,27 @@ public class RiskServiceImpl implements IRiskService
|
|||
{
|
||||
return riskMapper.deleteRiskByRiskId(riskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RiskVO> selectNotFinishedList(RiskQuery query) {
|
||||
query.setIsFinished(false);
|
||||
return this.selectRiskList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int batchFinish(List<Long> riskIds, Long realNameId) {
|
||||
if (CollectionUtils.isEmptyElement(riskIds)) {
|
||||
return 0;
|
||||
}
|
||||
return riskMapper.batchFinish(riskIds, LocalDateTime.now(), realNameId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limitWithdraw(Long userId, String reason) {
|
||||
Risk risk = new Risk();
|
||||
risk.setUserId(userId);
|
||||
risk.setType(Collections.singletonList(RiskType.WITHDRAW.getType()));
|
||||
risk.setReason(reason);
|
||||
return this.insertRisk(risk);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,10 @@ public class TransactionBillQuery extends TransactionBill {
|
|||
@ApiModelProperty("创建时间(结束)")
|
||||
private LocalDateTime createTimeEnd;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty("创建时间(起始)")
|
||||
private LocalDateTime createTimeStart;
|
||||
|
||||
@ApiModelProperty("收入人ID")
|
||||
private Long incomeUserId;
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="query.suitFeeType != null and query.suitFeeType != ''"> and stb.suit_fee_type = #{query.suitFeeType}</if>
|
||||
<if test="query.suitEnableLowPowerClose != null "> and stb.suit_enable_low_power_close = #{query.suitEnableLowPowerClose}</if>
|
||||
<if test="query.createTimeEnd != null "> and stb.create_time <= #{query.createTimeEnd}</if>
|
||||
<if test="query.createTimeStart != null "> and stb.create_time >= #{query.createTimeStart}</if>
|
||||
<if test="query.deviceProductId != null and query.deviceProductId != ''"> and stb.device_product_id = #{query.deviceProductId}</if>
|
||||
<if test="query.deviceServiceMode != null and query.deviceServiceMode != ''"> and stb.device_service_mode = #{query.deviceServiceMode}</if>
|
||||
<if test="query.version != null "> and stb.version = #{query.version}</if>
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.ruoyi.ss.transactionBill.domain.bo.RechargeBO;
|
|||
import com.ruoyi.ss.transactionBill.domain.bo.RechargePayDepositBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
|
||||
import com.ruoyi.ss.user.domain.SmUserVo;
|
||||
|
||||
/**
|
||||
* 2024/4/22
|
||||
|
@ -59,4 +60,10 @@ public interface TransactionBillValidator {
|
|||
* 判断用户是否是订单的代理商
|
||||
*/
|
||||
boolean isAgent(TransactionBillVO bill, Long userId);
|
||||
|
||||
/**
|
||||
* 校验退款风控
|
||||
* @param user 用户
|
||||
*/
|
||||
void checkRefundRisk(SmUserVo user);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.ruoyi.ss.transactionBill.service;
|
||||
|
||||
import com.ruoyi.common.core.domain.ValidateResult;
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.WithdrawBO;
|
||||
import com.ruoyi.ss.user.domain.SmUserVo;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
|
@ -14,4 +14,9 @@ public interface WithdrawValidator {
|
|||
*/
|
||||
boolean hasRisk(WithdrawBO bo);
|
||||
|
||||
/**
|
||||
* 校验实名认证风控
|
||||
* @param user 用户
|
||||
*/
|
||||
void checkWithdrawRisk(SmUserVo user);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.github.pagehelper.PageHelper;
|
|||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.redis.RedisLock;
|
||||
import com.ruoyi.common.core.redis.enums.RedisLockKey;
|
||||
import com.ruoyi.common.enums.ServiceCode;
|
||||
import com.ruoyi.common.enums.WithdrawServiceType;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.*;
|
||||
|
@ -39,6 +40,7 @@ import com.ruoyi.ss.record.time.domain.enums.RecordTimeType;
|
|||
import com.ruoyi.ss.record.time.service.IRecordTimeService;
|
||||
import com.ruoyi.ss.record.time.service.RecordTimeConverter;
|
||||
import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType;
|
||||
import com.ruoyi.ss.risk.service.IRiskService;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.suit.domain.SuitVO;
|
||||
import com.ruoyi.ss.suit.domain.enums.SuitFeeMode;
|
||||
|
@ -163,6 +165,9 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
|
|||
@Autowired
|
||||
private ReceiveBillService receiveBillService;
|
||||
|
||||
@Autowired
|
||||
private IRiskService riskService;
|
||||
|
||||
/**
|
||||
* 查询充值记录
|
||||
*
|
||||
|
@ -565,18 +570,10 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
|
|||
ServiceUtil.assertion(user.getIsReal() == null || !user.getIsReal(), "用户未实名认证,无法提现");
|
||||
|
||||
// 判断用户是否被限制提现
|
||||
boolean limitWithdraw = user.getLimitWithdraw() != null && user.getLimitWithdraw();
|
||||
if (limitWithdraw) {
|
||||
LocalDateTime limitWithdrawTime = user.getLimitWithdrawTime();
|
||||
if (limitWithdrawTime == null) {
|
||||
throw new ServiceException("您被永久限制提现:" + user.getLimitWithdrawReason());
|
||||
} else if (limitWithdrawTime.isAfter(LocalDateTime.now())){
|
||||
throw new ServiceException("您被限制提现至" + DateUtils.format(limitWithdrawTime, DateUtils.YYYY_MM_DD_HH_MM_SS) + ":" + user.getLimitWithdrawReason() );
|
||||
}
|
||||
}
|
||||
withdrawValidator.checkWithdrawRisk(user);
|
||||
|
||||
// 风控规则,判断用户是否有风险
|
||||
boolean enabled = sysConfigService.getBoolean(ConfigKey.RISK_WITHDRAW_ENABLED);
|
||||
boolean enabled = sysConfigService.getBoolean(ConfigKey.RISK_WITHDRAW_ENABLED); // 是否开启提现风控
|
||||
if (enabled) {
|
||||
boolean hasRisk = withdrawValidator.hasRisk(bo);
|
||||
if (hasRisk) {
|
||||
|
@ -585,14 +582,13 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
|
|||
// 若用户风险次数已达到阈值,将用户标记为提现风险
|
||||
int riskWithdrawCount = sysConfigService.getInt(ConfigKey.RISK_WITHDRAW_COUNT);
|
||||
if (user.getRiskCount() + 1 >= riskWithdrawCount) {
|
||||
userService.limitWithdraw(userId, LocalDateTime.now().plusDays(1), "根据风控规则判断,您的提现具有风险", "风险客户");
|
||||
riskService.limitWithdraw(userId, "根据风控规则判断,您的提现具有风险");
|
||||
// 返回错误
|
||||
throw new ServiceException("提现具有风险,无法提现");
|
||||
throw new ServiceException("提现具有风险,无法提现", ServiceCode.RISK_WITHDRAW.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 判断今天提现成功和正在审核中的提现是否超过限额
|
||||
String dailyLimitStr = sysConfigService.selectConfigByKey(ConfigKey.DAILY_WITHDRAW_AMOUNT.getKey());
|
||||
String dailyLimitCountStr = sysConfigService.selectConfigByKey(ConfigKey.DAILY_WITHDRAW_COUNT.getKey());
|
||||
|
|
|
@ -2,12 +2,20 @@ package com.ruoyi.ss.transactionBill.service.impl;
|
|||
|
||||
import com.ruoyi.common.core.domain.BaseValidator;
|
||||
import com.ruoyi.common.core.domain.ValidateResult;
|
||||
import com.ruoyi.common.enums.ServiceCode;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.collection.CollectionUtils;
|
||||
import com.ruoyi.ss.channel.domain.ChannelVO;
|
||||
import com.ruoyi.ss.device.domain.vo.DeviceVO;
|
||||
import com.ruoyi.ss.device.service.DeviceService;
|
||||
import com.ruoyi.ss.model.domain.enums.ModelTag;
|
||||
import com.ruoyi.ss.risk.domain.RiskQuery;
|
||||
import com.ruoyi.ss.risk.domain.RiskVO;
|
||||
import com.ruoyi.ss.risk.domain.enums.RiskType;
|
||||
import com.ruoyi.ss.risk.service.IRiskService;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.store.service.StoreValidator;
|
||||
import com.ruoyi.ss.suit.domain.SuitVO;
|
||||
|
@ -54,6 +62,9 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
|
|||
@Autowired
|
||||
private TransactionBillService transactionBillService;
|
||||
|
||||
@Autowired
|
||||
private IRiskService riskService;
|
||||
|
||||
@Override
|
||||
public ValidateResult preAddOrder(RechargeBO bo) {
|
||||
if (bo == null) {
|
||||
|
@ -320,4 +331,28 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
|
|||
public boolean isAgent(TransactionBillVO bill, Long userId) {
|
||||
return bill != null && userId != null && Objects.equals(bill.getAgentId(), userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkRefundRisk(SmUserVo user) {
|
||||
if (user == null || user.getUserId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询未结束的风控列表
|
||||
RiskQuery query = new RiskQuery();
|
||||
query.setUserId(user.getUserId());
|
||||
query.setContainsType(RiskType.REFUND.getType());
|
||||
List<RiskVO> riskList = riskService.selectNotFinishedList(query);
|
||||
|
||||
if (CollectionUtils.isNotEmptyElement(riskList)) {
|
||||
RiskVO risk = riskList.get(0);
|
||||
ServiceUtil.assertion(risk == null, "风控信息异常");
|
||||
LocalDateTime endTime = risk.getEndTime();
|
||||
if (endTime == null) {
|
||||
throw new ServiceException("您被限制退款:" + risk.getReason(), ServiceCode.RISK_REFUND.getCode());
|
||||
} else if (endTime.isAfter(LocalDateTime.now())){
|
||||
throw new ServiceException("您被限制退款至" + DateUtils.format(endTime, DateUtils.YYYY_MM_DD_HH_MM_SS) + ":" + risk.getReason(), ServiceCode.RISK_REFUND.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,15 @@ package com.ruoyi.ss.transactionBill.service.impl;
|
|||
|
||||
import com.ruoyi.common.core.domain.BaseValidator;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.enums.ServiceCode;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.collection.CollectionUtils;
|
||||
import com.ruoyi.ss.risk.domain.RiskQuery;
|
||||
import com.ruoyi.ss.risk.domain.RiskVO;
|
||||
import com.ruoyi.ss.risk.domain.enums.RiskType;
|
||||
import com.ruoyi.ss.risk.service.IRiskService;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.WithdrawBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.WithdrawDTO;
|
||||
|
@ -17,8 +25,10 @@ import com.ruoyi.system.service.ISysConfigService;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
|
@ -32,6 +42,10 @@ public class WithdrawValidatorImpl extends BaseValidator implements WithdrawVali
|
|||
|
||||
@Autowired
|
||||
private ISysConfigService sysConfigService;
|
||||
|
||||
@Autowired
|
||||
private IRiskService riskService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
|
@ -48,27 +62,47 @@ public class WithdrawValidatorImpl extends BaseValidator implements WithdrawVali
|
|||
SmUserVo user = bo.getUser();
|
||||
WithdrawDTO dto = bo.getDto();
|
||||
|
||||
// 查询最近一次的订单
|
||||
// 查询时间相近的充值订单
|
||||
int riskWithdrawTime = sysConfigService.getInt(ConfigKey.RISK_WITHDRAW_TIME); // 最低允许相隔时长(分钟)
|
||||
TransactionBillQuery query = new TransactionBillQuery();
|
||||
query.setUserId(user.getUserId());
|
||||
query.setType(TransactionBillType.RECHARGE.getType());
|
||||
query.setStatus(TransactionBillStatus.SUCCESS.getStatus());
|
||||
TransactionBillVO recharge = transactionBillService.selectLastOne(query);
|
||||
if (recharge == null || recharge.getPayTime() == null) {
|
||||
query.setCreateTimeStart(LocalDateTime.now().plusMinutes(-riskWithdrawTime));
|
||||
List<TransactionBillVO> list = transactionBillService.selectSmTransactionBillList(query);
|
||||
|
||||
// 计算总金额
|
||||
BigDecimal rechargeAmount = list.stream().map(TransactionBillVO::getArrivalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
if (rechargeAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断订单金额是否和提现一致、订单时间与提现时间是否相差较近
|
||||
// 金额一致
|
||||
boolean equalsMoney = recharge.getArrivalAmount() != null && dto.getMoney() != null && recharge.getArrivalAmount().compareTo(dto.getMoney()) == 0;
|
||||
// 提现时间相近
|
||||
int riskWithdrawTime = sysConfigService.getInt(ConfigKey.RISK_WITHDRAW_TIME); // 最低允许相隔时长(分钟)
|
||||
Duration between = Duration.between(DateUtils.toLocalDateTime(recharge.getPayTime()), LocalDateTime.now());
|
||||
boolean timeLimit = between.toMinutes() < riskWithdrawTime;
|
||||
if (equalsMoney && timeLimit) {
|
||||
return true;
|
||||
// 判断订单金额是否和提现一致,若一致则判定为风险
|
||||
// FIXME 可以考虑使用一个范围,在这个误差范围内,均为风险
|
||||
return dto.getMoney() != null && rechargeAmount.compareTo(dto.getMoney()) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkWithdrawRisk(SmUserVo user) {
|
||||
if (user == null || user.getUserId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
// 查询未结束的风控列表
|
||||
RiskQuery query = new RiskQuery();
|
||||
query.setUserId(user.getUserId());
|
||||
query.setContainsType(RiskType.WITHDRAW.getType());
|
||||
List<RiskVO> riskList = riskService.selectNotFinishedList(query);
|
||||
|
||||
if (CollectionUtils.isNotEmptyElement(riskList)) {
|
||||
RiskVO risk = riskList.get(0);
|
||||
ServiceUtil.assertion(risk == null, "风控信息异常");
|
||||
LocalDateTime endTime = risk.getEndTime();
|
||||
if (endTime == null) {
|
||||
throw new ServiceException("您被限制提现:" + risk.getReason(), ServiceCode.RISK_WITHDRAW.getCode());
|
||||
} else if (endTime.isAfter(LocalDateTime.now())){
|
||||
throw new ServiceException("您被限制提现至" + DateUtils.format(endTime, DateUtils.YYYY_MM_DD_HH_MM_SS) + ":" + risk.getReason(), ServiceCode.RISK_WITHDRAW.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public class UserFaceDTO {
|
|||
|
||||
private String token;
|
||||
private Long userId;
|
||||
private String type;
|
||||
private String name;
|
||||
private String idCard;
|
||||
private String mobile;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package com.ruoyi.ss.user.domain.dto;
|
||||
|
||||
import com.ruoyi.common.utils.RegexpUtils;
|
||||
import com.ruoyi.common.valid.EnumValid;
|
||||
import com.ruoyi.ss.realName.domain.enums.RealNameType;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
|
@ -18,6 +21,9 @@ public class UserRealNameDTO {
|
|||
@ApiModelProperty("用户ID")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty("实名认证类型")
|
||||
private String type;
|
||||
|
||||
@ApiModelProperty("姓名")
|
||||
@NotBlank(message = "姓名不允许为空")
|
||||
@Size(max = 20, message = "姓名长度不能超过20")
|
||||
|
|
|
@ -218,16 +218,6 @@ public interface ISmUserService
|
|||
*/
|
||||
int readMchLicence(Long userId);
|
||||
|
||||
/**
|
||||
* 限制提现
|
||||
* @param userId
|
||||
* @param limitTime
|
||||
* @param reason
|
||||
* @param remark
|
||||
* @return
|
||||
*/
|
||||
int limitWithdraw(Long userId, LocalDateTime limitTime, String reason, String remark);
|
||||
|
||||
/**
|
||||
* 添加一次风险次数
|
||||
*/
|
||||
|
|
|
@ -21,14 +21,18 @@ 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.domain.enums.RealNameType;
|
||||
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.risk.domain.Risk;
|
||||
import com.ruoyi.ss.risk.domain.RiskQuery;
|
||||
import com.ruoyi.ss.risk.domain.RiskVO;
|
||||
import com.ruoyi.ss.risk.service.IRiskService;
|
||||
import com.ruoyi.ss.store.domain.StoreQuery;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.store.service.StoreService;
|
||||
|
@ -51,7 +55,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -105,6 +108,9 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
@Autowired
|
||||
private WxAuthService wxAuthService;
|
||||
|
||||
@Autowired
|
||||
private IRiskService riskService;
|
||||
|
||||
/**
|
||||
* 查询普通用户信息
|
||||
*
|
||||
|
@ -348,12 +354,21 @@ 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(), "您已进行过实名认证,无需再次认证");
|
||||
|
||||
// 调用第三方API三要素验证
|
||||
// NOTE:无需三要素,只需要姓名、身份证即可
|
||||
// boolean check = RealNameValidUtils.validMobile3Info(dto.getRealIdCard(), dto.getRealPhone(), dto.getRealName());
|
||||
// ServiceUtil.assertion(!check, "请输入正确的身份信息:姓名、身份证、手机号需要一致");
|
||||
// 若用户已经实名,则需要用户填写一样的实名信息
|
||||
if (user.getIsReal()) {
|
||||
if (user.getRealName() != null) {
|
||||
dto.setRealName(user.getRealName());
|
||||
}
|
||||
if (user.getRealPhone() != null) {
|
||||
dto.setRealPhone(user.getRealPhone());
|
||||
}
|
||||
if (user.getRealIdCard() != null) {
|
||||
dto.setRealIdCard(user.getRealIdCard());
|
||||
}
|
||||
}
|
||||
ServiceUtil.assertion(StringUtils.isBlank(dto.getRealName()), "用户姓名不允许为空");
|
||||
ServiceUtil.assertion(StringUtils.isBlank(dto.getRealIdCard()), "用户身份证不允许为空");
|
||||
|
||||
// 获取活体检测token
|
||||
LivenessResponseTokenData tokenData = LivenessUtils.getToken(LIVENESS_RETURN_URL, null, null);
|
||||
|
@ -367,6 +382,7 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
faceDto.setName(dto.getRealName());
|
||||
faceDto.setMobile(dto.getRealPhone());
|
||||
faceDto.setIdCard(dto.getRealIdCard());
|
||||
faceDto.setType(dto.getType());
|
||||
redisCache.setCacheObject(cacheKey, faceDto, 10, TimeUnit.MINUTES);
|
||||
|
||||
// 返回数据
|
||||
|
@ -385,20 +401,6 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
return smUserMapper.updateSmUser(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limitWithdraw(Long userId, LocalDateTime limitTime, String reason, String remark) {
|
||||
if (userId == null) {
|
||||
return 0;
|
||||
}
|
||||
SmUser data = new SmUser();
|
||||
data.setUserId(userId);
|
||||
data.setLimitWithdraw(true);
|
||||
data.setLimitWithdrawTime(limitTime);
|
||||
data.setLimitWithdrawReason(reason);
|
||||
data.setRemark(remark);
|
||||
return smUserMapper.updateSmUser(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addRiskCount(Long userId, int count) {
|
||||
return smUserMapper.addRiskCount(userId, count);
|
||||
|
@ -435,15 +437,7 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
ServiceUtil.assertion(!FaceIncorrect.isSuccess(faceIdb.getIncorrect()), "人证比对未通过:" + faceIdb.getMsg());
|
||||
ServiceUtil.assertion(!FaceScoreResult.isSame(faceIdb.getScore()), "人证比对未通过,分数:" + faceIdb.getScore());
|
||||
|
||||
// 通过,修改实名认证信息(用户表、实名认证表)
|
||||
// 构造用户信息
|
||||
SmUser data = new SmUserQuery();
|
||||
data.setUserId(face.getUserId());
|
||||
data.setRealName(face.getName());
|
||||
data.setRealIdCard(face.getIdCard());
|
||||
data.setRealPhone(face.getMobile());
|
||||
data.setIsReal(true);
|
||||
// 构造实名认证表
|
||||
// 通过,构造实名认证表
|
||||
RealName realName = new RealName();
|
||||
realName.setUserId(userId);
|
||||
realName.setName(face.getName());
|
||||
|
@ -451,6 +445,63 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
realName.setMobile(face.getMobile());
|
||||
realName.setScore(faceIdb.getScore());
|
||||
realName.setFaceImage(imageUrl);
|
||||
realName.setType(face.getType());
|
||||
// 普通实名
|
||||
if (RealNameType.NORMAL.getType().equals(face.getType())) {
|
||||
return this.handleNormalRealName(realName, cacheKey);
|
||||
}
|
||||
// 风控实名,修改用户风控信息
|
||||
else {
|
||||
return this.handleRiskRealName(realName, cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理风控实名认证成功
|
||||
* @param realName 实名认证
|
||||
* @param cacheKey 缓存key
|
||||
*/
|
||||
private int handleRiskRealName(RealName realName, String cacheKey) {
|
||||
// 查询用户是否有风控信息
|
||||
RiskQuery query = new RiskQuery();
|
||||
query.setUserId(realName.getUserId());
|
||||
List<RiskVO> riskList = riskService.selectNotFinishedList(query);
|
||||
|
||||
Integer result = transactionTemplate.execute(status -> {
|
||||
// 插入实名认证信息
|
||||
int insert = realNameService.insertRealName(realName);
|
||||
ServiceUtil.assertion(insert != 1, "新增实名认证失败");
|
||||
|
||||
// 修改风控信息
|
||||
if (CollectionUtils.isNotEmptyElement(riskList)) {
|
||||
int update = riskService.batchFinish(CollectionUtils.map(riskList, Risk::getRiskId), realName.getId());
|
||||
ServiceUtil.assertion(update != riskList.size(), "修改风控信息失败");
|
||||
}
|
||||
|
||||
// 删除缓存
|
||||
redisCache.deleteObject(cacheKey);
|
||||
|
||||
return insert;
|
||||
});
|
||||
|
||||
return result == null ? 0 : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理普通实名认证成功
|
||||
* @param realName 实名认证
|
||||
* @param cacheKey 缓存key
|
||||
*/
|
||||
private int handleNormalRealName(RealName realName, String cacheKey) {
|
||||
|
||||
// 修改实名认证信息(用户表)
|
||||
SmUser data = new SmUserQuery();
|
||||
data.setUserId(realName.getUserId());
|
||||
data.setRealName(realName.getName());
|
||||
data.setRealIdCard(realName.getIdCard());
|
||||
data.setRealPhone(realName.getMobile());
|
||||
data.setIsReal(true);
|
||||
|
||||
// 数据库操作
|
||||
Integer result = transactionTemplate.execute(status -> {
|
||||
// 修改用户实名信息
|
||||
|
|
|
@ -253,15 +253,7 @@ public class AppTransactionBillController extends BaseController
|
|||
|
||||
// 判断是否限制退款
|
||||
SmUserVo user = userService.selectSmUserByUserId(userId);
|
||||
boolean limitRefund = user.getLimitRefund() != null && user.getLimitRefund();
|
||||
if (limitRefund) {
|
||||
LocalDateTime limitRefundTime = user.getLimitRefundTime();
|
||||
if (limitRefundTime == null) {
|
||||
throw new ServiceException("您被永久限制退款:" + user.getLimitRefundReason());
|
||||
} else if (limitRefundTime.isAfter(LocalDateTime.now())){
|
||||
throw new ServiceException("您被限制退款至" + DateUtils.format(limitRefundTime, DateUtils.YYYY_MM_DD_HH_MM_SS) + ":" + user.getLimitRefundReason() );
|
||||
}
|
||||
}
|
||||
transactionBillValidator.checkRefundRisk(user);
|
||||
|
||||
LoginUser loginUser = getLoginUser();
|
||||
dto.setUserName(loginUser.getUsername());
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.ruoyi.common.utils.ServiceUtil;
|
|||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.web.service.SysPasswordService;
|
||||
import com.ruoyi.framework.web.service.TokenService;
|
||||
import com.ruoyi.ss.realName.domain.enums.RealNameType;
|
||||
import com.ruoyi.ss.user.domain.SmUserVo;
|
||||
import com.ruoyi.ss.user.domain.dto.UserChangeMobileDTO;
|
||||
import com.ruoyi.ss.user.domain.dto.UserRealNameDTO;
|
||||
|
@ -101,6 +102,16 @@ public class AppUserController extends BaseController {
|
|||
@ApiOperation("用户实名认证")
|
||||
@PutMapping("/realName")
|
||||
public AjaxResult realName(@RequestBody @Validated UserRealNameDTO dto) {
|
||||
dto.setType(RealNameType.NORMAL.getType());
|
||||
dto.setUserId(getUserId());
|
||||
return success(userService.realName(dto));
|
||||
}
|
||||
|
||||
@ApiOperation("用户风控实名认证")
|
||||
@PutMapping("/riskRealName")
|
||||
public AjaxResult riskRealName() {
|
||||
UserRealNameDTO dto = new UserRealNameDTO();
|
||||
dto.setType(RealNameType.RISK.getType());
|
||||
dto.setUserId(getUserId());
|
||||
return success(userService.realName(dto));
|
||||
}
|
||||
|
|
|
@ -134,4 +134,4 @@ tm:
|
|||
|
||||
# 活体检测跳转地址
|
||||
liveness:
|
||||
returnUrl: http://192.168.2.106:3001/liveness
|
||||
returnUrl: http://192.168.2.15:3001/liveness
|
||||
|
|
Loading…
Reference in New Issue
Block a user