|
|
@@ -11,6 +11,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.ylx.common.exception.ServiceException;
|
|
|
import com.ylx.common.utils.DateUtils;
|
|
|
import com.ylx.common.utils.SecurityUtils;
|
|
|
+import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
|
|
|
+import com.ylx.common.weixinPay.service.WxPayV3Service;
|
|
|
import com.ylx.giftCard.domain.GiftCard;
|
|
|
import com.ylx.giftCard.domain.GiftCardOrder;
|
|
|
import com.ylx.giftCard.domain.dto.GiftCardManageQueryDTO;
|
|
|
@@ -26,10 +28,7 @@ import com.ylx.giftCard.domain.vo.GiftCardVO;
|
|
|
import com.ylx.giftCard.mapper.GiftCardMapper;
|
|
|
import com.ylx.giftCard.service.IGiftCardOrderService;
|
|
|
import com.ylx.giftCard.service.IGiftCardService;
|
|
|
-import com.ylx.shopingfundsdetail.domain.vo.ShoppingFundsDetailAddDto;
|
|
|
-import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
-import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
@@ -39,6 +38,7 @@ import java.time.LocalDate;
|
|
|
import java.time.ZoneId;
|
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Slf4j
|
|
|
@@ -48,7 +48,7 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
|
|
|
@Resource
|
|
|
private IGiftCardOrderService giftCardOrderService;
|
|
|
@Resource
|
|
|
- private ShoppingFundsDetailService shoppingFundsDetailService;
|
|
|
+ private WxPayV3Service wxPayV3Service;
|
|
|
|
|
|
private static final int NOT_DELETE = 0;
|
|
|
private static final int PUBLISHED = 1;
|
|
|
@@ -82,59 +82,43 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public boolean purchaseGiftCard(GiftCardPurchaseDTO dto) {
|
|
|
+ public Map<String, Object> purchaseGiftCard(GiftCardPurchaseDTO dto) {
|
|
|
+
|
|
|
+ // 1. 获取当前用户
|
|
|
+ WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser();
|
|
|
+ if (ObjectUtil.isNull(wxLoginUser)) {
|
|
|
+ log.warn("用户未登录,无法创建订单");
|
|
|
+ throw new ServiceException("用户未登录");
|
|
|
+ }
|
|
|
+
|
|
|
Long id = dto.getId();
|
|
|
Integer quantity = dto.getQuantity();
|
|
|
String merchantId = dto.getMerchantId();
|
|
|
|
|
|
+ // 2. 查询并校验购物卡
|
|
|
GiftCard card = this.getById(id);
|
|
|
- if (ObjectUtil.isNull(card)) {
|
|
|
- log.warn("购买失败,购物卡不存在,ID: {}", id);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 2. 校验购物卡状态
|
|
|
- if (card.getIsDelete() != NOT_DELETE) {
|
|
|
- log.warn("购买失败,购物卡已删除,ID: {}", id);
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (card.getIsPublished() != PUBLISHED) {
|
|
|
- log.warn("购买失败,购物卡未上架,ID: {}", id);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 校验库存是否充足
|
|
|
- if (card.getStock() < quantity) {
|
|
|
- log.warn("购买失败,库存不足,ID: {},库存: {},需求数量: {}", id, card.getStock(), quantity);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 扣减库存(使用乐观锁防止超卖)
|
|
|
- String updateExpr = String.format("stock = stock - %d, sales = sales + %d", quantity, quantity);
|
|
|
-
|
|
|
- LambdaUpdateWrapper<GiftCard> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
- updateWrapper.eq(GiftCard::getId, id)
|
|
|
- .eq(GiftCard::getIsDelete, NOT_DELETE)
|
|
|
- .eq(GiftCard::getIsPublished, PUBLISHED)
|
|
|
- .ge(GiftCard::getStock, quantity) // 关键:确保库存充足
|
|
|
- .setSql(updateExpr);
|
|
|
-
|
|
|
- int rowsAffected = this.baseMapper.update(null, updateWrapper);
|
|
|
+ validateGiftCard(card, quantity);
|
|
|
|
|
|
+ // 3. 乐观锁扣减库存(增加状态校验,防止无效更新)
|
|
|
+ int rowsAffected = deductStockOptimisticLock(card.getId(), dto.getQuantity());
|
|
|
if (rowsAffected <= 0) {
|
|
|
log.warn("购买失败,库存不足或商品不存在,购物卡ID: {}", id);
|
|
|
- return false;
|
|
|
+ throw new ServiceException("库存不足或商品状态异常");
|
|
|
}
|
|
|
|
|
|
log.info("购买成功,购物卡ID: {}, 数量: {}", id, quantity);
|
|
|
|
|
|
- // 5. 异步创建订单(传入完整的购物卡对象,避免异步方法中再次查询)
|
|
|
- GiftCardOrder giftCardOrder = createOrderAsync(card, quantity, merchantId);
|
|
|
+ // 4. 创建订单
|
|
|
+ GiftCardOrder order = this.giftCardOrderService.buildOrder(card, quantity, merchantId, wxLoginUser);
|
|
|
+ if(ObjectUtil.isNull(order)){
|
|
|
+ log.warn("购物卡订单创建失败,购物卡ID: {},下单人ID: {}", card.getId(), wxLoginUser.getId());
|
|
|
+ throw new ServiceException("购物卡订单创建失败");
|
|
|
+ }
|
|
|
|
|
|
- // 6. 异步添加购物金明细
|
|
|
- addShoppingFundsDetailAsync(card, giftCardOrder);
|
|
|
+ log.info("购物卡订单创建,购物卡ID: {}, 订单编号: {}", id, order.getOrderNo());
|
|
|
|
|
|
- return true;
|
|
|
+ // 5. 调用微信支付
|
|
|
+ return createWxPayOrder(order, wxLoginUser);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -262,44 +246,72 @@ public class GiftCardServiceImpl extends ServiceImpl<GiftCardMapper, GiftCard> i
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 异步创建订单
|
|
|
+ * 校验购物卡有效性
|
|
|
*/
|
|
|
- @Async
|
|
|
- public GiftCardOrder createOrderAsync(GiftCard card, Integer quantity, String merchantId) {
|
|
|
- try {
|
|
|
- // 注意:异步方法中不要依赖主线程的事务,订单创建失败不应影响库存扣减
|
|
|
- GiftCardOrder order = this.giftCardOrderService.buildOrder(card, quantity, merchantId);
|
|
|
-
|
|
|
- if (ObjectUtil.isNotNull(order)) {
|
|
|
- log.info("购物卡订单创建成功,购物卡ID: {}", card.getId());
|
|
|
- } else {
|
|
|
- log.warn("购物卡订单创建返回为空,购物卡ID: {}", card.getId());
|
|
|
- }
|
|
|
- return order;
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("异步创建订单失败,订单数据: cardId={}, quantity={}, merchantId={}",
|
|
|
- card.getId(), quantity, merchantId, e);
|
|
|
+ private void validateGiftCard(GiftCard card, Integer quantity) {
|
|
|
+ if (ObjectUtil.isNull(card)) {
|
|
|
+ throw new ServiceException("商品不存在");
|
|
|
}
|
|
|
- return null;
|
|
|
+ if (card.getIsDelete() != NOT_DELETE) {
|
|
|
+ log.warn("购买失败,购物卡已删除,ID: {}", card.getId());
|
|
|
+ throw new ServiceException("购物卡已删除");
|
|
|
+ }
|
|
|
+ if (card.getIsPublished() != PUBLISHED) {
|
|
|
+ log.warn("购买失败,购物卡未上架,ID: {}", card.getId());
|
|
|
+ throw new ServiceException("购物卡未上架");
|
|
|
+ }
|
|
|
+ if (card.getStock() < quantity) {
|
|
|
+ log.warn("购买失败,库存不足,ID: {},库存: {},需求数量: {}", card.getId(), card.getStock(), quantity);
|
|
|
+ throw new ServiceException("库存不足");
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 异步新增购物金明细
|
|
|
+ * 乐观锁扣减库存(增加状态条件,确保只更新有效记录)
|
|
|
*/
|
|
|
- @Async
|
|
|
- public void addShoppingFundsDetailAsync(GiftCard card, GiftCardOrder giftCardOrder) {
|
|
|
+ private int deductStockOptimisticLock(Long cardId, Integer quantity) {
|
|
|
+ LambdaUpdateWrapper<GiftCard> wrapper = new LambdaUpdateWrapper<>();
|
|
|
+ wrapper.eq(GiftCard::getId, cardId)
|
|
|
+ .eq(GiftCard::getIsDelete, NOT_DELETE)
|
|
|
+ .eq(GiftCard::getIsPublished, PUBLISHED)
|
|
|
+ .ge(GiftCard::getStock, quantity) // 库存充足时才更新
|
|
|
+ .setSql("stock = stock - " + quantity + ", sales = sales + " + quantity);
|
|
|
+ return baseMapper.update(null, wrapper);
|
|
|
+ }
|
|
|
|
|
|
- try {
|
|
|
- ShoppingFundsDetailAddDto dto = new ShoppingFundsDetailAddDto();
|
|
|
- dto.setGiftCardId(card.getId());
|
|
|
- dto.setAmount(card.getAmount());
|
|
|
- dto.setUserId(giftCardOrder.getUserId());
|
|
|
- dto.setExpenseType(0);
|
|
|
- this.shoppingFundsDetailService.addShoppingFundsDetail(dto);
|
|
|
+ /**
|
|
|
+ * 补偿回滚库存
|
|
|
+ */
|
|
|
+ private void recoverStock(Long cardId, Integer num) {
|
|
|
+ GiftCard updateEntity = new GiftCard();
|
|
|
+ LambdaUpdateWrapper<GiftCard> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
+ updateWrapper.eq(GiftCard::getId, cardId)
|
|
|
+ .eq(GiftCard::getIsDelete, NOT_DELETE);
|
|
|
+ updateWrapper.setSql("stock = stock + #{num}, sales = sales - #{num}");
|
|
|
+ // 数据放到实体里
|
|
|
+ updateEntity.setStock(num);
|
|
|
+ baseMapper.update(updateEntity, updateWrapper);
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * 创建微信支付订单(事务外执行,减少事务时长)
|
|
|
+ */
|
|
|
+ private Map<String, Object> createWxPayOrder(GiftCardOrder order, WxLoginUser wxLoginUser) {
|
|
|
+ try {
|
|
|
+ return wxPayV3Service.createV3JsapiOrder(
|
|
|
+ order.getOrderNo(),
|
|
|
+ order.getPayAmount(),
|
|
|
+ "购物卡购买",
|
|
|
+ wxLoginUser.getCOpenid(),
|
|
|
+ WxPayTypeEnum.GIFT_CARD.getCode()
|
|
|
+ );
|
|
|
} catch (Exception e) {
|
|
|
- log.error("异步新增购物金明细失败,订单数据: cardId={}, amount={}, userId={}",
|
|
|
- card.getId(), card.getAmount(), giftCardOrder.getUserId(), e);
|
|
|
+ log.error("微信支付下单失败,订单号: {}", order.getOrderNo(), e);
|
|
|
+ // 支付失败:恢复库存、修改订单为取消
|
|
|
+ recoverStock(order.getGiftCardId(), order.getPurchaseQuantity());
|
|
|
+ this.giftCardOrderService.cancelOrder(order.getId());
|
|
|
+ throw new ServiceException("支付服务异常,请稍后重试");
|
|
|
}
|
|
|
}
|
|
|
|