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 88928bec..45384f62 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;
@@ -192,4 +193,13 @@ public class SmUser extends BaseEntity
@JsonView(JsonViewProfile.App.class)
private Boolean readMchLicence;
+ @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/core/redis/enums/RedisLockKey.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/redis/enums/RedisLockKey.java
index c4d8b34d..aa3873f6 100644
--- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/redis/enums/RedisLockKey.java
+++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/core/redis/enums/RedisLockKey.java
@@ -22,7 +22,8 @@ public enum RedisLockKey {
ADD_TIME_BILL("add_time_bill", "创建时长订单"),
PAY_BILL("pay_bill", "支付订单"),
PREPAY_DEPOSIT("prepay_deposit", "支付押金"),
- ADD_RECHARGE_ORDER("add_recharge_order", "创建充值订单");
+ ADD_RECHARGE_ORDER("add_recharge_order", "创建充值订单"),
+ PAY_BILL_SUCCESS("pay_bill_success", "支付订单成功处理");
private final String key;
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-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
index 7e0e456f..3194ca84 100644
--- a/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
+++ b/smart-switch-ruoyi/smart-switch-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
@@ -85,7 +85,7 @@ public class HttpUtils
{
result.append(line);
}
- log.debug("recv - {}", result);
+// log.debug("recv - {}", result);
}
catch (ConnectException e)
{
@@ -153,7 +153,7 @@ public class HttpUtils
{
result.append(line);
}
- log.debug("recv - {}", result);
+// log.debug("recv - {}", result);
}
catch (ConnectException e)
{
@@ -224,7 +224,7 @@ public class HttpUtils
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
}
}
- log.debug("recv - {}", result);
+// log.debug("recv - {}", result);
conn.disconnect();
br.close();
}
@@ -329,7 +329,7 @@ public class HttpUtils
{
result.append(line);
}
- log.debug("recv - {}", result);
+// log.debug("recv - {}", result);
}
catch (ConnectException e)
{
@@ -396,7 +396,7 @@ public class HttpUtils
{
result.append(line);
}
- log.debug("recv - {}", result);
+// log.debug("recv - {}", result);
}
catch (ConnectException e)
{
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/iot/service/IotServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/iot/service/IotServiceImpl.java
index f8e8d0f1..4aa708d0 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/iot/service/IotServiceImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/iot/service/IotServiceImpl.java
@@ -105,10 +105,10 @@ public class IotServiceImpl implements IotService {
String sendUrl = iotHost + IotConstants.ADDS_HISTORY_DATAPOINTS + "?"+param;
String token = Token.getToken();
- log.info("IOT获取到Authorization:【{}】",token);
+// log.info("IOT获取到Authorization:【{}】",token);
String result = HttpUtils.sendGetWithToken(sendUrl, null, token);
- log.info("IOT返回的结果【{}】",result);
+// log.info("IOT返回的结果【{}】",result);
if (!StringUtils.hasText(result)) {
log.error("与OneNet通信异常");
return null;
@@ -159,7 +159,7 @@ public class IotServiceImpl implements IotService {
String param = "device_name=" + deviceName + "&product_id=" + productId;
String token = Token.getToken();
- log.info("IOT获取到Authorization:【{}】",token);
+// log.info("IOT获取到Authorization:【{}】",token);
String result = HttpUtils.sendGetWithToken(sendUrl, param, token);
if (!StringUtils.hasText(result)) {
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/device/service/impl/DeviceServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/device/service/impl/DeviceServiceImpl.java
index e354d483..d1a699c3 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/device/service/impl/DeviceServiceImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/device/service/impl/DeviceServiceImpl.java
@@ -579,23 +579,23 @@ public class DeviceServiceImpl implements DeviceService
this.pullDeviceInfoAsync(Collections.singletonList(deviceId), 3, TimeUnit.SECONDS);
// 时长结束后修改设备状态
- scheduledExecutorService.schedule(()-> {
- freshStatus(deviceId);
- }, seconds, TimeUnit.SECONDS);
+// scheduledExecutorService.schedule(()-> {
+// freshStatus(deviceId);
+// }, seconds, TimeUnit.SECONDS);
}
return success;
}
- private void freshStatus(Long deviceId) {
- DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
- freshStatus(device);
- }
+// private void freshStatus(Long deviceId) {
+// DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
+// freshStatus(device);
+// }
@Override
public void freshStatus(DeviceVO device) {
- if (device == null) {
+ if (device == null || device.getExpireTime() == null) {
return;
}
if (!DeviceStatus.USING.getStatus().equals(device.getStatus())) {
@@ -610,12 +610,12 @@ public class DeviceServiceImpl implements DeviceService
return;
}
- // 如果没有过期,则延迟至过期时间继续查询
- if (between.getSeconds() > 0) {
- scheduledExecutorService.schedule(() -> {
- freshStatus(device.getDeviceId());
- }, between.getSeconds() , TimeUnit.SECONDS);
- }
+// // 如果没有过期,则延迟至过期时间继续查询
+// if (between.getSeconds() > 0) {
+// scheduledExecutorService.schedule(() -> {
+// freshStatus(device.getDeviceId());
+// }, between.getSeconds() , TimeUnit.SECONDS);
+// }
}
/**
@@ -632,7 +632,7 @@ public class DeviceServiceImpl implements DeviceService
for (DeviceVO device : list) {
if (StringUtils.hasText(device.getMac())) {
String status = iotService.getOnlineStatus(device.getMac(), device.getModelProductId()).getStatus();
- log.info("device: {} {} online status is {}", device.getDeviceId(), device.getMac(), status);
+// log.info("device: {} {} online status is {}", device.getDeviceId(), device.getMac(), status);
device.setOnlineStatus(status);
} else {
device.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());
@@ -752,9 +752,9 @@ public class DeviceServiceImpl implements DeviceService
// 判断设备是否正在使用
// 设备过期时间 > 当前时间,则正在使用
- boolean hasTime = device.getExpireTime().isAfter(now);
+ boolean hasTime = device.getExpireTime() != null && device.getExpireTime().isAfter(now);
// 若当前设备有电量,则正在使用
- boolean hasEle = data.getSurplusEle().compareTo(BigDecimal.ZERO) > 0;
+ boolean hasEle = data.getSurplusEle() != null && data.getSurplusEle().compareTo(BigDecimal.ZERO) > 0;
// 若开关开启,则正在使用
boolean hasOpen = DevicePowerStatus.ON.getStatus().equals(data.getPowerStatus());
if (hasTime || hasEle || hasOpen) {
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java
index 7051a378..4975f504 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/payBill/service/impl/PayBillServiceImpl.java
@@ -277,35 +277,41 @@ public class PayBillServiceImpl implements PayBillService
private void handleSuccess(PayBillVO payBill, LocalDateTime payTime) {
ServiceUtil.assertion(payBill == null, "支付订单不存在");
- log.info("获取支付订单:{}", payBill.getPayNo());
- // 若已经支付成功,则跳过
- if (PayBillStatus.payedList().contains(payBill.getStatus())) {
- return;
- }
- ServiceUtil.assertion(!PayBillStatus.PAYING.getStatus().equals(payBill.getStatus()), "该支付订单不是正在支付的支付订单");
+ Long lockKey = payBill.getPayId();
- transactionTemplate.execute(status -> {
- // 修改支付订单状态
- PayBill data = new PayBill();
- data.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
- data.setPayTime(payTime);
- PayBillQuery query = new PayBillQuery();
- query.setStatus(PayBillStatus.PAYING.getStatus());
- query.setPayId(payBill.getPayId());
- int update = this.updateByQuery(data, query);
- ServiceUtil.assertion(update != 1, "支付订单状态已改变,请稍后再试");
-
- // 处理业务
- PayBillBstType bstType = PayBillBstType.parse(payBill.getBstType());
- if (bstType != null && bstType.getAfterPay() != null) {
- PayBillVO newPayBill = selectPayBillByPayId(payBill.getPayId());
- AfterPay afterPay = SpringUtils.getBean(bstType.getAfterPay());
- int bstResult = afterPay.onPaySuccess(newPayBill);
- ServiceUtil.assertion(bstResult == 0, "业务处理失败");
+ ServiceUtil.assertion(redisLock.lock(RedisLockKey.PAY_BILL_SUCCESS, lockKey), "支付订单正在处理中:payId=" + payBill.getPayId());
+ try {
+ // 若已经支付成功,则跳过
+ if (PayBillStatus.payedList().contains(payBill.getStatus())) {
+ return;
}
+ ServiceUtil.assertion(!PayBillStatus.PAYING.getStatus().equals(payBill.getStatus()), "该支付订单不是正在支付的支付订单");
- return update;
- });
+ transactionTemplate.execute(status -> {
+ // 修改支付订单状态
+ PayBill data = new PayBill();
+ data.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
+ data.setPayTime(payTime);
+ PayBillQuery query = new PayBillQuery();
+ query.setStatus(PayBillStatus.PAYING.getStatus());
+ query.setPayId(payBill.getPayId());
+ int update = this.updateByQuery(data, query);
+ ServiceUtil.assertion(update != 1, "支付订单状态已改变,请稍后再试");
+
+ // 处理业务
+ PayBillBstType bstType = PayBillBstType.parse(payBill.getBstType());
+ if (bstType != null && bstType.getAfterPay() != null) {
+ PayBillVO newPayBill = selectPayBillByPayId(payBill.getPayId());
+ AfterPay afterPay = SpringUtils.getBean(bstType.getAfterPay());
+ int bstResult = afterPay.onPaySuccess(newPayBill);
+ ServiceUtil.assertion(bstResult == 0, "业务处理失败");
+ }
+
+ return update;
+ });
+ } finally {
+ redisLock.unlock(RedisLockKey.PAY_BILL_SUCCESS, lockKey);
+ }
}
@Override
diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/record/time/service/impl/RecordTimeConverterImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/record/time/service/impl/RecordTimeConverterImpl.java
index b68532d0..95b2337e 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/ss/record/time/service/impl/RecordTimeConverterImpl.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/ss/record/time/service/impl/RecordTimeConverterImpl.java
@@ -41,6 +41,7 @@ public class RecordTimeConverterImpl implements RecordTimeConverter {
record.setDeviceId(device == null ? null : device.getDeviceId());
record.setAmount(seconds);
record.setReason(reason);
+ record.setType(type);
if (user != null) {
record.setOperatorId(user.getUserId());
record.setOperatorName(user.getUserName());
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 5e3c0591..cb591c4e 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
@@ -3,6 +3,7 @@ package com.ruoyi.ss.transactionBill.service.impl;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
+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.BusinessStatus;
@@ -52,6 +53,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;
@@ -157,6 +159,9 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
@Autowired
private TransactionBillConverter transactionBillConverter;
+ @Autowired
+ private WithdrawValidator withdrawValidator;
+
/**
* 查询充值记录
*
@@ -415,8 +420,36 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
AccountVO account = bo.getAccount();
ServiceUtil.assertion(user == null, "用户不存在");
- ServiceUtil.assertion(user.getIsReal() == null || !user.getIsReal(), "用户未实名认证,无法提现", HttpStatus.NO_REAL_NAME);
- ServiceUtil.assertion(user.getLimitWithdraw() != null && user.getLimitWithdraw(), "您被限制提现:" + user.getLimitWithdrawReason(), HttpStatus.LIMIT_WITHDRAW);
+ 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 {
+ 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());
@@ -725,13 +758,18 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
TransactionBillVO bill = transactionBillMapper.selectSmTransactionBillByBillId(billId);
ServiceUtil.assertion(bill == null || !TransactionBillType.RECHARGE.getType().equals(bill.getType()), "不存在的充值订单");
ServiceUtil.assertion(!TransactionBillStatus.SUCCESS.getStatus().equals(bill.getStatus()), "订单未支付");
- ServiceUtil.assertion(TransactionBillDeviceRechargeStatus.SUCCESS.getStatus().equals(bill.getDeviceRechargeStatus()), "设备已充值成功,不允许再次充值");
+
+ if (TransactionBillDeviceRechargeStatus.SUCCESS.getStatus().equals(bill.getDeviceRechargeStatus())) {
+ log.warn( "设备已充值成功,不允许再次充值:billId={}", billId);
+ return false;
+ }
+
ServiceUtil.assertion(TransactionBillDeviceRechargeStatus.BLUETOOTH.getStatus().equals(bill.getDeviceRechargeStatus()), "设备已选择蓝牙充值,请使用蓝牙进行充值");
- // 刷新设备数据
- deviceService.pullDeviceInfo(bill.getDeviceId());
+ Boolean result = transactionTemplate.execute(status -> {
+ // 刷新设备数据
+ deviceService.pullDeviceInfo(bill.getDeviceId());
- Boolean result = (Boolean) transactionTemplate.execute(status -> {
DeviceVO device = deviceService.selectSmDeviceByDeviceId(bill.getDeviceId());
// 如果设备离线,则直接返回失败
@@ -745,7 +783,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
// 修改设备充值状态:成功
int updateRecharge = transactionBillMapper.updateDeviceRechargeStatus(bill.getBillId(), TransactionBillDeviceRechargeStatus.SUCCESS.getStatus());
- ServiceUtil.assertion(updateRecharge != 1, "订单状态发生变化,请稍后重试");
+ ServiceUtil.assertion(updateRecharge != 1, "设备充值状态发生变化,请稍后重试");
try {
if (SuitFeeType.TIME.getType().equals(bill.getSuitFeeType())) {
@@ -851,14 +889,33 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
return updateCount;
});
+ // 异步充值设备,尝试3次
if (result != null && result == 1) {
- // 设备充值
- rechargeDevice(bill.getBillId());
+ this.tryRechargeDevice(bill.getBillId(), 3);
}
return result == null ? 0 : result;
}
+ /**
+ * 异步尝试充值设备
+ * @param billId
+ * @param tryCount
+ */
+ private void tryRechargeDevice(Long billId, int tryCount) {
+ if (tryCount <= 0) {
+ return;
+ }
+ scheduledExecutorService.schedule(()-> {
+ try {
+ boolean result = rechargeDevice(billId);
+ ServiceUtil.assertion(!result, String.format("尝试充值设备失败:billId=%s:剩余次数:%s,", billId, tryCount - 1));
+ } catch (Exception e) {
+ this.tryRechargeDevice(billId, tryCount - 1);
+ }
+ }, 0, TimeUnit.SECONDS);
+ }
+
@Override
public DoPayVO payDeposit(RechargePayDepositBO bo) {
// 校验
@@ -1433,7 +1490,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
if (SuitFeeType.TIME.getType().equals(bill.getSuitFeeType())) {
recordTimeService.insertRecordTime(recordTimeConverter.toRecordTime(device, bill.toSecondSuitTime(), reason, user, RecordTimeType.TIME.getType()));
} else if (SuitFeeType.COUNT.getType().equals(bill.getSuitFeeType())) {
- recordTimeService.insertRecordTime(recordTimeConverter.toRecordTime(device, bill.toSecondSuitTime(), reason, user, RecordTimeType.ELE.getType()));
+ recordTimeService.insertRecordTime(recordTimeConverter.toRecordTime(device, bill.getSuitTime(), reason, user, RecordTimeType.ELE.getType()));
}
}, 1L, TimeUnit.SECONDS);
}
@@ -1627,6 +1684,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 7d1f7aa7..e11d12b5 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
@@ -50,6 +50,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
su.limit_refund,
su.limit_refund_reason,
su.read_mch_licence,
+ 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
@@ -172,6 +174,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit_refund,
limit_refund_reason,
read_mch_licence,
+ risk_count,
+ limit_withdraw_time,
#{userName},
@@ -210,9 +214,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{limitRefund},
#{limitRefundReason},
#{readMchLicence},
+ #{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}
@@ -258,6 +270,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit_refund = #{limitRefund},
limit_refund_reason = #{limitRefundReason},
read_mch_licence = #{readMchLicence},
+ 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 95c6d77a..b1a14bea 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
@@ -9,6 +9,7 @@ import com.ruoyi.ss.user.domain.dto.UserRealNameDTO;
import com.ruoyi.ss.user.domain.vo.UserRealNameVO;
import java.math.BigDecimal;
+import java.time.LocalDateTime;
import java.util.List;
/**
@@ -215,4 +216,19 @@ 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);
+
+ /**
+ * 添加一次风险次数
+ */
+ 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 46b22093..a1d052b1 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
@@ -34,6 +34,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;
@@ -340,6 +341,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
diff --git a/smart-switch-service/src/main/java/com/ruoyi/task/device/DeviceStatusTask.java b/smart-switch-service/src/main/java/com/ruoyi/task/device/DeviceStatusTask.java
index 5ee77e0b..f0e1e973 100644
--- a/smart-switch-service/src/main/java/com/ruoyi/task/device/DeviceStatusTask.java
+++ b/smart-switch-service/src/main/java/com/ruoyi/task/device/DeviceStatusTask.java
@@ -31,18 +31,18 @@ public class DeviceStatusTask implements ApplicationRunner {
*/
@Override
public void run(ApplicationArguments args) throws Exception {
- log.info("项目启动,查询正在使用中的设备");
- DeviceQuery query = new DeviceQuery();
- query.setStatus(DeviceStatus.USING.getStatus());
- List list = deviceService.selectSmDeviceList(query);
- if (CollectionUtils.isEmptyElement(list)) {
- log.info("没有正在使用中的设备");
- return;
- }
-
- for (DeviceVO device : list) {
- deviceService.freshStatus(device);
- }
+// log.info("项目启动,查询正在使用中的设备");
+// DeviceQuery query = new DeviceQuery();
+// query.setStatus(DeviceStatus.USING.getStatus());
+// List list = deviceService.selectSmDeviceList(query);
+// if (CollectionUtils.isEmptyElement(list)) {
+// log.info("没有正在使用中的设备");
+// return;
+// }
+//
+// for (DeviceVO device : list) {
+// deviceService.freshStatus(device);
+// }
}
}
diff --git a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTransactionBillController.java b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTransactionBillController.java
index 3273783f..067a7422 100644
--- a/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTransactionBillController.java
+++ b/smart-switch-web/src/main/java/com/ruoyi/web/controller/app/AppTransactionBillController.java
@@ -272,6 +272,7 @@ public class AppTransactionBillController extends BaseController
@ApiOperation("支付押金")
@PutMapping("/payDeposit")
public AjaxResult payDeposit(@RequestBody @Validated PayDepositDTO dto) {
+ dto.setChannelId(1L);
return success(transactionBillService.payDeposit(transactionBillConverter.toRechargePayDepositBO(dto)));
}
@@ -344,6 +345,7 @@ public class AppTransactionBillController extends BaseController
@ApiOperation("支付订单")
@PutMapping("/pay")
public AjaxResult pay(@RequestBody BillPayDTO dto) {
+ dto.setChannelId(1L);
TransactionBillVO bill = transactionBillService.selectSmTransactionBillByBillNo(dto.getBillNo());
if (!transactionBillValidator.isUser(bill, getUserId())) {
return error("这不是您的订单,无法支付");
diff --git a/smart-switch-web/src/main/resources/application-prod.yml b/smart-switch-web/src/main/resources/application-prod.yml
index 2efc635e..6b591410 100644
--- a/smart-switch-web/src/main/resources/application-prod.yml
+++ b/smart-switch-web/src/main/resources/application-prod.yml
@@ -33,7 +33,7 @@ wx:
# 设备配置
device:
# 项目启动时抄表
- startRecord: true
+ startRecord: false
spring:
# redis 配置