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