You've already forked FrameTour-BE
feat(coupon): 添加优惠券领取功能
- 新增 CouponClaimRequest 和 CouponClaimResult 类用于处理优惠券领取请求和结果 - 在 ICouponService 接口中添加 claimCoupon 方法 - 在 CouponServiceImpl 中实现 claimCoupon 方法,包括参数验证、优惠券查询、库存检查、记录创建等步骤 - 优化日志记录和异常处理
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
package com.ycwl.basic.pricing.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券领取请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CouponClaimRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券ID
|
||||||
|
*/
|
||||||
|
private Long couponId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 景区ID(可选,记录在哪个景区领取)
|
||||||
|
*/
|
||||||
|
private String scenicId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取来源(可选,如:活动页面、签到奖励等)
|
||||||
|
*/
|
||||||
|
private String claimSource;
|
||||||
|
|
||||||
|
public CouponClaimRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CouponClaimRequest(Long userId, Long couponId) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.couponId = couponId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CouponClaimRequest(Long userId, Long couponId, String scenicId) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.couponId = couponId;
|
||||||
|
this.scenicId = scenicId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CouponClaimRequest(Long userId, Long couponId, String scenicId, String claimSource) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.couponId = couponId;
|
||||||
|
this.scenicId = scenicId;
|
||||||
|
this.claimSource = claimSource;
|
||||||
|
}
|
||||||
|
}
|
100
src/main/java/com/ycwl/basic/pricing/dto/CouponClaimResult.java
Normal file
100
src/main/java/com/ycwl/basic/pricing/dto/CouponClaimResult.java
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package com.ycwl.basic.pricing.dto;
|
||||||
|
|
||||||
|
import com.ycwl.basic.pricing.entity.PriceCouponClaimRecord;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券领取结果DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CouponClaimResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息(失败时)
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码(失败时)
|
||||||
|
*/
|
||||||
|
private String errorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取记录ID(成功时)
|
||||||
|
*/
|
||||||
|
private Long claimRecordId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券ID
|
||||||
|
*/
|
||||||
|
private Long couponId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券名称
|
||||||
|
*/
|
||||||
|
private String couponName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取时间
|
||||||
|
*/
|
||||||
|
private Date claimTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 景区ID
|
||||||
|
*/
|
||||||
|
private String scenicId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建成功结果
|
||||||
|
*/
|
||||||
|
public static CouponClaimResult success(PriceCouponClaimRecord record, String couponName) {
|
||||||
|
CouponClaimResult result = new CouponClaimResult();
|
||||||
|
result.success = true;
|
||||||
|
result.claimRecordId = record.getId();
|
||||||
|
result.couponId = record.getCouponId();
|
||||||
|
result.couponName = couponName;
|
||||||
|
result.claimTime = record.getClaimTime();
|
||||||
|
result.userId = record.getUserId();
|
||||||
|
result.scenicId = record.getScenicId();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建失败结果
|
||||||
|
*/
|
||||||
|
public static CouponClaimResult failure(String errorCode, String errorMessage) {
|
||||||
|
CouponClaimResult result = new CouponClaimResult();
|
||||||
|
result.success = false;
|
||||||
|
result.errorCode = errorCode;
|
||||||
|
result.errorMessage = errorMessage;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建失败结果(仅错误消息)
|
||||||
|
*/
|
||||||
|
public static CouponClaimResult failure(String errorMessage) {
|
||||||
|
return failure("CLAIM_FAILED", errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常用错误码常量
|
||||||
|
public static final String ERROR_COUPON_NOT_FOUND = "COUPON_NOT_FOUND";
|
||||||
|
public static final String ERROR_COUPON_EXPIRED = "COUPON_EXPIRED";
|
||||||
|
public static final String ERROR_COUPON_INACTIVE = "COUPON_INACTIVE";
|
||||||
|
public static final String ERROR_COUPON_OUT_OF_STOCK = "COUPON_OUT_OF_STOCK";
|
||||||
|
public static final String ERROR_ALREADY_CLAIMED = "ALREADY_CLAIMED";
|
||||||
|
public static final String ERROR_INVALID_PARAMS = "INVALID_PARAMS";
|
||||||
|
public static final String ERROR_SYSTEM_ERROR = "SYSTEM_ERROR";
|
||||||
|
}
|
@@ -3,6 +3,8 @@ package com.ycwl.basic.pricing.service;
|
|||||||
import com.ycwl.basic.pricing.dto.CouponInfo;
|
import com.ycwl.basic.pricing.dto.CouponInfo;
|
||||||
import com.ycwl.basic.pricing.dto.CouponUseRequest;
|
import com.ycwl.basic.pricing.dto.CouponUseRequest;
|
||||||
import com.ycwl.basic.pricing.dto.CouponUseResult;
|
import com.ycwl.basic.pricing.dto.CouponUseResult;
|
||||||
|
import com.ycwl.basic.pricing.dto.CouponClaimRequest;
|
||||||
|
import com.ycwl.basic.pricing.dto.CouponClaimResult;
|
||||||
import com.ycwl.basic.pricing.dto.ProductItem;
|
import com.ycwl.basic.pricing.dto.ProductItem;
|
||||||
import com.ycwl.basic.pricing.entity.PriceCouponConfig;
|
import com.ycwl.basic.pricing.entity.PriceCouponConfig;
|
||||||
|
|
||||||
@@ -59,4 +61,12 @@ public interface ICouponService {
|
|||||||
* @return 可用优惠券列表
|
* @return 可用优惠券列表
|
||||||
*/
|
*/
|
||||||
List<CouponInfo> getUserAvailableCoupons(Long userId);
|
List<CouponInfo> getUserAvailableCoupons(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取优惠券(内部调用方法)
|
||||||
|
*
|
||||||
|
* @param request 领取请求
|
||||||
|
* @return 领取结果
|
||||||
|
*/
|
||||||
|
CouponClaimResult claimCoupon(CouponClaimRequest request);
|
||||||
}
|
}
|
@@ -196,4 +196,90 @@ public class CouponServiceImpl implements ICouponService {
|
|||||||
info.setActualDiscountAmount(actualDiscountAmount);
|
info.setActualDiscountAmount(actualDiscountAmount);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CouponClaimResult claimCoupon(CouponClaimRequest request) {
|
||||||
|
log.info("开始领取优惠券: userId={}, couponId={}, scenicId={}",
|
||||||
|
request.getUserId(), request.getCouponId(), request.getScenicId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 参数验证
|
||||||
|
if (request.getUserId() == null || request.getCouponId() == null) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_INVALID_PARAMS, "用户ID和优惠券ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询优惠券配置
|
||||||
|
PriceCouponConfig coupon = couponConfigMapper.selectById(request.getCouponId());
|
||||||
|
if (coupon == null || coupon.getDeleted() == 1) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_NOT_FOUND, "优惠券不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查优惠券是否启用
|
||||||
|
if (!Boolean.TRUE.equals(coupon.getIsActive())) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_INACTIVE, "优惠券已停用");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查优惠券有效期
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
if (coupon.getValidFrom() != null && now.isBefore(coupon.getValidFrom())) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券尚未生效");
|
||||||
|
}
|
||||||
|
if (coupon.getValidUntil() != null && now.isAfter(coupon.getValidUntil())) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_EXPIRED, "优惠券已过期");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 检查库存(如果有总量限制)
|
||||||
|
if (coupon.getTotalQuantity() != null && coupon.getUsedQuantity() != null) {
|
||||||
|
if (coupon.getUsedQuantity() >= coupon.getTotalQuantity()) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_COUPON_OUT_OF_STOCK, "优惠券已领完");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 检查用户是否已经领取过该优惠券
|
||||||
|
PriceCouponClaimRecord existingRecord = couponClaimRecordMapper.selectUserCouponRecord(
|
||||||
|
request.getUserId(), request.getCouponId());
|
||||||
|
if (existingRecord != null) {
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_ALREADY_CLAIMED, "您已经领取过该优惠券");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 创建领取记录
|
||||||
|
Date claimTime = new Date();
|
||||||
|
PriceCouponClaimRecord claimRecord = new PriceCouponClaimRecord();
|
||||||
|
claimRecord.setCouponId(request.getCouponId());
|
||||||
|
claimRecord.setUserId(request.getUserId());
|
||||||
|
claimRecord.setClaimTime(claimTime);
|
||||||
|
claimRecord.setStatus(CouponStatus.CLAIMED);
|
||||||
|
claimRecord.setScenicId(request.getScenicId());
|
||||||
|
claimRecord.setCreateTime(claimTime);
|
||||||
|
claimRecord.setUpdateTime(claimTime);
|
||||||
|
claimRecord.setDeleted(0);
|
||||||
|
|
||||||
|
// 8. 插入领取记录
|
||||||
|
int insertResult = couponClaimRecordMapper.insert(claimRecord);
|
||||||
|
if (insertResult <= 0) {
|
||||||
|
log.error("插入优惠券领取记录失败: userId={}, couponId={}",
|
||||||
|
request.getUserId(), request.getCouponId());
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "领取失败,请稍后重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 更新优惠券已使用数量(如果有总量限制)
|
||||||
|
if (coupon.getTotalQuantity() != null) {
|
||||||
|
int updatedUsedQuantity = (coupon.getUsedQuantity() == null ? 0 : coupon.getUsedQuantity()) + 1;
|
||||||
|
coupon.setUsedQuantity(updatedUsedQuantity);
|
||||||
|
couponConfigMapper.updateById(coupon);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("优惠券领取成功: userId={}, couponId={}, claimRecordId={}",
|
||||||
|
request.getUserId(), request.getCouponId(), claimRecord.getId());
|
||||||
|
|
||||||
|
// 10. 返回成功结果
|
||||||
|
return CouponClaimResult.success(claimRecord, coupon.getCouponName());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("领取优惠券失败: userId={}, couponId={}",
|
||||||
|
request.getUserId(), request.getCouponId(), e);
|
||||||
|
return CouponClaimResult.failure(CouponClaimResult.ERROR_SYSTEM_ERROR, "系统错误,领取失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user