package cn.iocoder.yudao.module.promotion.service.reward; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.product.api.category.ProductCategoryApi; import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityBaseVO; import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper; import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.hutool.core.collection.CollUtil.intersectionDistinct; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; /** * 满减送活动 Service 实现类 * * @author 芋道源码 */ @Service @Validated public class RewardActivityServiceImpl implements RewardActivityService { @Resource private RewardActivityMapper rewardActivityMapper; @Resource private ProductCategoryApi productCategoryApi; @Resource private ProductSpuApi productSpuApi; @Override public Long createRewardActivity(RewardActivityCreateReqVO createReqVO) { // 1.1 校验商品范围 validateProductScope(createReqVO.getProductScope(), createReqVO.getProductScopeValues()); // 1.2 校验商品是否冲突 validateRewardActivitySpuConflicts(null, createReqVO); // 插入 RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class) .setStatus(CommonStatusEnum.ENABLE.getStatus()); rewardActivityMapper.insert(rewardActivity); // 返回 return rewardActivity.getId(); } @Override public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) { // 1.1 校验存在 RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId()); if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动,不能修改噢 throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED); } // 1.2 校验商品范围 validateProductScope(updateReqVO.getProductScope(), updateReqVO.getProductScopeValues()); // 1.3 校验商品是否冲突 validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO); // 2. 更新 RewardActivityDO updateObj = BeanUtils.toBean(updateReqVO, RewardActivityDO.class); rewardActivityMapper.updateById(updateObj); } @Override public void closeRewardActivity(Long id) { // 校验存在 RewardActivityDO dbRewardActivity = validateRewardActivityExists(id); if (dbRewardActivity.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { // 已关闭的活动,不能关闭噢 throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); } // 更新 rewardActivityMapper.updateById(new RewardActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus())); } @Override public void deleteRewardActivity(Long id) { // 校验存在 RewardActivityDO dbRewardActivity = validateRewardActivityExists(id); if (dbRewardActivity.getStatus().equals(CommonStatusEnum.ENABLE.getStatus())) { // 未关闭的活动,不能删除噢 throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED); } // 删除 rewardActivityMapper.deleteById(id); } private RewardActivityDO validateRewardActivityExists(Long id) { RewardActivityDO activity = rewardActivityMapper.selectById(id); if (activity == null) { throw exception(REWARD_ACTIVITY_NOT_EXISTS); } return activity; } /** * 校验商品参加的活动是否冲突 * * @param id 活动编号 * @param rewardActivity 请求 */ private void validateRewardActivitySpuConflicts(Long id, RewardActivityBaseVO rewardActivity) { // 1. 获得开启的所有的活动 List list = rewardActivityMapper.selectList(RewardActivityDO::getStatus, CommonStatusEnum.ENABLE.getStatus()); if (id != null) { // 排除自己这个活动 list.removeIf(activity -> id.equals(activity.getId())); } // 2. 完全不允许重叠 for (RewardActivityDO item : list) { // 2.1 校验满减送活动时间是否冲突,如果时段不冲突那么不同的时间段内则可以存在相同的商品范围 if (!LocalDateTimeUtil.isOverlap(item.getStartTime(), item.getEndTime(), rewardActivity.getStartTime(), rewardActivity.getEndTime())) { continue; } // 2.2 校验商品范围是否重叠 // 情况一:如果与该时间段内商品范围为全部的活动冲突,或 rewardActivity 商品范围为全部,那么则直接校验不通过 // 例如说,rewardActivity 是全部活动,结果有个 db 里的 activity 是某个分类,它也是冲突的。也就是说,当前时间段内,有且仅有只能有一个活动! if (PromotionProductScopeEnum.isAll(item.getProductScope()) || PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) { throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), PromotionProductScopeEnum.isAll(item.getProductScope()) ? "该活动商品范围为全部已覆盖包含本活动范围" : "本活动商品范围为全部已覆盖包含了该活动商品范围"); } // 情况二:如果与该时间段内商品范围为类别的活动冲突 if (PromotionProductScopeEnum.isCategory(item.getProductScope())) { // 校验分类是否冲突 if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) { if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) { throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "商品分类范围重叠"); } } // 校验商品分类是否冲突 if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) { List spuList = productSpuApi.getSpuList(rewardActivity.getProductScopeValues()); if (!intersectionDistinct(item.getProductScopeValues(), convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) { throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "该活动商品分类范围已包含本活动所选商品"); } } } // 情况三:如果与该时间段内商品范围为商品的活动冲突 if (PromotionProductScopeEnum.isSpu(item.getProductScope())) { // 校验商品是否冲突 if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) { if (!intersectionDistinct(item.getProductScopeValues(), rewardActivity.getProductScopeValues()).isEmpty()) { throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "活动商品范围所选商品重叠"); } } // 校验商品分类是否冲突 if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) { List spuList = productSpuApi.getSpuList(item.getProductScopeValues()); if (!intersectionDistinct(rewardActivity.getProductScopeValues(), convertSet(spuList, ProductSpuRespDTO::getCategoryId)).isEmpty()) { throw exception(REWARD_ACTIVITY_SCOPE_EXISTS, item.getName(), "本活动商品分类范围包含了该活动所选商品"); } } } } } private void validateProductScope(Integer productScope, List productScopeValues) { if (Objects.equals(PromotionProductScopeEnum.SPU.getScope(), productScope)) { productSpuApi.validateSpuList(productScopeValues); } else if (Objects.equals(PromotionProductScopeEnum.CATEGORY.getScope(), productScope)) { productCategoryApi.validateCategoryList(productScopeValues); } } @Override public RewardActivityDO getRewardActivity(Long id) { return rewardActivityMapper.selectById(id); } @Override public PageResult getRewardActivityPage(RewardActivityPageReqVO pageReqVO) { return rewardActivityMapper.selectPage(pageReqVO); } @Override public List getMatchRewardActivityListBySpuIds(Collection spuIds) { // 1. 查询商品分类 List spuList = productSpuApi.getSpuList(spuIds); if (CollUtil.isEmpty(spuList)) { return Collections.emptyList(); } Map spuMap = convertMap(spuList, ProductSpuRespDTO::getId); // 2. 查询出指定 spuId 的 spu 参加的活动 List activityList = rewardActivityMapper.selectListBySpuIdAndStatusAndNow( spuIds, convertSet(spuList, ProductSpuRespDTO::getCategoryId), CommonStatusEnum.ENABLE.getStatus()); if (CollUtil.isEmpty(activityList)) { return Collections.emptyList(); } // 3. 转换成 Response DTO return convertList(activityList, activity -> { RewardActivityMatchRespDTO activityDTO = BeanUtils.toBean(activity, RewardActivityMatchRespDTO.class); // 3.1 设置对应匹配的 spuIds activityDTO.setSpuIds(new ArrayList<>()); for (Long spuId : spuIds) { if (PromotionProductScopeEnum.isAll(activityDTO.getProductScope())) { activityDTO.getSpuIds().add(spuId); } else if (PromotionProductScopeEnum.isSpu(activityDTO.getProductScope())) { if (CollUtil.contains(activityDTO.getProductScopeValues(), spuId)) { activityDTO.getSpuIds().add(spuId); } } else if (PromotionProductScopeEnum.isCategory(activityDTO.getProductScope())) { ProductSpuRespDTO spu = spuMap.get(spuId); if (spu != null && CollUtil.contains(activityDTO.getProductScopeValues(), spu.getCategoryId())) { activityDTO.getSpuIds().add(spuId); } } } // 3.2 设置每个 Rule 的描述 activityDTO.setRules(convertList(activity.getRules(), rule -> BeanUtils.toBean(rule, RewardActivityMatchRespDTO.Rule.class) .setDescription(getRewardActivityRuleDescription(activityDTO.getConditionType(), rule)))); return activityDTO; }); } }