From fda9cc17efc907411fa4d059f4ebd99cc45f73e1 Mon Sep 17 00:00:00 2001 From: zk Date: Thu, 14 May 2026 20:59:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E7=9B=B8=E5=85=B3=20=E5=9B=9E=E8=B0=83=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/MemberProductService.java | 101 ++++++++++++++++-- .../WxPayNotifyMessageAbstractImpl.java | 53 +++++++-- 2 files changed, 136 insertions(+), 18 deletions(-) diff --git a/client-api/src/main/java/org/jiayunet/service/MemberProductService.java b/client-api/src/main/java/org/jiayunet/service/MemberProductService.java index 6e24be2..14ae528 100644 --- a/client-api/src/main/java/org/jiayunet/service/MemberProductService.java +++ b/client-api/src/main/java/org/jiayunet/service/MemberProductService.java @@ -7,14 +7,10 @@ import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest; import lombok.extern.slf4j.Slf4j; import org.jiayunet.exception.BusinessException; import org.jiayunet.exception.BusinessExpCodeEnum; -import org.jiayunet.mapper.MemberOrderMapper; -import org.jiayunet.mapper.MemberProductMapper; -import org.jiayunet.mapper.PayWechatFlowMapper; +import org.jiayunet.mapper.*; import org.jiayunet.pojo.dto.memberProduct.CreateOrderDto; import org.jiayunet.pojo.dto.memberProduct.OrderDetailDto; -import org.jiayunet.pojo.po.MemberOrder; -import org.jiayunet.pojo.po.MemberProduct; -import org.jiayunet.pojo.po.PayWechatFlow; +import org.jiayunet.pojo.po.*; import org.jiayunet.tool.UserSecurityTool; import org.jiayunet.wxPay.WxNativePayAbility; import org.springframework.beans.factory.annotation.Autowired; @@ -23,12 +19,14 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; /** * 会员商品服务 - *

依赖:WxNativePayAbility(微信Native支付下单)

- *

使用表:bg_member_product(查询商品)、bg_member_order(创建/查询订单)、bg_pay_wechat_flow(创建支付流水)

+ *

依赖:WxNativePayAbility(微信Native支付下单)、FuncPermissionService(功能权限发放)、RouteMenuService(菜单权限发放)

+ *

使用表:bg_member_product(查询商品)、bg_member_order(创建/查询订单)、bg_pay_wechat_flow(支付流水)、bg_member_user(会员状态)、bg_member_func_item(会员功能配置)、bg_member_route_item(会员菜单配置)

* * @author zk */ @@ -45,6 +43,21 @@ public class MemberProductService { @Autowired private PayWechatFlowMapper payWechatFlowMapper; + @Autowired + private MemberUserMapper memberUserMapper; + + @Autowired + private MemberFuncItemMapper memberFuncItemMapper; + + @Autowired + private MemberRouteItemMapper memberRouteItemMapper; + + @Autowired + private FuncPermissionService funcPermissionService; + + @Autowired + private RouteMenuService routeMenuService; + @Autowired private WxNativePayAbility wxNativePayAbility; @@ -99,7 +112,6 @@ public class MemberProductService { // 4. 调支付渠道下单,拿二维码URL String codeUrl; if (payChannel == 1) { - // 微信Native支付 codeUrl = prepayWechat(orderNo, product); } else { // TODO 支付宝当面付 @@ -113,11 +125,79 @@ public class MemberProductService { return dto; } + /** + * 支付成功回调公共处理 + *

微信回调和支付宝回调统一调用此方法

+ *

1. 查订单校验待支付(幂等) 2. 更新订单已支付 3. 续费会员+发放权限

+ */ + @Transactional(rollbackFor = Exception.class) + public void handlePaySuccess(String orderNo) { + // 查订单,幂等校验 + MemberOrder order = memberOrderMapper.selectOne( + new LambdaQueryWrapper().eq(MemberOrder::getOrderNo, orderNo) + ); + if (order == null) { + log.warn("支付回调订单不存在 orderNo:{}", orderNo); + return; + } + if (order.getStatus() != 0) { + log.info("订单已处理,跳过 orderNo:{} status:{}", orderNo, order.getStatus()); + return; + } + + // 更新订单为已支付 + memberOrderMapper.update(null, new LambdaUpdateWrapper() + .eq(MemberOrder::getId, order.getId()) + .eq(MemberOrder::getStatus, 0) + .set(MemberOrder::getStatus, 1) + .set(MemberOrder::getPayTime, Instant.now())); + + // 查商品拿 durationDays + MemberProduct product = memberProductMapper.selectById(order.getProductId()); + int durationDays = product.getDurationDays(); + Long userId = order.getUserId(); + Instant expireTime = Instant.now().plus(durationDays, ChronoUnit.DAYS); + + // 续费会员 + MemberUser memberUser = memberUserMapper.selectOne( + new LambdaQueryWrapper().eq(MemberUser::getUserId, userId) + ); + if (memberUser == null) { + MemberUser newMember = new MemberUser(); + newMember.setUserId(userId); + newMember.setExpireTime(expireTime); + memberUserMapper.insert(newMember); + } else { + Instant baseTime = memberUser.getExpireTime().isAfter(Instant.now()) ? memberUser.getExpireTime() : Instant.now(); + memberUserMapper.update(null, new LambdaUpdateWrapper() + .eq(MemberUser::getId, memberUser.getId()) + .set(MemberUser::getExpireTime, baseTime.plus(durationDays, ChronoUnit.DAYS))); + } + + // 发放功能权限 + List funcItems = memberFuncItemMapper.selectList(null); + for (MemberFuncItem item : funcItems) { + funcPermissionService.addTimeStock(userId, item.getFuncCode(), 1, expireTime); + if (item.getCountLimit() != null && item.getCountLimit() == 1) { + funcPermissionService.addCountStock(userId, item.getFuncCode(), 1, item.getAddCount()); + } else { + funcPermissionService.addCountStock(userId, item.getFuncCode(), 0, null); + } + } + + // 发放菜单权限 + List routeItems = memberRouteItemMapper.selectList(null); + for (MemberRouteItem item : routeItems) { + routeMenuService.addTimeStock(userId, item.getRouteId(), 1, expireTime); + } + + log.info("会员支付成功处理完成 orderNo:{} userId:{} durationDays:{}", orderNo, userId, durationDays); + } + /** * 微信Native支付下单 */ private String prepayWechat(String orderNo, MemberProduct product) { - // 创建微信支付流水 PayWechatFlow flow = new PayWechatFlow(); flow.setOrderType("member"); flow.setOrderNo(orderNo); @@ -125,7 +205,6 @@ public class MemberProductService { flow.setStatus(0); payWechatFlowMapper.insert(flow); - // 调微信Native下单 PrepayRequest request = new PrepayRequest(); request.setAppid(appId); request.setMchid(merchantId); diff --git a/client-api/src/main/java/org/jiayunet/service/WxPayNotifyMessageAbstractImpl.java b/client-api/src/main/java/org/jiayunet/service/WxPayNotifyMessageAbstractImpl.java index 54777e0..1fbc4c1 100644 --- a/client-api/src/main/java/org/jiayunet/service/WxPayNotifyMessageAbstractImpl.java +++ b/client-api/src/main/java/org/jiayunet/service/WxPayNotifyMessageAbstractImpl.java @@ -1,18 +1,24 @@ package org.jiayunet.service; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction; import com.wechat.pay.java.service.refund.model.RefundNotification; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jiayunet.mapper.PayWechatFlowMapper; +import org.jiayunet.pojo.po.PayWechatFlow; import org.jiayunet.wxPay.WxPayNotifyMessageAbstract; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; + /** * 微信支付回调实现 - *

依赖:WxPayNotifyMessageAbstract(common模块支付回调抽象)

- *

使用表:待对接商品订单表

+ *

依赖:MemberProductService(会员订单处理)、PayWechatFlowMapper(更新支付流水)

+ *

使用表:bg_pay_wechat_flow(更新流水状态)、bg_member_order(通过MemberProductService处理)

* * @author zk */ @@ -22,25 +28,58 @@ import org.springframework.transaction.annotation.Transactional; @AllArgsConstructor public class WxPayNotifyMessageAbstractImpl implements WxPayNotifyMessageAbstract { + private final MemberProductService memberProductService; + private final PayWechatFlowMapper payWechatFlowMapper; /** * 支付回调 - * - * @param transaction 支付回调消息 + *

1. 判断流水状态防重复处理 2. 更新微信支付流水 3. 调用公共订单处理逻辑

*/ @Override @Transactional(rollbackFor = Exception.class) public void payMessageHandle(Transaction transaction) { String outTradeNo = transaction.getOutTradeNo(); + String transactionId = transaction.getTransactionId(); + String tradeState = transaction.getTradeState().name(); + + log.info("微信支付回调 outTradeNo:{} transactionId:{} tradeState:{}", outTradeNo, transactionId, tradeState); + + // 只处理支付成功 + if (!"SUCCESS".equals(tradeState)) { + log.info("微信支付回调非成功状态,跳过 outTradeNo:{} tradeState:{}", outTradeNo, tradeState); + return; + } + + // 1. 查流水,已处理则直接返回(防重复回调) + PayWechatFlow flow = payWechatFlowMapper.selectOne(new LambdaQueryWrapper().eq(PayWechatFlow::getOrderNo, outTradeNo)); + if (flow == null) { + log.warn("微信支付回调流水不存在 outTradeNo:{}", outTradeNo); + return; + } + if (flow.getStatus() != 0) { + log.info("微信支付流水已处理,跳过 outTradeNo:{} status:{}", outTradeNo, flow.getStatus()); + return; + } + + // 2. 更新微信支付流水 + payWechatFlowMapper.update(null, new LambdaUpdateWrapper() + .eq(PayWechatFlow::getId, flow.getId()) + .eq(PayWechatFlow::getStatus, 0) + .set(PayWechatFlow::getStatus, 1) + .set(PayWechatFlow::getTransactionId, transactionId) + .set(PayWechatFlow::getSuccessTime, Instant.now()) + .set(PayWechatFlow::getNotifyData, transaction.toString())); + + // 3. 调用公共订单处理(更新订单状态 + 发放权限) + memberProductService.handlePaySuccess(outTradeNo); } /** * 退款回调 - * - * @param refundNotification 退款回调消息 + * TODO 暂不支持退款 */ @Override public void refundMessageHandle(RefundNotification refundNotification) { - + log.info("微信退款回调 refundId:{}", refundNotification.getOutRefundNo()); } }