You've already forked FrameTour-BE
feat(order): 添加重复购买检测功能
- 新增 DuplicatePurchaseException 类用于处理重复购买异常 - 在 OrderServiceImpl 中实现重复购买检查逻辑 - 更新 CustomExceptionHandle 以处理新的重复购买异常 -优化订单创建流程,在生成订单号前增加重复购买检查
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package com.ycwl.basic.order.exception;
|
||||
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.pricing.enums.ProductType;
|
||||
|
||||
/**
|
||||
* 重复购买异常
|
||||
* 当用户尝试购买已经购买过的内容时抛出此异常
|
||||
*/
|
||||
public class DuplicatePurchaseException extends BaseException {
|
||||
|
||||
private final Long existingOrderId;
|
||||
private final String existingOrderNo;
|
||||
private final ProductType productType;
|
||||
private final String productId;
|
||||
|
||||
public DuplicatePurchaseException(String message, Long existingOrderId, String existingOrderNo,
|
||||
ProductType productType, String productId) {
|
||||
super(message);
|
||||
this.existingOrderId = existingOrderId;
|
||||
this.existingOrderNo = existingOrderNo;
|
||||
this.productType = productType;
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public DuplicatePurchaseException(String message, Long existingOrderId, String existingOrderNo,
|
||||
ProductType productType) {
|
||||
this(message, existingOrderId, existingOrderNo, productType, null);
|
||||
}
|
||||
|
||||
public Long getExistingOrderId() {
|
||||
return existingOrderId;
|
||||
}
|
||||
|
||||
public String getExistingOrderNo() {
|
||||
return existingOrderNo;
|
||||
}
|
||||
|
||||
public ProductType getProductType() {
|
||||
return productType;
|
||||
}
|
||||
|
||||
public String getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取友好的错误消息
|
||||
*/
|
||||
public String getFriendlyMessage() {
|
||||
String productDesc = productType != null ? productType.getDescription() : "商品";
|
||||
if (productId != null) {
|
||||
return String.format("您已购买过该%s(商品ID:%s),订单号:%s", productDesc, productId, existingOrderNo);
|
||||
} else {
|
||||
return String.format("您已购买过%s,订单号:%s", productDesc, existingOrderNo);
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@ import com.ycwl.basic.order.entity.OrderItemV2;
|
||||
import com.ycwl.basic.order.entity.OrderRefundV2;
|
||||
import com.ycwl.basic.order.entity.OrderV2;
|
||||
import com.ycwl.basic.order.enums.*;
|
||||
import com.ycwl.basic.order.exception.DuplicatePurchaseException;
|
||||
import com.ycwl.basic.order.mapper.OrderDiscountMapper;
|
||||
import com.ycwl.basic.order.mapper.OrderItemMapper;
|
||||
import com.ycwl.basic.order.mapper.OrderV2Mapper;
|
||||
@@ -74,10 +75,18 @@ public class OrderServiceImpl implements IOrderService {
|
||||
public Long createOrder(MobileOrderRequest request, Long userId, Long scenicId, PriceCalculationResult priceResult) {
|
||||
Date now = new Date();
|
||||
MemberRespVO member = memberMapper.getById(userId);
|
||||
// 1. 生成订单号
|
||||
|
||||
// 1. 检查重复购买
|
||||
log.info("开始检查重复购买: userId={}, faceId={}, scenicId={}, productCount={}",
|
||||
userId, request.getFaceId(), scenicId, request.getProducts().size());
|
||||
checkDuplicatePurchase(userId, request.getFaceId(), scenicId, request.getProducts());
|
||||
log.info("重复购买检查通过: userId={}, faceId={}, scenicId={}",
|
||||
userId, request.getFaceId(), scenicId);
|
||||
|
||||
// 2. 生成订单号
|
||||
String orderNo = generateOrderNo();
|
||||
|
||||
// 2. 创建订单基础信息
|
||||
// 3. 创建订单基础信息
|
||||
OrderV2 order = new OrderV2();
|
||||
order.setOrderNo(orderNo);
|
||||
order.setMemberId(userId);
|
||||
@@ -107,7 +116,7 @@ public class OrderServiceImpl implements IOrderService {
|
||||
|
||||
log.info("订单基础信息创建成功: orderId={}, orderNo={}", orderId, orderNo);
|
||||
|
||||
// 3. 保存订单商品明细
|
||||
// 4. 保存订单商品明细
|
||||
for (ProductItem product : request.getProducts()) {
|
||||
// 重新计算商品价格信息并获取商品名称
|
||||
ProductPriceAndNameInfo priceInfo = calculateProductItemPriceAndName(product);
|
||||
@@ -129,7 +138,7 @@ public class OrderServiceImpl implements IOrderService {
|
||||
|
||||
log.info("订单商品明细保存成功: orderId={}, itemCount={}", orderId, request.getProducts().size());
|
||||
|
||||
// 4. 记录使用的优惠券信息并标记为已使用
|
||||
// 5. 记录使用的优惠券信息并标记为已使用
|
||||
if (priceResult.getUsedCoupon() != null) {
|
||||
OrderDiscountV2 couponDiscount = new OrderDiscountV2();
|
||||
couponDiscount.setOrderId(orderId);
|
||||
@@ -165,7 +174,7 @@ public class OrderServiceImpl implements IOrderService {
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 记录使用的券码信息并标记为已使用
|
||||
// 6. 记录使用的券码信息并标记为已使用
|
||||
if (priceResult.getUsedVoucher() != null) {
|
||||
VoucherInfo voucherInfo = priceResult.getUsedVoucher();
|
||||
OrderDiscountV2 voucherDiscount = new OrderDiscountV2();
|
||||
@@ -195,7 +204,7 @@ public class OrderServiceImpl implements IOrderService {
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 记录其他优惠信息(如限时立减等)
|
||||
// 7. 记录其他优惠信息(如限时立减等)
|
||||
if (priceResult.getDiscountDetails() != null) {
|
||||
for (DiscountDetail discount : priceResult.getDiscountDetails()) {
|
||||
// 跳过已经记录的优惠券和券码
|
||||
@@ -881,4 +890,128 @@ public class OrderServiceImpl implements IOrderService {
|
||||
return new ProductPriceAndNameInfo(defaultPrice, defaultPrice, fallbackName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查重复购买
|
||||
* 防止用户重复购买相同内容
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param faceId 人脸ID
|
||||
* @param scenicId 景区ID
|
||||
* @param products 商品列表
|
||||
* @throws DuplicatePurchaseException 如果检测到重复购买
|
||||
*/
|
||||
private void checkDuplicatePurchase(Long userId, Long faceId, Long scenicId, List<ProductItem> products) {
|
||||
for (ProductItem product : products) {
|
||||
switch (product.getProductType()) {
|
||||
case VLOG_VIDEO:
|
||||
checkVideoAlreadyPurchased(userId, faceId, scenicId, product.getProductId());
|
||||
break;
|
||||
case RECORDING_SET:
|
||||
case PHOTO_SET:
|
||||
checkSetAlreadyPurchased(userId, faceId, scenicId, product.getProductType());
|
||||
break;
|
||||
case PHOTO_PRINT:
|
||||
case MACHINE_PRINT:
|
||||
// 打印类商品允许重复购买,跳过检查
|
||||
log.debug("跳过打印类商品重复购买检查: productType={}, productId={}",
|
||||
product.getProductType(), product.getProductId());
|
||||
break;
|
||||
default:
|
||||
log.warn("未知的商品类型,跳过重复购买检查: productType={}", product.getProductType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查视频是否已经购买
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param faceId 人脸ID
|
||||
* @param scenicId 景区ID
|
||||
* @param videoId 视频ID
|
||||
* @throws DuplicatePurchaseException 如果已购买
|
||||
*/
|
||||
private void checkVideoAlreadyPurchased(Long userId, Long faceId, Long scenicId, String videoId) {
|
||||
// 构建查询条件:查找已支付的有效订单中包含该视频的订单
|
||||
QueryWrapper<OrderV2> orderQuery = new QueryWrapper<>();
|
||||
orderQuery.eq("member_id", userId)
|
||||
.eq("face_id", faceId)
|
||||
.eq("scenic_id", scenicId)
|
||||
.eq("payment_status", PaymentStatus.PAID.getCode())
|
||||
.in("order_status", OrderStatus.PAID.getCode(), OrderStatus.PROCESSING.getCode(), OrderStatus.COMPLETED.getCode())
|
||||
.eq("deleted", 0);
|
||||
|
||||
List<OrderV2> existingOrders = orderV2Mapper.selectList(orderQuery);
|
||||
|
||||
for (OrderV2 order : existingOrders) {
|
||||
// 检查订单明细中是否包含该视频
|
||||
QueryWrapper<OrderItemV2> itemQuery = new QueryWrapper<>();
|
||||
itemQuery.eq("order_id", order.getId())
|
||||
.eq("product_type", com.ycwl.basic.pricing.enums.ProductType.VLOG_VIDEO.name())
|
||||
.eq("product_id", videoId);
|
||||
|
||||
long count = orderItemMapper.selectCount(itemQuery);
|
||||
if (count > 0) {
|
||||
log.warn("检测到重复购买视频: userId={}, faceId={}, scenicId={}, videoId={}, existingOrderId={}",
|
||||
userId, faceId, scenicId, videoId, order.getId());
|
||||
throw new DuplicatePurchaseException(
|
||||
"您已购买过此视频",
|
||||
order.getId(),
|
||||
order.getOrderNo(),
|
||||
com.ycwl.basic.pricing.enums.ProductType.VLOG_VIDEO,
|
||||
videoId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("视频重复购买检查通过: userId={}, faceId={}, scenicId={}, videoId={}",
|
||||
userId, faceId, scenicId, videoId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查套餐(录像集/照相集)是否已经购买
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param faceId 人脸ID
|
||||
* @param scenicId 景区ID
|
||||
* @param productType 商品类型
|
||||
* @throws DuplicatePurchaseException 如果已购买
|
||||
*/
|
||||
private void checkSetAlreadyPurchased(Long userId, Long faceId, Long scenicId,
|
||||
com.ycwl.basic.pricing.enums.ProductType productType) {
|
||||
// 构建查询条件:查找已支付的有效订单中包含该类型套餐的订单
|
||||
QueryWrapper<OrderV2> orderQuery = new QueryWrapper<>();
|
||||
orderQuery.eq("member_id", userId)
|
||||
.eq("face_id", faceId)
|
||||
.eq("scenic_id", scenicId)
|
||||
.eq("payment_status", PaymentStatus.PAID.getCode())
|
||||
.in("order_status", OrderStatus.PAID.getCode(), OrderStatus.PROCESSING.getCode(), OrderStatus.COMPLETED.getCode())
|
||||
.eq("deleted", 0);
|
||||
|
||||
List<OrderV2> existingOrders = orderV2Mapper.selectList(orderQuery);
|
||||
|
||||
for (OrderV2 order : existingOrders) {
|
||||
// 检查订单明细中是否包含该类型的套餐
|
||||
QueryWrapper<OrderItemV2> itemQuery = new QueryWrapper<>();
|
||||
itemQuery.eq("order_id", order.getId())
|
||||
.eq("product_type", productType.name());
|
||||
|
||||
long count = orderItemMapper.selectCount(itemQuery);
|
||||
if (count > 0) {
|
||||
log.warn("检测到重复购买套餐: userId={}, faceId={}, scenicId={}, productType={}, existingOrderId={}",
|
||||
userId, faceId, scenicId, productType, order.getId());
|
||||
throw new DuplicatePurchaseException(
|
||||
"您已购买过此类型的套餐",
|
||||
order.getId(),
|
||||
order.getOrderNo(),
|
||||
productType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("套餐重复购买检查通过: userId={}, faceId={}, scenicId={}, productType={}",
|
||||
userId, faceId, scenicId, productType);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user