From f37bc4779ac91c938405e1c01184b441d3fc015d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A2=A8=E5=A4=A7=E5=8F=94?= <494979559@qq.com>
Date: Sat, 21 Sep 2024 19:28:59 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=8E=B0=E9=A3=8E=E6=8E=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ruoyi/common/constant/IotConstants.java | 2 +-
.../common/core/domain/entity/SmUser.java | 11 +++
.../com/ruoyi/common/utils/DateUtils.java | 9 +++
.../system/domain/enums/config/ConfigKey.java | 5 +-
.../device/domain/enums/DeviceOutageWay.java | 4 +-
.../service/TransactionBillService.java | 5 ++
.../service/WithdrawValidator.java | 17 +++++
.../impl/TransactionBillServiceImpl.java | 41 +++++++++-
.../service/impl/WithdrawValidatorImpl.java | 74 +++++++++++++++++++
.../ruoyi/ss/user/mapper/SmUserMapper.java | 8 ++
.../com/ruoyi/ss/user/mapper/SmUserMapper.xml | 14 ++++
.../ruoyi/ss/user/service/ISmUserService.java | 16 ++++
.../user/service/impl/SmUserServiceImpl.java | 20 +++++
13 files changed, 221 insertions(+), 5 deletions(-)
create mode 100644 smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/WithdrawValidator.java
create mode 100644 smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/WithdrawValidatorImpl.java
diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/constant/IotConstants.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
index a3f4d462..c498fe01 100644
--- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
+++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/constant/IotConstants.java
@@ -68,7 +68,7 @@ public class IotConstants {
public static final String COMMAND_RECHARGE = "time";
/**
- * 命令 设置断电方式
+ * 命令 设置通断电方式
*/
public static final String COMMAND_OUTAGE_WAY = "set";
diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/domain/entity/SmUser.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/domain/entity/SmUser.java
index 08726f16..1a0aa272 100644
--- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/domain/entity/SmUser.java
+++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/domain/entity/SmUser.java
@@ -1,6 +1,7 @@
package com.ruoyi.common.core.domain.entity;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
@@ -186,4 +187,14 @@ public class SmUser extends BaseEntity
@Excel(name = "限制退款原因")
@ApiModelProperty("限制退款原因")
private String limitRefundReason;
+
+ @Excel(name = "风险提现次数")
+ @ApiModelProperty("风险提现次数")
+ private Integer riskCount;
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @Excel(name = "限制提现时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+ @ApiModelProperty("限制提现时间")
+ private LocalDateTime limitWithdrawTime;
+
}
diff --git a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
index 5e469dc5..24508d67 100644
--- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
+++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -5,6 +5,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
+import java.time.temporal.Temporal;
import java.util.Calendar;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
@@ -316,4 +317,12 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
public static LocalDateTime toLocalDate(String timeStr, String format) {
return LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern(format));
}
+
+ public static LocalDateTime toLocalDateTime(Date date) {
+ return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
+
+ public static String format(LocalDateTime time, String format) {
+ return time.format(DateTimeFormatter.ofPattern(format));
+ }
}
diff --git a/smart-switch-ruoyi/smart-switch-system/src/main/java/com/ruoyi/system/domain/enums/config/ConfigKey.java b/smart-switch-ruoyi/smart-switch-system/src/main/java/com/ruoyi/system/domain/enums/config/ConfigKey.java
index 033c7bda..cc5ebf42 100644
--- a/smart-switch-ruoyi/smart-switch-system/src/main/java/com/ruoyi/system/domain/enums/config/ConfigKey.java
+++ b/smart-switch-ruoyi/smart-switch-system/src/main/java/com/ruoyi/system/domain/enums/config/ConfigKey.java
@@ -21,7 +21,10 @@ public enum ConfigKey {
DAILY_WITHDRAW_COUNT("daily.withdraw.count", "单日单用户提现次数(次)"),
NOVERIFY_WITHDRAW_SINGLE("noverify.withdraw.single", "提现单笔免审核额度(元)"),
RECHARGE_MIN_SERVICE("recharge.min.service","充值最低服务费(元)"),
- ORDER_AUTO_CLOSE_CD("order.auto.close.cd", "订单自动关闭冷却时间(分)");
+ ORDER_AUTO_CLOSE_CD("order.auto.close.cd", "订单自动关闭冷却时间(分)"),
+ RISK_WITHDRAW_TIME("risk.withdraw.time", "风控订单和提现相隔时长(分钟)"),
+ RISK_WITHDRAW_COUNT("risk.withdraw.count", "累计风险次数"),
+ RISK_WITHDRAW_ENABLED("risk.withdraw.enabled", "是否开启提现风控");
private final String key;
private final String msg;
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/device/domain/enums/DeviceOutageWay.java b/smart-switch-service/src/main/java/com/ruoyi/ss/device/domain/enums/DeviceOutageWay.java
index fc7a1db1..60c0501c 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/device/domain/enums/DeviceOutageWay.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/device/domain/enums/DeviceOutageWay.java
@@ -14,8 +14,8 @@ import java.util.Objects;
@Getter
public enum DeviceOutageWay {
- NOT_OUTAGE("0", "到时不断电"),
- IMMEDIATE("1", "到时立即断电");
+ NOT_OUTAGE("0", "正"),
+ IMMEDIATE("1", "反");
private final String value;
private final String msg;
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/TransactionBillService.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/TransactionBillService.java
index 3975b342..57bfef21 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/TransactionBillService.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/TransactionBillService.java
@@ -299,4 +299,9 @@ public interface TransactionBillService
* 开启/关闭订单设备
*/
int switchDevice(TransactionBillVO bill, boolean open);
+
+ /**
+ * 查询最后一个
+ */
+ TransactionBillVO selectLastOne(TransactionBillQuery query);
}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/WithdrawValidator.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/WithdrawValidator.java
new file mode 100644
index 00000000..1ed24141
--- /dev/null
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/WithdrawValidator.java
@@ -0,0 +1,17 @@
+package com.ruoyi.ss.transactionBill.service;
+
+import com.ruoyi.common.core.domain.ValidateResult;
+import com.ruoyi.ss.transactionBill.domain.bo.WithdrawBO;
+
+/**
+ * @author wjh
+ * 2024/9/21
+ */
+public interface WithdrawValidator {
+
+ /**
+ * 判断提现是否有风险
+ */
+ boolean hasRisk(WithdrawBO bo);
+
+}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java
index d97b8268..cb7dcc23 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java
@@ -2,6 +2,7 @@ package com.ruoyi.ss.transactionBill.service.impl;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.core.domain.ValidateResult;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.WithdrawServiceType;
@@ -50,6 +51,7 @@ import com.ruoyi.ss.transactionBill.mapper.TransactionBillMapper;
import com.ruoyi.ss.transactionBill.service.TransactionBillConverter;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.ss.transactionBill.service.TransactionBillValidator;
+import com.ruoyi.ss.transactionBill.service.WithdrawValidator;
import com.ruoyi.ss.transfer.domain.TransferVO;
import com.ruoyi.ss.transfer.interfaces.AfterTransfer;
import com.ruoyi.ss.transfer.service.TransferConverter;
@@ -155,6 +157,9 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
@Autowired
private TransactionBillConverter transactionBillConverter;
+ @Autowired
+ private WithdrawValidator withdrawValidator;
+
/**
* 查询充值记录
*
@@ -414,7 +419,35 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
ServiceUtil.assertion(user == null, "用户不存在");
ServiceUtil.assertion(user.getIsReal() == null || !user.getIsReal(), "用户未实名认证,无法提现");
- ServiceUtil.assertion(user.getLimitWithdraw() != null && user.getLimitWithdraw(), "您被限制提现:" + user.getLimitWithdrawReason());
+
+ // 判断用户是否被限制提现
+ boolean limitWithdraw = user.getLimitWithdraw() != null && user.getLimitWithdraw();
+ if (limitWithdraw) {
+ LocalDateTime limitWithdrawTime = user.getLimitWithdrawTime();
+ if (limitWithdrawTime == null) {
+ throw new ServiceException("您被永久限制提现:" + user.getLimitWithdrawReason());
+ } else {
+ throw new ServiceException("您被限制提现至" + DateUtils.format(limitWithdrawTime, DateUtils.YYYY_MM_DD_HH_MM_SS) + ":" + user.getLimitWithdrawReason() );
+ }
+ }
+
+ // 风控规则,判断用户是否有风险
+ boolean enabled = sysConfigService.getBoolean(ConfigKey.RISK_WITHDRAW_ENABLED);
+ if (enabled) {
+ boolean hasRisk = withdrawValidator.hasRisk(bo);
+ if (hasRisk) {
+ // 累计一次风险次数
+ userService.addRiskCount(userId, 1);
+ // 若用户风险次数已达到阈值,将用户标记为提现风险
+ int riskWithdrawCount = sysConfigService.getInt(ConfigKey.RISK_WITHDRAW_COUNT);
+ if (user.getRiskCount() + 1 >= riskWithdrawCount) {
+ userService.limitWithdraw(userId, LocalDateTime.now().plusDays(1), "根据风控规则判断,您的提现具有风险", "风险客户");
+ // 返回错误
+ throw new ServiceException("提现具有风险,无法提现");
+ }
+ }
+ }
+
// 判断今天提现成功和正在审核中的提现是否超过限额
String dailyLimitStr = sysConfigService.selectConfigByKey(ConfigKey.DAILY_WITHDRAW_AMOUNT.getKey());
@@ -1649,6 +1682,12 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
}
}
+ @Override
+ public TransactionBillVO selectLastOne(TransactionBillQuery query) {
+ PageHelper.orderBy("stb.bill_id desc");
+ return selectOne(query);
+ }
+
@Override
public UserWithdrawServiceVO getUserWithdrawService(Long userId, Long channelId) {
SmUserVo user = userService.selectSmUserByUserId(userId);
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/WithdrawValidatorImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/WithdrawValidatorImpl.java
new file mode 100644
index 00000000..e33477fc
--- /dev/null
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/WithdrawValidatorImpl.java
@@ -0,0 +1,74 @@
+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.utils.DateUtils;
+import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
+import com.ruoyi.ss.transactionBill.domain.bo.WithdrawBO;
+import com.ruoyi.ss.transactionBill.domain.dto.WithdrawDTO;
+import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
+import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
+import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
+import com.ruoyi.ss.transactionBill.service.TransactionBillService;
+import com.ruoyi.ss.transactionBill.service.WithdrawValidator;
+import com.ruoyi.ss.user.domain.SmUserVo;
+import com.ruoyi.system.domain.enums.config.ConfigKey;
+import com.ruoyi.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+/**
+ * @author wjh
+ * 2024/9/21
+ */
+@Service
+public class WithdrawValidatorImpl extends BaseValidator implements WithdrawValidator {
+
+ @Autowired
+ private TransactionBillService transactionBillService;
+
+ @Autowired
+ private ISysConfigService sysConfigService;
+ @Autowired
+ private RedisCache redisCache;
+
+ /**
+ * 判断提现是否有风险
+ *
+ * @param bo
+ */
+ @Override
+ public boolean hasRisk(WithdrawBO bo) {
+ if (bo == null || bo.getUser() == null || bo.getDto() == null) {
+ return false;
+ }
+ SmUserVo user = bo.getUser();
+ WithdrawDTO dto = bo.getDto();
+
+ // 查询最近一次的订单
+ 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) {
+ 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;
+ }
+
+ return false;
+ }
+}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.java b/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.java
index 5194d8ce..a5239dcb 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.java
@@ -109,4 +109,12 @@ public interface SmUserMapper
* 查询用户余额
*/
BigDecimal selectSumOfBalance(SmUserQuery query);
+
+ /**
+ * 增加一次风险次数
+ * @param userId
+ * @param count
+ * @return
+ */
+ int addRiskCount(@Param("userId") Long userId,@Param("count") int count);
}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.xml b/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.xml
index 7475b58d..cfde15de 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.xml
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/user/mapper/SmUserMapper.xml
@@ -49,6 +49,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
su.limit_withdraw_reason,
su.limit_refund,
su.limit_refund_reason,
+ su.risk_count,
+ su.limit_withdraw_time,
(select sum(stb.money) from sm_transaction_bill stb where stb.user_id = su.user_id and stb.type = '1' and stb.status = '2') as recharge_amount,
(select sum(stb.arrival_amount) from sm_transaction_bill stb where stb.user_id = su.user_id and stb.type = '2' and stb.status = '14') as with_drawl_amount,
(select sum(stb.arrival_amount) from sm_transaction_bill stb where stb.mch_id = su.user_id and stb.type = '1' and stb.status = '2') as total_income
@@ -169,6 +171,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit_withdraw_reason,
limit_refund,
limit_refund_reason,
+ risk_count,
+ limit_withdraw_time,
#{userName},
@@ -206,9 +210,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{limitWithdrawReason},
#{limitRefund},
#{limitRefundReason},
+ #{riskCount},
+ #{limitWithdrawTime},
+
+ update sm_user
+ set risk_count = risk_count + 1
+ where user_id = #{userId} and del_flag = '0'
+
+
update sm_user
set balance = balance + #{amount}
@@ -253,6 +265,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit_withdraw_reason = #{limitWithdrawReason},
limit_refund = #{limitRefund},
limit_refund_reason = #{limitRefundReason},
+ risk_count = #{riskCount},
+ limit_withdraw_time = #{limitWithdrawTime},
where user_id = #{userId}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/ISmUserService.java b/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/ISmUserService.java
index 1169ee92..03082299 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/ISmUserService.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/ISmUserService.java
@@ -8,6 +8,7 @@ import com.ruoyi.ss.user.domain.SmUserVo;
import com.ruoyi.ss.user.domain.dto.UserRealNameDTO;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.util.List;
/**
@@ -209,4 +210,19 @@ public interface ISmUserService
* 实名认证
*/
int realName(UserRealNameDTO dto);
+
+ /**
+ * 限制提现
+ * @param userId
+ * @param limitTime
+ * @param reason
+ * @param remark
+ * @return
+ */
+ int limitWithdraw(Long userId, LocalDateTime limitTime, String reason, String remark);
+
+ /**
+ * 添加一次风险次数
+ */
+ int addRiskCount(Long userId, int count);
}
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/impl/SmUserServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/impl/SmUserServiceImpl.java
index 0df818ec..435cf3ec 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/impl/SmUserServiceImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/user/service/impl/SmUserServiceImpl.java
@@ -26,6 +26,7 @@ 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;
@@ -297,6 +298,25 @@ 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);
+ }
+
/**
* 逻辑删除前校验
* @param userIds