临时提交

This commit is contained in:
墨大叔 2024-08-09 15:52:58 +08:00
parent 999f164830
commit 9842c59084
12 changed files with 179 additions and 74 deletions

View File

@ -0,0 +1,44 @@
package com.ruoyi.common.pay.wx.domain;
import com.ruoyi.common.pay.wx.domain.enums.TransferScene;
import java.math.BigDecimal;
/**
* 可批量转账的接口
* @author wjh
* 2024/8/9
*/
public interface BatchTransferAble {
/**
* 转账场景
*/
TransferScene transferScene();
/**
* 转账到账账号
*/
String transferAccountNo();
/**
* 转账金额
*/
BigDecimal transferAmount();
/**
* 转账外部订单号
*/
String transferOutBatchNo();
/**
* 转账备注
*/
String transferBatchRemark();
/**
* 转账名称
*/
String transferBatchName();
}

View File

@ -14,9 +14,9 @@ import java.math.BigDecimal;
@Getter
public enum TransferScene {
WITHDRAW(new BigDecimal(200), "提现");
WITHDRAW(new BigDecimal(500), "提现");
private final BigDecimal limitAmount; // 单笔限额
private final BigDecimal limitAmount; // 微信单笔限额
private final String remark; // 场景描述
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.pay.wx.service;
import com.ruoyi.common.pay.wx.domain.BatchTransferAble;
import com.ruoyi.common.pay.wx.domain.Payable;
import com.ruoyi.common.pay.wx.domain.RefundAble;
import com.ruoyi.common.pay.wx.domain.enums.TransferScene;
@ -48,10 +49,9 @@ public interface WxPayService {
boolean isSuccess(Transaction transaction);
/**
* 提现打款
* @param billId 单号
* 转账到零钱
*/
InitiateBatchTransferResponse payWithdraw(Long billId);
InitiateBatchTransferResponse batchTransfer(BatchTransferAble batchTransferAble);
/**
* 微信商户支付通知

View File

@ -2,17 +2,11 @@ package com.ruoyi.common.pay.wx.service;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.config.WxPayConfig;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.pay.wx.domain.NotifyEventType;
import com.ruoyi.common.pay.wx.domain.TransferBatchStatus;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.pay.wx.domain.*;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.SnowFlakeUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.pay.wx.domain.Payable;
import com.ruoyi.common.pay.wx.domain.RefundAble;
import com.ruoyi.common.pay.wx.domain.enums.TransferScene;
import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.core.notification.NotificationParser;
@ -27,7 +21,6 @@ import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferBatchGet;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
@ -37,13 +30,8 @@ import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 微信支付服务
@ -69,6 +57,9 @@ public class WxPayServiceImpl implements WxPayService {
@Autowired
private RefundService refundService;
@Autowired
private TransferBatchService transferBatchService;
private static final String CNY = "CNY";
@Override
@ -125,35 +116,22 @@ public class WxPayServiceImpl implements WxPayService {
}
/**
* 提现打款
* @param billId id
* 转账到零钱
*/
@Override
@Transactional
public InitiateBatchTransferResponse payWithdraw(Long billId) {
// ServiceUtil.assertion(billId == null, "订单id不允许为空");
// TransactionBill bill = transactionBillService.selectSmTransactionBillByBillId(billId);
// ServiceUtil.assertion(bill == null, "单据不存在");
// ServiceUtil.assertion(TransactionBillStatus.WITHDRAW_PASSED.getStatus().equals(bill.getStatus()), "当前提现单据状态异常");
// ServiceUtil.assertion(StringUtils.isBlank(bill.getAccountNo()), "提现账号异常");
//
// // 转账明细列表
// List<TransferDetailInput> transferDetailList = this.buildTransferDetailList(bill.getArrivalAmount(), bill.getAccountNo(), TransferScene.WITHDRAW);
//
// // 更新微信批次明细单号
// transactionBillService.updateWxTransferDetailIds(billId, transferDetailList.stream().map(TransferDetailInput::getOutDetailNo).collect(Collectors.toList()));
//
// // 发起转账请求
// InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
// request.setAppid(wxPayConfig.getAppId());
// request.setOutBatchNo(bill.getBillNo());
// request.setBatchName("提现");
// request.setBatchRemark(String.format("用户%s提现%f元", bill.getUserId(), bill.getArrivalAmount()));
// request.setTotalAmount(getTransferAmount(bill.getArrivalAmount()));
// request.setTotalNum(transferDetailList.size());
// request.setTransferDetailList(transferDetailList);
// return transferBatchService.initiateBatchTransfer(request);
throw new ServiceException("开发中");
public InitiateBatchTransferResponse batchTransfer(BatchTransferAble bill) {
// 转账明细列表
List<TransferDetailInput> transferDetailList = this.buildTransferDetailList(bill.transferAmount(), bill.transferAccountNo(), bill.transferScene());
// 发起转账请求
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
request.setAppid(wxPayConfig.getAppId());
request.setOutBatchNo(bill.transferOutBatchNo());
request.setBatchName(bill.transferBatchName());
request.setBatchRemark(bill.transferBatchRemark());
request.setTotalAmount(getTransferAmount(bill.transferAmount()));
request.setTotalNum(transferDetailList.size());
request.setTransferDetailList(transferDetailList);
return transferBatchService.initiateBatchTransfer(request);
}
@Override
@ -195,7 +173,7 @@ public class WxPayServiceImpl implements WxPayService {
ServiceUtil.assertion(transferScene == null, "转账场景不允许为空");
List<TransferDetailInput> transferDetailList = new ArrayList<>();
BigDecimal limitAmount = transferScene.getLimitAmount(); // 单笔限额200元
BigDecimal limitAmount = transferScene.getLimitAmount(); // 单笔限额
BigDecimal payAmount = totalAmount; // 需要转账的金额
while (BigDecimal.ZERO.compareTo(payAmount) < 0) {
BigDecimal detailPayAmount = payAmount.compareTo(limitAmount) > 0 ? limitAmount : payAmount; // 当前明细转账的金额

View File

@ -11,6 +11,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import javax.validation.constraints.Min;
/**
* 提现渠道对象 ss_channel_withdraw
*
@ -55,4 +57,12 @@ public class ChannelWithdraw extends BaseEntity
@JsonView(JsonViewProfile.App.class)
private String picture;
@ApiModelProperty("单笔最低提现金额")
@Min(value = 0, message = "单笔最低提现金额不能小于0")
private BigDecimal minAmount;
@ApiModelProperty("单笔最高提现金额")
@Min(value = 0, message = "单笔最高提现金额不能小于0")
private BigDecimal maxAmount;
}

View File

@ -18,7 +18,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
scw.service_rate,
scw.enabled,
scw.cost_rate,
scw.picture
scw.picture,
scw.min_amount,
scw.max_amount
from ss_channel_withdraw scw
</sql>
@ -58,6 +60,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="enabled != null">enabled,</if>
<if test="costRate != null">cost_rate,</if>
<if test="picture != null">picture,</if>
<if test="minAmount != null">min_amount,</if>
<if test="maxAmount != null">max_amount,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="name != null and name != ''">#{name},</if>
@ -67,6 +71,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="enabled != null">#{enabled},</if>
<if test="costRate != null">#{costRate},</if>
<if test="picture != null">#{picture},</if>
<if test="minAmount != null">#{minAmount},</if>
<if test="maxAmount != null">#{maxAmount},</if>
</trim>
</insert>
@ -80,6 +86,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.enabled != null">enabled = #{data.enabled},</if>
<if test="data.costRate != null">cost_rate = #{data.costRate},</if>
<if test="data.picture != null">picture = #{data.picture},</if>
<if test="data.minAmount != null">min_amount = #{data.minAmount},</if>
<if test="data.maxAmount != null">max_amount = #{data.maxAmount},</if>
</trim>
where channel_id = #{data.channelId}
</update>

View File

@ -409,7 +409,7 @@ public class DeviceServiceImpl implements DeviceService
ServiceUtil.assertion(!StringUtils.hasText(device.getMac()), "设备MAC号为空");
ServiceUtil.assertion(DeviceStatus.FIXING.getStatus().equals(device.getStatus()), "设备正在维修中,无法使用");
transactionTemplate.execute(status -> {
Boolean result = transactionTemplate.execute(status -> {
// 更新数据库时长
int updateCount = deviceMapper.addTime(deviceId, seconds);
ServiceUtil.assertion(updateCount != 1, "增加时长失败,请刷新后重试");
@ -423,22 +423,25 @@ public class DeviceServiceImpl implements DeviceService
long betweenSeconds = Duration.between(LocalDateTime.now(), newDevice.getExpireTime()).getSeconds();
if (betweenSeconds > 0) {
CommandResponse rechargeResult = iotService.setTime(device.getMac(), betweenSeconds);
return rechargeResult.isSuccess();
ServiceUtil.assertion(!rechargeResult.isSuccess(), "设备充值失败,请检查设备是否在线");
}
}
return Boolean.TRUE;
});
// 是否成功
boolean success = result != null && result;
if (success) {
// 拉取设备信息
this.pullDeviceInfoAsync(Collections.singletonList(deviceId), 3, TimeUnit.SECONDS);
// 拉取设备信息
this.pullDeviceInfoAsync(Collections.singletonList(deviceId), 3, TimeUnit.SECONDS);
// 时长结束后修改设备状态
scheduledExecutorService.schedule(()-> {
freshStatus(deviceId);
}, seconds, TimeUnit.SECONDS);
}
// 时长结束后修改设备状态
scheduledExecutorService.schedule(()-> {
freshStatus(deviceId);
}, seconds, TimeUnit.SECONDS);
return true;
return success;
}

View File

@ -3,6 +3,7 @@ package com.ruoyi.ss.transactionBill.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Min;
import java.math.BigDecimal;
@ -15,7 +16,7 @@ import java.math.BigDecimal;
public class WithdrawDTO {
@ApiModelProperty("提现金额")
@Min(value = 20, message = "提现金额不能小于20")
@DecimalMin(value = "0.01", message = "提现金额不能小于0.01")
private BigDecimal money;
@ApiModelProperty("提现到账金额")

View File

@ -2,19 +2,23 @@ package com.ruoyi.ss.transactionBill.domain.vo;
import com.fasterxml.jackson.annotation.JsonView;
import com.ruoyi.common.core.domain.JsonViewProfile;
import com.ruoyi.common.pay.wx.domain.BatchTransferAble;
import com.ruoyi.common.pay.wx.domain.enums.TransferScene;
import com.ruoyi.ss.suit.domain.enums.SuitTimeUnit;
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author
* 2024/3/4
*/
@ApiModel
@Data
public class TransactionBillVO extends TransactionBill {
public class TransactionBillVO extends TransactionBill implements BatchTransferAble {
@ApiModelProperty("用户名称")
@JsonView(JsonViewProfile.App.class)
private String userName;
@ -50,4 +54,52 @@ public class TransactionBillVO extends TransactionBill {
long time = this.getSuitTime() == null ? 0 : this.getSuitTime();
return time * unit.getConversion();
}
/**
* 转账场景
*/
@Override
public TransferScene transferScene() {
return TransferScene.WITHDRAW;
}
/**
* 转账到账账号
*/
@Override
public String transferAccountNo() {
return getAccountNo();
}
/**
* 转账金额
*/
@Override
public BigDecimal transferAmount() {
return getArrivalAmount();
}
/**
* 转账外部订单号
*/
@Override
public String transferOutBatchNo() {
return getBillNo();
}
/**
* 转账备注
*/
@Override
public String transferBatchRemark() {
return String.format("用户%s提现%f元", getUserName(), getArrivalAmount());
}
/**
* 转账名称
*/
@Override
public String transferBatchName() {
return "提现";
}
}

View File

@ -48,6 +48,7 @@ import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.task.bill.BillDelayedManager;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -364,6 +365,8 @@ public class TransactionBillServiceImpl implements TransactionBillService {
// 处理提现手续费
ServiceUtil.assertion(channel == null, "提现渠道不存在");
ServiceUtil.assertion(channel.getEnabled() == null || !channel.getEnabled(), "提现渠道不可用");
ServiceUtil.assertion(channel.getMinAmount().compareTo(dto.getMoney()) > 0, String.format("提现金额不能低于%s元", channel.getMinAmount()));
ServiceUtil.assertion(channel.getMaxAmount().compareTo(dto.getMoney()) < 0, String.format("提现金额不能高于%s元", channel.getMaxAmount()));
UserWithdrawServiceVO serviceInfo = this.getUserWithdrawService(user, channel);
ServiceUtil.assertion(serviceInfo == null, "服务费配置出错,请联系管理员处理");
ServiceUtil.assertion(StringUtils.isBlank(serviceInfo.getServiceType()), "服务费配置出错,请联系管理员处理");
@ -482,15 +485,24 @@ public class TransactionBillServiceImpl implements TransactionBillService {
* 提现线上打款
*/
private int startPayOnline(TransactionBillVO bill) {
throw new ServiceException("线上打款开发中");
ServiceUtil.assertion(bill == null, "单据不存在");
ServiceUtil.assertion(TransactionBillStatus.WITHDRAW_PASSED.getStatus().equals(bill.getStatus()), "当前提现单据状态异常");
ServiceUtil.assertion(StringUtils.isBlank(bill.getAccountNo()), "提现账号异常");
// 微信
// if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) {
// InitiateBatchTransferResponse res = wxPayService.payWithdraw(bill.getBillId());
// log.debug(String.valueOf(res));
// } else {
// throw new ServiceException("其他打款渠道正在开发中");
// }
if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) {
InitiateBatchTransferResponse res = wxPayService.batchTransfer(bill);
// TODO 更新微信批次明细单号
// transactionBillMapper.updateSmTransactionBill(billId, transferDetailList.stream().map(TransferDetailInput::getOutDetailNo).collect(Collectors.toList()));
log.debug(String.valueOf(res));
} else {
throw new ServiceException("其他打款渠道正在开发中");
}
return 1;
}
/**

View File

@ -71,7 +71,6 @@ public class AppDeviceController extends BaseController {
startPage();
smDevice.setUserId(getUserId());
List<DeviceVO> list = smDeviceService.selectSmDeviceList(smDevice);
deviceAssembler.assembleIotDeviceInfo(list);
return getDataTable(list);
}
@ -83,18 +82,16 @@ public class AppDeviceController extends BaseController {
query.setStoreId(storeId);
List<DeviceVO> list = smDeviceService.selectSmDeviceList(query);
deviceAssembler.assembleBusinessTime(list); // 店铺营业时间
deviceAssembler.assembleIotDeviceInfo(list);
return getDataTable(list);
}
@ApiOperation("获取设备详细信息")
@GetMapping(value = "/{deviceId}")
public AjaxResult getInfo(@PathVariable("deviceId") Long deviceId) {
smDeviceService.pullDeviceInfo(Collections.singletonList(deviceId));
// smDeviceService.pullDeviceInfo(Collections.singletonList(deviceId));
DeviceVO device = smDeviceService.selectSmDeviceByDeviceId(deviceId);
List<DeviceVO> list = Collections.singletonList(device);
deviceAssembler.assembleOrderCountInfo(list); // 订单统计信息
deviceAssembler.assembleIotDeviceInfo(list);
return success(device);
}

View File

@ -1,5 +1,5 @@
# debug模式部分权限校验不生效
debug: true
debug: false
# 项目相关配置
ruoyi:
@ -16,7 +16,7 @@ wx:
# 微信小程序id
appId: ${wx.appid}
# 商户id
merchantId: 1656437344
merchantId: 1676202154
# apiV3密钥
apiV3Key: 49819e0f0abdb2df3246f7b27f264d75
# 通知回调地址
@ -24,9 +24,9 @@ wx:
# 退款通知回调地址
refundNotifyUrl: http://124.221.246.124:2290/app/pay/notify/wx/refund
# 密钥所在位置
privateKeyPath: D:/project/证书/wxpay/apiclient_key.pem
privateKeyPath: D:/project/证书/wxpay-kg/apiclient_key.pem
# 证书序列号
merchantSerialNumber: 66910F800A60768020F07D39A56AE701574A16AE
merchantSerialNumber: 6AD69237C0F22A9AE51A64F1927E3A0962AC1FB0
# 设备配置
device: