From d66010a830f7932cbc3b43495b1c9145d1ade872 Mon Sep 17 00:00:00 2001 From: limqsh <540344226@qq.com> Date: Wed, 3 Jun 2026 16:40:06 +0800 Subject: [PATCH] =?UTF-8?q?fix=20-=20=E7=94=A8=E6=88=B7=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E4=BC=9A=E6=9C=89=E5=A4=9A=E4=B8=AA=E8=AE=A2=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5=E4=BC=98=E5=8C=96=E3=80=82=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=B9=82=E7=AD=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sv/api/controller/VenueController.java | 11 +- .../impl/LoginRegisterServiceImpl.java | 9 +- .../test/java/com/sv/wx/MakeBarcodeTest.java | 39 +++++++ .../test/java/com/sv/wx/WxServiceTest.java | 6 +- .../java/com/sv/dto/BasketEnterResult.java | 13 +++ .../com/sv/dto/api/BasketballPayResult.java | 42 +++++++ oms/src/main/java/com/OmsApplication.java | 2 +- .../main/java/com/sv/mapper/OrderMapper.java | 5 + .../service/api/MemberCardOrderService.java | 7 +- .../api/MemberLessonTicketService.java | 29 ++--- .../java/com/sv/service/api/OrderService.java | 103 ++++++++++++------ .../com/sv/service/api/VenueEnterService.java | 72 ++++++++++-- .../service/api/config/WechatPayService.java | 44 ++++++++ .../com/sv/service/common/MemberKeyLock.java | 19 ++++ .../com/sv/service/oms/MemberService.java | 6 +- .../sv/service/oms/VenueLessonService.java | 5 +- .../mybatis/mapper/sv/OrderMapper.xml | 16 +++ 17 files changed, 362 insertions(+), 66 deletions(-) create mode 100644 api/src/test/java/com/sv/wx/MakeBarcodeTest.java create mode 100644 entity/src/main/java/com/sv/dto/api/BasketballPayResult.java create mode 100644 service/src/main/java/com/sv/service/common/MemberKeyLock.java diff --git a/api/src/main/java/com/sv/api/controller/VenueController.java b/api/src/main/java/com/sv/api/controller/VenueController.java index a494e54..fdb55c7 100644 --- a/api/src/main/java/com/sv/api/controller/VenueController.java +++ b/api/src/main/java/com/sv/api/controller/VenueController.java @@ -3,6 +3,7 @@ package com.sv.api.controller; import com.enums.EnterEnum; import com.enums.PayTypeEnum; import com.sv.dto.BasketEnterResult; +import com.sv.dto.api.BasketballPayResult; import com.sv.entity.Device; import com.sv.entity.Order; import com.sv.entity.Venue; @@ -204,7 +205,7 @@ public class VenueController extends BaseApiController { } /** - * 篮球入场下订单 + * 篮球入场下订单(防重复下单) */ @RequestMapping(value = "/venue/basketball/pay", method = RequestMethod.POST) @AccessToken @@ -219,8 +220,14 @@ public class VenueController extends BaseApiController { throw new ServiceException("有人正在使用门禁,请稍后再试"); } Integer memberId = getMemberIdByAccessToken(); + BasketballPayResult result = orderService.createEnterVenueOrder(venueId, memberId, enterFlag, PayTypeEnum.WEI_XIN, new BigDecimal(payMoney)); + if (result.getPaidFlag()) { + return ResponseDTO.ok().addAttribute("paid_flag", true).addAttribute("order_sn", result.getOrderSn()); + } return ResponseDTO.ok(). - addAttribute("pay",orderService.createEnterVenueOrder(venueId, memberId, enterFlag, PayTypeEnum.WEI_XIN, new BigDecimal(payMoney))); + addAttribute("paid_flag", false). + addAttribute("order_sn", result.getOrderSn()). + addAttribute("pay", result.getWechatPayParam()); } /** diff --git a/api/src/main/java/com/sv/api/service/impl/LoginRegisterServiceImpl.java b/api/src/main/java/com/sv/api/service/impl/LoginRegisterServiceImpl.java index 3cd6cd8..aececd9 100644 --- a/api/src/main/java/com/sv/api/service/impl/LoginRegisterServiceImpl.java +++ b/api/src/main/java/com/sv/api/service/impl/LoginRegisterServiceImpl.java @@ -17,6 +17,7 @@ import com.sv.service.api.MemberService; import com.sv.service.api.MemberTokenService; import com.sv.service.common.ApiConstants; import com.sv.service.common.CaptchaCacheServiceImpl; +import com.sv.service.common.MemberKeyLock; import com.sv.service.common.OSSClientUtil; import com.ydd.framework.core.common.utils.ValidationUtils; import com.ydd.framework.core.entity.enums.DeletedEnum; @@ -64,6 +65,8 @@ public class LoginRegisterServiceImpl extends BaseServiceImpl { private OSSClientUtil ossClientUtil; @Resource private WechatService wechatService; + @Resource + private MemberKeyLock memberKeyLock; /** * 手机号注册 @@ -137,7 +140,7 @@ public class LoginRegisterServiceImpl extends BaseServiceImpl { public MemberTokenDTO loginWithXcx(String openId){ MemberTokenDTO memberTokenDTO = null; Integer platformId = PlatformContext.getPlatFormValue(); - synchronized (openId.intern()){ + synchronized (memberKeyLock.getLock(openId)){ // 判断用户是否注册过,用微信登录过 MemberAuth memberAuth = memberAuthService.findByAuthId(openId); // 登录过,则直接登录成功 @@ -166,7 +169,7 @@ public class LoginRegisterServiceImpl extends BaseServiceImpl { throw new ServiceException("token过期"); } MemberTokenDTO memberTokenDTO = null; - synchronized (openId){ + synchronized (memberKeyLock.getLock(memberId)){ // 1.根据openId,查找登录表,是否有记录,无,直接登录,插入信息到登录表 Member member = memberService.findById(memberId); // memberService.verify(member); @@ -364,7 +367,7 @@ public class LoginRegisterServiceImpl extends BaseServiceImpl { String mobile = wechatResult.getPhoneNumber(); MemberTokenDTO memberTokenDTO = null; - synchronized (openId.intern()){ + synchronized (memberKeyLock.getLock(openId)){ Member member = memberService.findByMobile(mobile); if (member == null) { MemberAuth memberAuth = memberAuthService.findByAuthId(openId); diff --git a/api/src/test/java/com/sv/wx/MakeBarcodeTest.java b/api/src/test/java/com/sv/wx/MakeBarcodeTest.java new file mode 100644 index 0000000..f1be488 --- /dev/null +++ b/api/src/test/java/com/sv/wx/MakeBarcodeTest.java @@ -0,0 +1,39 @@ +package com.sv.wx; + +import com.WeiXinApplication; +import com.enums.EnterEnum; +import com.enums.VenueTypeEnum; +import com.sv.entity.Constants; +import com.sv.entity.Venue; +import com.sv.entity.VenueLesson; +import com.sv.mapper.WxConfigMapper; +import com.sv.service.api.VenueLessonService; +import com.sv.service.api.VenueService; +import com.sv.service.api.util.DateUtilCard; +import com.sv.service.common.BarcodeService; +import com.sv.service.message.SendMsg; +import com.sv.service.message.WeiXinSendUtils; +import com.ydd.oms.entity.sys.WxConfig; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes={WeiXinApplication.class}) +public class MakeBarcodeTest { + + @Resource + BarcodeService barcodeService; + + @Test + public void testTask(){ + barcodeService.newBarcode("123123","2312313123", EnterEnum.ENTER, 1, 1); + } + + +} diff --git a/api/src/test/java/com/sv/wx/WxServiceTest.java b/api/src/test/java/com/sv/wx/WxServiceTest.java index edc95aa..0e833d7 100644 --- a/api/src/test/java/com/sv/wx/WxServiceTest.java +++ b/api/src/test/java/com/sv/wx/WxServiceTest.java @@ -32,8 +32,8 @@ public class WxServiceTest { public void refund(){ // wechatPayService.createUnifiedOrder("testlmq20231224",new BigDecimal(0.02),"127.0.0.1","JSAPI", 535); // 根据orderSn查出对应订单信息 - String orderSn = "231231014749545739"; - Integer memberId = 535; + String orderSn = "260603140301802382"; + Integer memberId = 1; Order order = orderService.findOrderSn(orderSn,memberId); if (order == null){ throw new ServiceException(com.sv.exception.api.ExceptionCodeTemplate.ORDER_ERROR); @@ -49,7 +49,7 @@ public class WxServiceTest { memberRefund.setOutRefundNo(orderService.createSn());//商户退款单号 memberRefund.setPlatformId(1); memberRefundMapper.insert(memberRefund); - wechatPayService.refundInputMoney(memberRefund, order.getPrice(), new BigDecimal(0.01)); + wechatPayService.refundInputMoney(memberRefund, order.getPrice(), new BigDecimal(0.02)); } diff --git a/entity/src/main/java/com/sv/dto/BasketEnterResult.java b/entity/src/main/java/com/sv/dto/BasketEnterResult.java index 1b3b444..23fd5e1 100644 --- a/entity/src/main/java/com/sv/dto/BasketEnterResult.java +++ b/entity/src/main/java/com/sv/dto/BasketEnterResult.java @@ -1,5 +1,7 @@ package com.sv.dto; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.io.Serializable; import java.math.BigDecimal; @@ -13,6 +15,17 @@ public class BasketEnterResult implements Serializable { private BigDecimal money; + private String orderSn; + + @JsonProperty("order_sn") + public String getOrderSn() { + return orderSn; + } + + public void setOrderSn(String orderSn) { + this.orderSn = orderSn; + } + public BigDecimal getMoney() { return money; } diff --git a/entity/src/main/java/com/sv/dto/api/BasketballPayResult.java b/entity/src/main/java/com/sv/dto/api/BasketballPayResult.java new file mode 100644 index 0000000..e1c237e --- /dev/null +++ b/entity/src/main/java/com/sv/dto/api/BasketballPayResult.java @@ -0,0 +1,42 @@ +package com.sv.dto.api; + +import com.sv.dto.api.wechat.OrderPaySignResponse; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; + +public class BasketballPayResult implements Serializable { + + private Boolean paidFlag; + + private String orderSn; + + private OrderPaySignResponse.WechatPayParam wechatPayParam; + + @JsonProperty("paid_flag") + public Boolean getPaidFlag() { + return paidFlag; + } + + public void setPaidFlag(Boolean paidFlag) { + this.paidFlag = paidFlag; + } + + @JsonProperty("order_sn") + public String getOrderSn() { + return orderSn; + } + + public void setOrderSn(String orderSn) { + this.orderSn = orderSn; + } + + @JsonProperty("wechatpay") + public OrderPaySignResponse.WechatPayParam getWechatPayParam() { + return wechatPayParam; + } + + public void setWechatPayParam(OrderPaySignResponse.WechatPayParam wechatPayParam) { + this.wechatPayParam = wechatPayParam; + } +} diff --git a/oms/src/main/java/com/OmsApplication.java b/oms/src/main/java/com/OmsApplication.java index 732357e..0f5c255 100644 --- a/oms/src/main/java/com/OmsApplication.java +++ b/oms/src/main/java/com/OmsApplication.java @@ -12,7 +12,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; * @since 2017-06-19 */ @SpringBootApplication -@EnableScheduling +//@EnableScheduling @MapperScan(value = {"com.ydd.oms.mapper","com.sv.mapper"}) public class OmsApplication { diff --git a/service/src/main/java/com/sv/mapper/OrderMapper.java b/service/src/main/java/com/sv/mapper/OrderMapper.java index a158782..1b79012 100644 --- a/service/src/main/java/com/sv/mapper/OrderMapper.java +++ b/service/src/main/java/com/sv/mapper/OrderMapper.java @@ -92,6 +92,11 @@ public interface OrderMapper { Order findLastOrder(@Param("memberId") Integer memberId); + /** + * api 查询用户在当前场馆的待支付篮球订单(近10分钟内) + */ + Order findPendingBasketballOrder(@Param("memberId") Integer memberId, @Param("venueId") Integer venueId); + /** * api 根据订单编号查询总价钱 */ diff --git a/service/src/main/java/com/sv/service/api/MemberCardOrderService.java b/service/src/main/java/com/sv/service/api/MemberCardOrderService.java index 355c2ad..6663457 100644 --- a/service/src/main/java/com/sv/service/api/MemberCardOrderService.java +++ b/service/src/main/java/com/sv/service/api/MemberCardOrderService.java @@ -9,6 +9,7 @@ import com.sv.dto.api.wechat.OrderPaySignResponse; import com.sv.entity.*; import com.sv.mapper.MemberCardOrderMapper; import com.sv.service.api.config.WechatPayService; +import com.sv.service.common.MemberKeyLock; import com.ydd.framework.core.common.Pagination; import com.ydd.framework.core.common.utils.ValidationUtils; import com.ydd.framework.core.exception.ExceptionCodeTemplate; @@ -51,6 +52,8 @@ public class MemberCardOrderService extends BaseServiceImpl { private MemberService memberService; @Resource private MemberCardService memberCardService; + @Resource + private MemberKeyLock memberKeyLock; /** * 创建用户会员卡订单 @@ -138,8 +141,9 @@ public class MemberCardOrderService extends BaseServiceImpl { * @return */ @Transactional - public synchronized OrderPaySignResponse.WechatPayParam createCardOrder(Integer memberId,Integer cardType, + public OrderPaySignResponse.WechatPayParam createCardOrder(Integer memberId,Integer cardType, Integer type,Integer venueId,Integer num,Integer platformId,String ip){ + synchronized (memberKeyLock.getLock(memberId)) { // 判断用户是否登录 if (memberId == null){ throw new ServiceException(ExceptionCodeTemplate.NEED_LOGIN); @@ -183,6 +187,7 @@ public class MemberCardOrderService extends BaseServiceImpl { // 4.调支付 OrderPaySignResponse.WechatPayParam wechatPayParam = createUnifiedOrder(order.getOrderSn(), memberId,ip,"购买商品"); return wechatPayParam; + } } /** diff --git a/service/src/main/java/com/sv/service/api/MemberLessonTicketService.java b/service/src/main/java/com/sv/service/api/MemberLessonTicketService.java index e3f6f0c..9541474 100644 --- a/service/src/main/java/com/sv/service/api/MemberLessonTicketService.java +++ b/service/src/main/java/com/sv/service/api/MemberLessonTicketService.java @@ -10,6 +10,7 @@ import com.sv.entity.*; import com.sv.exception.api.ExceptionCodeTemplate; import com.sv.mapper.MemberLessonTicketMapper; import com.sv.service.api.util.DateUtilCard; +import com.sv.service.common.MemberKeyLock; import com.sv.service.common.RedisLock; import com.sv.service.message.AsyncTaskUtil; import com.sv.service.message.NotifyAdminMsgThread; @@ -73,6 +74,8 @@ public class MemberLessonTicketService extends BaseServiceImpl { private MemberRefundService memberRefundService; @Resource private RedisTemplate redisTemplate; + @Resource + private MemberKeyLock memberKeyLock; /** @@ -175,7 +178,7 @@ public class MemberLessonTicketService extends BaseServiceImpl { * 预约课程,微信下单LessonTicketOrderDTO */ @Transactional - public synchronized OrderPaySignResponse.WechatPayParam createOrder(Integer memberId, LessonTicketOrderDTO lessonTicketOrderDTO, Integer platformId, String ip) { + public OrderPaySignResponse.WechatPayParam createOrder(Integer memberId, LessonTicketOrderDTO lessonTicketOrderDTO, Integer platformId, String ip) { // 判断用户信息 if (memberId == null) { @@ -237,17 +240,16 @@ public class MemberLessonTicketService extends BaseServiceImpl { } OrderPaySignResponse.WechatPayParam wechatPayParam = new OrderPaySignResponse.WechatPayParam(); - synchronized (lessonTicketOrderDTO.getLessonId()) { - // 2.判断票是否足够 - Integer count = venueLessonTicketService.findCount(lessonTicketOrderDTO.getLessonId()); - if (count.intValue() < lessonTicketOrderDTO.getNum().intValue()) { - throw new ServiceException(com.sv.exception.api.ExceptionCodeTemplate.LESSON_TICKET_ERROR); - } - /** - * 课程库存锁 - */ - RedisLock redisLock = new RedisLock(redisTemplate, LESSON_TICKET_LOCK_KEY + venueLesson.getId()); + // 2.判断票是否足够 + Integer count = venueLessonTicketService.findCount(lessonTicketOrderDTO.getLessonId()); + if (count.intValue() < lessonTicketOrderDTO.getNum().intValue()) { + throw new ServiceException(com.sv.exception.api.ExceptionCodeTemplate.LESSON_TICKET_ERROR); + } + /** + * 课程库存锁 + */ + RedisLock redisLock = new RedisLock(redisTemplate, LESSON_TICKET_LOCK_KEY + venueLesson.getId()); try { //加锁 boolean flag = redisLock.lock(); @@ -298,7 +300,6 @@ public class MemberLessonTicketService extends BaseServiceImpl { redisLock.unlock(); throw new ServiceException(com.sv.exception.api.ExceptionCodeTemplate.SERVER_ERROR); } - } return wechatPayParam; } @@ -313,7 +314,7 @@ public class MemberLessonTicketService extends BaseServiceImpl { * 预约课程,会员卡支付 */ @Transactional - public synchronized String createOrderByMemberCard(Integer memberId, LessonTicketOrderDTO lessonTicketOrderDTO, Integer platformId) { + public String createOrderByMemberCard(Integer memberId, LessonTicketOrderDTO lessonTicketOrderDTO, Integer platformId) { // 判断用户信息 if (memberId == null) { @@ -355,7 +356,7 @@ public class MemberLessonTicketService extends BaseServiceImpl { memberCardService.findByStatus(memberCard.getVenueId(),memberCard.getVeneuType(),memberId,memberCard); }*/ String orderSn = ""; - synchronized (lessonTicketOrderDTO.getLessonId()) { + synchronized (memberKeyLock.getLock(lessonTicketOrderDTO.getLessonId())) { // 2.判断票是否足够 Integer count = venueLessonTicketService.findCount(lessonTicketOrderDTO.getLessonId()); if (count.intValue() < lessonTicketOrderDTO.getNum().intValue()) { diff --git a/service/src/main/java/com/sv/service/api/OrderService.java b/service/src/main/java/com/sv/service/api/OrderService.java index 19b96e8..edbefad 100644 --- a/service/src/main/java/com/sv/service/api/OrderService.java +++ b/service/src/main/java/com/sv/service/api/OrderService.java @@ -2,6 +2,8 @@ package com.sv.service.api; import com.enums.*; import com.github.pagehelper.PageHelper; +import com.dw.ccm.wechat.base.pay.response.OrderResponse; +import com.sv.dto.api.BasketballPayResult; import com.sv.dto.api.wechat.BaseResult; import com.sv.dto.api.wechat.OrderPaySignResponse; import com.sv.entity.*; @@ -11,6 +13,7 @@ import com.sv.mapper.OrderMapper; import com.sv.mapper.VenueMapper; import com.sv.mapper.VenuePriceMapper; import com.sv.service.api.config.WechatPayService; +import com.sv.service.common.MemberKeyLock; import com.ydd.framework.core.common.Pagination; import com.ydd.framework.core.common.utils.RequestUtils; import com.ydd.framework.core.exception.ServiceException; @@ -61,6 +64,10 @@ public class OrderService extends BaseServiceImpl { private VenuePriceMapper venuePriceMapper; @Resource private BarcodeOrderTimeMapper barcodeOrderTimeMapper; + @Resource + private VenueEnterService venueEnterService; + @Resource + private MemberKeyLock memberKeyLock; /** * 创建订单 @@ -244,11 +251,11 @@ public class OrderService extends BaseServiceImpl { } /** - * 生成进场订单 + * 生成进场订单(防重复下单) * @param memberId * @return */ - public OrderPaySignResponse.WechatPayParam createEnterVenueOrder(Integer venueId, Integer memberId, Integer enterFlag,PayTypeEnum payTypeEnum, BigDecimal price){ + public BasketballPayResult createEnterVenueOrder(Integer venueId, Integer memberId, Integer enterFlag,PayTypeEnum payTypeEnum, BigDecimal price){ Venue venue = venueMapper.findById(venueId); if (venue == null) { throw new ServiceException("未找到该场馆"); @@ -256,7 +263,6 @@ public class OrderService extends BaseServiceImpl { Member member = memberService.findById(memberId); memberService.verify(member); - // 判断用户是否使用微信登录 MemberAuth memberAuth = memberAuthService.findByMemberId(memberId); if (memberAuth == null){ throw new ServiceException(ExceptionCodeTemplate.BUY_ERROR); @@ -269,43 +275,76 @@ public class OrderService extends BaseServiceImpl { boolean checkPrice = false; for (VenuePrice venuePrice : priceList) { if (price.divideAndRemainder(price)[1].compareTo(BigDecimal.ZERO) == 0) { - // 取余数为0 checkPrice = true; } } if (!checkPrice) { throw new ServiceException("价格非法!"); } - //创建订单 - Order order = new Order(); - order.setOrderSn(createSn()); - order.setPayType(payTypeEnum.value); - order.setPrice(price); - order.setPayStatus(PayStatusEnum.NOT_PAY.value); - order.setType(OrderTypeEnum.BASKETBALL_ORDER.value); - order.setPlatformId(venue.getPlatformId()); - order.setMemberId(memberId); - order.setParentOrderId(venueId); /** 篮球入场这里输入场馆ID */ - order.setCreatedId(enterFlag); /** 篮球入场这里输入出场结算还是入场结算 */ - orderMapper.insert(order); - //扣除用户余额 目前篮球订单不支持余额支付 -// if(payTypeEnum.value.equals(PayTypeEnum.BALANCE.value)) { -// memberMoneyLogService.create(memberId, venue.getPlatformId(), MoneyLogEnum.JOIN.value, price.negate(), PayTypeEnum.BALANCE.value, null, venue.getId(), venue.getType(), null); -// } - if (EnterEnum.OUT.value == enterFlag) { - // 出场的话,将出场的订单设定 未结算 支付金额为 -1 , 这样批处理不会被结算,需要支付回调处理 - BarcodeOrderTime lastOrder = barcodeOrderTimeMapper.findLastOrder(memberId, venueId); - if (lastOrder != null) { - lastOrder.setPaying(1); - lastOrder.setPayMoney(new BigDecimal(-1)); - lastOrder.setOrderAddSn(order.getOrderSn()); -// lastOrder.setOrderSn(order.getOrderSn()); - lastOrder.setModifiedTime(new Date()); - barcodeOrderTimeMapper.updateByPrimaryKey(lastOrder); + + synchronized (memberKeyLock.getLock(memberId)) { + Order pendingOrder = orderMapper.findPendingBasketballOrder(memberId, venueId); + if (pendingOrder != null) { + OrderResponse wxResponse = wechatPayService.queryWechatOrder(pendingOrder.getOrderSn()); + if (wxResponse != null && wxResponse.isSuccess() && "SUCCESS".equals(wxResponse.getTradeState())) { + pendingOrder.setPayStatus(PayStatusEnum.PAYED.value); + pendingOrder.setTradeSn(wxResponse.getTransactionId()); + pendingOrder.setPayTime(new Date()); + orderMapper.update(pendingOrder); + venueEnterService.paySuccess(pendingOrder); + + BasketballPayResult paidResult = new BasketballPayResult(); + paidResult.setPaidFlag(true); + paidResult.setOrderSn(pendingOrder.getOrderSn()); + return paidResult; + } + + OrderPaySignResponse.WechatPayParam wechatPayParam; + if (pendingOrder.getPrepayId() != null) { + wechatPayParam = wechatPayService.resignPayParam(pendingOrder.getOrderSn(), pendingOrder.getPrepayId()); + } else { + wechatPayParam = memberCardOrderService.createUnifiedOrder(pendingOrder.getOrderSn(), memberId, RequestUtils.getIp(), "入场结算订单"); + } + wechatPayParam.setOrderSn(pendingOrder.getOrderSn()); + + BasketballPayResult result = new BasketballPayResult(); + result.setPaidFlag(false); + result.setOrderSn(pendingOrder.getOrderSn()); + result.setWechatPayParam(wechatPayParam); + return result; } + + Order order = new Order(); + order.setOrderSn(createSn()); + order.setPayType(payTypeEnum.value); + order.setPrice(price); + order.setPayStatus(PayStatusEnum.NOT_PAY.value); + order.setType(OrderTypeEnum.BASKETBALL_ORDER.value); + order.setPlatformId(venue.getPlatformId()); + order.setMemberId(memberId); + order.setParentOrderId(venueId); + order.setCreatedId(enterFlag); + orderMapper.insert(order); + + if (EnterEnum.OUT.value == enterFlag) { + BarcodeOrderTime lastOrder = barcodeOrderTimeMapper.findLastOrder(memberId, venueId); + if (lastOrder != null) { + lastOrder.setPaying(1); + lastOrder.setPayMoney(new BigDecimal(-1)); + lastOrder.setOrderAddSn(order.getOrderSn()); + lastOrder.setModifiedTime(new Date()); + barcodeOrderTimeMapper.updateByPrimaryKey(lastOrder); + } + } + OrderPaySignResponse.WechatPayParam wechatPayParam = memberCardOrderService.createUnifiedOrder(order.getOrderSn(), memberId, RequestUtils.getIp(),"入场结算订单"); + wechatPayParam.setOrderSn(order.getOrderSn()); + + BasketballPayResult result = new BasketballPayResult(); + result.setPaidFlag(false); + result.setOrderSn(order.getOrderSn()); + result.setWechatPayParam(wechatPayParam); + return result; } - OrderPaySignResponse.WechatPayParam wechatPayParam = memberCardOrderService.createUnifiedOrder(order.getOrderSn(), memberId, RequestUtils.getIp(),"入场结算订单"); - return wechatPayParam; } } diff --git a/service/src/main/java/com/sv/service/api/VenueEnterService.java b/service/src/main/java/com/sv/service/api/VenueEnterService.java index c717cbb..46dd66e 100644 --- a/service/src/main/java/com/sv/service/api/VenueEnterService.java +++ b/service/src/main/java/com/sv/service/api/VenueEnterService.java @@ -1,5 +1,6 @@ package com.sv.service.api; +import com.dw.ccm.wechat.base.pay.response.OrderResponse; import com.enums.*; import com.github.pagehelper.PageHelper; import com.sv.dto.BasketEnterResult; @@ -9,6 +10,7 @@ import com.sv.exception.api.ExceptionCodeTemplate; import com.sv.mapper.BarcodeOrderTimeMapper; import com.sv.mapper.OrderMapper; import com.sv.mapper.VenueMapper; +import com.sv.service.api.config.WechatPayService; import com.sv.service.api.util.DateUtilCard; import com.ydd.framework.core.common.Pagination; import com.ydd.framework.core.common.utils.ValidationUtils; @@ -18,6 +20,7 @@ import com.ydd.oms.entity.enums.PayStatusEnum; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -49,6 +52,9 @@ public class VenueEnterService extends BaseServiceImpl { BarcodeOrderTimeMapper barcodeOrderTimeMapper; @Resource OrderMapper orderMapper; + @Lazy + @Resource + private WechatPayService wechatPayService; /** * api 接口 @@ -84,6 +90,30 @@ public class VenueEnterService extends BaseServiceImpl { if (!hasEffOrder) { logger.info(memberId + "&用户创建订单,开始进场,需要支付金额"); if (price.compareTo(BigDecimal.ZERO) > 0) { + Order pendingOrder = orderMapper.findPendingBasketballOrder(memberId, venueId); + if (pendingOrder != null) { + OrderResponse wxResponse = wechatPayService.queryWechatOrder(pendingOrder.getOrderSn()); + if (wxResponse != null && wxResponse.isSuccess() && "SUCCESS".equals(wxResponse.getTradeState())) { + pendingOrder.setPayStatus(PayStatusEnum.PAYED.value); + pendingOrder.setTradeSn(wxResponse.getTransactionId()); + pendingOrder.setPayTime(new Date()); + orderMapper.update(pendingOrder); + paySuccess(pendingOrder); + basketEnterResult.setFlg(0); + basketEnterResult.setOrderSn(pendingOrder.getOrderSn()); + basketEnterResult.setMsg("已支付成功,请入场"); + return basketEnterResult; + } + basketEnterResult.setFlg(2); + basketEnterResult.setOrderSn(pendingOrder.getOrderSn()); + if (PayStyleEnum.HOUR.getValue() == venue.getPayStyle()) { + basketEnterResult.setMsg("您有未完成的订单,请继续支付。预付押金" + price + "元,出门后按分钟结算"); + } else { + basketEnterResult.setMsg("您有未完成的订单,请继续支付。订单金额" + price + "元"); + } + basketEnterResult.setMoney(price); + return basketEnterResult; + } checkOrderPaying(memberId); basketEnterResult.setFlg(2); if (PayStyleEnum.HOUR.getValue() == venue.getPayStyle()) { @@ -101,15 +131,18 @@ public class VenueEnterService extends BaseServiceImpl { createBarcodeTimeOrder(memberId, venueId, timePayHour, "000"); } } else { - // 已有有效订单 - if (lastOrder.getPaying() == 1 && PayStyleEnum.TIME.getValue() == venue.getPayStyle()) { - // 按次计费:10分钟内重新入场,沿用原订单 - lastOrder.setPaying(0); - lastOrder.setModifiedTime(new Date()); - barcodeOrderTimeMapper.updateByPrimaryKey(lastOrder); - logger.info(memberId + "&用户10分钟内重新入场,沿用原订单"); + basketEnterResult.setOrderSn(lastOrder.getOrderSn()); + if (lastOrder.getLastEnter() == null) { + logger.info(memberId + "&支付成功但门禁未开门,允许重试"); } else { - logger.info(memberId + "&用户已在场内,无需重复入场"); + if (lastOrder.getPaying() == 1 && PayStyleEnum.TIME.getValue() == venue.getPayStyle()) { + lastOrder.setPaying(0); + lastOrder.setModifiedTime(new Date()); + barcodeOrderTimeMapper.updateByPrimaryKey(lastOrder); + logger.info(memberId + "&用户10分钟内重新入场,沿用原订单"); + } else { + logger.info(memberId + "&用户已在场内,无需重复入场"); + } } } return basketEnterResult; @@ -150,6 +183,10 @@ public class VenueEnterService extends BaseServiceImpl { } BigDecimal basePrice = venuePriceEnter.getPrice(); + // FIXME orderStart 是支付回调时间(paySuccess→createBarcodeTimeOrder 设的 new Date()) + // 门禁异常场景下用户实际入场时间(lastEnter)可能晚于 orderStart, + // 导致结算时长偏多。如需精准计算,应优先取 lastEnter: + // Date startTime = lastOrder.getLastEnter() != null ? lastOrder.getLastEnter() : lastOrder.getOrderStart(); int minutes = DateUtilCard.diffMinute(lastOrder.getOrderStart(), dateNow); BigDecimal actualCost = basePrice.multiply(new BigDecimal(minutes)) @@ -161,6 +198,25 @@ public class VenueEnterService extends BaseServiceImpl { + difference); if (difference.compareTo(BigDecimal.ZERO) > 0) { + Order pendingOrder = orderMapper.findPendingBasketballOrder(memberId, venueId); + if (pendingOrder != null) { + OrderResponse wxResponse = wechatPayService.queryWechatOrder(pendingOrder.getOrderSn()); + if (wxResponse != null && wxResponse.isSuccess() && "SUCCESS".equals(wxResponse.getTradeState())) { + pendingOrder.setPayStatus(PayStatusEnum.PAYED.value); + pendingOrder.setTradeSn(wxResponse.getTransactionId()); + pendingOrder.setPayTime(new Date()); + orderMapper.update(pendingOrder); + paySuccess(pendingOrder); + result.setFlg(0); + result.setMsg("补交费用已支付成功,出场成功"); + return result; + } + result.setFlg(2); + result.setMoney(difference); + result.setOrderSn(pendingOrder.getOrderSn()); + result.setMsg("您有未完成的补交订单,请继续支付。实际费用" + actualCost + "元,需补交" + difference + "元"); + return result; + } checkOrderPaying(memberId); result.setFlg(2); result.setMoney(difference); diff --git a/service/src/main/java/com/sv/service/api/config/WechatPayService.java b/service/src/main/java/com/sv/service/api/config/WechatPayService.java index 48e5ed2..1dafde3 100644 --- a/service/src/main/java/com/sv/service/api/config/WechatPayService.java +++ b/service/src/main/java/com/sv/service/api/config/WechatPayService.java @@ -53,6 +53,50 @@ public class WechatPayService extends BaseServiceImpl { @Resource private VenueEnterService venueEnterService; + /** + * 查询微信订单状态 + * + * @param orderSn 商户订单号 + * @return OrderResponse + */ + public OrderResponse queryWechatOrder(String orderSn) { + try { + PayConfig payConfig = payConfigService.findKey("face"); + Assert.hasText(payConfig.getCert(), "证书找不到"); + WeChatPayHelper weChatPayHelper = new WeChatPayHelper(payConfig.getCert()); + weChatPayHelper.setAppId(payConfig.getAppId()); + weChatPayHelper.setMchId(payConfig.getMchId()); + weChatPayHelper.setKey(payConfig.getKey()); + return weChatPayHelper.getOrder(null, orderSn, CommonUtils.CreateNonceStr(30)); + } catch (Exception e) { + logger.error("查询微信订单异常", e); + return null; + } + } + + /** + * 根据已存在的订单重新签名支付参数(不调用统一下单) + */ + public OrderPaySignResponse.WechatPayParam resignPayParam(String orderSn, String prepayId) { + PayConfig payConfig = payConfigService.findKey("face"); + OrderPaySignResponse.WechatPayParam wechatPayParam = new OrderPaySignResponse.WechatPayParam(); + Map param = Maps.newHashMap(); + param.put("appId", payConfig.getAppId()); + param.put("nonceStr", CommonUtils.CreateNonceStr(30)); + param.put("package", "prepay_id=" + prepayId); + param.put("signType", "MD5"); + param.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); + param.put("paySign", CommonUtils.getSign(param, payConfig.getKey())); + wechatPayParam.setAppId(param.get("appId")); + wechatPayParam.setPrepayId(prepayId); + wechatPayParam.setTimeStamp(param.get("timeStamp")); + wechatPayParam.setPaySign(param.get("paySign")); + wechatPayParam.setNonceStr(param.get("nonceStr")); + wechatPayParam.setSignType("MD5"); + wechatPayParam.setOrderSn(orderSn); + return wechatPayParam; + } + /** * 创建微信支付的统一订单 * diff --git a/service/src/main/java/com/sv/service/common/MemberKeyLock.java b/service/src/main/java/com/sv/service/common/MemberKeyLock.java new file mode 100644 index 0000000..9b23cae --- /dev/null +++ b/service/src/main/java/com/sv/service/common/MemberKeyLock.java @@ -0,0 +1,19 @@ +package com.sv.service.common; + +import org.springframework.stereotype.Component; + +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class MemberKeyLock { + + private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); + + public Object getLock(Object key) { + return lockMap.computeIfAbsent(key, k -> new Object()); + } + + public void removeLock(Object key) { + lockMap.remove(key); + } +} diff --git a/service/src/main/java/com/sv/service/oms/MemberService.java b/service/src/main/java/com/sv/service/oms/MemberService.java index a96b6a1..91f5634 100644 --- a/service/src/main/java/com/sv/service/oms/MemberService.java +++ b/service/src/main/java/com/sv/service/oms/MemberService.java @@ -10,6 +10,7 @@ import com.sv.mapper.MemberMapper; import com.sv.service.api.MemberAuthService; import com.sv.service.api.MemberTokenService; import com.sv.service.common.MemberCardCommonService; +import com.sv.service.common.MemberKeyLock; import com.sv.service.common.MemberMoney; import com.ydd.framework.core.common.Pagination; import com.ydd.framework.core.common.utils.ValidationUtils; @@ -150,6 +151,9 @@ public class MemberService extends MemberCardCommonService { return memberMapper.updateStatus(ids, status); } + @Resource + private MemberKeyLock memberKeyLock; + /** * 保存用户 */ @@ -157,7 +161,7 @@ public class MemberService extends MemberCardCommonService { ValidationUtils.assertNotBlank(member.getMobile(), "请输入手机号码"); VenueValidateUtils.assertMobile(member.getMobile()); - synchronized (member.getMobile()){ + synchronized (memberKeyLock.getLock(member.getMobile())){ // 根据手机号码创建新用户 Member memberInfo = createByMobile(member); } diff --git a/service/src/main/java/com/sv/service/oms/VenueLessonService.java b/service/src/main/java/com/sv/service/oms/VenueLessonService.java index 66887ee..d49c0ae 100644 --- a/service/src/main/java/com/sv/service/oms/VenueLessonService.java +++ b/service/src/main/java/com/sv/service/oms/VenueLessonService.java @@ -12,6 +12,7 @@ import com.sv.mapper.VenueLessonTicketMapper; import com.sv.service.api.util.DateUtilCard; import com.sv.service.common.OSSClientUtil; import com.sv.service.common.PlatformService; +import com.sv.service.common.MemberKeyLock; import com.sv.service.common.RedisLock; import com.ydd.framework.core.common.Pagination; import com.ydd.framework.core.service.CacheService; @@ -66,6 +67,8 @@ public class VenueLessonService extends BaseServiceImpl { @Resource private OSSClientUtil ossClientUtil; + @Resource + private MemberKeyLock memberKeyLock; /** * 创建场馆课程 @@ -77,7 +80,7 @@ public class VenueLessonService extends BaseServiceImpl { validateLessonDate(venueLesson); RedisLock redisLock = new RedisLock(redisTemplate,LESSON_TICKET_LOCK_KEY+venueLesson.getId()); if (venueLesson.getId() != null && venueLesson.getId() > 0) { - synchronized ("lesson" + venueLesson.getId().toString().intern()) { + synchronized (memberKeyLock.getLock(venueLesson.getId())) { venueLessonMapper.deleteImagesById(venueLesson.getId()); venueLessonTagMapper.deleteByLessonId(venueLesson.getId()); VenueLesson dbVenueLesson = venueLessonMapper.findById(venueLesson.getId()); diff --git a/service/src/main/resources/mybatis/mapper/sv/OrderMapper.xml b/service/src/main/resources/mybatis/mapper/sv/OrderMapper.xml index 7df352a..35ef609 100644 --- a/service/src/main/resources/mybatis/mapper/sv/OrderMapper.xml +++ b/service/src/main/resources/mybatis/mapper/sv/OrderMapper.xml @@ -436,4 +436,20 @@ WHERE deleted = 0 and member_id = #{memberId} order by modified_time desc limit 1 + +