| Pass |
- 17:45:43 |
+ 16:55:31 |
Test passed
|
@@ -128,13 +128,13 @@
Started
-
ÁùÔÂ 22, 2026 17:45:41
+
ÁùÔÂ 23, 2026 16:55:30
Ended
-
ÁùÔÂ 22, 2026 17:45:43
+
ÁùÔÂ 23, 2026 16:55:31
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java
index 4f3eac27..e378bef2 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java
+++ b/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.nio.charset.StandardCharsets;
import java.security.PrivateKey;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -110,7 +112,8 @@ public class WechatPayController {
}
if(description.equals("å¢žåŠ é…é€ä½£é‡‘")){
- outTradeNo = "ORDERDE_" + outTradeNo;
+ String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+ outTradeNo = "ORDERDE_" + timestamp.substring(timestamp.length() - 2) + outTradeNo ;
}else{
outTradeNo = "ORDER_" + outTradeNo;
}
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderDeliveryFeeRecordMapper.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderDeliveryFeeRecordMapper.java
new file mode 100644
index 00000000..16376b40
--- /dev/null
+++ b/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 {
+}
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderDeliveryFeeRecord.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderDeliveryFeeRecord.java
new file mode 100644
index 00000000..eee4c548
--- /dev/null
+++ b/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;
+}
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
index 5652332c..e55b37b2 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
+++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
@@ -966,6 +966,9 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId());
if (goodsList == null || goodsList.isEmpty()) return;
List returnList = goodsList.stream()
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
index 643e80fb..b90008e3 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
+++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
@@ -146,6 +146,9 @@ public class MallOrderServiceImpl extends ServiceImpl= 1) {
@@ -1215,7 +1223,7 @@ public class MallOrderServiceImpl extends ServiceImpl countByShop(String shopId) {
Map result = new HashMap();
+ //统计ä¸åŒä¸šåŠ¡ç±»åž‹çš„è®¢å•æ•°é‡
List> orderStatusCount = this.baseMapper.selectOrderStatusCount(shopId);
result.put("orderStatusCount", orderStatusCount);
// 1. èŽ·å–æŒ‡å®šæ—¥æœŸï¼ˆä¾‹å¦‚:今天)
@@ -1262,15 +1271,19 @@ public class MallOrderServiceImpl extends ServiceImpl orderRevenueAndCount = this.baseMapper.selectOrderRevenueAndCount(shopId,startTime,endTime);
result.put("orderRevenueAndCount", orderRevenueAndCount);
+ //待处ç†å·®è¯„è®¢å•æ•°
Integer pendingBadReviewCount = this.baseMapper.selectPendingBadReviewCount(shopId);
result.put("pendingBadReviewCount", pendingBadReviewCount);
+ //待处ç†é€€æ¬¾/å”®åŽæ•°
Integer refundCount = this.baseMapper.selectRefundCount(shopId);
result.put("refundCount", refundCount);
+ //待处ç†å•†å®¶è‡ªé…é€è®¢å•æ•°
Integer shopDeliveryPendingCount = this.baseMapper.selectShopDeliveryPendingCount(shopId);
result.put("shopDeliveryPendingCount", shopDeliveryPendingCount == null ? 0 : shopDeliveryPendingCount);
+ //待处ç†ä¸è½¬é…é€è®¢å•æ•°
Integer transferDeliveryPendingCount = this.baseMapper.selectTransferDeliveryPendingCount(shopId);
result.put("transferDeliveryPendingCount", transferDeliveryPendingCount == null ? 0 : transferDeliveryPendingCount);
return result;
@@ -1847,6 +1860,13 @@ public class MallOrderServiceImpl extends ServiceImpl 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 goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId());
if (goodsList == null || goodsList.isEmpty()) return;
List returnList = goodsList.stream()
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
index 6c88af6a..a531c2e3 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
+++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
@@ -89,6 +89,9 @@ public class MallRefundRecordServiceImpl extends ServiceImpl item.getRefundAmount()) // æå– BigDecimal 金é¢
.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")){
//é…é€å‘˜åŒæ„退款。扣掉余é¢
Worker worker = workerServiceImpl.findByWorkerId(mallRefundRecord.getLinkId());
@@ -451,7 +454,7 @@ public class MallRefundRecordServiceImpl extends ServiceImpl 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 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 selectAddedDeliveryFeeRecords(String orderId) {
+ LambdaQueryWrapper qw = new LambdaQueryWrapper<>();
+ qw.eq(MallOrderDeliveryFeeRecord::getOrderId, orderId);
+ return mallOrderDeliveryFeeRecordMapper.selectList(qw);
+ }
+
+ private void refundAddedDeliveryFeeRecords(List 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 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;
+ }
+}
diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java
index 8268cf99..cfd8848e 100644
--- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java
+++ b/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 ObjectMapper objectMapper = new ObjectMapper();
public Boolean refund(String orderId,long totalFee,long refundFee) {
- try {
- // 2. æž„å»ºé€€æ¬¾è¯·æ±‚å‚æ•°
- // 原支付交易å·ï¼ˆä¸Žä¸‹å•时一致,带 ORDER_ å‰ç¼€ï¼‰
- String outTradeNo = "ORDER_" + orderId;
- // 退款å•å·ï¼ˆå”¯ä¸€ï¼Œä½¿ç”¨ REFUND_ + orderId + 时间戳é¿å…é‡å¤ï¼‰
- String outRefundNo = "REFUND_" + orderId + "_" + System.currentTimeMillis();
+ return refundByOutTradeNo("ORDER_" + orderId, "REFUND_" + orderId + "_" + System.currentTimeMillis(), totalFee, refundFee);
+ }
+ 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 = "订å•退款";
- // 3. 构建请求 Body
+ // 构建请求 Body
Map reqBody = new HashMap<>();
reqBody.put("out_trade_no", outTradeNo);
reqBody.put("out_refund_no", outRefundNo);
@@ -51,11 +53,10 @@ public class WechatPayUtil {
String jsonBody = objectMapper.writeValueAsString(reqBody);
- // 4. ç¾å并调用微信退款 API
+ // ç¾å并调用微信退款 API
String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
String authorization = signer.sign("POST", refundUrl, jsonBody);
- // ã€ä¿®æ”¹ç‚¹ã€‘ä½¿ç”¨æ ‡å‡†çš„ create æ–¹æ³•ï¼Œå‚æ•°é¡ºåºæ˜¯ (MediaType, String)
okhttp3.RequestBody body = okhttp3.RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
jsonBody
@@ -75,16 +76,16 @@ public class WechatPayUtil {
if (response.isSuccessful()) {
// 退款申请æˆåŠŸï¼ˆæ³¨æ„:微信退款是异æ¥çš„,æ¤å¤„åªè¡¨ç¤ºç”³è¯·æäº¤æˆåŠŸï¼‰
- log.info("微信退款申请æˆåŠŸ, orderId={}, outRefundNo={}, response={}", orderId, outRefundNo, responseBody);
+ log.info("微信退款申请æˆåŠŸ, outTradeNo={}, outRefundNo={}, response={}", outTradeNo, outRefundNo, responseBody);
return true;
} else {
- log.error("微信退款申请失败, orderId={}, httpCode={}, response={}", orderId, response.code(), responseBody);
+ log.error("微信退款申请失败, outTradeNo={}, httpCode={}, response={}", outTradeNo, response.code(), responseBody);
return false;
}
}
} catch (Exception e) {
- log.error("微信退款异常, orderId={}", orderId, e);
+ log.error("微信退款异常, outTradeNo={}", outTradeNo, e);
return false;
}
}
diff --git a/hiver-modules/hiver-mall/src/main/resources/db/mall_order_delivery_fee_record.sql b/hiver-modules/hiver-mall/src/main/resources/db/mall_order_delivery_fee_record.sql
new file mode 100644
index 00000000..8950797d
--- /dev/null
+++ b/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='订å•è¿½åŠ é…é€ä½£é‡‘记录表';