From 004f3f352e6430aeba68ac44ab86e2eeb3a6c9e3 Mon Sep 17 00:00:00 2001 From: Cui Zhi Feng <cuizf@fengyuntec.com> Date: 星期一, 02 九月 2024 20:18:52 +0800 Subject: [PATCH] Merge branch 'master' into master-v2 --- src/main/java/com/mzl/flower/service/impl/coupon/CouponRecordServiceImpl.java | 250 +++++++++++++++++++++++++++++++++---------------- 1 files changed, 169 insertions(+), 81 deletions(-) diff --git a/src/main/java/com/mzl/flower/service/impl/coupon/CouponRecordServiceImpl.java b/src/main/java/com/mzl/flower/service/impl/coupon/CouponRecordServiceImpl.java index 3724c94..2de8630 100644 --- a/src/main/java/com/mzl/flower/service/impl/coupon/CouponRecordServiceImpl.java +++ b/src/main/java/com/mzl/flower/service/impl/coupon/CouponRecordServiceImpl.java @@ -6,25 +6,24 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.mzl.flower.config.exception.ValidationException; import com.mzl.flower.config.security.SecurityUtils; -import com.mzl.flower.dto.request.coupon.CreateCouponRecordDTO; -import com.mzl.flower.dto.request.coupon.QueryCouponRecordDTO; -import com.mzl.flower.dto.request.coupon.QueryCouponStatisticsBO; -import com.mzl.flower.dto.request.coupon.QueryExistCouponDTO; +import com.mzl.flower.dto.request.coupon.*; import com.mzl.flower.dto.response.coupon.CouponRecordVO; import com.mzl.flower.entity.coupon.CouponRecordDO; import com.mzl.flower.entity.coupon.CouponTemplateDO; import com.mzl.flower.entity.customer.Customer; -import com.mzl.flower.entity.system.User; import com.mzl.flower.enums.*; import com.mzl.flower.mapper.coupon.CouponRecordMapper; import com.mzl.flower.mapper.coupon.CouponRecordMapperCustom; import com.mzl.flower.mapper.customer.CustomerMapper; import com.mzl.flower.service.coupon.CouponRecordService; import com.mzl.flower.service.coupon.CouponTemplateService2; +import com.mzl.flower.service.customer.CustomerService; import com.mzl.flower.service.system.UserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -33,6 +32,7 @@ import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -59,97 +59,146 @@ @Autowired private CustomerMapper customerMapper; + @Autowired + private CustomerService customerService; + + + @Override public List<CouponRecordVO> getList(QueryCouponRecordDTO dto) { return couponRecordMapperCustom.getList(dto); } + @Autowired + RedissonClient redissonClient; + + private static final String COUPON_KEY="com:mzl:flower:service:impl:coupon:%s"; + + @Transactional @Override public boolean createCouponRecord(CreateCouponRecordDTO dto) { + + final CouponTemplateDO couponTemplateDO = couponTemplateService.getById(dto.getCouponId()); + if(couponTemplateDO==null){ + throw new ValidationException("优惠券不存在"); + } final Customer customer = customerMapper.selectById(dto.getCustomerId()); if(null==customer){ throw new ValidationException("商户信息不存在"); } - final CouponTemplateDO couponTemplateDO = couponTemplateService.getById(dto.getCouponId()); - - // TODO 活动优惠券和积分优惠券需要根据库存来控制- 根据优惠券的发放数量来控制有没有超发 - - - // TODO 根据用户领取设置的getLimit 查看当前用户是否已经超领优惠券 - - - CouponRecordDO couponRecordDO=new CouponRecordDO(); - BeanUtils.copyProperties(dto,couponRecordDO); - couponRecordDO.create(SecurityUtils.getUserId()); - // 设置为待使用状态 - couponRecordDO.setStatus(CouponUsedStatusEnum.UNUSED.getType()); - - // 根据商户设置用户id - if(StringUtils.isBlank(dto.getUserId()) && StringUtils.isNotBlank(customer.getUserId())){ - couponRecordDO.setUserId(customer.getUserId()); - } - - // 优惠券字段冗余 - - if(null!=couponTemplateDO){ - couponRecordDO.setCategory(couponTemplateDO.getGetUserType()); - couponRecordDO.setCouponCode(couponTemplateDO.getCouponCode()); - couponRecordDO.setCouponName(couponTemplateDO.getCouponName()); - couponRecordDO.setCouponDiscountValue(couponTemplateDO.getCouponDiscountValue()); - couponRecordDO.setMinOrderAmount(couponTemplateDO.getMinOrderAmount()); - couponRecordDO.setGetType(couponTemplateDO.getGetType()); - couponRecordDO.setCouponDiscountType(couponTemplateDO.getCouponDiscountType()); - couponRecordDO.setGetUserType(couponTemplateDO.getGetUserType()); - couponRecordDO.setPoint(couponTemplateDO.getPoint()); - - - // 根据优惠券模板来计算优惠券的生效开始时间和结束时间 - if(StringUtils.isNotBlank(couponTemplateDO.getCategory()) && couponTemplateDO.getCategory().equals(CouponCategoryEnum.MEMBER.getStatus())){ - // 如果是会员优惠券的话,则设置为优惠券的使用条件为优惠券的时间 - couponRecordDO.setEffectiveStart(couponTemplateDO.getUsageStartDate()); - couponRecordDO.setEffectiveEnd(couponTemplateDO.getUsageEndDate()); - }else{ - // 非会员的根据领取时间类型来计算优惠券的时间 - if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.GET.getType())){ - // 与领取时间一致 - couponRecordDO.setEffectiveStart(couponTemplateDO.getGetStartDate()); - couponRecordDO.setEffectiveEnd(couponTemplateDO.getGetEndDate()); - } - if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.FIXED.getType())){ - // 固定时间 - couponRecordDO.setEffectiveStart(couponTemplateDO.getUsageStartDate()); - couponRecordDO.setEffectiveEnd(couponTemplateDO.getUsageEndDate()); - } - if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.GET_AFTER_TIME.getType())){ - // 领取后有段时间 领取后有效时间 - // 根据发放后有效期来设置时间 - if (couponTemplateDO.getUsageTimeNum() == null || couponTemplateDO.getUsageTimeNum() <= 0) { - throw new IllegalArgumentException("使用时间数量必须为正整数"); - } - LocalDateTime currentTime = LocalDateTime.now(); - couponRecordDO.setEffectiveStart(currentTime); - if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.DAY.getType())) { - // 天 - couponRecordDO.setEffectiveEnd(currentTime.plusDays(couponTemplateDO.getUsageTimeNum())); - } - if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.HOUR.getType())) { - // 小时 - couponRecordDO.setEffectiveEnd(currentTime.plusHours(couponTemplateDO.getUsageTimeNum())); - } - if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.MINUTE.getType())) { - // 分钟 - couponRecordDO.setEffectiveEnd(currentTime.plusMinutes(couponTemplateDO.getUsageTimeNum())); + RLock lock = redissonClient.getLock(String.format(COUPON_KEY, couponTemplateDO.getId())); + try { + // 获取锁,最多等待 10 秒,锁自动释放时间 30 秒 + if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { + try { + // 活动优惠券和积分优惠券需要根据库存来控制- 根据优惠券的发放数量来控制有没有超发 + if(StringUtils.isNotBlank(couponTemplateDO.getCategory()) && ( + couponTemplateDO.getCategory().equals(CouponCategoryEnum.ACTIVITY.getStatus()) || couponTemplateDO.getCategory().equals(CouponCategoryEnum.POINT.getStatus()) + )){ + // 获取当前优惠券已经领取的数量 + final Integer gainTotal = getExistGainCouponRecordAmountById(couponTemplateDO.getId()); + if(couponTemplateDO.getCouponAmount().compareTo(gainTotal)<=0){ + throw new ValidationException("当前优惠券已经领完!"); + } } + // 根据用户领取设置的getLimit 查看当前用户是否已经超领优惠券 + if(StringUtils.isNotBlank(couponTemplateDO.getCategory()) && couponTemplateDO.getCategory().equals(CouponCategoryEnum.ACTIVITY.getStatus()) ){ + // 查看当前优惠券的领取时间在不在设置的领取时间范围类 + LocalDateTime now = LocalDateTime.now(); + // 判断当前时间是否在领取时间范围内 + if (!(now.isAfter(couponTemplateDO.getGetStartDate()) && now.isBefore(couponTemplateDO.getGetEndDate()))) { + throw new ValidationException("当前时间不在优惠券的领取时间范围内,无法操作!"); + } + + // 获取当前优惠券已经领取的数量 + final Integer customGainTotal = getUserGainCouponRecordAmountById(couponTemplateDO.getId(),customer.getId()); + if(couponTemplateDO.getGetLimit().compareTo(customGainTotal)<=0){ + throw new ValidationException("超出个人领取限制,每人限领"+couponTemplateDO.getGetLimit()+"张!"); + } + } + + + CouponRecordDO couponRecordDO=new CouponRecordDO(); + BeanUtils.copyProperties(dto,couponRecordDO); + couponRecordDO.create(SecurityUtils.getUserId()); + // 设置为待使用状态 + couponRecordDO.setStatus(CouponUsedStatusEnum.UNUSED.getType()); + + // 根据商户设置用户id + if(StringUtils.isBlank(dto.getUserId()) && StringUtils.isNotBlank(customer.getUserId())){ + couponRecordDO.setUserId(customer.getUserId()); + } + + // 优惠券字段冗余 + couponRecordDO.setCategory(couponTemplateDO.getGetUserType()); + couponRecordDO.setCouponCode(couponTemplateDO.getCouponCode()); + couponRecordDO.setCouponName(couponTemplateDO.getCouponName()); + couponRecordDO.setCouponDiscountValue(couponTemplateDO.getCouponDiscountValue()); + couponRecordDO.setMinOrderAmount(couponTemplateDO.getMinOrderAmount()); + couponRecordDO.setGetType(couponTemplateDO.getGetType()); + couponRecordDO.setCouponDiscountType(couponTemplateDO.getCouponDiscountType()); + couponRecordDO.setGetUserType(couponTemplateDO.getGetUserType()); + couponRecordDO.setPoint(couponTemplateDO.getPoint()); + couponRecordDO.setMemberId(couponRecordDO.getMemberId()); + + + // 根据优惠券模板来计算优惠券的生效开始时间和结束时间 + if(StringUtils.isNotBlank(couponTemplateDO.getCategory()) && couponTemplateDO.getCategory().equals(CouponCategoryEnum.MEMBER.getStatus())){ + // 如果是会员优惠券的话,则设置为优惠券的使用条件为优惠券的时间 + couponRecordDO.setEffectiveStart(couponTemplateDO.getUsageStartDate()); + couponRecordDO.setEffectiveEnd(couponTemplateDO.getUsageEndDate()); + }else{ + // 非会员的根据领取时间类型来计算优惠券的时间 + if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.GET.getType())){ + // 与领取时间一致 + couponRecordDO.setEffectiveStart(couponTemplateDO.getGetStartDate()); + couponRecordDO.setEffectiveEnd(couponTemplateDO.getGetEndDate()); + } + if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.FIXED.getType())){ + // 固定时间 + couponRecordDO.setEffectiveStart(couponTemplateDO.getUsageStartDate()); + couponRecordDO.setEffectiveEnd(couponTemplateDO.getUsageEndDate()); + } + if(StringUtils.isNotBlank(couponTemplateDO.getUsageType()) && couponTemplateDO.getUsageType().equals(CouponUsageTypeEnum.GET_AFTER_TIME.getType())){ + // 领取后有段时间 领取后有效时间 + // 根据发放后有效期来设置时间 + if (couponTemplateDO.getUsageTimeNum() == null || couponTemplateDO.getUsageTimeNum() <= 0) { + throw new IllegalArgumentException("使用时间数量必须为正整数"); + } + LocalDateTime currentTime = LocalDateTime.now(); + couponRecordDO.setEffectiveStart(currentTime); + if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.DAY.getType())) { + // 天 + couponRecordDO.setEffectiveEnd(currentTime.plusDays(couponTemplateDO.getUsageTimeNum())); + } + if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.HOUR.getType())) { + // 小时 + couponRecordDO.setEffectiveEnd(currentTime.plusHours(couponTemplateDO.getUsageTimeNum())); + } + if (couponTemplateDO.getUsageTimeType().equals(CouponUsageTimeTypeEnum.MINUTE.getType())) { + // 分钟 + couponRecordDO.setEffectiveEnd(currentTime.plusMinutes(couponTemplateDO.getUsageTimeNum())); + } + + } + } + + return baseMapper.insert(couponRecordDO)>0; + + } finally { + lock.unlock(); } } - + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + // 处理异常 } + return false; - return baseMapper.insert(couponRecordDO)>0; } @Override @@ -208,16 +257,20 @@ // 遍历所有相同等级用户信息,并根据优惠券设置的规则构造优惠券 final List<CouponTemplateDO> updateCouponTemplateList = vipTemplateList.stream().map(couponTemplateDO -> { - final List<User> vipGradeUserList = userService.getVipGradeUserList(couponTemplateDO.getVipGrade()); - final List<CouponRecordDO> gradeCouponRecordList = vipGradeUserList.stream().map(user -> { + // 获取当前等级下的所有用户 + final List<Customer> customerList = customerService.getCustomerListByLevelId(couponTemplateDO.getMemberId()); + + final List<CouponRecordDO> gradeCouponRecordList = customerList.stream().map(customer -> { CouponRecordDO couponRecordDO = new CouponRecordDO(); BeanUtils.copyProperties(couponTemplateDO, couponRecordDO); couponRecordDO.setId(IdUtil.simpleUUID()); couponRecordDO.setCouponId(couponTemplateDO.getId()); - couponRecordDO.setUserId(user.getId()); + couponRecordDO.setUserId(customer.getUserId()); + couponRecordDO.setCustomerId(customer.getId()); couponRecordDO.setStatus(CouponUsedStatusEnum.UNUSED.getType()); couponRecordDO.setEffectiveStart(firstDayStart); couponRecordDO.setEffectiveEnd(lastDayEnd); + couponRecordDO.setMemberId(couponTemplateDO.getMemberId()); // 创建信息 couponRecordDO.create(); @@ -298,13 +351,48 @@ } @Override - public int getExistCouponAmount(QueryExistCouponDTO dto) { + public Integer getExistCouponAmount(QueryExistCouponDTO dto) { QueryWrapper<CouponRecordDO> queryWrapper=new QueryWrapper<>(); queryWrapper.lambda().eq(CouponRecordDO::getDeleted, TrueOrFalseEnum.FALSE.isFlag()) .eq(StringUtils.isNotBlank(dto.getCouponId()), CouponRecordDO::getCouponId,dto.getCouponId()) - .eq(StringUtils.isNotBlank(dto.getCustomerId()),CouponRecordDO::getCustomerId,dto.getCustomerId()) + .eq(null!=dto.getCustomerId(),CouponRecordDO::getCustomerId,dto.getCustomerId()) .eq(StringUtils.isNotBlank(dto.getCategory()),CouponRecordDO::getCategory,dto.getCategory()); return baseMapper.selectCount(queryWrapper); } + + @Override + public Integer getExistGainCouponRecordAmountById(String couponId) { + QueryWrapper<CouponRecordDO> queryWrapper=new QueryWrapper<>(); + queryWrapper.lambda().eq(CouponRecordDO::getDeleted, TrueOrFalseEnum.FALSE.isFlag()) + .eq(StringUtils.isNotBlank(couponId), CouponRecordDO::getCouponId,couponId) + ; + return baseMapper.selectCount(queryWrapper); + } + + @Override + public Integer getUserGainCouponRecordAmountById(String couponId, Long customerId) { + QueryWrapper<CouponRecordDO> queryWrapper=new QueryWrapper<>(); + queryWrapper.lambda().eq(CouponRecordDO::getDeleted, TrueOrFalseEnum.FALSE.isFlag()) + .eq(StringUtils.isNotBlank(couponId), CouponRecordDO::getCouponId,couponId) + .eq(null!=customerId,CouponRecordDO::getCustomerId,customerId) + ; + + return baseMapper.selectCount(queryWrapper); + } + + @Override + public List<CouponRecordVO> getMineCouponRecordList(QueryMineCouponRecordDTO dto) { + checkCouponExpired(dto); + return couponRecordMapperCustom.getMineCouponRecordList(dto); + } + + @Override + public void checkCouponExpired(QueryMineCouponRecordDTO dto) { + if(StringUtils.isBlank(dto.getUserId())){ + dto.setUserId(SecurityUtils.getUserId()); + } + // 将未使用的优惠券直接过期 + couponRecordMapperCustom.checkCouponExpired(dto); + } } -- Gitblit v1.9.3