|
|
@@ -2,9 +2,13 @@ package com.etotem.cfc.service;
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.etotem.cfc.entity.Coupon;
|
|
|
+import com.etotem.cfc.entity.CouponGrantLog;
|
|
|
import com.etotem.cfc.entity.FamilyMember;
|
|
|
import com.etotem.cfc.entity.PointsExchangeProduct;
|
|
|
import com.etotem.cfc.entity.PointsExchangeRecord;
|
|
|
+import com.etotem.cfc.mapper.CouponGrantLogMapper;
|
|
|
+import com.etotem.cfc.mapper.CouponMapper;
|
|
|
import com.etotem.cfc.mapper.FamilyMemberMapper;
|
|
|
import com.etotem.cfc.mapper.PointsExchangeProductMapper;
|
|
|
import com.etotem.cfc.mapper.PointsExchangeRecordMapper;
|
|
|
@@ -35,6 +39,15 @@ public class PointsExchangeService {
|
|
|
@Resource
|
|
|
private SysConfigService sysConfigService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private CouponMapper couponMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private CouponService couponService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private CouponGrantLogMapper couponGrantLogMapper;
|
|
|
+
|
|
|
public Page<PointsExchangeProduct> getProductList(String category, Integer pageNum, Integer pageSize) {
|
|
|
Page<PointsExchangeProduct> pageParam = new Page<>(pageNum, pageSize);
|
|
|
LambdaQueryWrapper<PointsExchangeProduct> wrapper = new LambdaQueryWrapper<>();
|
|
|
@@ -157,6 +170,125 @@ public class PointsExchangeService {
|
|
|
return record;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 积分兑换优惠券:校验 -> 扣积分 -> 发券 -> 落兑换记录
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public PointsExchangeRecord exchangeCoupon(Long userId, Long childId, Long couponId) {
|
|
|
+ Coupon coupon = couponMapper.selectById(couponId);
|
|
|
+ if (coupon == null) {
|
|
|
+ throw new RuntimeException("优惠券不存在");
|
|
|
+ }
|
|
|
+ if (coupon.getPointsPrice() == null || coupon.getPointsPrice() <= 0) {
|
|
|
+ throw new RuntimeException("该优惠券不支持积分兑换");
|
|
|
+ }
|
|
|
+ Date now = new Date();
|
|
|
+ if (coupon.getValidFrom() != null && now.before(coupon.getValidFrom())) {
|
|
|
+ throw new RuntimeException("优惠券尚未开始");
|
|
|
+ }
|
|
|
+ if (coupon.getValidUntil() != null && now.after(coupon.getValidUntil())) {
|
|
|
+ throw new RuntimeException("优惠券已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 目标家庭成员(复用现有解析逻辑)
|
|
|
+ Long targetFamilyMemberId = childId;
|
|
|
+ if (targetFamilyMemberId == null) {
|
|
|
+ FamilyMember child = familyMemberMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<FamilyMember>().eq(FamilyMember::getUserId, userId).last("LIMIT 1")
|
|
|
+ );
|
|
|
+ if (child != null) {
|
|
|
+ targetFamilyMemberId = child.getId();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (targetFamilyMemberId == null) {
|
|
|
+ throw new RuntimeException("未找到关联的家庭成员信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 全局日限次(复用 exchange_daily_limit)
|
|
|
+ int dailyLimit = 5;
|
|
|
+ try {
|
|
|
+ String limitStr = sysConfigService.getValue("exchange_daily_limit");
|
|
|
+ if (limitStr != null && !limitStr.isEmpty()) {
|
|
|
+ dailyLimit = Integer.parseInt(limitStr);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ dailyLimit = 5;
|
|
|
+ }
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
+ cal.set(Calendar.MINUTE, 0);
|
|
|
+ cal.set(Calendar.SECOND, 0);
|
|
|
+ cal.set(Calendar.MILLISECOND, 0);
|
|
|
+ Date todayStart = cal.getTime();
|
|
|
+ long todayCount = pointsExchangeRecordMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<PointsExchangeRecord>()
|
|
|
+ .eq(PointsExchangeRecord::getUserId, userId)
|
|
|
+ .ge(PointsExchangeRecord::getCreatedAt, todayStart)
|
|
|
+ );
|
|
|
+ if (todayCount >= dailyLimit) {
|
|
|
+ throw new RuntimeException("今日兑换次数已达上限");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 单券模板日限次(coupon_exchange_daily_limit 默认1)
|
|
|
+ int couponDailyLimit = 1;
|
|
|
+ try {
|
|
|
+ String cdlStr = sysConfigService.getValue("coupon_exchange_daily_limit");
|
|
|
+ if (cdlStr != null && !cdlStr.isEmpty()) {
|
|
|
+ couponDailyLimit = Integer.parseInt(cdlStr);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ couponDailyLimit = 1;
|
|
|
+ }
|
|
|
+ long couponTodayCount = couponGrantLogMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<CouponGrantLog>()
|
|
|
+ .eq(CouponGrantLog::getUserId, userId)
|
|
|
+ .eq(CouponGrantLog::getCouponId, couponId)
|
|
|
+ .eq(CouponGrantLog::getGrantType, "EXCHANGE")
|
|
|
+ .ge(CouponGrantLog::getCreatedAt, todayStart)
|
|
|
+ );
|
|
|
+ if (couponTodayCount >= couponDailyLimit) {
|
|
|
+ throw new RuntimeException("该优惠券今日兑换次数已达上限");
|
|
|
+ }
|
|
|
+
|
|
|
+ int minPoints = 100;
|
|
|
+ try {
|
|
|
+ String minStr = sysConfigService.getValue("exchange_points_min");
|
|
|
+ if (minStr != null && !minStr.isEmpty()) {
|
|
|
+ minPoints = Integer.parseInt(minStr);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ minPoints = 100;
|
|
|
+ }
|
|
|
+ if (coupon.getPointsPrice() < minPoints) {
|
|
|
+ throw new RuntimeException("兑换积分不得低于最低限制");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 扣积分(system_points)
|
|
|
+ int deductResult = pointsService.deductSystemPoints(targetFamilyMemberId, coupon.getPointsPrice(),
|
|
|
+ "积分兑换优惠券: " + coupon.getName());
|
|
|
+ if (deductResult < 0) {
|
|
|
+ throw new RuntimeException("积分不足");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发券 + 流水(EXCHANGE,period=null)
|
|
|
+ couponService.grant(userId, couponId, "EXCHANGE", null, null);
|
|
|
+
|
|
|
+ String redeemCode = UUID.randomUUID().toString().replace("-", "").substring(0, 12).toUpperCase();
|
|
|
+ PointsExchangeRecord record = new PointsExchangeRecord();
|
|
|
+ record.setUserId(userId);
|
|
|
+ record.setFamilyMemberId(targetFamilyMemberId);
|
|
|
+ record.setProductId(couponId);
|
|
|
+ record.setProductName(coupon.getName());
|
|
|
+ record.setPointsCost(coupon.getPointsPrice());
|
|
|
+ record.setQuantity(1);
|
|
|
+ record.setStatus("completed");
|
|
|
+ record.setRedeemCode(redeemCode);
|
|
|
+ record.setCreatedAt(new Date());
|
|
|
+ record.setUpdatedAt(new Date());
|
|
|
+ pointsExchangeRecordMapper.insert(record);
|
|
|
+ return record;
|
|
|
+ }
|
|
|
+
|
|
|
public List<PointsExchangeRecord> getExchangeRecords(Long userId) {
|
|
|
LambdaQueryWrapper<PointsExchangeRecord> wrapper = new LambdaQueryWrapper<>();
|
|
|
wrapper.eq(PointsExchangeRecord::getUserId, userId)
|