Browse Source

对接拼团数据1

master
wangfukang 1 month ago
parent
commit
8770a5ed4e
  1. 16
      hiver-admin/test-output/test-report.html
  2. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java
  3. 9
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderDeliveryFeeRecordMapper.java
  4. 46
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderDeliveryFeeRecord.java
  5. 6
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
  6. 6
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java
  7. 30
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  8. 34
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
  9. 118
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/MallOrderDeliveryFeeRefundHelper.java
  10. 25
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java
  11. 12
      hiver-modules/hiver-mall/src/main/resources/db/mall_order_delivery_fee_record.sql

16
hiver-admin/test-output/test-report.html

@ -35,7 +35,7 @@
<a href="#"><span class="badge badge-primary">Hiver</span></a> <a href="#"><span class="badge badge-primary">Hiver</span></a>
</li> </li>
<li class="m-r-10"> <li class="m-r-10">
<a href="#"><span class="badge badge-primary">六月 22, 2026 17:45:41</span></a> <a href="#"><span class="badge badge-primary">六月 23, 2026 16:55:30</span></a>
</li> </li>
</ul> </ul>
</div> </div>
@ -84,7 +84,7 @@
<div class="test-detail"> <div class="test-detail">
<span class="meta text-white badge badge-sm"></span> <span class="meta text-white badge badge-sm"></span>
<p class="name">passTest</p> <p class="name">passTest</p>
<p class="text-sm"><span>17:45:43 下午</span> / <span>0.031 secs</span></p> <p class="text-sm"><span>16:55:31 下午</span> / <span>0.032 secs</span></p>
</div> </div>
<div class="test-contents d-none"> <div class="test-contents d-none">
<div class="detail-head"> <div class="detail-head">
@ -92,9 +92,9 @@
<div class="info"> <div class="info">
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div> <div class='float-right'><span class='badge badge-default'>#test-id=1</span></div>
<h5 class="test-status text-pass">passTest</h5> <h5 class="test-status text-pass">passTest</h5>
<span class='badge badge-success'>06.22.2026 17:45:43</span> <span class='badge badge-success'>06.23.2026 16:55:31</span>
<span class='badge badge-danger'>06.22.2026 17:45:43</span> <span class='badge badge-danger'>06.23.2026 16:55:31</span>
<span class='badge badge-default'>0.031 secs</span> <span class='badge badge-default'>0.032 secs</span>
</div> </div>
<div class="m-t-10 m-l-5"></div> <div class="m-t-10 m-l-5"></div>
</div> </div>
@ -104,7 +104,7 @@
<tbody> <tbody>
<tr class="event-row"> <tr class="event-row">
<td><span class="badge log pass-bg">Pass</span></td> <td><span class="badge log pass-bg">Pass</span></td>
<td>17:45:43</td> <td>16:55:31</td>
<td> <td>
Test passed Test passed
</td> </td>
@ -128,13 +128,13 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Started</p> <p class="m-b-0">Started</p>
<h3>六月 22, 2026 17:45:41</h3> <h3>六月 23, 2026 16:55:30</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Ended</p> <p class="m-b-0">Ended</p>
<h3>六月 22, 2026 17:45:43</h3> <h3>六月 23, 2026 16:55:31</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java

@ -30,6 +30,8 @@ import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -110,7 +112,8 @@ public class WechatPayController {
} }
if(description.equals("增加配送佣金")){ if(description.equals("增加配送佣金")){
outTradeNo = "ORDERDE_" + outTradeNo; String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
outTradeNo = "ORDERDE_" + timestamp.substring(timestamp.length() - 2) + outTradeNo ;
}else{ }else{
outTradeNo = "ORDER_" + outTradeNo; outTradeNo = "ORDER_" + outTradeNo;
} }

9
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderDeliveryFeeRecordMapper.java

@ -0,0 +1,9 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallOrderDeliveryFeeRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface MallOrderDeliveryFeeRecordMapper extends BaseMapper<MallOrderDeliveryFeeRecord> {
}

