记得上下班打卡 | git大法好,push需谨慎

Commit 0d43204a authored by wangyifan's avatar wangyifan

修复收钱吧订单下单、退款bug;优化逻辑

parent 0b919365
...@@ -8,6 +8,8 @@ import com.liquidnet.commons.lang.util.IDGenerator; ...@@ -8,6 +8,8 @@ import com.liquidnet.commons.lang.util.IDGenerator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.liquidnet.commons.lang.util.JsonUtils; import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*; import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinSqbOrder; import com.liquidnet.service.goblin.entity.GoblinSqbOrder;
...@@ -23,10 +25,12 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -23,10 +25,12 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList;
import java.util.List; import java.util.List;
/** /**
...@@ -115,6 +119,11 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -115,6 +119,11 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
// ================================ 申请退款 ================================ // ================================ 申请退款 ================================
/**
* 申请退款。
* 流程:校验订单状态 → 主单锁定为退款中(61) → 调收钱吧退款API → 成功后子单(61) + 扩展单落库。
* 退款最终完成以收钱吧回调 targetState=20 为准,本方法不主动扣/还库存。
*/
@Override @Override
public ResponseDto<Boolean> refund(String userId, String orderId, String reason) { public ResponseDto<Boolean> refund(String userId, String orderId, String reason) {
log.info("[收钱吧-退款] 开始 userId={}, orderId={}", userId, orderId); log.info("[收钱吧-退款] 开始 userId={}, orderId={}", userId, orderId);
...@@ -175,11 +184,18 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -175,11 +184,18 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
} }
try { try {
List<CouponRefundRequest.RefundItem> refundItems = buildRefundItem(orderSkuVos);
if (CollectionUtils.isEmpty(refundItems)) {
log.error("[收钱吧-退款] 退款商品明细为空, orderId={}", orderId);
syncOrderStatus(orderId, 1);
return ResponseDto.failure("退款商品数据异常");
}
CouponRefundData refundData = sqbBiz.refundCoupon(orderVo.getSqbOrderSn(), CouponRefundData refundData = sqbBiz.refundCoupon(orderVo.getSqbOrderSn(),
orderVo.getSqbOrderSignature(), orderVo.getSqbOrderSignature(),
orderVo.getAmount(), orderVo.getAmount(),
(byte) 2, // 2-按金额退款 (byte) 2, // 2-按金额退款
buildRefundItem(orderSkuVos), // 需要构建退款商品 refundItems,
reason, reason,
buildRequestId(IDGenerator.nextSnowId()) buildRequestId(IDGenerator.nextSnowId())
); );
...@@ -192,15 +208,14 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -192,15 +208,14 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
return ResponseDto.failure("退款失败,请稍后重试"); return ResponseDto.failure("退款失败,请稍后重试");
} }
// 退款成功 // 退款申请成功(最终完成以回调为准)
orderVo.setRefundSn(refundData.getTicketsSn()); orderVo.setRefundSn(refundData.getTicketsSn());
orderVo.setRefundSignature(refundData.getTicketSignature()); orderVo.setRefundSignature(refundData.getTicketSignature());
orderVo.setRefundReason(reason); orderVo.setRefundReason(reason);
orderVo.setUpdatedAt(LocalDateTime.now().format(DTF)); orderVo.setUpdatedAt(LocalDateTime.now().format(DTF));
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo); goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity()); persistRefundSuccess(orderId, orderVo, orderSkuVos, now);
// syncOrderStatus(orderId, 3);
log.info("[收钱吧-退款] 调用收钱吧退款成功,orderId={}", orderId); log.info("[收钱吧-退款] 调用收钱吧退款成功,orderId={}", orderId);
return ResponseDto.success(Boolean.TRUE); return ResponseDto.success(Boolean.TRUE);
...@@ -213,6 +228,39 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -213,6 +228,39 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
} }
} }
/**
* 退款申请成功后,批量持久化子单状态(Redis/Mongo) + 扩展单退款字段 + 子单 status (MySQL)。
* 合并为一次 MQ 消息发送,避免多消息顺序不一致。
*/
private void persistRefundSuccess(String orderId, GoblinSqbOrderVo orderVo, List<GoblinOrderSkuVo> orderSkuVos, String nowStr) {
LocalDateTime nowLdt = LocalDateTime.now();
LinkedList<String> sqls = new LinkedList<>();
sqls.add(SqlMapping.get("goblin_sqb_order.update_refund"));
sqls.add(SqlMapping.get("goblin_order.store.refundSkuPrice"));
LinkedList<Object[]> sqbArgs = new LinkedList<>();
sqbArgs.add(new Object[]{
orderVo.getRefundSn(), orderVo.getRefundSignature(), orderVo.getRefundReason(),
orderVo.getUpdatedAt(), orderId, orderVo.getUpdatedAt(), orderVo.getUpdatedAt()
});
LinkedList<Object[]> skuArgs = new LinkedList<>();
if (!CollectionUtils.isEmpty(orderSkuVos)) {
for (GoblinOrderSkuVo skuVo : orderSkuVos) {
skuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_61.getValue());
skuVo.setPriceRefund(BigDecimal.ZERO);
goblinRedisUtils.setGoblinOrderSku(skuVo.getOrderSkuId(), skuVo);
goblinMongoUtils.updateGoblinOrderSkuVo(skuVo.getOrderSkuId(), skuVo);
skuArgs.add(new Object[]{BigDecimal.ZERO, GoblinStatusConst.Status.ORDER_STATUS_61.getValue(), nowLdt, skuVo.getOrderSkuId(), nowLdt, nowLdt});
}
}
if (queueUtils != null) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
SqlMapping.gets(sqls, sqbArgs, skuArgs));
}
}
private List<CouponRefundRequest.RefundItem> buildRefundItem(List<GoblinOrderSkuVo> orderSkuVos) { private List<CouponRefundRequest.RefundItem> buildRefundItem(List<GoblinOrderSkuVo> orderSkuVos) {
List<CouponRefundRequest.RefundItem> refundItems = new ArrayList<>(); List<CouponRefundRequest.RefundItem> refundItems = new ArrayList<>();
for (GoblinOrderSkuVo orderSkuVo : orderSkuVos) { for (GoblinOrderSkuVo orderSkuVo : orderSkuVos) {
...@@ -504,6 +552,10 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -504,6 +552,10 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
* @param orderId * @param orderId
* @param status * @param status
*/ */
/**
* 将收钱吧内部状态码(0待支付/1已支付/2已核销/3已退款/4退款中)映射为商城主单 status,
* 并同步更新 Redis / MongoDB / MySQL。
*/
private void syncOrderStatus(String orderId, int status) { private void syncOrderStatus(String orderId, int status) {
final int zhengzaiStatus = sqbOrderStatusConvert(status); final int zhengzaiStatus = sqbOrderStatusConvert(status);
...@@ -526,22 +578,23 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -526,22 +578,23 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
// 3. 同步 MySQL (复用下单 MQ 发送 Update 语句) // 3. 同步 MySQL (复用下单 MQ 发送 Update 语句)
String sql; String sql;
Object[] data; Object[] data;
LocalDateTime nowLdt = LocalDateTime.now();
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) { if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
sql = "UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ?"; sql = SqlMapping.get("goblin_sqb.syncStatusPay");
data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId}; data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId, nowLdt, nowLdt};
} else { } else {
sql = "UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ?"; sql = SqlMapping.get("goblin_sqb.syncStatus");
data = new Object[]{zhengzaiStatus, now, orderId}; data = new Object[]{zhengzaiStatus, now, orderId, nowLdt, nowLdt};
} }
java.util.LinkedList<String> sqls = new java.util.LinkedList<>(); LinkedList<String> sqls = new LinkedList<>();
sqls.add(sql); sqls.add(sql);
java.util.LinkedList<Object[]> sqlData = new java.util.LinkedList<>(); LinkedList<Object[]> sqlData = new LinkedList<>();
sqlData.add(data); sqlData.add(data);
if (queueUtils != null) { if (queueUtils != null) {
queueUtils.sendMsgByRedis(com.liquidnet.service.base.constant.MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(), queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
com.liquidnet.service.base.SqlMapping.gets(sqls, sqlData)); SqlMapping.gets(sqls, sqlData));
} }
} }
......
...@@ -205,4 +205,9 @@ goblin_bracelet_order_insert = INSERT INTO `goblin_bracelet_order`(`order_id`, ` ...@@ -205,4 +205,9 @@ goblin_bracelet_order_insert = INSERT INTO `goblin_bracelet_order`(`order_id`, `
gpblin_bracelet_order_update= update goblin_bracelet_order set out_trans_id=?, end_time=?, acct_date=?, price_actual=?, time_pay=?, status=?, pay_status=?, updated_at=? where order_id=? gpblin_bracelet_order_update= update goblin_bracelet_order set out_trans_id=?, end_time=?, acct_date=?, price_actual=?, time_pay=?, status=?, pay_status=?, updated_at=? where order_id=?
goblin_bracelet_order_update_cardno=UPDATE goblin_bracelet_order SET cardno=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_cardno=UPDATE goblin_bracelet_order SET cardno=?,updated_at=? WHERE order_id=?
goblin_bracelet_order_update_fout_trade_no=UPDATE goblin_bracelet_order SET fout_trade_no=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_fout_trade_no=UPDATE goblin_bracelet_order SET fout_trade_no=?,updated_at=? WHERE order_id=?
goblin_bracelet_order_update_refund=UPDATE goblin_bracelet_order SET status=?,refund_status=?,refund_status_note=?,updated_at=? WHERE order_id=? goblin_bracelet_order_update_refund=UPDATE goblin_bracelet_order SET status=?,refund_status=?,refund_status_note=?,updated_at=? WHERE order_id=?
\ No newline at end of file # ---- 收钱吧退款 ----
goblin_order.store.refundSkuPrice=UPDATE goblin_order_sku SET price_refund = ? ,status = ? , updated_at = ? WHERE order_sku_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_sqb_order.update_refund=UPDATE goblin_sqb_order SET sqb_refund_sn = ?, sqb_refund_signature = ?, refund_reason = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_sqb.syncStatus=UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_sqb.syncStatusPay=UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
\ No newline at end of file
...@@ -16,6 +16,8 @@ import com.liquidnet.commons.lang.util.IDGenerator; ...@@ -16,6 +16,8 @@ import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.JsonUtils; import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil; import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst; import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.manage.GoblinSqbOrderParam; import com.liquidnet.service.goblin.dto.manage.GoblinSqbOrderParam;
import com.liquidnet.service.goblin.dto.vo.*; import com.liquidnet.service.goblin.dto.vo.*;
...@@ -142,6 +144,7 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -142,6 +144,7 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
GoblinSqbGoodsExtVo sqbGoodsExt = goblinSqbRedisUtils.getSqbGoodsExt(spuId, skuId); GoblinSqbGoodsExtVo sqbGoodsExt = goblinSqbRedisUtils.getSqbGoodsExt(spuId, skuId);
if (sqbGoodsExt == null) { if (sqbGoodsExt == null) {
log.error("[收钱吧下单] 正在商品对应收钱吧参数为空, spuId: {}, skuId: {}", spuId, skuId); log.error("[收钱吧下单] 正在商品对应收钱吧参数为空, spuId: {}, skuId: {}", spuId, skuId);
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("下单失败"); return ResponseDto.failure("下单失败");
} }
...@@ -185,52 +188,14 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -185,52 +188,14 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
String sqbAcquiringSign = orderData.getAcquiring() != null ? orderData.getAcquiring().getSignature() : null; // 收单密钥 String sqbAcquiringSign = orderData.getAcquiring() != null ? orderData.getAcquiring().getSignature() : null; // 收单密钥
log.info("[收钱吧下单] 创建订单成功,订单编号={}, 收单号={}", sqbOrderSn, sqbAcquiringSn); log.info("[收钱吧下单] 创建订单成功,订单编号={}, 收单号={}", sqbOrderSn, sqbAcquiringSn);
// Step 4.3: queryCashier → 得 selectedSignature + seq // Step 4.3: 创建微信预支付凭据
CashierQueryData cashierData = sqbBiz.queryCashier(sqbAcquiringSn, sqbAcquiringSign, userId); CreateWechatPrepayOrderData.PaymentVoucher pv = createPrepayVoucher(
if (cashierData == null) { sqbAcquiringSn, sqbAcquiringSign, userId, openId, String.valueOf(payAmountFen),
goblinRedisUtils.incrSkuStock(null, skuId, quantity); "[收钱吧下单]");
return ResponseDto.failure("查询收银台失败"); if (pv == null) {
}
String selectedSignature = cashierData.getSelectedSignature(); // 支付工具签名
String seq = cashierData.getSeq(); // 响应唯一序列号
log.info("[收钱吧下单] 查询收银台成功,selectedSignature={}, seq={}", selectedSignature, seq);
List<CashierQueryData.PayTool> payTools = cashierData.getPayTools();
if (payTools.isEmpty()) {
log.error("[收钱吧下单] 支付工具为空");
return ResponseDto.failure("下单失败");
}
CashierQueryData.PayTool payTool = payTools.stream().filter(p ->
p.getType().equals(0)).collect(Collectors.toList()).get(0);
if (null == payTool) {
log.error("[收钱吧下单] 微信支付工具为空");
return ResponseDto.failure("下单失败");
}
// TODO 设置AppID
Map<String, Object> channelExt = new HashMap<>();
channelExt.put("sub_appid", "wx4732efeaa2b08086");
// 预支付金额与结算一致,均用 payAmountFen(分),不使用收银台返回金额
CreateWechatPrepayOrderData prepayData = sqbBiz.createWechatPrepayOrder(sqbAcquiringSn,
sqbAcquiringSign,
userId,
openId,
String.valueOf(payAmountFen),
buildRequestId(IDGenerator.nextSnowId()),
payTool,
channelExt,
selectedSignature,
seq
);
if (prepayData == null || prepayData.getPaymentVoucher() == null) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity); goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("创建微信预支付失败"); return ResponseDto.failure("创建微信预支付失败");
} }
CreateWechatPrepayOrderData.PaymentVoucher pv = prepayData.getPaymentVoucher();
log.info("[收钱吧下单] 创建微信预支付订单成功,timeStamp={}", pv.getTimeStamp()); log.info("[收钱吧下单] 创建微信预支付订单成功,timeStamp={}", pv.getTimeStamp());
final String orderId = IDGenerator.nextSnowId(); final String orderId = IDGenerator.nextSnowId();
...@@ -485,6 +450,11 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -485,6 +450,11 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
// ================================ 回调处理 ================================ // ================================ 回调处理 ================================
/**
* 支付成功回调(orderStateCode=35)。
* 更新主单 status→2(已支付) + 子单 status→2,三方(Redis/Mongo/MySQL)同步。
* 已支付/已完成/已退款等终态幂等跳过。
*/
@Override @Override
public ResponseDto<String> handlePayCallback(OrderCallbackContent orderCallbackContent) { public ResponseDto<String> handlePayCallback(OrderCallbackContent orderCallbackContent) {
log.info("[收钱吧支付回调] 收到回调参数: {}", orderCallbackContent); log.info("[收钱吧支付回调] 收到回调参数: {}", orderCallbackContent);
...@@ -503,7 +473,10 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -503,7 +473,10 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
if (orderVo == null) return ResponseDto.failure("订单不存在"); if (orderVo == null) return ResponseDto.failure("订单不存在");
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId); GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo != null && storeOrderVo.getStatus() != null) { if (storeOrderVo == null) {
return ResponseDto.failure("订单不存在");
}
if (storeOrderVo.getStatus() != null) {
int st = storeOrderVo.getStatus(); int st = storeOrderVo.getStatus();
if (st == GoblinStatusConst.Status.ORDER_STATUS_2.getValue() if (st == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()
|| st == GoblinStatusConst.Status.ORDER_STATUS_4.getValue() || st == GoblinStatusConst.Status.ORDER_STATUS_4.getValue()
...@@ -514,13 +487,40 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -514,13 +487,40 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
} }
String now = LocalDateTime.now().format(DTF); String now = LocalDateTime.now().format(DTF);
LocalDateTime nowLdt = LocalDateTime.now();
orderVo.setUpdatedAt(now); orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo); goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 1); syncOrderStatus(orderId, 1);
// 子单同步 status → 2
if (!CollectionUtils.isEmpty(storeOrderVo.getOrderSkuVoIds())) {
LinkedList<String> sqls = new LinkedList<>();
sqls.add(SqlMapping.get("goblin_order.pay.sku"));
LinkedList<Object[]> skuArgs = new LinkedList<>();
for (String skuVoId : storeOrderVo.getOrderSkuVoIds()) {
GoblinOrderSkuVo skuVo = goblinRedisUtils.getGoblinOrderSkuVo(skuVoId);
if (skuVo == null) continue;
if (Integer.valueOf(GoblinStatusConst.Status.ORDER_STATUS_2.getValue()).equals(skuVo.getStatus())) continue;
skuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_2.getValue());
goblinRedisUtils.setGoblinOrderSku(skuVo.getOrderSkuId(), skuVo);
goblinMongoUtils.updateGoblinOrderSkuVo(skuVo.getOrderSkuId(), skuVo);
skuArgs.add(new Object[]{skuVo.getStatus(), nowLdt, "", skuVo.getOrderSkuId(), nowLdt, nowLdt});
}
if (!skuArgs.isEmpty() && queueUtils != null) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
SqlMapping.gets(sqls, skuArgs));
}
}
log.info("[收钱吧支付回调] orderStateCode=35 主单已支付,orderId={}", orderId); log.info("[收钱吧支付回调] orderStateCode=35 主单已支付,orderId={}", orderId);
return ResponseDto.success("success"); return ResponseDto.success("success");
} }
/**
* 退款完成回调(targetState=20)。
* 主单 status→6(退款通过) + priceRefund→实付金额,子单 status→6 + priceRefund→实付金额,
* 恢复库存,主单/子单/扩展单批量落库 MySQL。
*/
@Override @Override
public ResponseDto<String> handleRefundCallback(RefundCallbackContent refundCallbackContent) { public ResponseDto<String> handleRefundCallback(RefundCallbackContent refundCallbackContent) {
log.info("[收钱吧退款回调] 收到回调参数: {}", refundCallbackContent); log.info("[收钱吧退款回调] 收到回调参数: {}", refundCallbackContent);
...@@ -539,15 +539,71 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -539,15 +539,71 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId); GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) return ResponseDto.failure("订单不存在"); if (orderVo == null) return ResponseDto.failure("订单不存在");
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId); GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo != null && Integer.valueOf(GoblinStatusConst.Status.ORDER_STATUS_6.getValue()).equals(storeOrderVo.getStatus())) { if (storeOrderVo == null) {
return ResponseDto.failure("订单不存在");
}
if (Integer.valueOf(GoblinStatusConst.Status.ORDER_STATUS_6.getValue()).equals(storeOrderVo.getStatus())) {
return ResponseDto.success("success"); return ResponseDto.success("success");
} }
String now = LocalDateTime.now().format(DTF); String now = LocalDateTime.now().format(DTF);
LocalDateTime nowLdt = LocalDateTime.now();
orderVo.setUpdatedAt(now); orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo); goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
// 更新主单退款金额与状态
storeOrderVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_6.getValue());
storeOrderVo.setPriceRefund(storeOrderVo.getPriceActual() != null ? storeOrderVo.getPriceActual() : BigDecimal.ZERO);
// 更新子单退款金额与状态
List<GoblinOrderSkuVo> orderSkuVos = new ArrayList<>();
if (!CollectionUtils.isEmpty(storeOrderVo.getOrderSkuVoIds())) {
for (String skuVoId : storeOrderVo.getOrderSkuVoIds()) {
GoblinOrderSkuVo skuVo = goblinRedisUtils.getGoblinOrderSkuVo(skuVoId);
if (skuVo != null) {
skuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_6.getValue());
skuVo.setPriceRefund(skuVo.getSkuPriceActual() != null ? skuVo.getSkuPriceActual() : BigDecimal.ZERO);
goblinRedisUtils.setGoblinOrderSku(skuVo.getOrderSkuId(), skuVo);
goblinMongoUtils.updateGoblinOrderSkuVo(skuVo.getOrderSkuId(), skuVo);
orderSkuVos.add(skuVo);
}
}
}
// 持久化 Redis / Mongo
goblinRedisUtils.setGoblinOrder(orderId, storeOrderVo);
goblinMongoUtils.updateGoblinStoreOrderVo(orderId, storeOrderVo);
// 恢复库存
goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity()); goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity());
syncOrderStatus(orderId, 3);
// 批量落库 MySQL
LinkedList<String> sqls = new LinkedList<>();
sqls.add(SqlMapping.get("goblin_order.store.refundPrice"));
sqls.add(SqlMapping.get("goblin_order.store.refundSkuPrice"));
sqls.add(SqlMapping.get("goblin_sqb_order.update_refund"));
LinkedList<Object[]> argsOrder = new LinkedList<>();
argsOrder.add(new Object[]{storeOrderVo.getPriceRefund(), storeOrderVo.getStatus(), nowLdt, orderId, nowLdt, nowLdt});
LinkedList<Object[]> argsSku = new LinkedList<>();
for (GoblinOrderSkuVo skuVo : orderSkuVos) {
argsSku.add(new Object[]{skuVo.getPriceRefund(), skuVo.getStatus(), nowLdt, skuVo.getOrderSkuId(), nowLdt, nowLdt});
}
LinkedList<Object[]> argsSqb = new LinkedList<>();
argsSqb.add(new Object[]{
orderVo.getRefundSn() != null ? orderVo.getRefundSn() : "",
orderVo.getRefundSignature() != null ? orderVo.getRefundSignature() : "",
orderVo.getRefundReason() != null ? orderVo.getRefundReason() : "",
now, orderId, now, now
});
if (queueUtils != null) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
SqlMapping.gets(sqls, argsOrder, argsSku, argsSqb));
}
log.info("[收钱吧退款回调] targetState=20 更新退款状态成功,orderId={}", orderId); log.info("[收钱吧退款回调] targetState=20 更新退款状态成功,orderId={}", orderId);
return ResponseDto.success("success"); return ResponseDto.success("success");
} }
...@@ -637,53 +693,27 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -637,53 +693,27 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
return ResponseDto.failure("订单数据异常,缺少收单号"); return ResponseDto.failure("订单数据异常,缺少收单号");
} }
try { if (orderVo.getOpenId() == null || orderVo.getOpenId().trim().isEmpty()) {
if (orderVo.getOpenId() == null || orderVo.getOpenId().trim().isEmpty()) { return ResponseDto.failure("缺少微信 openId,无法拉起支付");
return ResponseDto.failure("缺少微信 openId,无法拉起支付"); }
} Long repayFen = orderVo.getAmount();
// 重新查询收银台,获取新的 selectedSignature + seq if (repayFen == null) {
CashierQueryData cashierData = sqbBiz.queryCashier(orderVo.getSqbAcquiringSn(), orderVo.getSqbAcquiringSign(), userId); return ResponseDto.failure("订单金额异常");
if (cashierData == null) return ResponseDto.failure("查询收银台失败,请稍后重试"); }
List<CashierQueryData.PayTool> payTools = cashierData.getPayTools();
if (payTools.isEmpty()) {
log.error("[收钱吧下单] 支付工具为空");
return ResponseDto.failure("下单失败");
}
CashierQueryData.PayTool payTool = payTools.stream().filter(p ->
p.getType().equals(0)).collect(Collectors.toList()).get(0);
if (null == payTool) {
log.error("[收钱吧下单] 微信支付工具为空");
return ResponseDto.failure("下单失败");
}
// TODO 设置AppID
Map<String, Object> channelExt = new HashMap<>();
channelExt.put("sub_appid", "wx4732efeaa2b08086");
Long repayFen = orderVo.getAmount(); boolean locked = goblinSqbRedisUtils.tryOrderLock(userId, orderVo.getSkuId());
if (repayFen == null) { if (!locked) {
return ResponseDto.failure("订单金额异常"); return ResponseDto.failure("请勿重复操作");
} }
// 重新创建微信预支付(金额与下单时 orderVo.amount 一致,单位:分) try {
CreateWechatPrepayOrderData prepayData = sqbBiz.createWechatPrepayOrder( CreateWechatPrepayOrderData.PaymentVoucher pv = createPrepayVoucher(
orderVo.getSqbAcquiringSn(), orderVo.getSqbAcquiringSn(), orderVo.getSqbAcquiringSign(),
orderVo.getSqbAcquiringSign(), userId, orderVo.getOpenId(), String.valueOf(repayFen),
userId, "[收钱吧再次付款]");
orderVo.getOpenId(), if (pv == null) {
String.valueOf(repayFen), return ResponseDto.failure("再次付款失败,请稍后重试");
buildRequestId(IDGenerator.nextSnowId()),
payTool,
channelExt,
cashierData.getSelectedSignature(),
cashierData.getSeq()
);
if (prepayData == null || prepayData.getPaymentVoucher() == null) {
return ResponseDto.failure("创建微信预支付失败,请稍后重试");
} }
CreateWechatPrepayOrderData.PaymentVoucher pv = prepayData.getPaymentVoucher();
GoblinSqbOrderCreateVo createVo = new GoblinSqbOrderCreateVo(); GoblinSqbOrderCreateVo createVo = new GoblinSqbOrderCreateVo();
createVo.setOrderId(orderId); createVo.setOrderId(orderId);
createVo.setAcquiringSn(orderVo.getSqbAcquiringSn()); createVo.setAcquiringSn(orderVo.getSqbAcquiringSn());
...@@ -700,6 +730,8 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -700,6 +730,8 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
} catch (Exception e) { } catch (Exception e) {
log.error("[收钱吧再次付款] 异常,orderId={}", orderId, e); log.error("[收钱吧再次付款] 异常,orderId={}", orderId, e);
return ResponseDto.failure("再次付款失败:" + e.getMessage()); return ResponseDto.failure("再次付款失败:" + e.getMessage());
} finally {
goblinSqbRedisUtils.releaseOrderLock(userId, orderVo.getSkuId());
} }
} }
...@@ -792,6 +824,44 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -792,6 +824,44 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
return name.length() > 32 ? name.substring(0, 32) : name; return name.length() > 32 ? name.substring(0, 32) : name;
} }
/**
* 查询收银台 → 取微信支付工具 → 调 createWechatPrepayOrder,返回支付凭据。
* 失败返回 null,调用方自行处理错误信息与资源(库存/锁)。
*/
private CreateWechatPrepayOrderData.PaymentVoucher createPrepayVoucher(
String acquiringSn, String acquiringSign, String userId,
String openId, String amountFen, String logPrefix) {
CashierQueryData cashierData = sqbBiz.queryCashier(acquiringSn, acquiringSign, userId);
if (cashierData == null) {
log.error("{} 查询收银台失败", logPrefix);
return null;
}
List<CashierQueryData.PayTool> payTools = cashierData.getPayTools();
if (payTools.isEmpty()) {
log.error("{} 支付工具为空", logPrefix);
return null;
}
CashierQueryData.PayTool payTool = payTools.stream()
.filter(p -> p.getType().equals(0)).collect(Collectors.toList()).get(0);
if (payTool == null) {
log.error("{} 微信支付工具为空", logPrefix);
return null;
}
Map<String, Object> channelExt = new HashMap<>();
channelExt.put("sub_appid", "wx4732efeaa2b08086");
CreateWechatPrepayOrderData prepayData = sqbBiz.createWechatPrepayOrder(
acquiringSn, acquiringSign, userId, openId, amountFen,
buildRequestId(IDGenerator.nextSnowId()), payTool, channelExt,
cashierData.getSelectedSignature(), cashierData.getSeq());
if (prepayData == null || prepayData.getPaymentVoucher() == null) {
log.error("{} 创建微信预支付失败", logPrefix);
return null;
}
return prepayData.getPaymentVoucher();
}
/** /**
* 按主单 goblin_store_order.status 映射为支付轮询用整型(0待支付 1已付 2已完成 3退款 4退款中 5取消) * 按主单 goblin_store_order.status 映射为支付轮询用整型(0待支付 1已付 2已完成 3退款 4退款中 5取消)
...@@ -822,6 +892,9 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -822,6 +892,9 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
return 0; return 0;
} }
/**
* 将收钱吧内部状态码映射为商城主单 status,同步 Redis / MongoDB / MySQL。
*/
private void syncOrderStatus(String orderId, int status) { private void syncOrderStatus(String orderId, int status) {
final int zhengzaiStatus = sqbOrderStatusConvert(status); final int zhengzaiStatus = sqbOrderStatusConvert(status);
...@@ -843,22 +916,23 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -843,22 +916,23 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
// 3. 同步 MySQL (复用下单 MQ 发送 Update 语句) // 3. 同步 MySQL (复用下单 MQ 发送 Update 语句)
String sql; String sql;
Object[] data; Object[] data;
LocalDateTime nowLdt = LocalDateTime.now();
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) { if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
sql = "UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ?"; sql = SqlMapping.get("goblin_sqb.syncStatusPay");
data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId}; data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId, nowLdt, nowLdt};
} else { } else {
sql = "UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ?"; sql = SqlMapping.get("goblin_sqb.syncStatus");
data = new Object[]{zhengzaiStatus, now, orderId}; data = new Object[]{zhengzaiStatus, now, orderId, nowLdt, nowLdt};
} }
java.util.LinkedList<String> sqls = new java.util.LinkedList<>(); LinkedList<String> sqls = new LinkedList<>();
sqls.add(sql); sqls.add(sql);
java.util.LinkedList<Object[]> sqlData = new java.util.LinkedList<>(); LinkedList<Object[]> sqlData = new LinkedList<>();
sqlData.add(data); sqlData.add(data);
if (queueUtils != null) { if (queueUtils != null) {
queueUtils.sendMsgByRedis(com.liquidnet.service.base.constant.MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(), queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
com.liquidnet.service.base.SqlMapping.gets(sqls, sqlData)); SqlMapping.gets(sqls, sqlData));
} }
} }
......
...@@ -78,4 +78,8 @@ dragon_orders.updateOrderStatus=update dragon_orders t set t.status = ? ,t.paym ...@@ -78,4 +78,8 @@ dragon_orders.updateOrderStatus=update dragon_orders t set t.status = ? ,t.paym
dragon_orders.updateNotifyStatus=update dragon_orders t set t.notify_status = ? ,t.updated_at =? where t.code = ? dragon_orders.updateNotifyStatus=update dragon_orders t set t.notify_status = ? ,t.updated_at =? where t.code = ?
#---- \u85CF\u54C1\u8BA2\u5355\u5173\u8054\u85CF\u54C1ID #---- \u85CF\u54C1\u8BA2\u5355\u5173\u8054\u85CF\u54C1ID
goblin_nft_order.update_artwork=UPDATE goblin_nft_order SET artwork_id=? WHERE order_id=? goblin_nft_order.update_artwork=UPDATE goblin_nft_order SET artwork_id=? WHERE order_id=?
# ---- 收钱吧退款回调 ----
goblin_sqb_order.update_refund=UPDATE goblin_sqb_order SET sqb_refund_sn = ?, sqb_refund_signature = ?, refund_reason = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_sqb.syncStatus=UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_sqb.syncStatusPay=UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
#---- #----
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment