You've already forked FrameTour-BE
feat(pricing): 增加券码重复使用功能并优化相关数据结构
- 在 PriceVoucherUsageRecord 和 VoucherUsageRecordResp 中添加 usageSequence 字段,用于记录券码的使用序号- 更新 PriceVoucherCode 实体和相关 mapper,增加 currentUseCount 和 lastUsedTime 字段 - 修改 VoucherCodeServiceImpl 和 VoucherServiceImpl 中的券码使用逻辑,支持重复使用 - 新增VoucherBatchOverviewResp、VoucherDetailResp、VoucherUsageSummaryResp 和 VoucherValidationResp 等新的响应 DTO 类,用于提供券码批次概览、详情、使用统计和验证等功能
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package com.ycwl.basic.pricing.dto.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 券码批次概览响应
|
||||
*/
|
||||
@Data
|
||||
public class VoucherBatchOverviewResp {
|
||||
|
||||
/**
|
||||
* 批次ID
|
||||
*/
|
||||
private Long batchId;
|
||||
|
||||
/**
|
||||
* 批次名称
|
||||
*/
|
||||
private String batchName;
|
||||
|
||||
/**
|
||||
* 券码总数
|
||||
*/
|
||||
private Integer totalCodes;
|
||||
|
||||
/**
|
||||
* 已领取券码数
|
||||
*/
|
||||
private Integer claimedCodes;
|
||||
|
||||
/**
|
||||
* 已使用券码数(至少使用过一次)
|
||||
*/
|
||||
private Integer usedCodes;
|
||||
|
||||
/**
|
||||
* 已达到最大使用次数的券码数
|
||||
*/
|
||||
private Integer exhaustedCodes;
|
||||
|
||||
/**
|
||||
* 总使用记录数(支持重复使用)
|
||||
*/
|
||||
private Integer totalUsageRecords;
|
||||
|
||||
/**
|
||||
* 平均每个券码使用次数
|
||||
*/
|
||||
private BigDecimal averageUsagePerCode;
|
||||
|
||||
/**
|
||||
* 重复使用率 (重复使用的券码数/已使用券码数 * 100)
|
||||
*/
|
||||
private BigDecimal repeatUsageRate;
|
||||
|
||||
/**
|
||||
* 可重复使用设置
|
||||
*/
|
||||
private RepeatableSettings repeatableSettings;
|
||||
|
||||
@Data
|
||||
public static class RepeatableSettings {
|
||||
/**
|
||||
* 每个券码最大使用次数
|
||||
*/
|
||||
private Integer maxUseCount;
|
||||
|
||||
/**
|
||||
* 每个用户最大使用次数
|
||||
*/
|
||||
private Integer maxUsePerUser;
|
||||
|
||||
/**
|
||||
* 使用间隔小时数
|
||||
*/
|
||||
private Integer useIntervalHours;
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
package com.ycwl.basic.pricing.dto.resp;
|
||||
|
||||
import com.ycwl.basic.pricing.enums.ProductType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 券码详情响应
|
||||
*/
|
||||
@Data
|
||||
public class VoucherDetailResp {
|
||||
|
||||
/**
|
||||
* 券码
|
||||
*/
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 批次信息
|
||||
*/
|
||||
private BatchInfo batchInfo;
|
||||
|
||||
/**
|
||||
* 使用信息
|
||||
*/
|
||||
private UsageInfo usageInfo;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private UserInfo userInfo;
|
||||
|
||||
/**
|
||||
* 状态信息
|
||||
*/
|
||||
private StatusInfo statusInfo;
|
||||
|
||||
@Data
|
||||
public static class BatchInfo {
|
||||
/**
|
||||
* 批次ID
|
||||
*/
|
||||
private Long batchId;
|
||||
|
||||
/**
|
||||
* 批次名称
|
||||
*/
|
||||
private String batchName;
|
||||
|
||||
/**
|
||||
* 优惠类型 (0: 赠品兑换, 1: 金额抵扣, 2: 百分比折扣)
|
||||
*/
|
||||
private Integer discountType;
|
||||
|
||||
/**
|
||||
* 优惠值
|
||||
*/
|
||||
private BigDecimal discountValue;
|
||||
|
||||
/**
|
||||
* 适用商品类型
|
||||
*/
|
||||
private List<ProductType> applicableProducts;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UsageInfo {
|
||||
/**
|
||||
* 最大使用次数
|
||||
*/
|
||||
private Integer maxUseCount;
|
||||
|
||||
/**
|
||||
* 当前已使用次数
|
||||
*/
|
||||
private Integer currentUseCount;
|
||||
|
||||
/**
|
||||
* 剩余使用次数
|
||||
*/
|
||||
private Integer remainingUseCount;
|
||||
|
||||
/**
|
||||
* 每个用户最大使用次数
|
||||
*/
|
||||
private Integer maxUsePerUser;
|
||||
|
||||
/**
|
||||
* 使用间隔小时数
|
||||
*/
|
||||
private Integer useIntervalHours;
|
||||
|
||||
/**
|
||||
* 是否还能使用
|
||||
*/
|
||||
private Boolean canUseMore;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UserInfo {
|
||||
/**
|
||||
* 用户人脸ID
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 该用户已使用此券码的次数
|
||||
*/
|
||||
private Integer userUsageCount;
|
||||
|
||||
/**
|
||||
* 最后使用时间
|
||||
*/
|
||||
private Date lastUsedTime;
|
||||
|
||||
/**
|
||||
* 下次可用时间
|
||||
*/
|
||||
private Date nextAvailableTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class StatusInfo {
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 状态名称
|
||||
*/
|
||||
private String statusName;
|
||||
|
||||
/**
|
||||
* 领取时间
|
||||
*/
|
||||
private Date claimedTime;
|
||||
}
|
||||
}
|
@@ -47,6 +47,11 @@ public class VoucherUsageRecordResp {
|
||||
*/
|
||||
private String batchName;
|
||||
|
||||
/**
|
||||
* 使用序号(该券码的第几次使用)
|
||||
*/
|
||||
private Integer usageSequence;
|
||||
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
|
@@ -0,0 +1,100 @@
|
||||
package com.ycwl.basic.pricing.dto.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 券码使用统计摘要响应
|
||||
*/
|
||||
@Data
|
||||
public class VoucherUsageSummaryResp {
|
||||
|
||||
/**
|
||||
* 券码
|
||||
*/
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 批次ID
|
||||
*/
|
||||
private Long batchId;
|
||||
|
||||
/**
|
||||
* 批次名称
|
||||
*/
|
||||
private String batchName;
|
||||
|
||||
/**
|
||||
* 总使用记录数(重复使用产生多条记录)
|
||||
*/
|
||||
private Integer totalUsageRecords;
|
||||
|
||||
/**
|
||||
* 使用过的独立用户数
|
||||
*/
|
||||
private Integer uniqueUsers;
|
||||
|
||||
/**
|
||||
* 使用统计
|
||||
*/
|
||||
private UsageStatistics usageStatistics;
|
||||
|
||||
/**
|
||||
* 使用时间轴
|
||||
*/
|
||||
private List<UsageTimeline> usageTimeline;
|
||||
|
||||
@Data
|
||||
public static class UsageStatistics {
|
||||
/**
|
||||
* 最大可使用次数
|
||||
*/
|
||||
private Integer maxUseCount;
|
||||
|
||||
/**
|
||||
* 当前已使用次数
|
||||
*/
|
||||
private Integer currentUseCount;
|
||||
|
||||
/**
|
||||
* 剩余使用次数
|
||||
*/
|
||||
private Integer remainingUseCount;
|
||||
|
||||
/**
|
||||
* 使用率
|
||||
*/
|
||||
private BigDecimal usageRate;
|
||||
|
||||
/**
|
||||
* 是否还能使用
|
||||
*/
|
||||
private Boolean canUseMore;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UsageTimeline {
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
private Date useTime;
|
||||
|
||||
/**
|
||||
* 用户人脸ID
|
||||
*/
|
||||
private Long faceId;
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 优惠金额
|
||||
*/
|
||||
private BigDecimal discountAmount;
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.ycwl.basic.pricing.dto.resp;
|
||||
|
||||
import com.ycwl.basic.pricing.dto.VoucherInfo;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 券码验证响应
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class VoucherValidationResp {
|
||||
|
||||
/**
|
||||
* 券码
|
||||
*/
|
||||
private String voucherCode;
|
||||
|
||||
/**
|
||||
* 是否可用
|
||||
*/
|
||||
private Boolean available;
|
||||
|
||||
/**
|
||||
* 不可用原因
|
||||
*/
|
||||
private String unavailableReason;
|
||||
|
||||
/**
|
||||
* 券码详细信息
|
||||
*/
|
||||
private VoucherInfo voucherInfo;
|
||||
}
|
@@ -44,6 +44,11 @@ public class PriceVoucherUsageRecord {
|
||||
*/
|
||||
private Long batchId;
|
||||
|
||||
/**
|
||||
* 使用序号(该券码的第几次使用)
|
||||
*/
|
||||
private Integer usageSequence;
|
||||
|
||||
/**
|
||||
* 使用时间
|
||||
*/
|
||||
|
@@ -58,17 +58,6 @@ public interface PriceVoucherCodeMapper extends BaseMapper<PriceVoucherCode> {
|
||||
@Param("faceId") Long faceId,
|
||||
@Param("claimedTime") LocalDateTime claimedTime);
|
||||
|
||||
/**
|
||||
* 使用券码(更新状态为已使用)
|
||||
* @param code 券码
|
||||
* @param usedTime 使用时间
|
||||
* @param remark 使用备注
|
||||
* @return 影响行数
|
||||
*/
|
||||
int useVoucher(@Param("code") String code,
|
||||
@Param("usedTime") LocalDateTime usedTime,
|
||||
@Param("remark") String remark);
|
||||
|
||||
/**
|
||||
* 根据批次ID查询券码列表(支持分页)
|
||||
* @param batchId 批次ID
|
||||
|
@@ -49,74 +49,79 @@ public class VoucherCodeServiceImpl implements VoucherCodeService {
|
||||
private VoucherBatchService voucherBatchService;
|
||||
|
||||
@Override
|
||||
public void generateVoucherCodes(Long batchId, Long scenicId, Integer count) {
|
||||
List<PriceVoucherCode> codes = new ArrayList<>();
|
||||
public void generateVoucherCodes(Long batchId, Long scenicId, Integer count) {
|
||||
List<PriceVoucherCode> codes = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
PriceVoucherCode code = new PriceVoucherCode();
|
||||
code.setBatchId(batchId);
|
||||
code.setScenicId(scenicId);
|
||||
code.setCode(generateVoucherCode());
|
||||
code.setStatus(VoucherCodeStatus.UNCLAIMED.getCode());
|
||||
code.setCreateTime(new Date());
|
||||
code.setDeleted(0);
|
||||
codes.add(code);
|
||||
}
|
||||
|
||||
for (PriceVoucherCode code : codes) {
|
||||
voucherCodeMapper.insert(code);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
PriceVoucherCode code = new PriceVoucherCode();
|
||||
code.setBatchId(batchId);
|
||||
code.setScenicId(scenicId);
|
||||
code.setCode(generateVoucherCode());
|
||||
code.setStatus(VoucherCodeStatus.UNCLAIMED.getCode());
|
||||
code.setCurrentUseCount(0); // 初始化使用次数为0
|
||||
code.setCreateTime(new Date());
|
||||
code.setDeleted(0);
|
||||
codes.add(code);
|
||||
}
|
||||
|
||||
for (PriceVoucherCode code : codes) {
|
||||
voucherCodeMapper.insert(code);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public VoucherCodeResp claimVoucher(VoucherClaimReq req) {
|
||||
if (req.getScenicId() == null) {
|
||||
throw new BizException(400, "景区ID不能为空");
|
||||
}
|
||||
if (req.getFaceId() == null) {
|
||||
throw new BizException(400, "用户faceId不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(req.getCode())) {
|
||||
throw new BizException(400, "券码不能为空");
|
||||
}
|
||||
|
||||
// 验证券码是否存在且未被领取
|
||||
LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PriceVoucherCode::getCode, req.getCode())
|
||||
.eq(PriceVoucherCode::getScenicId, req.getScenicId())
|
||||
.eq(PriceVoucherCode::getDeleted, 0);
|
||||
|
||||
PriceVoucherCode voucherCode = voucherCodeMapper.selectOne(wrapper);
|
||||
if (voucherCode == null) {
|
||||
throw new BizException(400, "券码不存在或不属于该景区");
|
||||
}
|
||||
|
||||
if (!Objects.equals(voucherCode.getStatus(), VoucherCodeStatus.UNCLAIMED.getCode())) {
|
||||
throw new BizException(400, "券码已被领取或已使用");
|
||||
}
|
||||
|
||||
if (!canClaimVoucher(req.getFaceId(), req.getScenicId())) {
|
||||
throw new BizException(400, "该用户在此景区已领取过券码");
|
||||
}
|
||||
|
||||
// 获取券码所属批次
|
||||
PriceVoucherBatchConfig batch = voucherBatchMapper.selectById(voucherCode.getBatchId());
|
||||
if (batch == null || batch.getDeleted() == 1) {
|
||||
throw new BizException(400, "券码批次不存在");
|
||||
}
|
||||
|
||||
// 更新券码状态
|
||||
voucherCode.setFaceId(req.getFaceId());
|
||||
voucherCode.setStatus(VoucherCodeStatus.CLAIMED_UNUSED.getCode());
|
||||
voucherCode.setClaimedTime(new Date());
|
||||
|
||||
voucherCodeMapper.updateById(voucherCode);
|
||||
|
||||
voucherBatchService.updateBatchClaimedCount(batch.getId());
|
||||
|
||||
return convertToResp(voucherCode, batch);
|
||||
@Transactional
|
||||
public VoucherCodeResp claimVoucher(VoucherClaimReq req) {
|
||||
if (req.getScenicId() == null) {
|
||||
throw new BizException(400, "景区ID不能为空");
|
||||
}
|
||||
if (req.getFaceId() == null) {
|
||||
throw new BizException(400, "用户faceId不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(req.getCode())) {
|
||||
throw new BizException(400, "券码不能为空");
|
||||
}
|
||||
|
||||
// 验证券码是否存在且未被领取
|
||||
LambdaQueryWrapper<PriceVoucherCode> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PriceVoucherCode::getCode, req.getCode())
|
||||
.eq(PriceVoucherCode::getScenicId, req.getScenicId())
|
||||
.eq(PriceVoucherCode::getDeleted, 0);
|
||||
|
||||
PriceVoucherCode voucherCode = voucherCodeMapper.selectOne(wrapper);
|
||||
if (voucherCode == null) {
|
||||
throw new BizException(400, "券码不存在或不属于该景区");
|
||||
}
|
||||
|
||||
if (!Objects.equals(voucherCode.getStatus(), VoucherCodeStatus.UNCLAIMED.getCode())) {
|
||||
throw new BizException(400, "券码已被领取或已使用");
|
||||
}
|
||||
|
||||
if (!canClaimVoucher(req.getFaceId(), req.getScenicId())) {
|
||||
throw new BizException(400, "该用户在此景区已领取过券码");
|
||||
}
|
||||
|
||||
// 获取券码所属批次
|
||||
PriceVoucherBatchConfig batch = voucherBatchMapper.selectById(voucherCode.getBatchId());
|
||||
if (batch == null || batch.getDeleted() == 1) {
|
||||
throw new BizException(400, "券码批次不存在");
|
||||
}
|
||||
|
||||
// 更新券码状态
|
||||
voucherCode.setFaceId(req.getFaceId());
|
||||
voucherCode.setStatus(VoucherCodeStatus.CLAIMED_UNUSED.getCode());
|
||||
voucherCode.setClaimedTime(new Date());
|
||||
// 确保currentUseCount被初始化
|
||||
if (voucherCode.getCurrentUseCount() == null) {
|
||||
voucherCode.setCurrentUseCount(0);
|
||||
}
|
||||
|
||||
voucherCodeMapper.updateById(voucherCode);
|
||||
|
||||
voucherBatchService.updateBatchClaimedCount(batch.getId());
|
||||
|
||||
return convertToResp(voucherCode, batch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<VoucherCodeResp> queryCodeList(VoucherCodeQueryReq req) {
|
||||
@@ -165,26 +170,31 @@ public class VoucherCodeServiceImpl implements VoucherCodeService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void markCodeAsUsed(Long codeId, String remark) {
|
||||
PriceVoucherCode code = voucherCodeMapper.selectById(codeId);
|
||||
if (code == null || code.getDeleted() == 1) {
|
||||
throw new BizException(404, "券码不存在");
|
||||
}
|
||||
|
||||
if (!Objects.equals(code.getStatus(), VoucherCodeStatus.CLAIMED_UNUSED.getCode())) {
|
||||
throw new BizException(400, "券码状态异常,无法使用");
|
||||
}
|
||||
|
||||
code.setStatus(VoucherCodeStatus.USED.getCode());
|
||||
code.setUsedTime(new Date());
|
||||
code.setRemark(remark);
|
||||
|
||||
voucherCodeMapper.updateById(code);
|
||||
|
||||
voucherBatchService.updateBatchUsedCount(code.getBatchId());
|
||||
@Transactional
|
||||
public void markCodeAsUsed(Long codeId, String remark) {
|
||||
PriceVoucherCode code = voucherCodeMapper.selectById(codeId);
|
||||
if (code == null || code.getDeleted() == 1) {
|
||||
throw new BizException(404, "券码不存在");
|
||||
}
|
||||
|
||||
if (!Objects.equals(code.getStatus(), VoucherCodeStatus.CLAIMED_UNUSED.getCode())) {
|
||||
throw new BizException(400, "券码状态异常,无法使用");
|
||||
}
|
||||
|
||||
// 更新使用计数和时间
|
||||
Integer currentUseCount = code.getCurrentUseCount() != null ? code.getCurrentUseCount() : 0;
|
||||
code.setCurrentUseCount(currentUseCount + 1);
|
||||
code.setLastUsedTime(new Date());
|
||||
|
||||
code.setStatus(VoucherCodeStatus.USED.getCode());
|
||||
code.setUsedTime(new Date());
|
||||
code.setRemark(remark);
|
||||
|
||||
voucherCodeMapper.updateById(code);
|
||||
|
||||
voucherBatchService.updateBatchUsedCount(code.getBatchId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClaimVoucher(Long faceId, Long scenicId) {
|
||||
Integer count = voucherCodeMapper.countByFaceIdAndScenicId(faceId, scenicId);
|
||||
|
@@ -181,78 +181,82 @@ public class VoucherServiceImpl implements IVoucherService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记券码为已使用(支持可重复使用)
|
||||
*
|
||||
* @param voucherCode 券码
|
||||
* @param remark 使用备注
|
||||
* @param orderId 订单ID
|
||||
* @param discountAmount 优惠金额
|
||||
* @param faceId 人脸ID
|
||||
*/
|
||||
@Override
|
||||
public void markVoucherAsUsed(String voucherCode, String remark, String orderId, BigDecimal discountAmount, Long faceId) {
|
||||
if (!StringUtils.hasText(voucherCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PriceVoucherCode voucherCodeEntity = voucherCodeMapper.selectByCode(voucherCode);
|
||||
if (voucherCodeEntity == null || voucherCodeEntity.getDeleted() == 1) {
|
||||
log.warn("券码不存在或已删除: {}", voucherCode);
|
||||
return;
|
||||
}
|
||||
|
||||
PriceVoucherBatchConfig batchConfig = voucherBatchConfigMapper.selectById(voucherCodeEntity.getBatchId());
|
||||
if (batchConfig == null || batchConfig.getDeleted() == 1) {
|
||||
log.warn("券码批次不存在或已删除: batchId={}", voucherCodeEntity.getBatchId());
|
||||
return;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
// 创建使用记录
|
||||
PriceVoucherUsageRecord usageRecord = new PriceVoucherUsageRecord();
|
||||
usageRecord.setVoucherCodeId(voucherCodeEntity.getId());
|
||||
usageRecord.setVoucherCode(voucherCode);
|
||||
usageRecord.setFaceId(faceId != null ? faceId : voucherCodeEntity.getFaceId());
|
||||
usageRecord.setScenicId(voucherCodeEntity.getScenicId());
|
||||
usageRecord.setBatchId(voucherCodeEntity.getBatchId());
|
||||
usageRecord.setUseTime(now);
|
||||
usageRecord.setOrderId(orderId);
|
||||
usageRecord.setDiscountAmount(discountAmount);
|
||||
usageRecord.setRemark(remark);
|
||||
usageRecord.setCreateTime(now);
|
||||
usageRecord.setDeleted(0);
|
||||
|
||||
usageRecordMapper.insert(usageRecord);
|
||||
|
||||
// 更新券码使用次数和状态
|
||||
Integer currentUseCount = voucherCodeEntity.getCurrentUseCount() != null ?
|
||||
voucherCodeEntity.getCurrentUseCount() + 1 : 1;
|
||||
voucherCodeEntity.setCurrentUseCount(currentUseCount);
|
||||
voucherCodeEntity.setLastUsedTime(now);
|
||||
|
||||
// 检查是否达到最大使用次数
|
||||
Integer maxUseCount = batchConfig.getMaxUseCount();
|
||||
if (maxUseCount != null && currentUseCount >= maxUseCount) {
|
||||
if (maxUseCount == 1) {
|
||||
// 兼容原有逻辑,单次使用设为USED状态
|
||||
voucherCodeEntity.setStatus(VoucherCodeStatus.USED.getCode());
|
||||
voucherCodeEntity.setUsedTime(now);
|
||||
} else {
|
||||
// 多次使用达到上限设为CLAIMED_EXHAUSTED状态
|
||||
voucherCodeEntity.setStatus(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
voucherCodeMapper.updateById(voucherCodeEntity);
|
||||
|
||||
// 更新批次统计(使用记录数,不是使用券码数)
|
||||
voucherBatchConfigMapper.updateUsedCount(voucherCodeEntity.getBatchId(), 1);
|
||||
|
||||
log.info("券码使用记录已创建: code={}, useCount={}, maxUseCount={}, faceId={}",
|
||||
voucherCode, currentUseCount, maxUseCount, faceId);
|
||||
* 标记券码为已使用(支持可重复使用)
|
||||
*
|
||||
* @param voucherCode 券码
|
||||
* @param remark 使用备注
|
||||
* @param orderId 订单ID
|
||||
* @param discountAmount 优惠金额
|
||||
* @param faceId 人脸ID
|
||||
*/
|
||||
@Override
|
||||
public void markVoucherAsUsed(String voucherCode, String remark, String orderId, BigDecimal discountAmount, Long faceId) {
|
||||
if (!StringUtils.hasText(voucherCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PriceVoucherCode voucherCodeEntity = voucherCodeMapper.selectByCode(voucherCode);
|
||||
if (voucherCodeEntity == null || voucherCodeEntity.getDeleted() == 1) {
|
||||
log.warn("券码不存在或已删除: {}", voucherCode);
|
||||
return;
|
||||
}
|
||||
|
||||
PriceVoucherBatchConfig batchConfig = voucherBatchConfigMapper.selectById(voucherCodeEntity.getBatchId());
|
||||
if (batchConfig == null || batchConfig.getDeleted() == 1) {
|
||||
log.warn("券码批次不存在或已删除: batchId={}", voucherCodeEntity.getBatchId());
|
||||
return;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
// 计算新的使用次数和序号
|
||||
Integer currentUseCount = voucherCodeEntity.getCurrentUseCount() != null ?
|
||||
voucherCodeEntity.getCurrentUseCount() : 0;
|
||||
Integer newUseCount = currentUseCount + 1;
|
||||
|
||||
// 创建使用记录
|
||||
PriceVoucherUsageRecord usageRecord = new PriceVoucherUsageRecord();
|
||||
usageRecord.setVoucherCodeId(voucherCodeEntity.getId());
|
||||
usageRecord.setVoucherCode(voucherCode);
|
||||
usageRecord.setFaceId(faceId != null ? faceId : voucherCodeEntity.getFaceId());
|
||||
usageRecord.setScenicId(voucherCodeEntity.getScenicId());
|
||||
usageRecord.setBatchId(voucherCodeEntity.getBatchId());
|
||||
usageRecord.setUsageSequence(newUseCount); // 设置使用序号,表示这是该券码的第几次使用
|
||||
usageRecord.setUseTime(now);
|
||||
usageRecord.setOrderId(orderId);
|
||||
usageRecord.setDiscountAmount(discountAmount);
|
||||
usageRecord.setRemark(remark);
|
||||
usageRecord.setCreateTime(now);
|
||||
usageRecord.setDeleted(0);
|
||||
|
||||
usageRecordMapper.insert(usageRecord);
|
||||
|
||||
// 更新券码使用次数和状态
|
||||
voucherCodeEntity.setCurrentUseCount(newUseCount);
|
||||
voucherCodeEntity.setLastUsedTime(now);
|
||||
|
||||
// 检查是否达到最大使用次数
|
||||
Integer maxUseCount = batchConfig.getMaxUseCount();
|
||||
if (maxUseCount != null && newUseCount >= maxUseCount) {
|
||||
if (maxUseCount == 1) {
|
||||
// 兼容原有逻辑,单次使用设为USED状态
|
||||
voucherCodeEntity.setStatus(VoucherCodeStatus.USED.getCode());
|
||||
voucherCodeEntity.setUsedTime(now);
|
||||
} else {
|
||||
// 多次使用达到上限设为CLAIMED_EXHAUSTED状态
|
||||
voucherCodeEntity.setStatus(VoucherCodeStatus.CLAIMED_EXHAUSTED.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
voucherCodeMapper.updateById(voucherCodeEntity);
|
||||
|
||||
// 更新批次统计(使用记录数,不是使用券码数)
|
||||
voucherBatchConfigMapper.updateUsedCount(voucherCodeEntity.getBatchId(), 1);
|
||||
|
||||
log.info("券码使用记录已创建: code={}, useCount={}, maxUseCount={}, faceId={}, sequence={}",
|
||||
voucherCode, newUseCount, maxUseCount, faceId, newUseCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClaimVoucher(Long faceId, Long scenicId) {
|
||||
if (faceId == null || scenicId == null) {
|
||||
|
@@ -11,6 +11,8 @@
|
||||
<result column="face_id" property="faceId" jdbcType="BIGINT"/>
|
||||
<result column="claimed_time" property="claimedTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="used_time" property="usedTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="current_use_count" property="currentUseCount" jdbcType="INTEGER"/>
|
||||
<result column="last_used_time" property="lastUsedTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="remark" property="remark" jdbcType="VARCHAR"/>
|
||||
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
|
||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||
@@ -20,7 +22,7 @@
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id, batch_id, scenic_id, code, status, face_id, claimed_time, used_time,
|
||||
remark, create_time, update_time, deleted, deleted_at
|
||||
current_use_count, last_used_time, remark, create_time, update_time, deleted, deleted_at
|
||||
</sql>
|
||||
|
||||
<select id="selectByCode" resultMap="BaseResultMap">
|
||||
@@ -69,16 +71,7 @@
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
<update id="useVoucher">
|
||||
UPDATE price_voucher_code
|
||||
SET status = 2,
|
||||
used_time = #{usedTime},
|
||||
remark = #{remark},
|
||||
update_time = NOW()
|
||||
WHERE code = #{code}
|
||||
AND (status = 1 OR status = 0)
|
||||
AND deleted = 0
|
||||
</update>
|
||||
|
||||
|
||||
<select id="selectByBatchId" resultMap="BaseResultMap">
|
||||
SELECT <include refid="Base_Column_List"/>
|
||||
|
Reference in New Issue
Block a user