46
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderDeliveryFeeRecord.java

@ -0,0 +1,46 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
@Entity
@Table(name = "mall_order_delivery_fee_record")
@TableName("mall_order_delivery_fee_record")
@ApiModel(value = "订单追加配送佣金记录表")
public class MallOrderDeliveryFeeRecord implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableId
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "关联核心订单ID")
private String orderId;
@ApiModelProperty(value = "微信支付商户订单号")
private String wxOrderId;
@ApiModelProperty(value = "追加配送佣金金额")
private BigDecimal amount;
@ApiModelProperty(value = "退款状态 0未退 1已退")
private Integer refundStatus;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "退款时间")
private Date refundTime;
}

6
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java

@ -966,6 +966,9 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
newRecord2.put("status", 2); // 状态 newRecord2.put("status", 2); // 状态
newRecord2.put("orderCount", orderPutCount); newRecord2.put("orderCount", orderPutCount);
returnList.add(newRecord2); returnList.add(newRecord2);
if (orderWaitCount + orderGetCount + orderPutCount == 0) {
returnList = this.baseMapper.countOrdersByStatus(workerId);
}
}else{ }else{
returnList = this.baseMapper.countOrdersByStatus(workerId); returnList = this.baseMapper.countOrdersByStatus(workerId);
} }
@ -1034,6 +1037,9 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderM
newRecord2.put("status", 2); // 状态 newRecord2.put("status", 2); // 状态
newRecord2.put("orderCount", orderPutCount); newRecord2.put("orderCount", orderPutCount);
returnList.add(newRecord2); returnList.add(newRecord2);
if (orderWaitCount + orderGetCount + orderPutCount == 0) {
returnList = this.baseMapper.countOrdersByStatus(workerId);
}
}else{ }else{
returnList = this.baseMapper.countOrdersByStatus(workerId); returnList = this.baseMapper.countOrdersByStatus(workerId);
} }

6
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java

@ -28,7 +28,6 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@ -107,6 +106,9 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private WechatPayUtil wechatPayUtil;
@Autowired
private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper;
@Autowired @Autowired
private ProductMapper productMapper; private ProductMapper productMapper;
@ -483,7 +485,7 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
refund.setStatus(0); refund.setStatus(0);
refund.setCreateTime(new Date()); refund.setCreateTime(new Date());
mallRefundRecordMapper.insert(refund); mallRefundRecordMapper.insert(refund);
wechatPayUtil.refund(order.getId(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue(),order.getTotalAmount().multiply(new BigDecimal(100)).longValue()); mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, order.getTotalAmount());
List<MallOrderGoods> goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId()); List<MallOrderGoods> goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId());
if (goodsList == null || goodsList.isEmpty()) return; if (goodsList == null || goodsList.isEmpty()) return;
List<MallReturnOrderGoods> returnList = goodsList.stream() List<MallReturnOrderGoods> returnList = goodsList.stream()

30
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java

