部署
This commit is contained in:
parent
c6edec8083
commit
f75febd2b4
|
@ -7,6 +7,7 @@ import com.wechat.pay.java.core.notification.NotificationParser;
|
|||
import com.wechat.pay.java.service.payments.app.AppService;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
||||
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
||||
import com.wechat.pay.java.service.refund.RefundService;
|
||||
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -39,6 +40,10 @@ public class WxPayConfig {
|
|||
@Value("${wx.pay.notifyUrl}")
|
||||
private String notifyUrl;
|
||||
|
||||
// 退款通知回调地址
|
||||
@Value("${wx.pay.refundNotifyUrl}")
|
||||
private String refundNotifyUrl;
|
||||
|
||||
// 私钥证书路径
|
||||
@Value("${wx.pay.privateKeyPath}")
|
||||
private String privateKeyPath;
|
||||
|
@ -93,6 +98,23 @@ public class WxPayConfig {
|
|||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RefundService refundService() {
|
||||
// 初始化商户配置
|
||||
Config config = new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(merchantId)
|
||||
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||||
.privateKeyFromPath(privateKeyPath)
|
||||
.merchantSerialNumber(merchantSerialNumber)
|
||||
.apiV3Key(apiV3Key)
|
||||
.build();
|
||||
// 初始化服务
|
||||
return new RefundService
|
||||
.Builder()
|
||||
.config(config)
|
||||
.build();
|
||||
}
|
||||
|
||||
// 微信通知解析器
|
||||
@Bean
|
||||
public NotificationParser notificationParser() {
|
||||
|
|
|
@ -13,7 +13,8 @@ import lombok.Getter;
|
|||
public enum NotifyEventType {
|
||||
|
||||
TRANSACTION_SUCCESS("TRANSACTION.SUCCESS", "支付成功"),
|
||||
MCHTRANSFER_BATCH_FINISHED("MCHTRANSFER.BATCH.FINISHED", "商户转账批次完成通知");
|
||||
MCHTRANSFER_BATCH_FINISHED("MCHTRANSFER.BATCH.FINISHED", "商户转账批次完成通知"),
|
||||
REFUND_SUCCESS("REFUND.SUCCESS", "退款成功");
|
||||
|
||||
private final String value;
|
||||
private final String name;
|
||||
|
|
|
@ -27,6 +27,10 @@ public class CollectionUtils extends org.springframework.util.CollectionUtils {
|
|||
return org.springframework.util.CollectionUtils.isEmpty(collect);
|
||||
}
|
||||
|
||||
public static boolean isNotEmptyElement(List<?> list) {
|
||||
return !isEmptyElement(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充列表中的元素
|
||||
* @param oldList 旧列表
|
||||
|
@ -90,4 +94,8 @@ public class CollectionUtils extends org.springframework.util.CollectionUtils {
|
|||
.map(date -> dateToObjectMap.getOrDefault(date, customFactory.apply(date)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static boolean isNotEmpty(List<?> deviceList) {
|
||||
return !isEmpty(deviceList);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,8 +252,8 @@ public class SmDeviceServiceImpl implements ISmDeviceService
|
|||
public int register(DeviceRegisterDTO dto) {
|
||||
// 添加
|
||||
SmDevice device = new SmDevice();
|
||||
device.setMac(dto.getMac());
|
||||
device.setDeviceName("未命名");
|
||||
device.setMac(dto.getMac());
|
||||
device.setDeviceNo(dto.getSn());
|
||||
return this.addInitDevice(device);
|
||||
}
|
||||
|
@ -734,44 +734,15 @@ public class SmDeviceServiceImpl implements ISmDeviceService
|
|||
device = selectByDeviceNo(smDevice.getDeviceNo());
|
||||
ServiceUtil.assertion(device != null, "该设备SN已存在,无需重复录入");
|
||||
|
||||
// 查询设备的型号
|
||||
IotDeviceInfo deviceInfo = null;
|
||||
try {
|
||||
deviceInfo = iotService.getDeviceInfo(smDevice.getMac());
|
||||
if (deviceInfo == null) {
|
||||
log.info("iot device info is null");
|
||||
throw new ServiceException("OneNet设备不存在");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Integer result = transactionTemplate.execute(status -> {
|
||||
int i = this.insertSmDevice(smDevice); // 添加设备
|
||||
ServiceUtil.assertion(i != 1, "添加设备失败");
|
||||
|
||||
// 创建OneNet设备
|
||||
boolean create = iotService.create(smDevice.getMac());
|
||||
ServiceUtil.assertion(!create, "创建OneNet设备失败");
|
||||
}
|
||||
|
||||
log.info("select model in:{}", deviceInfo.getModel());
|
||||
SmModelVo model = smModelService.selectByModel(deviceInfo.getModel());
|
||||
|
||||
IotDeviceInfo finalDeviceInfo = deviceInfo;
|
||||
Integer result = transactionTemplate.execute(status -> {
|
||||
SmModelVo finalModel = model;
|
||||
|
||||
// 如果设备型号为空,则添加新的设备型号
|
||||
if (finalModel == null) {
|
||||
log.info("model is null");
|
||||
int addModel = smModelService.addInitModel(finalDeviceInfo);
|
||||
ServiceUtil.assertion(addModel != 1, "添加设备型号失败");
|
||||
finalModel = smModelService.selectByModel(finalDeviceInfo.getModel());
|
||||
}
|
||||
|
||||
// 添加设备
|
||||
SmDeviceVO byMac = selectByMac(smDevice.getMac());
|
||||
if (byMac == null) {
|
||||
smDevice.setModelId(finalModel != null ? finalModel.getModelId() : null);
|
||||
smDevice.setCreateTime(DateUtils.getNowDate());
|
||||
int i = smDeviceMapper.insertSmDevice(smDevice); // 添加设备
|
||||
this.pullDeviceInfo(Collections.singletonList(smDevice.getDeviceId())); // 拉取设备信息
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
return i;
|
||||
});
|
||||
|
||||
return result == null ? 0 : result;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package com.ruoyi.ss.refund.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.ruoyi.ss.wxPay.domain.RefundAble;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 退款订单对象 ss_refund
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-07-09
|
||||
*/
|
||||
@Data
|
||||
public class Refund extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 退款ID */
|
||||
private Long refundId;
|
||||
|
||||
/** 退款订单编号 */
|
||||
@Excel(name = "退款订单编号")
|
||||
private String refundNo;
|
||||
|
||||
/** 原订单ID */
|
||||
@Excel(name = "原订单ID")
|
||||
private Long billId;
|
||||
|
||||
/** 退款金额(元) */
|
||||
@Excel(name = "退款金额", readConverterExp = "元=")
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 退款订单状态 */
|
||||
@Excel(name = "退款订单状态")
|
||||
private String status;
|
||||
|
||||
@ApiModelProperty("退款原因")
|
||||
private String reason;
|
||||
|
||||
@ApiModelProperty("商户退款金额")
|
||||
private BigDecimal mchAmount;
|
||||
|
||||
@ApiModelProperty("手续费退款金额")
|
||||
private BigDecimal serviceAmount;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.ruoyi.ss.refund.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
@Data
|
||||
public class RefundQuery extends Refund {
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.ruoyi.ss.refund.domain;
|
||||
|
||||
import com.ruoyi.ss.wxPay.domain.RefundAble;
|
||||
import com.wechat.pay.java.service.refund.model.AmountReq;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
@Data
|
||||
public class RefundVO extends Refund implements RefundAble {
|
||||
|
||||
@ApiModelProperty("原订单编号")
|
||||
private String billNo;
|
||||
|
||||
@ApiModelProperty("原订单交易总额")
|
||||
private BigDecimal billAmount;
|
||||
|
||||
@Override
|
||||
public String refundOutTradeNo() {
|
||||
return this.billNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refundOutRefundNo() {
|
||||
return this.getRefundNo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refundReason() {
|
||||
return this.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmountReq refundAmount() {
|
||||
BigDecimal decimal100 = new BigDecimal(100);
|
||||
AmountReq amount = new AmountReq();
|
||||
amount.setRefund(this.getAmount().multiply(decimal100).longValue());
|
||||
amount.setTotal(this.billAmount.multiply(decimal100).longValue());
|
||||
amount.setCurrency("CNY");
|
||||
return amount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.ruoyi.ss.refund.domain.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RefundStatus {
|
||||
|
||||
REFUNDING("1", "退款中"),
|
||||
REFUND_SUCCESS("2", "退款成功"),
|
||||
REFUND_FAIL("3", "退款失败");
|
||||
|
||||
private final String status;
|
||||
private final String desc;
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.ruoyi.ss.refund.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.domain.RefundQuery;
|
||||
import com.ruoyi.ss.refund.domain.RefundVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 退款订单Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-07-09
|
||||
*/
|
||||
public interface RefundMapper
|
||||
{
|
||||
/**
|
||||
* 查询退款订单
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 退款订单
|
||||
*/
|
||||
public RefundVO selectRefundByRefundId(Long refundId);
|
||||
|
||||
/**
|
||||
* 查询退款订单列表
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 退款订单集合
|
||||
*/
|
||||
public List<RefundVO> selectRefundList(@Param("query") RefundQuery refund);
|
||||
|
||||
/**
|
||||
* 新增退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertRefund(Refund refund);
|
||||
|
||||
/**
|
||||
* 修改退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateRefund(@Param("data") Refund refund);
|
||||
|
||||
/**
|
||||
* 删除退款订单
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteRefundByRefundId(Long refundId);
|
||||
|
||||
/**
|
||||
* 批量删除退款订单
|
||||
*
|
||||
* @param refundIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteRefundByRefundIds(Long[] refundIds);
|
||||
|
||||
RefundVO selectOne(@Param("query") RefundQuery query);
|
||||
|
||||
/**
|
||||
* 条件更新
|
||||
*/
|
||||
int updateByQuery(@Param("data") Refund data, @Param("query") RefundQuery query);
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.ss.refund.mapper.RefundMapper">
|
||||
|
||||
<resultMap type="RefundVO" id="RefundResult" autoMapping="true"/>
|
||||
|
||||
<sql id="selectRefundVo">
|
||||
select
|
||||
sr.refund_id,
|
||||
sr.refund_no,
|
||||
sr.bill_id,
|
||||
sr.amount,
|
||||
sr.create_time,
|
||||
sr.status,
|
||||
sr.reason,
|
||||
sr.mch_amount,
|
||||
sr.service_amount,
|
||||
smb.money as bill_amount,
|
||||
smb.bill_no as bill_no
|
||||
from ss_refund sr
|
||||
left join sm_transaction_bill smb on smb.bill_id = sr.bill_id
|
||||
</sql>
|
||||
|
||||
<sql id="searchCondition">
|
||||
<if test="query.refundId != null "> and sr.refund_id = #{query.refundId}</if>
|
||||
<if test="query.refundNo != null and query.refundNo != ''"> and sr.refund_no = #{query.refundNo}</if>
|
||||
<if test="query.billId != null "> and sr.bill_id = #{query.billId}</if>
|
||||
<if test="query.status != null and query.status != ''"> and sr.status = #{query.status}</if>
|
||||
</sql>
|
||||
|
||||
<select id="selectRefundList" parameterType="Refund" resultMap="RefundResult">
|
||||
<include refid="selectRefundVo"/>
|
||||
<where>
|
||||
<include refid="searchCondition"/>
|
||||
</where>
|
||||
order by sr.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectRefundByRefundId" parameterType="Long" resultMap="RefundResult">
|
||||
<include refid="selectRefundVo"/>
|
||||
where refund_id = #{refundId}
|
||||
</select>
|
||||
|
||||
<select id="selectOne" resultMap="RefundResult">
|
||||
<include refid="selectRefundVo"/>
|
||||
<where>
|
||||
<include refid="searchCondition"/>
|
||||
</where>
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insertRefund" parameterType="Refund" useGeneratedKeys="true" keyProperty="refundId">
|
||||
insert into ss_refund
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="refundNo != null and refundNo != ''">refund_no,</if>
|
||||
<if test="billId != null">bill_id,</if>
|
||||
<if test="amount != null">amount,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="status != null and status != ''">`status`,</if>
|
||||
<if test="reason != null and reason != ''">`reason`,</if>
|
||||
<if test="mchAmount != null">mch_amount,</if>
|
||||
<if test="serviceAmount != null">service_amount,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="refundNo != null and refundNo != ''">#{refundNo},</if>
|
||||
<if test="billId != null">#{billId},</if>
|
||||
<if test="amount != null">#{amount},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
<if test="reason != null and reason != ''">#{reason},</if>
|
||||
<if test="mchAmount != null">#{mchAmount},</if>
|
||||
<if test="serviceAmount != null">#{serviceAmount},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateRefund" parameterType="Refund">
|
||||
update ss_refund
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<include refid="updateColumns"/>
|
||||
</trim>
|
||||
where refund_id = #{refundId}
|
||||
</update>
|
||||
|
||||
<sql id="updateColumns">
|
||||
<if test="data.refundNo != null and data.refundNo != ''">refund_no = #{data.refundNo},</if>
|
||||
<if test="data.billId != null">bill_id = #{data.billId},</if>
|
||||
<if test="data.amount != null">amount = #{data.amount},</if>
|
||||
<if test="data.mchAmount != null">mch_amount = #{data.mchAmount},</if>
|
||||
<if test="data.serviceAmount != null">service_amount = #{data.serviceAmount},</if>
|
||||
<if test="data.createTime != null">create_time = #{data.createTime},</if>
|
||||
<if test="data.status != null and data.status != ''">`status` = #{data.status},</if>
|
||||
<if test="data.reason != null and data.reason != ''">`reason` = #{data.reason},</if>
|
||||
</sql>
|
||||
|
||||
<update id="updateByQuery">
|
||||
update ss_refund sr
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<include refid="updateColumns"/>
|
||||
</trim>
|
||||
<where>
|
||||
<include refid="searchCondition"/>
|
||||
</where>
|
||||
</update>
|
||||
|
||||
<delete id="deleteRefundByRefundId" parameterType="Long">
|
||||
delete from ss_refund where refund_id = #{refundId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteRefundByRefundIds" parameterType="String">
|
||||
delete from ss_refund where refund_id in
|
||||
<foreach item="refundId" collection="array" open="(" separator="," close=")">
|
||||
#{refundId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
|
@ -0,0 +1,17 @@
|
|||
package com.ruoyi.ss.refund.service;
|
||||
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
public interface RefundConverter {
|
||||
|
||||
/**
|
||||
* 订单退款DTO -> 退款PO
|
||||
*/
|
||||
Refund toPo(BillRefundDTO dto);
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.ruoyi.ss.refund.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.domain.RefundQuery;
|
||||
import com.ruoyi.ss.refund.domain.RefundVO;
|
||||
|
||||
/**
|
||||
* 退款订单Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-07-09
|
||||
*/
|
||||
public interface RefundService
|
||||
{
|
||||
/**
|
||||
* 查询退款订单
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 退款订单
|
||||
*/
|
||||
public RefundVO selectRefundByRefundId(Long refundId);
|
||||
|
||||
/**
|
||||
* 查询退款订单列表
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 退款订单集合
|
||||
*/
|
||||
public List<RefundVO> selectRefundList(RefundQuery refund);
|
||||
|
||||
/**
|
||||
* 新增退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertRefund(Refund refund);
|
||||
|
||||
/**
|
||||
* 修改退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateRefund(Refund refund);
|
||||
|
||||
/**
|
||||
* 批量删除退款订单
|
||||
*
|
||||
* @param refundIds 需要删除的退款订单主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteRefundByRefundIds(Long[] refundIds);
|
||||
|
||||
/**
|
||||
* 删除退款订单信息
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteRefundByRefundId(Long refundId);
|
||||
|
||||
/**
|
||||
* 查询退款订单
|
||||
*/
|
||||
RefundVO selectRefundByRefundNo(String refundNo);
|
||||
|
||||
RefundVO selectOne(RefundQuery query);
|
||||
|
||||
/**
|
||||
* 退款成功后操作
|
||||
*/
|
||||
void handleRefundSuccess(String refundNo);
|
||||
|
||||
/**
|
||||
* 条件更新
|
||||
*/
|
||||
int updateByQuery(Refund data, RefundQuery query);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.ruoyi.ss.refund.service.impl;
|
||||
|
||||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.SnowFlakeUtil;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.domain.enums.RefundStatus;
|
||||
import com.ruoyi.ss.refund.service.RefundConverter;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
@Service
|
||||
public class RefundConverterImpl implements RefundConverter {
|
||||
|
||||
@Autowired
|
||||
private TransactionBillService transactionBillService;
|
||||
|
||||
@Override
|
||||
public Refund toPo(BillRefundDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
TransactionBillVo bill = transactionBillService.selectSmTransactionBillByBillId(dto.getBillId());
|
||||
ServiceUtil.assertion(bill == null, "待退款订单不存在");
|
||||
|
||||
// 按比例计算退款金额
|
||||
BigDecimal refundAmount = dto.getRefundAmount();
|
||||
BigDecimal refundRate = refundAmount.divide(bill.getMoney(), 2, RoundingMode.HALF_UP); // 退款金额比例
|
||||
BigDecimal refundServiceAmount = bill.getServiceCharge().multiply(refundRate); // 退款的手续费
|
||||
BigDecimal refundMchAmount = refundAmount.subtract(refundServiceAmount); // 退款的商户余额
|
||||
|
||||
Refund refund = new Refund();
|
||||
refund.setBillId(dto.getBillId());
|
||||
refund.setAmount(dto.getRefundAmount());
|
||||
refund.setStatus(RefundStatus.REFUNDING.getStatus());
|
||||
refund.setReason(String.format("充值订单%s退款", bill.getBillNo()));
|
||||
refund.setMchAmount(refundMchAmount);
|
||||
refund.setServiceAmount(refundServiceAmount);
|
||||
return refund;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
package com.ruoyi.ss.refund.service.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.SnowFlakeUtil;
|
||||
import com.ruoyi.ss.refund.domain.RefundQuery;
|
||||
import com.ruoyi.ss.refund.domain.RefundVO;
|
||||
import com.ruoyi.ss.refund.domain.enums.RefundStatus;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
|
||||
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
|
||||
import com.ruoyi.ss.user.service.ISmUserService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.ss.refund.mapper.RefundMapper;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.service.RefundService;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
/**
|
||||
* 退款订单Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-07-09
|
||||
*/
|
||||
@Service
|
||||
public class RefundServiceImpl implements RefundService
|
||||
{
|
||||
@Autowired
|
||||
private RefundMapper refundMapper;
|
||||
|
||||
@Autowired
|
||||
private TransactionTemplate transactionTemplate;
|
||||
|
||||
@Autowired
|
||||
private TransactionBillService transactionBillService;
|
||||
|
||||
@Autowired
|
||||
private ISmUserService userService;
|
||||
|
||||
/**
|
||||
* 查询退款订单
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 退款订单
|
||||
*/
|
||||
@Override
|
||||
public RefundVO selectRefundByRefundId(Long refundId)
|
||||
{
|
||||
return refundMapper.selectRefundByRefundId(refundId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询退款订单列表
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 退款订单
|
||||
*/
|
||||
@Override
|
||||
public List<RefundVO> selectRefundList(RefundQuery refund)
|
||||
{
|
||||
return refundMapper.selectRefundList(refund);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int insertRefund(Refund refund)
|
||||
{
|
||||
refund.setCreateTime(DateUtils.getNowDate());
|
||||
refund.setRefundNo(String.valueOf(SnowFlakeUtil.newId()));
|
||||
return refundMapper.insertRefund(refund);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改退款订单
|
||||
*
|
||||
* @param refund 退款订单
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateRefund(Refund refund)
|
||||
{
|
||||
return refundMapper.updateRefund(refund);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除退款订单
|
||||
*
|
||||
* @param refundIds 需要删除的退款订单主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteRefundByRefundIds(Long[] refundIds)
|
||||
{
|
||||
return refundMapper.deleteRefundByRefundIds(refundIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除退款订单信息
|
||||
*
|
||||
* @param refundId 退款订单主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteRefundByRefundId(Long refundId)
|
||||
{
|
||||
return refundMapper.deleteRefundByRefundId(refundId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefundVO selectRefundByRefundNo(String refundNo) {
|
||||
RefundQuery query = new RefundQuery();
|
||||
query.setRefundNo(refundNo);
|
||||
return this.selectOne(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefundVO selectOne(RefundQuery query) {
|
||||
return refundMapper.selectOne(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRefundSuccess(String refundNo) {
|
||||
ServiceUtil.assertion(StringUtils.isBlank(refundNo), "退款订单编号不允许为空");
|
||||
|
||||
RefundVO refund = this.selectRefundByRefundNo(refundNo);
|
||||
ServiceUtil.assertion(refund == null, "退款订单不存在");
|
||||
|
||||
TransactionBillVo bill = transactionBillService.selectSmTransactionBillByBillId(refund.getBillId());
|
||||
ServiceUtil.assertion(bill == null, "原订单不存在");
|
||||
|
||||
transactionTemplate.execute(status -> {
|
||||
// 退款订单修改状态
|
||||
Refund refundData = new Refund();
|
||||
refundData.setStatus(RefundStatus.REFUND_SUCCESS.getStatus());
|
||||
RefundQuery refundQuery = new RefundQuery();
|
||||
refundQuery.setRefundId(refund.getRefundId());
|
||||
refundQuery.setStatus(RefundStatus.REFUNDING.getStatus());
|
||||
int updateRefund = this.updateByQuery(refundData, refundQuery);
|
||||
ServiceUtil.assertion(updateRefund != 1, "修改退款订单状态失败");
|
||||
|
||||
// 修改原订单状态
|
||||
TransactionBillQuery billQuery = new TransactionBillQuery();
|
||||
billQuery.setBillId(refund.getBillId());
|
||||
billQuery.setStatus(TransactionBillStatus.REFUNDING.getStatus());
|
||||
TransactionBill billData = new TransactionBill();
|
||||
billData.setStatus(TransactionBillStatus.REFUNDED.getStatus());
|
||||
int updateBill = transactionBillService.updateByQuery(billData, billQuery);
|
||||
ServiceUtil.assertion(updateBill != 1, "修改原订单状态失败");
|
||||
|
||||
return updateRefund;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByQuery(Refund data, RefundQuery query) {
|
||||
if (query == null) {
|
||||
return 0;
|
||||
}
|
||||
return refundMapper.updateByQuery(data, query);
|
||||
}
|
||||
}
|
|
@ -4,22 +4,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.ss.suit.mapper.SuitMapper">
|
||||
|
||||
<resultMap type="SuitVo" id="SuitResult">
|
||||
<result property="suitId" column="suit_id" />
|
||||
<result property="deviceId" column="device_id" />
|
||||
<result property="name" column="name" />
|
||||
<result property="value" column="value" />
|
||||
<result property="price" column="price" />
|
||||
<result property="description" column="description" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="deleted" column="deleted" />
|
||||
<result property="deviceName" column="device_name" />
|
||||
<result property="storeName" column="store_name" />
|
||||
<result property="userName" column="user_name" />
|
||||
</resultMap>
|
||||
<resultMap type="SuitVo" id="SuitResult" autoMapping="true"/>
|
||||
|
||||
<sql id="selectSuitVo">
|
||||
select
|
||||
|
@ -40,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
from sm_suit ss
|
||||
left join sm_device sd on sd.device_id = ss.device_id
|
||||
left join sm_store store on store.store_id = sd.store_id
|
||||
left join sm_user su on su.user_id = store.user_id
|
||||
left join sm_user su on su.user_id = sd.user_id
|
||||
</sql>
|
||||
|
||||
<sql id="searchCondition">
|
||||
|
|
|
@ -60,7 +60,7 @@ public class TransactionBill extends BaseEntity
|
|||
/** 商户(到账用户) */
|
||||
@Excel(name = "商户(到账用户)")
|
||||
@ApiModelProperty("商户(到账用户)id")
|
||||
@JsonView(JsonViewProfile.AppMch.class)
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private Long mchId;
|
||||
|
||||
/** 交易金额 */
|
||||
|
|
|
@ -18,10 +18,13 @@ public class TransactionBillVo extends TransactionBill {
|
|||
private String userName;
|
||||
|
||||
@ApiModelProperty("商户(到账用户)名称")
|
||||
@JsonView(JsonViewProfile.AppMch.class)
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private String mchName;
|
||||
|
||||
@ApiModelProperty("支付渠道名称")
|
||||
@JsonView(JsonViewProfile.App.class)
|
||||
private String channelName;
|
||||
|
||||
@ApiModelProperty("商户手机号")
|
||||
private String mchMobile;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package com.ruoyi.ss.transactionBill.domain.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 订单退款DTO
|
||||
* @author wjh
|
||||
* 2024/7/9
|
||||
*/
|
||||
@Data
|
||||
public class BillRefundDTO {
|
||||
|
||||
@ApiModelProperty("订单ID")
|
||||
@NotNull(message = "订单ID不允许为空")
|
||||
private Long billId;
|
||||
|
||||
@ApiModelProperty("退款金额")
|
||||
@NotNull(message = "退款金额不允许为空")
|
||||
@Min(value = 0, message = "退款金额不允许小于0")
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ public enum TransactionBillStatus {
|
|||
CANCELED("4", "已取消(用户)"),
|
||||
SYS_CANCELED("5", "已取消(系统)"),
|
||||
PAYING("6", "支付中"),
|
||||
REFUNDING("7", "退款中"),
|
||||
|
||||
WITHDRAW_APPROVING("11", "提现申请中"),
|
||||
WITHDRAW_PASSED("12", "提现申请通过"),
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
|||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -31,7 +32,7 @@ public interface TransactionBillMapper
|
|||
* @param smTransactionBill 充值记录
|
||||
* @return 充值记录集合
|
||||
*/
|
||||
public List<TransactionBillVo> selectSmTransactionBillList(TransactionBillQuery smTransactionBill);
|
||||
public List<TransactionBillVo> selectSmTransactionBillList(@Param("query") TransactionBillQuery smTransactionBill);
|
||||
|
||||
/**
|
||||
* 新增充值记录
|
||||
|
@ -47,7 +48,7 @@ public interface TransactionBillMapper
|
|||
* @param transactionBill 充值记录
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateSmTransactionBill(TransactionBill transactionBill);
|
||||
public int updateSmTransactionBill(@Param("data")TransactionBill transactionBill);
|
||||
|
||||
/**
|
||||
* 删除充值记录
|
||||
|
@ -69,7 +70,7 @@ public interface TransactionBillMapper
|
|||
* 获取账单统计数据
|
||||
* @param dto 查询条件
|
||||
*/
|
||||
List<BillCountVo> selectCount(TransactionBillQuery dto);
|
||||
List<BillCountVo> selectCount(@Param("query")TransactionBillQuery dto);
|
||||
|
||||
/**
|
||||
* 充值成功
|
||||
|
@ -82,7 +83,7 @@ public interface TransactionBillMapper
|
|||
* @param billNo 订单编号
|
||||
* @return
|
||||
*/
|
||||
TransactionBill selectSmTransactionBillByBillNo(String billNo);
|
||||
TransactionBillVo selectSmTransactionBillByBillNo(String billNo);
|
||||
|
||||
/**
|
||||
* 订单取消
|
||||
|
@ -144,4 +145,22 @@ public interface TransactionBillMapper
|
|||
* 蓝牙充值成功
|
||||
*/
|
||||
int bluetoothRechargeSuccess(String billNo);
|
||||
|
||||
/**
|
||||
* 根据条件修改
|
||||
*/
|
||||
int updateByQuery(@Param("data") TransactionBill data, @Param("query") TransactionBillQuery query);
|
||||
|
||||
/**
|
||||
* 添加退款金额
|
||||
* @param billId 订单ID
|
||||
* @param refundAmount 退款金额
|
||||
* @param refundMchAmount 商户退款金额
|
||||
* @param refundServiceAmount 服务费退款金额
|
||||
*/
|
||||
int addRefundAmount(@Param("billId") Long billId,
|
||||
@Param("refundAmount") BigDecimal refundAmount,
|
||||
@Param("refundMchAmount") BigDecimal refundMchAmount,
|
||||
@Param("refundServiceAmount") BigDecimal refundServiceAmount
|
||||
);
|
||||
}
|
||||
|
|
|
@ -42,8 +42,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
stb.device_no,
|
||||
stb.suit_name,
|
||||
stb.device_mac,
|
||||
su.user_name user_name,
|
||||
su1.user_name mch_name
|
||||
su.user_name as user_name,
|
||||
su1.user_name as mch_name,
|
||||
su1.phonenumber as mch_mobile
|
||||
from sm_transaction_bill stb
|
||||
left join sm_user su on su.user_id = stb.user_id
|
||||
left join sm_user su1 on su1.user_id = stb.mch_id
|
||||
|
@ -52,52 +53,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</sql>
|
||||
|
||||
<sql id="searchCondition">
|
||||
<if test="userId != null "> and stb.user_id = #{userId}</if>
|
||||
<if test="deviceId != null "> and stb.device_id = #{deviceId}</if>
|
||||
<if test="type != null "> and stb.type = #{type} </if>
|
||||
<if test="mchId != null "> and stb.mch_id = #{mchId}</if>
|
||||
<if test="userName != null "> and su.user_name like concat('%', #{userName}, '%')</if>
|
||||
<if test="mchName != null "> and su1.user_name like concat('%', #{mchName}, '%')</if>
|
||||
<if test="deviceName != null "> and stb.device_name like concat('%', #{deviceName}, '%')</if>
|
||||
<if test="createTime != null"> and stb.create_time = #{createTime}</if>
|
||||
<if test="createDate != null"> and date(stb.create_time) = date(#{createDate})</if>
|
||||
<if test="year != null "> and year(stb.create_time) = #{year}</if>
|
||||
<if test="month != null"> and month(stb.create_time) = #{month}</if>
|
||||
<if test="hour != null"> and hour(stb.create_time) = #{hour}</if>
|
||||
<if test="status != null"> and stb.status = #{status}</if>
|
||||
<if test="expire != null"> and NOW() >= stb.expire_time </if>
|
||||
<if test="startDate != null"> and date(stb.create_time) >= date(#{startDate}) </if>
|
||||
<if test="endDate != null"> and date(stb.create_time) <= date(#{endDate}) </if>
|
||||
<if test="deviceRechargeStatus != null"> and stb.device_recharge_status = #{deviceRechargeStatus} </if>
|
||||
<if test="suitId != null"> and stb.suit_id = #{suitId} </if>
|
||||
<if test="storeId != null"> and stb.store_id = #{storeId} </if>
|
||||
<if test="billIds != null and billIds.size() > 0">
|
||||
<if test="query.userId != null "> and stb.user_id = #{query.userId}</if>
|
||||
<if test="query.billId != null "> and stb.bill_id = #{query.billId}</if>
|
||||
<if test="query.deviceId != null "> and stb.device_id = #{query.deviceId}</if>
|
||||
<if test="query.type != null "> and stb.type = #{query.type} </if>
|
||||
<if test="query.mchId != null "> and stb.mch_id = #{query.mchId}</if>
|
||||
<if test="query.userName != null "> and su.user_name like concat('%', #{query.userName}, '%')</if>
|
||||
<if test="query.mchName != null "> and su1.user_name like concat('%', #{query.mchName}, '%')</if>
|
||||
<if test="query.deviceName != null "> and stb.device_name like concat('%', #{query.deviceName}, '%')</if>
|
||||
<if test="query.createTime != null"> and stb.create_time = #{query.createTime}</if>
|
||||
<if test="query.createDate != null"> and date(stb.create_time) = date(#{query.createDate})</if>
|
||||
<if test="query.year != null "> and year(stb.create_time) = #{query.year}</if>
|
||||
<if test="query.month != null"> and month(stb.create_time) = #{query.month}</if>
|
||||
<if test="query.hour != null"> and hour(stb.create_time) = #{query.hour}</if>
|
||||
<if test="query.status != null"> and stb.status = #{query.status}</if>
|
||||
<if test="query.expire != null"> and NOW() >= stb.expire_time </if>
|
||||
<if test="query.startDate != null"> and date(stb.create_time) >= date(#{query.startDate}) </if>
|
||||
<if test="query.endDate != null"> and date(stb.create_time) <= date(#{query.endDate}) </if>
|
||||
<if test="query.deviceRechargeStatus != null"> and stb.device_recharge_status = #{query.deviceRechargeStatus} </if>
|
||||
<if test="query.suitId != null"> and stb.suit_id = #{query.suitId} </if>
|
||||
<if test="query.storeId != null"> and stb.store_id = #{query.storeId} </if>
|
||||
<if test="query.billIds != null and query.billIds.size() > 0">
|
||||
and stb.bill_id in
|
||||
<foreach item="item" collection="billIds" open="(" separator="," close=")">
|
||||
<foreach item="item" collection="query.billIds" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="deviceRechargeStatusList != null and deviceRechargeStatusList.size() > 0">
|
||||
<if test="query.deviceRechargeStatusList != null and query.deviceRechargeStatusList.size() > 0">
|
||||
and stb.device_recharge_status in
|
||||
<foreach item="item" collection="deviceRechargeStatusList" open="(" separator="," close=")">
|
||||
<foreach item="item" collection="query.deviceRechargeStatusList" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="statusList != null and statusList.size() > 0">
|
||||
<if test="query.statusList != null and query.statusList.size() > 0">
|
||||
and stb.status in
|
||||
<foreach item="item" collection="statusList" open="(" separator="," close=")">
|
||||
<foreach item="item" collection="query.statusList" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="storeIds != null and storeIds.size() > 0">
|
||||
<if test="query.storeIds != null and query.storeIds.size() > 0">
|
||||
and stb.store_id in
|
||||
<foreach item="item" collection="storeIds" open="(" separator="," close=")">
|
||||
<foreach item="item" collection="query.storeIds" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="deviceIds != null and deviceIds.size() > 0">
|
||||
<if test="query.deviceIds != null and query.deviceIds.size() > 0">
|
||||
and stb.device_id in
|
||||
<foreach item="item" collection="deviceIds" open="(" separator="," close=")">
|
||||
<foreach item="item" collection="query.deviceIds" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
|
@ -133,28 +135,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
|
||||
<select id="selectCount" resultMap="BillCountVo">
|
||||
SELECT
|
||||
<if test="groupBy == 'create_date'">
|
||||
<if test="query.groupBy == 'create_date'">
|
||||
DATE( stb.create_time ) AS `create_date`,
|
||||
</if>
|
||||
<if test="groupBy == 'create_hour'">
|
||||
<if test="query.groupBy == 'create_hour'">
|
||||
HOUR( stb.create_time ) AS `create_hour`,
|
||||
</if>
|
||||
<if test="groupBy == 'create_year' or groupBy == 'create_year_month' or groupBy == 'create_date' or groupBy == 'create_month'">
|
||||
<if test="query.groupBy == 'create_year' or query.groupBy == 'create_year_month' or query.groupBy == 'create_date' or query.groupBy == 'create_month'">
|
||||
YEAR( stb.create_time ) AS `create_year`,
|
||||
</if>
|
||||
<if test="groupBy == 'create_month' or groupBy == 'create_year_month' or groupBy == 'create_date'">
|
||||
<if test="query.groupBy == 'create_month' or query.groupBy == 'create_year_month' or query.groupBy == 'create_date'">
|
||||
MONTH( stb.create_time ) AS `create_month`,
|
||||
</if>
|
||||
<if test="groupBy == 'create_day' or groupBy == 'create_date'">
|
||||
<if test="query.groupBy == 'create_day' or query.groupBy == 'create_date'">
|
||||
DAY( stb.create_time ) AS `create_day`,
|
||||
</if>
|
||||
<if test="groupBy == 'create_year_month' or groupBy == 'create_date' or groupBy == 'create_month'">
|
||||
<if test="query.groupBy == 'create_year_month' or query.groupBy == 'create_date' or query.groupBy == 'create_month'">
|
||||
DATE_FORMAT( stb.create_time, '%Y-%m' ) AS `create_year_month`,
|
||||
</if>
|
||||
<if test="groupBy == 'store_id'">
|
||||
<if test="query.groupBy == 'store_id'">
|
||||
stb.store_id as store_id,
|
||||
</if>
|
||||
<if test="groupBy == 'device_id'">
|
||||
<if test="query.groupBy == 'device_id'">
|
||||
stb.device_id as device_id,
|
||||
</if>
|
||||
SUM(IF( stb.type = '1' and stb.status = '2', stb.arrival_amount, 0 )) AS recharge,
|
||||
|
@ -168,8 +170,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<where>
|
||||
<include refid="searchCondition"/>
|
||||
</where>
|
||||
<if test="groupBy != null">
|
||||
GROUP BY ${groupBy}
|
||||
<if test="query.groupBy != null">
|
||||
GROUP BY ${query.groupBy}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
|
@ -251,43 +253,66 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</trim>
|
||||
</insert>
|
||||
|
||||
|
||||
<update id="addRefundAmount">
|
||||
update sm_transaction_bill
|
||||
set refund_amount = refund_amount + #{refundAmount},
|
||||
refund_mch_amount = refund_mch_amount + #{refundMchAmount},
|
||||
refund_service_amount = refund_service_amount + #{refundServiceAmount}
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<update id="updateSmTransactionBill" parameterType="TransactionBill">
|
||||
update sm_transaction_bill
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="userId != null">user_id = #{userId},</if>
|
||||
<if test="type != null">`type` = #{type},</if>
|
||||
<if test="deviceId != null">device_id = #{deviceId},</if>
|
||||
<if test="mchId != null">mch_id = #{mchId},</if>
|
||||
<if test="money != null">money = #{money},</if>
|
||||
<if test="arrivalAmount != null">arrival_amount = #{arrivalAmount},</if>
|
||||
<if test="serviceCharge != null">service_charge = #{serviceCharge},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="status != null">`status` = #{status},</if>
|
||||
<if test="channelId != null">channel_id = #{channelId},</if>
|
||||
<if test="afterBalance != null">after_balance = #{afterBalance},</if>
|
||||
<if test="payTime != null">pay_time = #{payTime},</if>
|
||||
<if test="expireTime != null">expire_time = #{expireTime},</if>
|
||||
<if test="accountNo != null">account_no = #{accountNo},</if>
|
||||
<if test="payedAmount != null">payed_amount = #{payedAmount},</if>
|
||||
<if test="transferIds != null">transfer_ids = #{transferIds,typeHandler=com.ruoyi.system.mapper.typehandler.StringListTypeHandler},</if>
|
||||
<if test="channelCost != null">channel_cost = #{channelCost},</if>
|
||||
<if test="suitId != null">suit_id = #{suitId},</if>
|
||||
<if test="suitTime != null">suit_time = #{suitTime},</if>
|
||||
<if test="suitStartTime != null">suit_start_time = #{suitStartTime},</if>
|
||||
<if test="suitEndTime != null">suit_end_time = #{suitEndTime},</if>
|
||||
<if test="suitExpireTime != null">suit_expire_time = #{suitExpireTime},</if>
|
||||
<if test="storeId != null ">store_id = #{storeId},</if>
|
||||
<if test="storeName != null ">store_name = #{storeName},</if>
|
||||
<if test="storeAddress != null ">store_address = #{storeAddress},</if>
|
||||
<if test="deviceName != null ">device_name = #{deviceName},</if>
|
||||
<if test="deviceNo != null ">device_no = #{deviceNo},</if>
|
||||
<if test="suitName != null ">suit_name = #{suitName},</if>
|
||||
<if test="deviceMac != null ">device_mac = #{deviceMac},</if>
|
||||
<include refid="updateColumns"/>
|
||||
</trim>
|
||||
where bill_id = #{billId}
|
||||
</update>
|
||||
|
||||
<sql id="updateColumns">
|
||||
<if test="data.userId != null">user_id = #{data.userId},</if>
|
||||
<if test="data.type != null">`type` = #{data.type},</if>
|
||||
<if test="data.deviceId != null">device_id = #{data.deviceId},</if>
|
||||
<if test="data.mchId != null">mch_id = #{data.mchId},</if>
|
||||
<if test="data.money != null">money = #{data.money},</if>
|
||||
<if test="data.arrivalAmount != null">arrival_amount = #{data.arrivalAmount},</if>
|
||||
<if test="data.serviceCharge != null">service_charge = #{data.serviceCharge},</if>
|
||||
<if test="data.createTime != null">create_time = #{data.createTime},</if>
|
||||
<if test="data.remark != null">remark = #{data.remark},</if>
|
||||
<if test="data.status != null">`status` = #{data.status},</if>
|
||||
<if test="data.channelId != null">channel_id = #{data.channelId},</if>
|
||||
<if test="data.afterBalance != null">after_balance = #{data.afterBalance},</if>
|
||||
<if test="data.payTime != null">pay_time = #{data.payTime},</if>
|
||||
<if test="data.expireTime != null">expire_time = #{data.expireTime},</if>
|
||||
<if test="data.accountNo != null">account_no = #{data.accountNo},</if>
|
||||
<if test="data.payedAmount != null">payed_amount = #{data.payedAmount},</if>
|
||||
<if test="data.transferIds != null">transfer_ids = #{data.transferIds,typeHandler=com.ruoyi.system.mapper.typehandler.StringListTypeHandler},</if>
|
||||
<if test="data.channelCost != null">channel_cost = #{data.channelCost},</if>
|
||||
<if test="data.suitId != null">suit_id = #{data.suitId},</if>
|
||||
<if test="data.suitTime != null">suit_time = #{data.suitTime},</if>
|
||||
<if test="data.suitStartTime != null">suit_start_time = #{data.suitStartTime},</if>
|
||||
<if test="data.suitEndTime != null">suit_end_time = #{data.suitEndTime},</if>
|
||||
<if test="data.suitExpireTime != null">suit_expire_time = #{data.suitExpireTime},</if>
|
||||
<if test="data.storeId != null ">store_id = #{data.storeId},</if>
|
||||
<if test="data.storeName != null ">store_name = #{data.storeName},</if>
|
||||
<if test="data.storeAddress != null ">store_address = #{data.storeAddress},</if>
|
||||
<if test="data.deviceName != null ">device_name = #{data.deviceName},</if>
|
||||
<if test="data.deviceNo != null ">device_no = #{data.deviceNo},</if>
|
||||
<if test="data.suitName != null ">suit_name = #{data.suitName},</if>
|
||||
<if test="data.deviceMac != null ">device_mac = #{data.deviceMac},</if>
|
||||
</sql>
|
||||
|
||||
<update id="updateByQuery">
|
||||
update sm_transaction_bill stb
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<include refid="updateColumns"/>
|
||||
</trim>
|
||||
<where>
|
||||
<include refid="searchCondition"/>
|
||||
</where>
|
||||
</update>
|
||||
|
||||
<update id="updatechannelId">
|
||||
update sm_transaction_bill
|
||||
set channel_id = #{channelId},
|
||||
|
@ -383,4 +408,5 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
set device_recharge_status = '1'
|
||||
where bill_no = #{billNo} and type = '1' and device_recharge_status = '3' and status = '2'
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
|||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.TransactionBillBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
@ -106,9 +107,10 @@ public interface TransactionBillService
|
|||
|
||||
/**
|
||||
* 根据订单编号查询数据
|
||||
*
|
||||
* @param billNo 订单编号
|
||||
*/
|
||||
TransactionBill selectSmTransactionBillByBillNo(String billNo);
|
||||
TransactionBillVo selectSmTransactionBillByBillNo(String billNo);
|
||||
|
||||
/**
|
||||
* 获取未支付的充值订单
|
||||
|
@ -169,9 +171,9 @@ public interface TransactionBillService
|
|||
|
||||
/**
|
||||
* 刷新订单支付结果
|
||||
* @param billIds
|
||||
* @param billId
|
||||
*/
|
||||
void refreshPayResult(List<Long> billIds);
|
||||
int refreshPayResult(Long billId);
|
||||
|
||||
/**
|
||||
* 支付中
|
||||
|
@ -223,4 +225,33 @@ public interface TransactionBillService
|
|||
* @return
|
||||
*/
|
||||
boolean bluetoothRechargeSuccess(String billNo);
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
*/
|
||||
int refund(BillRefundDTO dto);
|
||||
|
||||
/**
|
||||
* 根据条件更新
|
||||
* @param data
|
||||
* @param query
|
||||
*/
|
||||
int updateByQuery(TransactionBill data, TransactionBillQuery query);
|
||||
|
||||
/**
|
||||
* 刷新支付结果
|
||||
*/
|
||||
int refreshPayResult(String billNo);
|
||||
|
||||
int refreshPayResult(List<TransactionBillVo> billList);
|
||||
|
||||
/**
|
||||
* 增加订单退款金额
|
||||
*
|
||||
* @param billId 订单ID
|
||||
* @param refundAmount 退款金额
|
||||
* @param refundMchAmount 商户退款金额
|
||||
* @param refundServiceAmount 退款手续费
|
||||
*/
|
||||
int addRefundAmount(Long billId, BigDecimal refundAmount, BigDecimal refundMchAmount, BigDecimal refundServiceAmount);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ import com.ruoyi.ss.device.domain.vo.SmDeviceVO;
|
|||
import com.ruoyi.ss.device.service.ISmDeviceService;
|
||||
import com.ruoyi.ss.record.time.service.IRecordTimeService;
|
||||
import com.ruoyi.ss.record.time.service.RecordTimeConverter;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.domain.RefundVO;
|
||||
import com.ruoyi.ss.refund.service.RefundConverter;
|
||||
import com.ruoyi.ss.refund.service.RefundService;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.store.service.IStoreService;
|
||||
import com.ruoyi.ss.suit.domain.SuitVo;
|
||||
|
@ -24,6 +28,7 @@ import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
|||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.TransactionBillBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.*;
|
||||
import com.ruoyi.ss.transactionBill.mapper.TransactionBillMapper;
|
||||
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
|
||||
|
@ -117,6 +122,12 @@ public class TransactionBillServiceImpl implements TransactionBillService {
|
|||
@Autowired
|
||||
private RecordTimeConverter recordTimeConverter;
|
||||
|
||||
@Autowired
|
||||
private RefundService refundService;
|
||||
|
||||
@Autowired
|
||||
private RefundConverter refundConverter;
|
||||
|
||||
/**
|
||||
* 查询充值记录
|
||||
*
|
||||
|
@ -510,28 +521,18 @@ public class TransactionBillServiceImpl implements TransactionBillService {
|
|||
|
||||
/**
|
||||
* 刷新支付结果
|
||||
* @param billIds
|
||||
*
|
||||
* @param billId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public void refreshPayResult(List<Long> billIds) {
|
||||
TransactionBillQuery dto = new TransactionBillQuery();
|
||||
dto.setBillIds(billIds);
|
||||
List<TransactionBillVo> billList = transactionBillMapper.selectSmTransactionBillList(dto);
|
||||
if (CollectionUtils.isEmpty(billList)) {
|
||||
return;
|
||||
}
|
||||
public int refreshPayResult(Long billId) {
|
||||
return this.refreshPayResult(this.selectSmTransactionBillByBillId(billId));
|
||||
}
|
||||
|
||||
// 获取支付结果,并判断是否支付成功,若成功则更新数据
|
||||
Date now = new Date();
|
||||
for (TransactionBillVo bill : billList) {
|
||||
if (TransactionBillStatus.PAYING.getStatus().equals(bill.getStatus()) || TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus()) ) {
|
||||
boolean payResult = getPayResult(bill.getBillNo());
|
||||
if (payResult) {
|
||||
this.rechargeSuccess(bill.getBillNo(), now);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Transactional
|
||||
public int refreshPayResult(TransactionBillVo bill) {
|
||||
return this.refreshPayResult(Collections.singletonList(bill));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -677,7 +678,7 @@ public class TransactionBillServiceImpl implements TransactionBillService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public TransactionBill selectSmTransactionBillByBillNo(String billNo) {
|
||||
public TransactionBillVo selectSmTransactionBillByBillNo(String billNo) {
|
||||
return transactionBillMapper.selectSmTransactionBillByBillNo(billNo);
|
||||
}
|
||||
|
||||
|
@ -838,4 +839,91 @@ public class TransactionBillServiceImpl implements TransactionBillService {
|
|||
|
||||
return execute != null && execute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int refund(BillRefundDTO dto) {
|
||||
// 校验订单
|
||||
TransactionBillVo bill = this.selectSmTransactionBillByBillId(dto.getBillId());
|
||||
if (bill == null) {
|
||||
return 0;
|
||||
}
|
||||
ServiceUtil.assertion(dto.getRefundAmount().compareTo(bill.getMoney()) > 0, "退款金额不允许大于订单金额");
|
||||
ServiceUtil.assertion(!TransactionBillStatus.SUCCESS.getStatus().equals(bill.getStatus()), "当前订单状态不允许退款");
|
||||
|
||||
Integer result = transactionTemplate.execute(status -> {
|
||||
// 修改订单状态
|
||||
TransactionBill data = new TransactionBill();
|
||||
data.setStatus(TransactionBillStatus.REFUNDING.getStatus());
|
||||
TransactionBillQuery billQuery = new TransactionBillQuery();
|
||||
billQuery.setBillId(dto.getBillId());
|
||||
billQuery.setStatus(TransactionBillStatus.SUCCESS.getStatus());
|
||||
int updateBill = this.updateByQuery(data, billQuery);
|
||||
ServiceUtil.assertion(updateBill != 1, "修改订单状态失败");
|
||||
|
||||
// 创建退款订单
|
||||
Refund refund = refundConverter.toPo(dto);
|
||||
int updateRefund = refundService.insertRefund(refund);
|
||||
ServiceUtil.assertion(updateRefund != 1, "创建退款订单失败");
|
||||
RefundVO refundVO = refundService.selectRefundByRefundNo(refund.getRefundNo());
|
||||
|
||||
// 商户余额按照比例扣减
|
||||
userService.subtractBalance(bill.getMchId(), refund.getMchAmount());
|
||||
|
||||
// 修改原订单的退款金额和退款手续费
|
||||
int updateRefundAmount = this.addRefundAmount(bill.getBillId(), refund.getAmount(), refund.getMchAmount(), refund.getServiceAmount());
|
||||
ServiceUtil.assertion(updateRefundAmount != 1, "修改原订单的退款金额和退款手续费失败");
|
||||
|
||||
// 发起退款
|
||||
if (TransactionBillPayType.WECHAT.getType().equals(bill.getChannelId())) {
|
||||
wxPayService.refund(refundVO);
|
||||
} else {
|
||||
throw new ServiceException("当前支付方式不支持退款");
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
|
||||
return result == null ? 0 : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByQuery(TransactionBill data, TransactionBillQuery query) {
|
||||
if (query == null) {
|
||||
return 0;
|
||||
}
|
||||
return transactionBillMapper.updateByQuery(data, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public int refreshPayResult(String billNo) {
|
||||
return this.refreshPayResult(this.selectSmTransactionBillByBillNo(billNo));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public int refreshPayResult(List<TransactionBillVo> billList) {
|
||||
if (CollectionUtils.isEmptyElement(billList)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取支付结果,并判断是否支付成功,若成功则更新数据
|
||||
Date now = new Date();
|
||||
for (TransactionBillVo bill : billList) {
|
||||
if (TransactionBillStatus.PAYING.getStatus().equals(bill.getStatus()) || TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus()) ) {
|
||||
boolean payResult = getPayResult(bill.getBillNo());
|
||||
if (payResult) {
|
||||
this.rechargeSuccess(bill.getBillNo(), now);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addRefundAmount(Long billId, BigDecimal refundAmount, BigDecimal refundMchAmount, BigDecimal refundServiceAmount) {
|
||||
if (billId == null) {
|
||||
return 0;
|
||||
}
|
||||
return transactionBillMapper.addRefundAmount(billId, refundAmount, refundMchAmount, refundServiceAmount);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,13 @@ import com.ruoyi.common.enums.UserType;
|
|||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.collection.CollectionUtils;
|
||||
import com.ruoyi.ss.device.domain.SmDeviceQuery;
|
||||
import com.ruoyi.ss.device.domain.vo.SmDeviceVO;
|
||||
import com.ruoyi.ss.device.service.ISmDeviceService;
|
||||
import com.ruoyi.ss.store.domain.StoreQuery;
|
||||
import com.ruoyi.ss.store.domain.StoreVo;
|
||||
import com.ruoyi.ss.store.service.IStoreService;
|
||||
import com.ruoyi.ss.user.domain.SmUserQuery;
|
||||
import com.ruoyi.ss.user.domain.SmUserVo;
|
||||
import com.ruoyi.ss.user.domain.bo.UserUpdateServiceRateBO;
|
||||
|
@ -15,7 +21,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
|
@ -42,6 +47,9 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
@Autowired
|
||||
private ISmDeviceService deviceService;
|
||||
|
||||
@Autowired
|
||||
private IStoreService storeService;
|
||||
|
||||
/**
|
||||
* 查询普通用户信息
|
||||
*
|
||||
|
@ -244,8 +252,19 @@ public class SmUserServiceImpl implements ISmUserService
|
|||
* @param userIds
|
||||
*/
|
||||
private void validatePreLogicDel(List<Long> userIds) {
|
||||
ServiceUtil.assertion(!CollectionUtils.isEmpty(userIds), "用户id不能为空");
|
||||
ServiceUtil.assertion(CollectionUtils.isEmpty(userIds), "用户id不能为空");
|
||||
ServiceUtil.assertion(!isExistUsers(userIds), "用户不存在");
|
||||
|
||||
SmDeviceQuery deviceQuery = new SmDeviceQuery();
|
||||
deviceQuery.setUserIds(userIds);
|
||||
List<SmDeviceVO> deviceList = deviceService.selectSmDeviceList(deviceQuery);
|
||||
ServiceUtil.assertion(CollectionUtils.isNotEmpty(deviceList), "用户存在设备,不允许删除");
|
||||
|
||||
|
||||
StoreQuery storeQuery = new StoreQuery();
|
||||
storeQuery.setUserIds(userIds);
|
||||
List<StoreVo> storeList = storeService.selectSmStoreList(storeQuery);
|
||||
ServiceUtil.assertion(CollectionUtils.isNotEmpty(storeList), "用户存在店铺,不允许删除");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.ruoyi.ss.wxPay.domain;
|
||||
|
||||
import com.wechat.pay.java.service.refund.model.AmountReq;
|
||||
|
||||
/**
|
||||
* @author wjh
|
||||
* 2024/7/10
|
||||
*/
|
||||
public interface RefundAble {
|
||||
|
||||
// 原商户订单号
|
||||
String refundOutTradeNo();
|
||||
|
||||
// 退款单号
|
||||
String refundOutRefundNo();
|
||||
|
||||
// 退款原因
|
||||
String refundReason();
|
||||
|
||||
// 退款金额
|
||||
AmountReq refundAmount();
|
||||
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package com.ruoyi.ss.wxPay.service;
|
||||
|
||||
import com.ruoyi.ss.wxPay.domain.RefundAble;
|
||||
import com.ruoyi.ss.wxPay.domain.enums.TransferScene;
|
||||
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.refund.model.Refund;
|
||||
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
|
||||
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
|
||||
|
||||
|
@ -70,4 +73,14 @@ public interface IWxPayService {
|
|||
* @param transferScene 转账场景
|
||||
*/
|
||||
List<TransferDetailInput> buildTransferDetailList(BigDecimal totalAmount, String openId, TransferScene transferScene);
|
||||
|
||||
/**
|
||||
* 发起退款
|
||||
*/
|
||||
Refund refund(RefundAble refund);
|
||||
|
||||
/**
|
||||
* 验签并解析
|
||||
*/
|
||||
<T> T checkAndParse(HttpServletRequest request, String body, Class<T> clazz);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.ruoyi.common.utils.DateUtils;
|
|||
import com.ruoyi.common.utils.ServiceUtil;
|
||||
import com.ruoyi.common.utils.SnowFlakeUtil;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.ss.account.service.ISmAccountService;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillPayType;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
|
||||
|
@ -18,6 +17,7 @@ import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
|
|||
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
|
||||
import com.ruoyi.ss.user.domain.SmUserVo;
|
||||
import com.ruoyi.ss.user.service.ISmUserService;
|
||||
import com.ruoyi.ss.wxPay.domain.RefundAble;
|
||||
import com.ruoyi.ss.wxPay.domain.enums.TransferScene;
|
||||
import com.ruoyi.ss.wxPayNotify.domain.SmWxPayNotify;
|
||||
import com.ruoyi.ss.wxPayNotify.mapper.SmWxPayNotifyMapper;
|
||||
|
@ -28,6 +28,9 @@ import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
|||
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
||||
import com.wechat.pay.java.service.payments.jsapi.model.*;
|
||||
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||
import com.wechat.pay.java.service.refund.RefundService;
|
||||
import com.wechat.pay.java.service.refund.model.CreateRequest;
|
||||
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;
|
||||
|
@ -45,6 +48,7 @@ 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;
|
||||
|
||||
/**
|
||||
|
@ -81,7 +85,7 @@ public class WxPayService implements IWxPayService {
|
|||
private RedisLock redisLock;
|
||||
|
||||
@Autowired
|
||||
private ISmAccountService accountService;
|
||||
private RefundService refundService;
|
||||
|
||||
@Autowired
|
||||
private TransferBatchService transferBatchService;
|
||||
|
@ -125,9 +129,9 @@ public class WxPayService implements IWxPayService {
|
|||
transactionBillService.paying(bill.getBillId());
|
||||
|
||||
// 每隔20秒查询支付结果,直到过期,首次10秒查询
|
||||
// scheduledExecutorService.schedule(() -> {
|
||||
// transactionBillService.refreshPayResultBeforeExpire(bill.getBillNo(), 20, TimeUnit.SECONDS);
|
||||
// }, 10, TimeUnit.SECONDS);
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
transactionBillService.refreshPayResultBeforeExpire(bill.getBillNo(), 20, TimeUnit.SECONDS);
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
|
||||
} catch (Exception e) {
|
||||
this.closeOrder(bill.getBillNo());
|
||||
|
@ -286,6 +290,25 @@ public class WxPayService implements IWxPayService {
|
|||
return transferDetailList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起退款
|
||||
*
|
||||
* @param refund
|
||||
*/
|
||||
@Override
|
||||
public Refund refund(RefundAble refund) {
|
||||
CreateRequest request = new CreateRequest();
|
||||
request.setOutTradeNo(refund.refundOutTradeNo());
|
||||
request.setOutRefundNo(refund.refundOutRefundNo());
|
||||
request.setReason(refund.refundReason());
|
||||
request.setAmount(refund.refundAmount());
|
||||
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
|
||||
log.info("【退款】请求微信参数: {}", JSON.toJSONString(request));
|
||||
Refund res = refundService.create(request);
|
||||
log.info("【退款】微信返回结果:【{}】",JSON.toJSONString(refund));
|
||||
return res;
|
||||
}
|
||||
|
||||
private Long getTransferAmount(BigDecimal money) {
|
||||
return money.multiply(new BigDecimal(100)).longValue();
|
||||
}
|
||||
|
@ -322,7 +345,8 @@ public class WxPayService implements IWxPayService {
|
|||
* @param body 请求体
|
||||
* @param clazz 返回值类型
|
||||
*/
|
||||
private <T> T checkAndParse(HttpServletRequest request, String body, Class<T> clazz) {
|
||||
@Override
|
||||
public <T> T checkAndParse(HttpServletRequest request, String body, Class<T> clazz) {
|
||||
// 构造 RequestParam
|
||||
RequestParam requestParam = new RequestParam.Builder()
|
||||
.serialNumber(request.getHeader("Wechatpay-Serial"))
|
||||
|
|
|
@ -49,7 +49,7 @@ public class PayTask implements ApplicationRunner {
|
|||
return;
|
||||
}
|
||||
|
||||
transactionBillService.refreshPayResult(billList.stream().map(TransactionBill::getBillId).collect(Collectors.toList()));
|
||||
transactionBillService.refreshPayResult(billList);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
package com.ruoyi.web.controller.app;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.pay.wx.domain.NotifyEventType;
|
||||
import com.ruoyi.common.utils.http.HttpUtils;
|
||||
import com.ruoyi.ss.refund.service.RefundService;
|
||||
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
|
||||
import com.ruoyi.ss.wxPay.service.IWxPayService;
|
||||
import com.wechat.pay.java.core.exception.ValidationException;
|
||||
import com.wechat.pay.java.core.notification.Notification;
|
||||
import com.wechat.pay.java.service.refund.model.RefundNotification;
|
||||
import com.wechat.pay.java.service.refund.model.Status;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
|
@ -32,6 +41,9 @@ public class AppPayController {
|
|||
@Autowired
|
||||
private TransactionBillService transactionBillService;
|
||||
|
||||
@Autowired
|
||||
private RefundService refundService;
|
||||
|
||||
@ApiOperation("微信支付")
|
||||
@GetMapping("/wx/{billNo}")
|
||||
public AjaxResult wxPay(@PathVariable @ApiParam("订单编号") String billNo) {
|
||||
|
@ -54,6 +66,35 @@ public class AppPayController {
|
|||
return ResponseEntity.status(HttpStatus.OK).body(null);
|
||||
}
|
||||
|
||||
// 微信退款回调
|
||||
@PostMapping("/notify/wx/refund")
|
||||
@Anonymous
|
||||
public ResponseEntity<Boolean> wxPayNotifyRefund(HttpServletRequest request) {
|
||||
try {
|
||||
String body = HttpUtils.getBody(request);
|
||||
log.info("【微信退款回调】接收对象 : " + JSON.toJSONString(body));
|
||||
// 解析通知数据
|
||||
Notification notification = JSON.parseObject(body, Notification.class);
|
||||
log.info("【微信退款回调】转换成notification: " + JSON.toJSONString(notification));
|
||||
// 退款成功通知
|
||||
if (NotifyEventType.REFUND_SUCCESS.getValue().equals(notification.getEventType())) {
|
||||
// 验签、解密并转换成 RefundNotification
|
||||
RefundNotification refundNotification = wxPayService.checkAndParse(request, body, RefundNotification.class);
|
||||
log.info("【微信退款回调】转换成RefundNotification: " + JSON.toJSONString(refundNotification));
|
||||
if (Status.SUCCESS.equals(refundNotification.getRefundStatus())) {
|
||||
// 退款成功操作
|
||||
String outRefundNo = refundNotification.getOutRefundNo();
|
||||
refundService.handleRefundSuccess(outRefundNo);
|
||||
}
|
||||
}
|
||||
} catch (ValidationException e) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.OK).body(null);
|
||||
}
|
||||
|
||||
@ApiOperation("查询支付结果")
|
||||
@GetMapping("/result/{billNo}")
|
||||
public AjaxResult payResult(@PathVariable @ApiParam("订单编号") String billNo) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.ruoyi.ss.transactionBill.domain.TransactionBill;
|
|||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.TransactionBillBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillGroupBy;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
|
||||
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
|
||||
|
@ -209,4 +210,23 @@ public class AppTransactionBillController extends BaseController
|
|||
return success(smTransactionBillService.bluetoothRechargeSuccess(billNo));
|
||||
}
|
||||
|
||||
@ApiOperation("订单退款")
|
||||
@PutMapping("/refund")
|
||||
public AjaxResult refund(@RequestBody @Validated BillRefundDTO dto) {
|
||||
// 判断用户是否订单收款人
|
||||
TransactionBillVo bill = smTransactionBillService.selectSmTransactionBillByBillId(dto.getBillId());
|
||||
if (bill == null) {
|
||||
return error("订单不存在");
|
||||
}
|
||||
if (!Objects.equals(bill.getMchId(), getUserId())) {
|
||||
return error("您无权操作退款");
|
||||
}
|
||||
return toAjax(smTransactionBillService.refund(dto));
|
||||
}
|
||||
|
||||
@ApiOperation("刷新支付结果")
|
||||
@PutMapping("/{billNo}/refreshPayResult")
|
||||
public AjaxResult refreshPayResult(@PathVariable String billNo ) {
|
||||
return toAjax(smTransactionBillService.refreshPayResult(billNo));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package com.ruoyi.web.controller.ss;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.ss.refund.domain.RefundQuery;
|
||||
import com.ruoyi.ss.refund.domain.RefundVO;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.ss.refund.domain.Refund;
|
||||
import com.ruoyi.ss.refund.service.RefundService;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 退款订单Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-07-09
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ss/refund")
|
||||
public class RefundController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private RefundService refundService;
|
||||
|
||||
/**
|
||||
* 查询退款订单列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(RefundQuery refund)
|
||||
{
|
||||
startPage();
|
||||
List<RefundVO> list = refundService.selectRefundList(refund);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出退款订单列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:export')")
|
||||
@Log(title = "退款订单", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, RefundQuery refund)
|
||||
{
|
||||
List<RefundVO> list = refundService.selectRefundList(refund);
|
||||
ExcelUtil<RefundVO> util = new ExcelUtil<RefundVO>(RefundVO.class);
|
||||
util.exportExcel(response, list, "退款订单数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取退款订单详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:query')")
|
||||
@GetMapping(value = "/{refundId}")
|
||||
public AjaxResult getInfo(@PathVariable("refundId") Long refundId)
|
||||
{
|
||||
return success(refundService.selectRefundByRefundId(refundId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增退款订单
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:add')")
|
||||
@Log(title = "退款订单", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody Refund refund)
|
||||
{
|
||||
return toAjax(refundService.insertRefund(refund));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改退款订单
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:edit')")
|
||||
@Log(title = "退款订单", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody Refund refund)
|
||||
{
|
||||
return toAjax(refundService.updateRefund(refund));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除退款订单
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('ss:refund:remove')")
|
||||
@Log(title = "退款订单", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{refundIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] refundIds)
|
||||
{
|
||||
return toAjax(refundService.deleteRefundByRefundIds(refundIds));
|
||||
}
|
||||
}
|
|
@ -2,15 +2,18 @@ package com.ruoyi.web.controller.ss;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.ruoyi.ss.transactionBill.domain.bo.TransactionBillBO;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
|
||||
import com.ruoyi.ss.transactionBill.domain.TransactionBillVo;
|
||||
import com.ruoyi.ss.transactionBill.domain.dto.BillRefundDTO;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
|
@ -145,11 +148,11 @@ public class SmTransactionBillController extends BaseController
|
|||
|
||||
@ApiModelProperty("刷新支付结果")
|
||||
@PreAuthorize("@ss.hasPermi('system:bill:edit')")
|
||||
@GetMapping("/refreshPayResult/{billIds}")
|
||||
@GetMapping("/refreshPayResult/{billId}")
|
||||
@Log(title = "刷新支付结果", businessType = BusinessType.UPDATE)
|
||||
public AjaxResult refreshPayResult(@PathVariable Long[] billIds)
|
||||
public AjaxResult refreshPayResult(@PathVariable Long billId)
|
||||
{
|
||||
smTransactionBillService.refreshPayResult(Arrays.asList(billIds));
|
||||
smTransactionBillService.refreshPayResult(billId);
|
||||
return success();
|
||||
}
|
||||
|
||||
|
@ -162,4 +165,11 @@ public class SmTransactionBillController extends BaseController
|
|||
return success();
|
||||
}
|
||||
|
||||
|
||||
// 订单退款
|
||||
@PutMapping("/refund")
|
||||
@PreAuthorize("@ss.hasPermi('system:bill:refund')")
|
||||
public AjaxResult refund(@RequestBody @Validated BillRefundDTO dto) {
|
||||
return toAjax(smTransactionBillService.refund(dto));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,9 +137,9 @@ public class SmUserController extends BaseController
|
|||
@PreAuthorize("@ss.hasPermi('system:smUser:remove')")
|
||||
@Log(title = "普通用户信息", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{userIds}")
|
||||
public AjaxResult remove(@PathVariable Long[] userIds)
|
||||
public AjaxResult remove(@PathVariable List<Long> userIds)
|
||||
{
|
||||
return toAjax(smUserService.deleteSmUserByUserIds(userIds));
|
||||
return toAjax(smUserService.logicDel(userIds));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@ public class DeviceAdminRequiredAspect {
|
|||
// 判断当前用户是否有权限访问
|
||||
@Before("@annotation(required)")
|
||||
public void doBefore(JoinPoint point, DeviceAdminRequired required) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
SmUser user = loginUser.getSmUser();
|
||||
SmUserVo user = smUserService.selectSmUserByUserId(SecurityUtils.getUserId());
|
||||
ServiceUtil.assertion(user == null || user.getDeviceAdmin() == null || !user.getDeviceAdmin(), "您无权操作" );
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ wx:
|
|||
# apiV3密钥
|
||||
apiV3Key: 49819e0f0abdb2df3246f7b27f264d75
|
||||
# 通知回调地址
|
||||
notifyUrl: http://124.221.246.124:2290/dev-api/app/pay/notify/wx # 内网穿透
|
||||
notifyUrl: http://124.221.246.124:2290/app/pay/notify/wx # 内网穿透
|
||||
# 退款通知回调地址
|
||||
refundNotifyUrl: http://124.221.246.124:2290/app/pay/notify/wx/refund
|
||||
# 密钥所在位置
|
||||
privateKeyPath: D:/project/证书/wxpay/apiclient_key.pem
|
||||
# 证书序列号
|
||||
|
|
|
@ -6,7 +6,7 @@ spring:
|
|||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://106.75.233.135:3306/smart-switch?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://106.75.233.135:3306/smart-switch-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: 9671e015b05b3f11
|
||||
# 从库数据源
|
||||
|
|
|
@ -21,6 +21,8 @@ wx:
|
|||
apiV3Key: 49819e0f0abdb2df3246f7b27f264d75
|
||||
# 通知回调地址
|
||||
notifyUrl: https://kg.chuantewulian.cn/prod-api/app/pay/notify/wx # 正式环境
|
||||
# 退款通知回调地址
|
||||
refundNotifyUrl: https://kg.chuantewulian.cn/prod-api/app/pay/notify/wx/refund
|
||||
# 密钥所在位置
|
||||
privateKeyPath: /www/wwwroot/smart-switch/wxpay/apiclient_key.pem
|
||||
# 证书序列号
|
||||
|
|
|
@ -21,6 +21,8 @@ wx:
|
|||
apiV3Key: 49819e0f0abdb2df3246f7b27f264d75
|
||||
# 通知回调地址
|
||||
notifyUrl: https://kg.chuantewulian.cn/test-api/app/pay/notify/wx # 测试环境
|
||||
# 退款通知回调地址
|
||||
refundNotifyUrl: https://kg.chuantewulian.cn/test-api/app/pay/notify/wx/refund
|
||||
# 密钥所在位置
|
||||
privateKeyPath: /home/www/projects/smart-switch/wxpay/apiclient_key.pem
|
||||
# 证书序列号
|
||||
|
|
Loading…
Reference in New Issue
Block a user