@ -146,6 +146,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private WechatPayUtil wechatPayUtil;
@Autowired
private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper;
@Autowired @Autowired
WorkerOrderCacheUtil workerOrderCacheUtil; WorkerOrderCacheUtil workerOrderCacheUtil;
@ -723,7 +726,11 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
} }
}else if(orderId != null && orderId.startsWith("ORDERDE_")){ }else if(orderId != null && orderId.startsWith("ORDERDE_")){
//增加配送佣金 //增加配送佣金
orderId = orderId.substring(8); String wxOrderId = orderId;
if (mallOrderDeliveryFeeRefundHelper.existsAddedDeliveryFee(wxOrderId)) {
return;
}
orderId = orderId.substring(10);
MallOrder order = this.getById(orderId); MallOrder order = this.getById(orderId);
if(order.getOrderType() == ORDER_TYPE_FACETOFACE){ if(order.getOrderType() == ORDER_TYPE_FACETOFACE){
MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectByGroupId(orderId); MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectByGroupId(orderId);
@ -734,6 +741,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectOne(qw); MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectOne(qw);
mallDeliveryOrderService.addDeliveryFee(order,delivery, totalFee,order.getDeliveryFee()); mallDeliveryOrderService.addDeliveryFee(order,delivery, totalFee,order.getDeliveryFee());
} }
mallOrderDeliveryFeeRefundHelper.recordAddedDeliveryFee(orderId, wxOrderId, totalFee);
} }
} }
@ -1021,7 +1029,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (group.getSuccessTime() != null) { if (group.getSuccessTime() != null) {
long diffMs = System.currentTimeMillis() - group.getSuccessTime().getTime(); long diffMs = System.currentTimeMillis() - group.getSuccessTime().getTime();
if (diffMs < 10 * 60 * 1000L) { if (diffMs < 10 * 60 * 1000L) {
throw new RuntimeException("拼团订单10分钟内无法退款"); throw new RuntimeException("拼团配送订单成团后10分钟内无法退款");
} }
} }
if (delivery != null && delivery.getStatus() != null && delivery.getStatus() >= 1) { if (delivery != null && delivery.getStatus() != null && delivery.getStatus() >= 1) {
@ -1215,7 +1223,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
} }
//优惠券变成已使用 //优惠券变成已使用
mallUserCouponService.useCoupon(orderId); mallUserCouponService.useCoupon(orderId);
updateOrderStatus(orderId, STATUS_DONE); updateOrderStatusForDaoDian(orderId, STATUS_DONE);
rewardIeMatchQuota(order.getUserId()); rewardIeMatchQuota(order.getUserId());
//白嫖星球-订单完成发放星球券(不影响主流程) //白嫖星球-订单完成发放星球券(不影响主流程)
planetRewardHook.onOrderFinish(order.getUserId(), order.getRegionId(), order.getId()); planetRewardHook.onOrderFinish(order.getUserId(), order.getRegionId(), order.getId());
@ -1252,6 +1260,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
@Override @Override
public Map<String, Object> countByShop(String shopId) { public Map<String, Object> countByShop(String shopId) {
Map<String, Object> result = new HashMap<String,Object>(); Map<String, Object> result = new HashMap<String,Object>();
//统计不同业务类型的订单数量
List<HashMap<Integer, String>> orderStatusCount = this.baseMapper.selectOrderStatusCount(shopId); List<HashMap<Integer, String>> orderStatusCount = this.baseMapper.selectOrderStatusCount(shopId);
result.put("orderStatusCount", orderStatusCount); result.put("orderStatusCount", orderStatusCount);
// 1. 获取指定日期(例如:今天) // 1. 获取指定日期(例如:今天)
@ -1262,15 +1271,19 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
// 3. 生成结束时间:当天的 23:59:59 // 3. 生成结束时间:当天的 23:59:59
LocalDateTime endTime = date.atTime(LocalTime.MAX); LocalDateTime endTime = date.atTime(LocalTime.MAX);
//统计营业额和订单量
HashMap<String, Object> orderRevenueAndCount = this.baseMapper.selectOrderRevenueAndCount(shopId,startTime,endTime); HashMap<String, Object> orderRevenueAndCount = this.baseMapper.selectOrderRevenueAndCount(shopId,startTime,endTime);
result.put("orderRevenueAndCount", orderRevenueAndCount); result.put("orderRevenueAndCount", orderRevenueAndCount);
//待处理差评订单数
Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId); Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId);
result.put("pendingBadReviewCount", pendingBadReviewCount); result.put("pendingBadReviewCount", pendingBadReviewCount);
//待处理退款/售后数
Integer refundCount = this.baseMapper.selectRefundCount(shopId); Integer refundCount = this.baseMapper.selectRefundCount(shopId);
result.put("refundCount", refundCount); result.put("refundCount", refundCount);
//待处理商家自配送订单数
Integer shopDeliveryPendingCount = this.baseMapper.selectShopDeliveryPendingCount(shopId); Integer shopDeliveryPendingCount = this.baseMapper.selectShopDeliveryPendingCount(shopId);
result.put("shopDeliveryPendingCount", shopDeliveryPendingCount == null ? 0 : shopDeliveryPendingCount); result.put("shopDeliveryPendingCount", shopDeliveryPendingCount == null ? 0 : shopDeliveryPendingCount);
//待处理中转配送订单数
Integer transferDeliveryPendingCount = this.baseMapper.selectTransferDeliveryPendingCount(shopId); Integer transferDeliveryPendingCount = this.baseMapper.selectTransferDeliveryPendingCount(shopId);
result.put("transferDeliveryPendingCount", transferDeliveryPendingCount == null ? 0 : transferDeliveryPendingCount); result.put("transferDeliveryPendingCount", transferDeliveryPendingCount == null ? 0 : transferDeliveryPendingCount);
return result; return result;
@ -1847,6 +1860,13 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
this.update(uw); this.update(uw);
} }
private void updateOrderStatusForDaoDian(String orderId, int status) {
LambdaUpdateWrapper<MallOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallOrder::getId, orderId).set(MallOrder::getStatus, status)
.set(MallOrder::getShopMakeTime,new Date());
this.update(uw);
}
/** /**
* 更新订单状态为商家拒绝退款 * 更新订单状态为商家拒绝退款
*/ */
@ -2144,7 +2164,7 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
mallRefundRecordMapper.insert(record); mallRefundRecordMapper.insert(record);
//回退优惠券 //回退优惠券
mallUserCouponService.refundCoupon(order.getId()); mallUserCouponService.refundCoupon(order.getId());
wechatPayUtil.refund(order.getId(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue()); mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, order.getTotalAmount());
List<MallOrderGoods> goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId()); List<MallOrderGoods> goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId());
if (goodsList == null || goodsList.isEmpty()) return; if (goodsList == null || goodsList.isEmpty()) return;
List<MallReturnOrderGoods> returnList = goodsList.stream() List<MallReturnOrderGoods> returnList = goodsList.stream()

34
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java

@ -89,6 +89,9 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private WechatPayUtil wechatPayUtil;
@Autowired
private MallOrderDeliveryFeeRefundHelper mallOrderDeliveryFeeRefundHelper;
@Autowired @Autowired
WorkerOrderCacheUtil workerOrderCacheUtil; WorkerOrderCacheUtil workerOrderCacheUtil;
@ -333,7 +336,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
BigDecimal totalRefund = result.getRecords().stream() BigDecimal totalRefund = result.getRecords().stream()
.map(item -> item.getRefundAmount()) // 提取 BigDecimal 金额 .map(item -> item.getRefundAmount()) // 提取 BigDecimal 金额
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
wechatPayUtil.refund(mallRefundRecord.getOrderId(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue(), totalRefund.multiply(new BigDecimal(100)).longValue()); mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, totalRefund);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额 //配送员同意退款。扣掉余额
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@ -451,7 +454,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
}*/ }*/
wechatPayUtil.refund(mallRefundRecord.getOrderId(),order.getTotalAmount().multiply(new BigDecimal(100)).longValue(), mallRefundRecord.getRefundAmount().multiply(new BigDecimal(100)).longValue()); refundWechatForRefundRecord(order, mallRefundRecord);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
//配送员同意退款。扣掉余额 //配送员同意退款。扣掉余额
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@ -614,7 +617,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
workerOrderCacheUtil.remove(deliveryOrder.getWorkerId(),deliveryOrder.getId()); workerOrderCacheUtil.remove(deliveryOrder.getWorkerId(),deliveryOrder.getId());
} }
} }
wechatPayUtil.refund(mallRefundRecord.getOrderId(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue(), mallRefundRecord.getRefundAmount().multiply(new BigDecimal(100)).longValue()); refundWechatForRefundRecord(order, mallRefundRecord);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
BigDecimal refundAmount = mallRefundRecord.getRefundAmount(); BigDecimal refundAmount = mallRefundRecord.getRefundAmount();
@ -648,7 +651,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
//同意售后 //同意售后
MallOrder order = mallOrderService.getById(mallRefundRecord.getOrderId()); MallOrder order = mallOrderService.getById(mallRefundRecord.getOrderId());
updateOrderStatus(mallRefundRecord.getOrderId(), STATUS_LAST_REFUNDED); updateOrderStatus(mallRefundRecord.getOrderId(), STATUS_LAST_REFUNDED);
wechatPayUtil.refund(mallRefundRecord.getOrderId(), order.getTotalAmount().multiply(new BigDecimal(100)).longValue(), mallRefundRecord.getRefundAmount().multiply(new BigDecimal(100)).longValue()); refundWechatForRefundRecord(order, mallRefundRecord);
if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){ if(mallRefundRecord.getLinkId().toUpperCase().startsWith("W")){
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId()); Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
BigDecimal refundAmount = mallRefundRecord.getRefundAmount(); BigDecimal refundAmount = mallRefundRecord.getRefundAmount();
@ -694,6 +697,29 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
} }
} }
private void refundWechatForRefundRecord(MallOrder order, MallRefundRecord mallRefundRecord) {
if (shouldRefundAddedDeliveryFee(mallRefundRecord)) {
mallOrderDeliveryFeeRefundHelper.refundOrderWithAddedDeliveryFee(order, mallRefundRecord.getRefundAmount());
} else {
mallOrderDeliveryFeeRefundHelper.refundOrder(order, mallRefundRecord.getRefundAmount());
}
}
private boolean shouldRefundAddedDeliveryFee(MallRefundRecord mallRefundRecord) {
if (mallRefundRecord == null) {
return false;
}
boolean isWorkerRefund = mallRefundRecord.getLinkId() != null
&& mallRefundRecord.getLinkId().toUpperCase().startsWith("W");
if (mallRefundRecord.getRefundTypeStatus() != null && mallRefundRecord.getRefundTypeStatus() == 3) {
return isWorkerRefund;
}
if (mallRefundRecord.getRefundType() != null && mallRefundRecord.getRefundType() == 2) {
return true;
}
return mallRefundRecord.getRefundType() != null && mallRefundRecord.getRefundType() == 3;
}
/** /**
* 更新订单状态 * 更新订单状态
*/ */

118
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/MallOrderDeliveryFeeRefundHelper.java

@ -0,0 +1,118 @@
package cc.hiver.mall.utils;
import cc.hiver.mall.dao.mapper.MallOrderDeliveryFeeRecordMapper;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.entity.MallOrderDeliveryFeeRecord;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Component
public class MallOrderDeliveryFeeRefundHelper {
private static final int DELIVERY_TYPE_EXPRESS = 1;
private static final int REFUND_STATUS_WAIT = 0;
private static final int REFUND_STATUS_DONE = 1;
@Autowired
private MallOrderDeliveryFeeRecordMapper mallOrderDeliveryFeeRecordMapper;
@Autowired
private WechatPayUtil wechatPayUtil;
public void recordAddedDeliveryFee(String orderId, String wxOrderId, BigDecimal amount) {
MallOrderDeliveryFeeRecord record = new MallOrderDeliveryFeeRecord();
record.setOrderId(orderId);
record.setWxOrderId(wxOrderId);
record.setAmount(amount);
record.setRefundStatus(REFUND_STATUS_WAIT);
record.setCreateTime(new Date());
mallOrderDeliveryFeeRecordMapper.insert(record);
}
public boolean existsAddedDeliveryFee(String wxOrderId) {
LambdaQueryWrapper<MallOrderDeliveryFeeRecord> qw = new LambdaQueryWrapper<>();
qw.eq(MallOrderDeliveryFeeRecord::getWxOrderId, wxOrderId).last("LIMIT 1");
return mallOrderDeliveryFeeRecordMapper.selectOne(qw) != null;
}
public void refundOrder(MallOrder order, BigDecimal refundAmount) {
refundOrder(order, refundAmount, false);
}
public void refundOrderWithAddedDeliveryFee(MallOrder order, BigDecimal refundAmount) {
refundOrder(order, refundAmount, true);
}
private void refundOrder(MallOrder order, BigDecimal refundAmount, boolean refundAddedDeliveryFee) {
if (!isDeliveryOrder(order)) {
wechatPayUtil.refund(order.getId(), toCent(order.getTotalAmount()), toCent(refundAmount));
return;
}
List<MallOrderDeliveryFeeRecord> records = selectAddedDeliveryFeeRecords(order.getId());
if (records == null || records.isEmpty()) {
wechatPayUtil.refund(order.getId(), toCent(order.getTotalAmount()), toCent(refundAmount));
return;
}
BigDecimal addedTotal = records.stream()
.map(MallOrderDeliveryFeeRecord::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal mainOrderTotal = subtractNonNegative(order.getTotalAmount(), addedTotal);
BigDecimal mainRefundAmount = refundAddedDeliveryFee
? subtractNonNegative(refundAmount, addedTotal)
: refundAmount;
if (mainRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
wechatPayUtil.refund(order.getId(), toCent(mainOrderTotal), toCent(mainRefundAmount));
}
if (refundAddedDeliveryFee) {
refundAddedDeliveryFeeRecords(records);
}
}
private List<MallOrderDeliveryFeeRecord> selectAddedDeliveryFeeRecords(String orderId) {
LambdaQueryWrapper<MallOrderDeliveryFeeRecord> qw = new LambdaQueryWrapper<>();
qw.eq(MallOrderDeliveryFeeRecord::getOrderId, orderId);
return mallOrderDeliveryFeeRecordMapper.selectList(qw);
}
private void refundAddedDeliveryFeeRecords(List<MallOrderDeliveryFeeRecord> records) {
for (MallOrderDeliveryFeeRecord record : records) {
if (record.getRefundStatus() != null && record.getRefundStatus() == REFUND_STATUS_DONE) {
continue;
}
Boolean success = wechatPayUtil.refundDeliveryFeeRecord(record.getWxOrderId(), toCent(record.getAmount()), toCent(record.getAmount()));
if (Boolean.TRUE.equals(success)) {
LambdaUpdateWrapper<MallOrderDeliveryFeeRecord> uw = new LambdaUpdateWrapper<>();
uw.eq(MallOrderDeliveryFeeRecord::getId, record.getId())
.set(MallOrderDeliveryFeeRecord::getRefundStatus, REFUND_STATUS_DONE)
.set(MallOrderDeliveryFeeRecord::getRefundTime, new Date());
mallOrderDeliveryFeeRecordMapper.update(null, uw);
}
}
}
private boolean isDeliveryOrder(MallOrder order) {
return order != null && order.getDeliveryType() != null && order.getDeliveryType() == DELIVERY_TYPE_EXPRESS;
}
private BigDecimal subtractNonNegative(BigDecimal amount, BigDecimal subtractAmount) {
BigDecimal result = safeAmount(amount).subtract(safeAmount(subtractAmount));
return result.compareTo(BigDecimal.ZERO) > 0 ? result : BigDecimal.ZERO;
}
private long toCent(BigDecimal amount) {
return safeAmount(amount).multiply(new BigDecimal(100)).longValue();
}
private BigDecimal safeAmount(BigDecimal amount) {
return amount == null ? BigDecimal.ZERO : amount;
}
}

25
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java

@ -28,16 +28,18 @@ public class WechatPayUtil {
private final OkHttpClient httpClient = new OkHttpClient(); private final OkHttpClient httpClient = new OkHttpClient();
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
public Boolean refund(String orderId,long totalFee,long refundFee) { public Boolean refund(String orderId,long totalFee,long refundFee) {
try { return refundByOutTradeNo("ORDER_" + orderId, "REFUND_" + orderId + "_" + System.currentTimeMillis(), totalFee, refundFee);
// 2. 构建退款请求参数 }
// 原支付交易号(与下单时一致,带 ORDER_ 前缀)
String outTradeNo = "ORDER_" + orderId;
// 退款单号(唯一,使用 REFUND_ + orderId + 时间戳避免重复)
String outRefundNo = "REFUND_" + orderId + "_" + System.currentTimeMillis();
public Boolean refundDeliveryFeeRecord(String wxOrderId, long totalFee, long refundFee) {
return refundByOutTradeNo(wxOrderId, "REFUND_" + wxOrderId + "_" + System.currentTimeMillis(), totalFee, refundFee);
}
private Boolean refundByOutTradeNo(String outTradeNo, String outRefundNo, long totalFee, long refundFee) {
try {
// 退款原因 // 退款原因
String reason = "订单退款"; String reason = "订单退款";
// 3. 构建请求 Body // 构建请求 Body
Map<String, Object> reqBody = new HashMap<>(); Map<String, Object> reqBody = new HashMap<>();
reqBody.put("out_trade_no", outTradeNo); reqBody.put("out_trade_no", outTradeNo);
reqBody.put("out_refund_no", outRefundNo); reqBody.put("out_refund_no", outRefundNo);
@ -51,11 +53,10 @@ public class WechatPayUtil {
String jsonBody = objectMapper.writeValueAsString(reqBody); String jsonBody = objectMapper.writeValueAsString(reqBody);
// 4. 签名并调用微信退款 API // 签名并调用微信退款 API
String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
String authorization = signer.sign("POST", refundUrl, jsonBody); String authorization = signer.sign("POST", refundUrl, jsonBody);
// 【修改点】使用标准的 create 方法,参数顺序是 (MediaType, String)
okhttp3.RequestBody body = okhttp3.RequestBody.create( okhttp3.RequestBody body = okhttp3.RequestBody.create(
MediaType.parse("application/json; charset=utf-8"), MediaType.parse("application/json; charset=utf-8"),
jsonBody jsonBody
@ -75,16 +76,16 @@ public class WechatPayUtil {
if (response.isSuccessful()) { if (response.isSuccessful()) {
// 退款申请成功(注意:微信退款是异步的,此处只表示申请提交成功) // 退款申请成功(注意:微信退款是异步的,此处只表示申请提交成功)
log.info("微信退款申请成功, orderId={}, outRefundNo={}, response={}", orderId, outRefundNo, responseBody); log.info("微信退款申请成功, outTradeNo={}, outRefundNo={}, response={}", outTradeNo, outRefundNo, responseBody);
return true; return true;
} else { } else {
log.error("微信退款申请失败, orderId={}, httpCode={}, response={}", orderId, response.code(), responseBody); log.error("微信退款申请失败, outTradeNo={}, httpCode={}, response={}", outTradeNo, response.code(), responseBody);
return false; return false;
} }
} }
} catch (Exception e) { } catch (Exception e) {
log.error("微信退款异常, orderId={}", orderId, e); log.error("微信退款异常, outTradeNo={}", outTradeNo, e);
return false; return false;
} }
} }

12
hiver-modules/hiver-mall/src/main/resources/db/mall_order_delivery_fee_record.sql

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `mall_order_delivery_fee_record` (
`id` varchar(64) NOT NULL COMMENT '主键ID',
`order_id` varchar(64) NOT NULL COMMENT '关联核心订单ID',
`wx_order_id` varchar(128) NOT NULL COMMENT '微信支付商户订单号',
`amount` decimal(10,2) NOT NULL COMMENT '追加配送佣金金额',
`refund_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '退款状态 0未退 1已退',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`refund_time` datetime DEFAULT NULL COMMENT '退款时间',
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`),
UNIQUE KEY `uk_wx_order_id` (`wx_order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单追加配送佣金记录表';
Loading…
Cancel
Save