Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApi.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/discount/DiscountActivityApiImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/discount/DiscountProductMapper.xml
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
This commit is contained in:
YunaiV
2024-09-15 20:32:58 +08:00
119 changed files with 1658 additions and 2111 deletions

View File

@@ -1,19 +1,17 @@
package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;
/**
* 拼团活动 API 实现类
*
@@ -37,12 +35,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
}
@Override
public boolean isCombinationRecordSuccess(Long userId, Long orderId) {
public CombinationRecordRespDTO getCombinationRecordByOrderId(Long userId, Long orderId) {
CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId);
if (record == null) {
throw exception(COMBINATION_RECORD_NOT_EXISTS);
}
return CombinationRecordStatusEnum.isSuccess(record.getStatus());
return BeanUtils.toBean(record, CombinationRecordRespDTO.class);
}
@Override

View File

@@ -1,11 +1,9 @@
package cn.iocoder.yudao.module.promotion.api.coupon;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.service.coupon.CouponService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -26,6 +24,11 @@ public class CouponApiImpl implements CouponApi {
@Resource
private CouponService couponService;
@Override
public List<CouponRespDTO> getCouponListByUserId(Long userId, Integer status) {
return BeanUtils.toBean(couponService.getCouponList(userId, status), CouponRespDTO.class);
}
@Override
public void useCoupon(CouponUseReqDTO useReqDTO) {
couponService.useCoupon(useReqDTO.getId(), useReqDTO.getUserId(),
@@ -37,12 +40,6 @@ public class CouponApiImpl implements CouponApi {
couponService.returnUsedCoupon(id);
}
@Override
public CouponRespDTO validateCoupon(CouponValidReqDTO validReqDTO) {
CouponDO coupon = couponService.validCoupon(validReqDTO.getId(), validReqDTO.getUserId());
return CouponConvert.INSTANCE.convert(coupon);
}
@Override
public List<Long> takeCouponsByAdmin(Map<Long, Integer> giveCoupons, Long userId) {
return couponService.takeCouponsByAdmin(giveCoupons, userId);

View File

@@ -1,7 +1,8 @@
package cn.iocoder.yudao.module.promotion.api.discount;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -23,8 +24,9 @@ public class DiscountActivityApiImpl implements DiscountActivityApi {
private DiscountActivityService discountActivityService;
@Override
public List<DiscountProductRespDTO> getMatchDiscountProductList(Collection<Long> spuIds) {
return DiscountActivityConvert.INSTANCE.convertList02(discountActivityService.getMatchDiscountProductList(spuIds));
public List<DiscountProductRespDTO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {
List<DiscountProductDO> list = discountActivityService.getMatchDiscountProductListBySkuIds(skuIds);
return BeanUtils.toBean(list, DiscountProductRespDTO.class);
}
}

View File

@@ -1,14 +1,12 @@
package cn.iocoder.yudao.module.promotion.api.reward;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
@@ -24,9 +22,8 @@ public class RewardActivityApiImpl implements RewardActivityApi {
private RewardActivityService rewardActivityService;
@Override
public List<RewardActivityMatchRespDTO> getRewardActivityListByStatusAndNow(Integer status, LocalDateTime dateTime) {
List<RewardActivityDO> list = rewardActivityService.getRewardActivityListByStatusAndDateTimeLt(status, dateTime);
return BeanUtils.toBean(list, RewardActivityMatchRespDTO.class);
public List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds) {
return rewardActivityService.getMatchRewardActivityListBySpuIds(spuIds);
}
}

View File

@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@@ -16,18 +17,20 @@ import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordSe
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.hutool.core.collection.CollectionUtil.newArrayList;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 拼团活动")
@@ -87,6 +90,23 @@ public class CombinationActivityController {
return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<CombinationActivityDO> activityList = combinationActivityService.getCombinationActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return success(Collections.emptyList());
}
// 2. 拼接返回
List<CombinationProductDO> productList = combinationActivityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return success(CombinationActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")

View File

@@ -27,4 +27,14 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
private List<CombinationProductRespVO> products;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
private String picUrl; // 从 SPU 的 picUrl 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer marketPrice; // 从 SPU 的 marketPrice 读取
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice; // 从 products 获取最小 price 读取
}

View File

@@ -9,12 +9,12 @@ import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityType
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

View File

@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@@ -13,15 +14,17 @@ import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - 秒杀活动")
@@ -89,11 +92,28 @@ public class SeckillActivityController {
}
// 拼接数据
List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityId(
List<SeckillProductDO> products = seckillActivityService.getSeckillProductListByActivityIds(
convertSet(pageResult.getList(), SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(
convertSet(pageResult.getList(), SeckillActivityDO::getSpuId));
return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, products, spuList));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得秒杀活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public CommonResult<List<SeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<SeckillActivityDO> activityList = seckillActivityService.getSeckillActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return success(Collections.emptyList());
}
// 2. 拼接返回
List<SeckillProductDO> productList = seckillActivityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return success(SeckillActivityConvert.INSTANCE.convertList(activityList, productList, spuList));
}
}

View File

@@ -54,4 +54,7 @@ public class SeckillActivityRespVO extends SeckillActivityBaseVO {
example = "50")
private Integer marketPrice;
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer seckillPrice; // 从 products 获取最小 price 读取
}

View File

@@ -1,25 +1,13 @@
package cn.iocoder.yudao.module.promotion.controller.app.activity;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.controller.app.activity.vo.AppActivityRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -31,11 +19,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口
@RestController
@@ -49,152 +36,31 @@ public class AppActivityController {
private SeckillActivityService seckillActivityService;
@Resource
private BargainActivityService bargainActivityService;
@Resource
private DiscountActivityService discountActivityService;
@Resource
private RewardActivityService rewardActivityService;
@Resource
private ProductSpuApi productSpuApi;
@GetMapping("/list-by-spu-id")
@Operation(summary = "获得单个商品,近期参与的每个活动")
@Operation(summary = "获得单个商品,进行中的拼团、秒杀、砍价活动信息", description = "每种活动,只返回一个")
@Parameter(name = "spuId", description = "商品编号", required = true)
public CommonResult<List<AppActivityRespVO>> getActivityListBySpuId(@RequestParam("spuId") Long spuId) {
// 每种活动,只返回一个
return success(getAppActivityList(Collections.singletonList(spuId)));
}
@GetMapping("/list-by-spu-ids")
@Operation(summary = "获得多个商品,近期参与的每个活动")
@Parameter(name = "spuIds", description = "商品编号数组", required = true)
public CommonResult<Map<Long, List<AppActivityRespVO>>> getActivityListBySpuIds(@RequestParam("spuIds") List<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return success(MapUtil.empty());
}
// 每种活动只返回一个key 为 SPU 编号
return success(convertMultiMap(getAppActivityList(spuIds), AppActivityRespVO::getSpuId));
}
private List<AppActivityRespVO> getAppActivityList(Collection<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return new ArrayList<>();
}
// 获取开启的且开始的且没有结束的活动
List<AppActivityRespVO> activityList = new ArrayList<>();
LocalDateTime now = LocalDateTime.now();
List<AppActivityRespVO> activityVOList = new ArrayList<>();
// 1. 拼团活动
getCombinationActivities(spuIds, now, activityList);
CombinationActivityDO combinationActivity = combinationActivityService.getMatchCombinationActivityBySpuId(spuId);
if (combinationActivity != null) {
activityVOList.add(new AppActivityRespVO(combinationActivity.getId(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(),
combinationActivity.getName(), combinationActivity.getSpuId(), combinationActivity.getStartTime(), combinationActivity.getEndTime()));
}
// 2. 秒杀活动
getSeckillActivities(spuIds, now, activityList);
SeckillActivityDO seckillActivity = seckillActivityService.getMatchSeckillActivityBySpuId(spuId);
if (seckillActivity != null) {
activityVOList.add(new AppActivityRespVO(seckillActivity.getId(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),
seckillActivity.getName(), seckillActivity.getSpuId(), seckillActivity.getStartTime(), seckillActivity.getEndTime()));
}
// 3. 砍价活动
getBargainActivities(spuIds, now, activityList);
// 4. 限时折扣活动
getDiscountActivities(spuIds, now, activityList);
// 5. 满减送活动
getRewardActivityList(spuIds, now, activityList);
return activityList;
}
private void getCombinationActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isEmpty(combinationActivities)) {
return;
}
combinationActivities.forEach(item -> {
activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(),
item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
});
}
private void getSeckillActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isEmpty(seckillActivities)) {
return;
}
seckillActivities.forEach(item -> {
activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),
item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
});
}
private void getBargainActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isNotEmpty(bargainActivities)) {
return;
}
bargainActivities.forEach(item -> {
activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
});
}
private void getDiscountActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
List<DiscountActivityDO> discountActivities = discountActivityService.getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(
spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isEmpty(discountActivities)) {
return;
}
List<DiscountProductDO> products = discountActivityService.getDiscountProductsByActivityId(
convertSet(discountActivities, DiscountActivityDO::getId));
Map<Long, Long> productMap = convertMap(products, DiscountProductDO::getActivityId, DiscountProductDO::getSpuId);
discountActivities.forEach(item -> activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
item.getName(), productMap.get(item.getId()), item.getStartTime(), item.getEndTime())));
}
private void getRewardActivityList(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
// 1.1 获得所有的活动
List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityListByStatusAndDateTimeLt(
CommonStatusEnum.ENABLE.getStatus(), now);
if (CollUtil.isEmpty(rewardActivityList)) {
return;
}
// 1.2 获得所有的商品信息
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
if (CollUtil.isEmpty(spuList)) {
return;
}
// 2. 构建活动
for (RewardActivityDO rewardActivity : rewardActivityList) {
// 情况一:所有商品都能参加
if (PromotionProductScopeEnum.isAll(rewardActivity.getProductScope())) {
buildAppActivityRespVO(rewardActivity, spuIds, activityList);
}
// 情况二:指定商品参加
if (PromotionProductScopeEnum.isSpu(rewardActivity.getProductScope())) {
List<Long> fSpuIds = spuList.stream().map(ProductSpuRespDTO::getId).filter(id ->
rewardActivity.getProductScopeValues().contains(id)).toList();
buildAppActivityRespVO(rewardActivity, fSpuIds, activityList);
}
// 情况三:指定商品类型参加
if (PromotionProductScopeEnum.isCategory(rewardActivity.getProductScope())) {
List<Long> fSpuIds = spuList.stream().filter(spuItem -> rewardActivity.getProductScopeValues()
.contains(spuItem.getCategoryId())).map(ProductSpuRespDTO::getId).toList();
buildAppActivityRespVO(rewardActivity, fSpuIds, activityList);
}
}
}
private static void buildAppActivityRespVO(RewardActivityDO rewardActivity, Collection<Long> spuIds,
List<AppActivityRespVO> activityList) {
for (Long spuId : spuIds) {
// 校验商品是否已经加入过活动
if (anyMatch(activityList, appActivity -> ObjUtil.equal(appActivity.getId(), rewardActivity.getId()) &&
ObjUtil.equal(appActivity.getSpuId(), spuId))) {
continue;
}
activityList.add(new AppActivityRespVO(rewardActivity.getId(),
PromotionTypeEnum.REWARD_ACTIVITY.getType(), rewardActivity.getName(), spuId,
rewardActivity.getStartTime(), rewardActivity.getEndTime()));
BargainActivityDO bargainActivity = bargainActivityService.getMatchBargainActivityBySpuId(spuId);
if (bargainActivity != null) {
activityVOList.add(new AppActivityRespVO(bargainActivity.getId(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
bargainActivity.getName(), bargainActivity.getSpuId(), bargainActivity.getStartTime(), bargainActivity.getEndTime()));
}
return success(activityVOList);
}
}

View File

@@ -14,24 +14,20 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "用户 APP - 拼团活动")
@@ -40,45 +36,12 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
@Validated
public class AppCombinationActivityController {
/**
* {@link AppCombinationActivityRespVO} 缓存,通过它异步刷新 {@link #getCombinationActivityList0(Integer)} 所要的首页数据
*/
private final LoadingCache<Integer, List<AppCombinationActivityRespVO>> combinationActivityListCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
new CacheLoader<Integer, List<AppCombinationActivityRespVO>>() {
@Override
public List<AppCombinationActivityRespVO> load(Integer count) {
return getCombinationActivityList0(count);
}
});
@Resource
private CombinationActivityService activityService;
@Resource
private ProductSpuApi spuApi;
@GetMapping("/list")
@Operation(summary = "获得拼团活动列表", description = "用于小程序首页")
@Parameter(name = "count", description = "需要展示的数量", example = "6")
public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityList(
@RequestParam(name = "count", defaultValue = "6") Integer count) {
return success(combinationActivityListCache.getUnchecked(count));
}
private List<AppCombinationActivityRespVO> getCombinationActivityList0(Integer count) {
List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByCount(count);
if (CollUtil.isEmpty(activityList)) {
return Collections.emptyList();
}
// 拼接返回
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList);
}
@GetMapping("/page")
@Operation(summary = "获得拼团活动分页")
public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) {
@@ -93,6 +56,23 @@ public class AppCombinationActivityController {
return success(CombinationActivityConvert.INSTANCE.convertAppPage(pageResult, productList, spuList));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<CombinationActivityDO> activityList = activityService.getCombinationActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return success(Collections.emptyList());
}
// 2. 拼接返回
List<CombinationProductDO> productList = activityService.getCombinationProductListByActivityIds(
convertList(activityList, CombinationActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, CombinationActivityDO::getSpuId));
return success(CombinationActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
}
@GetMapping("/get-detail")
@Operation(summary = "获得拼团活动明细")
@Parameter(name = "id", description = "活动编号", required = true, example = "1024")

View File

@@ -19,15 +19,14 @@ public class AppCombinationActivityRespVO {
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
// 从 SPU 的 picUrl 读取
private String picUrl;
private String picUrl; // 从 SPU 的 picUrl 读取
@Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
// 从 SPU 的 marketPrice 读取
private Integer marketPrice;
private Integer marketPrice; // 从 SPU 的 marketPrice 读取
@Schema(description = "拼团金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
private Integer combinationPrice;
private Integer combinationPrice; // 从 products 获取最小 price 读取
}

View File

@@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.*;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponTakeReqVO;
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
@@ -15,13 +17,12 @@ import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -56,14 +57,6 @@ public class AppCouponController {
return success(canTakeAgain);
}
@GetMapping("/match-list")
@Operation(summary = "获得匹配指定商品的优惠劵列表", description = "用于下单页,展示优惠劵列表")
public CommonResult<List<AppCouponMatchRespVO>> getMatchCouponList(AppCouponMatchReqVO matchReqVO) {
// todo: 优化:优惠金额倒序
List<CouponDO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);
return success(BeanUtils.toBean(list, AppCouponMatchRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "我的优惠劵列表")
@PreAuthenticated

View File

@@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "用户 App - 优惠劵的匹配 Request VO")
@Data
public class AppCouponMatchReqVO {
@Schema(description = "商品金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "商品金额不能为空")
private Integer price;
@Schema(description = "商品 SPU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "商品 SPU 编号不能为空")
private List<Long> spuIds;
@Schema(description = "商品 SKU 编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "商品 SKU 编号不能为空")
private List<Long> skuIds;
@Schema(description = "分类编号的数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[10, 20]")
@NotEmpty(message = "分类编号不能为空")
private List<Long> categoryIds;
}

View File

@@ -1,16 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 App - 优惠劵 Response VO")
@Data
public class AppCouponMatchRespVO extends AppCouponRespVO {
@Schema(description = "是否匹配", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean match;
@Schema(description = "匹配条件的提示", example = "所结算商品没有符合条件的商品")
private String description;
}

View File

@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.Min;
import java.time.LocalDateTime;
import java.util.List;
@@ -42,7 +41,6 @@ public class AppCouponRespVO {
private Integer discountPercent;
@Schema(description = "优惠金额", example = "10")
@Min(value = 0, message = "优惠金额需要大于等于 0")
private Integer discountPrice;
@Schema(description = "折扣上限", example = "100") // 单位:分,仅在 discountType 为 PERCENT 使用

View File

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "用户 App - 满减送活动 Response VO")
@@ -19,6 +20,12 @@ public class AppRewardActivityRespVO {
@Schema(description = "活动标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "满啦满啦")
private String name;
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "条件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer conditionType;
@@ -26,7 +33,7 @@ public class AppRewardActivityRespVO {
private Integer productScope;
@Schema(description = "商品 SPU 编号的数组", example = "1,2,3")
private List<Long> productSpuIds;
private List<Long> productScopeValues;
@Schema(description = "优惠规则的数组")
private List<RewardActivityBaseVO.Rule> rules;

View File

@@ -23,6 +23,7 @@ import com.google.common.cache.LoadingCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
@@ -30,11 +31,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -86,7 +87,7 @@ public class AppSeckillActivityController {
// 2.1 查询满足当前阶段的活动
List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(config.getId(), CommonStatusEnum.ENABLE.getStatus());
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
// 2.2 获取 spu 信息
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
@@ -101,7 +102,7 @@ public class AppSeckillActivityController {
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty(pageResult.getTotal()));
}
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(pageResult.getList(), SeckillActivityDO::getId));
// 2. 拼接数据
@@ -149,4 +150,21 @@ public class AppSeckillActivityController {
return success(SeckillActivityConvert.INSTANCE.convert3(activity, productList, startTime, endTime));
}
@GetMapping("/list-by-ids")
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
public CommonResult<List<AppSeckillActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
// 1. 获得开启的活动列表
List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByIds(ids);
activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus()));
if (CollUtil.isEmpty(activityList)) {
return success(Collections.emptyList());
}
// 2. 拼接返回
List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityIds(
convertList(activityList, SeckillActivityDO::getId));
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId));
return success(SeckillActivityConvert.INSTANCE.convertAppList(activityList, productList, spuList));
}
}

View File

@@ -16,6 +16,9 @@ public class AppSeckillActivityRespVO {
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long spuId;
@Schema(description = "商品 SPU 名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "一个白菜")
private String spuName; // 从 SPU 的 name 读取
@Schema(description = "商品图片", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取
example = "https://www.iocoder.cn/xx.png")
private String picUrl;

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
@@ -127,40 +128,42 @@ public interface CombinationActivityConvert {
.setSpuName(spu.getName()).setPicUrl(sku.getPicUrl());
}
List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list);
default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<AppCombinationActivityRespVO> activityList = convertAppList(list);
default List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<CombinationActivityRespVO> activityList = BeanUtils.toBean(list, CombinationActivityRespVO.class);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
return CollectionUtils.convertList(activityList, item -> {
// 设置 product 信息
item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
}
PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result);
default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<AppCombinationActivityRespVO> activityList = BeanUtils.toBean(list, AppCombinationActivityRespVO.class);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
return CollectionUtils.convertList(activityList, item -> {
// 设置 product 信息
item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
}
default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<CombinationProductDO>> productMap = convertMultiMap(productList, CombinationProductDO::getActivityId);
List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> {
// 设置 product 信息
item.setCombinationPrice(getMinValue(productMap.get(item.getId()), CombinationProductDO::getCombinationPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
appPage.setList(list);
return appPage;
return new PageResult<>(convertAppList(result.getList(), productList, spuList), result.getTotal());
}
AppCombinationActivityDetailRespVO convert2(CombinationActivityDO combinationActivity);

View File

@@ -4,9 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageItemRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponRespVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
@@ -16,7 +14,6 @@ import org.mapstruct.factory.Mappers;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
* 优惠劵 Convert

View File

@@ -1,18 +1,19 @@
package cn.iocoder.yudao.module.promotion.convert.discount;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
* 限时折扣活动 Convert
@@ -31,24 +32,105 @@ public interface DiscountActivityConvert {
DiscountActivityRespVO convert(DiscountActivityDO bean);
List<DiscountActivityRespVO> convertList(List<DiscountActivityDO> list);
List<DiscountActivityBaseVO.Product> convertList2(List<DiscountProductDO> list);
List<DiscountProductRespDTO> convertList02(List<DiscountProductDO> list);
PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page);
default PageResult<DiscountActivityRespVO> convertPage(PageResult<DiscountActivityDO> page,
List<DiscountProductDO> discountProductDOList) {
List<DiscountProductDO> discountProductDOList,
List<ProductSpuRespDTO> spuList) {
PageResult<DiscountActivityRespVO> pageResult = convertPage(page);
pageResult.getList().forEach(item -> item.setProducts(convertList2(discountProductDOList)));
// 拼接商品 TODO @zhangshuai类似空行的问题也可以看看
Map<Long, DiscountProductDO> discountActivityMap = CollectionUtils.convertMap(discountProductDOList, DiscountProductDO::getActivityId);
Map<Long, ProductSpuRespDTO> spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId);
pageResult.getList().forEach(item -> {
item.setProducts(convertList2(discountProductDOList));
item.setSpuId(discountActivityMap.get(item.getId())==null?null: discountActivityMap.get(item.getId()).getSpuId());
if (item.getSpuId() != null) {
MapUtils.findAndThen(spuMap, item.getSpuId(),
spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
}
});
return pageResult;
}
DiscountProductDO convert(DiscountActivityBaseVO.Product bean);
default DiscountActivityRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products) {
return BeanUtils.toBean(activity, DiscountActivityRespVO.class).setProducts(convertList2(products));
default DiscountActivityDetailRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products){
if ( activity == null && products == null ) {
return null;
}
DiscountActivityDetailRespVO discountActivityDetailRespVO = new DiscountActivityDetailRespVO();
if ( activity != null ) {
discountActivityDetailRespVO.setName( activity.getName() );
discountActivityDetailRespVO.setStartTime( activity.getStartTime() );
discountActivityDetailRespVO.setEndTime( activity.getEndTime() );
discountActivityDetailRespVO.setRemark( activity.getRemark() );
discountActivityDetailRespVO.setId( activity.getId() );
discountActivityDetailRespVO.setStatus( activity.getStatus() );
discountActivityDetailRespVO.setCreateTime( activity.getCreateTime() );
}
if (!products.isEmpty()) {
discountActivityDetailRespVO.setSpuId(products.get(0).getSpuId());
}
discountActivityDetailRespVO.setProducts( convertList2( products ) );
return discountActivityDetailRespVO;
}
// =========== 比较是否相等 ==========
/**
* 比较两个限时折扣商品是否相等
*
* @param productDO 数据库中的商品
* @param productVO 前端传入的商品
* @return 是否匹配
*/
@SuppressWarnings("DuplicatedCode")
default boolean isEquals(DiscountProductDO productDO, DiscountActivityBaseVO.Product productVO) {
if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
|| ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
|| ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
return false;
}
if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
}
if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
}
return true;
}
/**
* 比较两个限时折扣商品是否相等
* 注意,比较时忽略 id 编号
*
* @param productDO 商品 1
* @param productVO 商品 2
* @return 是否匹配
*/
@SuppressWarnings("DuplicatedCode")
default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
|| ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
|| ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())
|| ObjectUtil.notEqual(productDO.getActivityEndTime(), productVO.getActivityEndTime())
|| ObjectUtil.notEqual(productDO.getActivityStartTime(), productVO.getActivityStartTime())
|| ObjectUtil.notEqual(productDO.getActivityStatus(), productVO.getActivityStatus())) {
return false;
}
if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
}
if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
}
return true;
}
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.convert.seckill;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
@@ -87,6 +88,38 @@ public interface SeckillActivityConvert {
return CollectionUtils.convertList(products, item -> convert(activity, item).setActivityStatus(activity.getStatus()));
}
default List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list,
List<SeckillProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<SeckillActivityRespVO> activityList = BeanUtils.toBean(list, SeckillActivityRespVO.class);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);
return CollectionUtils.convertList(activityList, item -> {
// 设置 product 信息
item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
}
default List<AppSeckillActivityRespVO> convertAppList(List<SeckillActivityDO> list,
List<SeckillProductDO> productList,
List<ProductSpuRespDTO> spuList) {
List<AppSeckillActivityRespVO> activityList = BeanUtils.toBean(list, AppSeckillActivityRespVO.class);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
Map<Long, List<SeckillProductDO>> productMap = convertMultiMap(productList, SeckillProductDO::getActivityId);
return CollectionUtils.convertList(activityList, item -> {
// 设置 product 信息
item.setSeckillPrice(getMinValue(productMap.get(item.getId()), SeckillProductDO::getSeckillPrice));
// 设置 SPU 信息
findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName())
.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()));
return item;
});
}
List<SeckillProductRespVO> convertList2(List<SeckillProductDO> list);
List<AppSeckillActivityRespVO> convertList3(List<SeckillActivityDO> activityList);

View File

@@ -66,10 +66,16 @@ public class DiscountProductDO extends BaseDO {
*/
private Integer discountPrice;
/**
* 活动标题
*
* 冗余 {@link DiscountActivityDO#getName()}
*/
private String activityName;
/**
* 活动状态
*
* 关联 {@link DiscountActivityDO#getStatus()}
* 冗余 {@link DiscountActivityDO#getStatus()}
*/
private Integer activityStatus;
/**

View File

@@ -6,14 +6,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 砍价活动 Mapper
@@ -86,35 +83,13 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
.last("LIMIT " + count));
}
/**
* 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
*
* @param spuIds spu 编号
* @param status 状态
* @return 包含 spuId 和 activityId 的 map 对象列表
*/
default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
return selectMaps(new QueryWrapper<BargainActivityDO>()
.select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
.in("spu_id", spuIds)
.eq("status", status)
.groupBy("spu_id"));
}
/**
* 获取指定活动编号的活动列表且
* 开始时间和结束时间小于给定时间 dateTime 的活动列表
*
* @param ids 活动编号
* @param dateTime 指定日期
* @return 活动列表
*/
default List<BargainActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
return selectList(new LambdaQueryWrapperX<BargainActivityDO>()
.in(BargainActivityDO::getId, ids)
.lt(BargainActivityDO::getStartTime, dateTime)
.gt(BargainActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
.orderByDesc(BargainActivityDO::getCreateTime));
default BargainActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {
LocalDateTime now = LocalDateTime.now();
return selectOne(new LambdaQueryWrapperX<BargainActivityDO>()
.eq(BargainActivityDO::getSpuId, spuId)
.eq(BargainActivityDO::getStatus, status)
.lt(BargainActivityDO::getStartTime, now)
.gt(BargainActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间,也就是说获取指定时间段的活动
}
}

View File

@@ -6,14 +6,10 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 拼团活动 Mapper
@@ -39,40 +35,13 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi
.eq(CombinationActivityDO::getStatus, status));
}
default List<CombinationActivityDO> selectListByStatus(Integer status, Integer count) {
return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
default CombinationActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {
LocalDateTime now = LocalDateTime.now();
return selectOne(new LambdaQueryWrapperX<CombinationActivityDO>()
.eq(CombinationActivityDO::getSpuId, spuId)
.eq(CombinationActivityDO::getStatus, status)
.last("LIMIT " + count));
.lt(CombinationActivityDO::getStartTime, now)
.gt(CombinationActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间,也就是说获取指定时间段的活动
}
/**
* 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
* @param spuIds spu 编号
* @param status 状态
* @return 包含 spuId 和 activityId 的 map 对象列表
*/
default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status) {
return selectMaps(new QueryWrapper<CombinationActivityDO>()
.select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
.in("spu_id", spuIds)
.eq("status", status)
.groupBy("spu_id"));
}
/**
* 获取指定活动编号的活动列表且
* 开始时间和结束时间小于给定时间 dateTime 的活动列表
*
* @param ids 活动编号
* @param dateTime 指定日期
* @return 活动列表
*/
default List<CombinationActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
.in(CombinationActivityDO::getId, ids)
.lt(CombinationActivityDO::getStartTime, dateTime)
.gt(CombinationActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
.orderByDesc(CombinationActivityDO::getCreateTime));
}
}
}

View File

@@ -1,13 +1,11 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.coupon;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.toolkit.MPJWrappers;
import org.apache.ibatis.annotations.Mapper;
@@ -16,8 +14,6 @@ import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -84,22 +80,6 @@ public interface CouponMapper extends BaseMapperX<CouponDO> {
return convertMap(list, map -> MapUtil.getLong(map, templateIdAlias), map -> MapUtil.getInt(map, countAlias));
}
default List<CouponDO> selectListByUserIdAndStatusAndUsePriceLeAndProductScope(
Long userId, Integer status, Integer usePrice, List<Long> spuIds, List<Long> categoryIds) {
Function<List<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
.collect(Collectors.joining(" OR "));
return selectList(new LambdaQueryWrapperX<CouponDO>()
.eq(CouponDO::getUserId, userId)
.eq(CouponDO::getStatus, status)
.le(CouponDO::getUsePrice, usePrice) // 价格小于等于,满足价格使用条件
.and(w -> w.eq(CouponDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()) // 商品范围一:全部
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.SPU.getScope()) // 商品范围二:满足指定商品
.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
.or(ww -> ww.eq(CouponDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope()) // 商品范围三:满足指定分类
.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))));
}
default List<CouponDO> selectListByStatusAndValidEndTimeLe(Integer status, LocalDateTime validEndTime) {
return selectList(new LambdaQueryWrapperX<CouponDO>()
.eq(CouponDO::getStatus, status)

View File

@@ -70,7 +70,7 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> {
.in(CouponTemplateDO::getTakeType, canTakeTypes) // 2. 领取方式一致
.and(ww -> ww.gt(CouponTemplateDO::getValidEndTime, LocalDateTime.now()) // 3.1 未过期
.or().eq(CouponTemplateDO::getValidityType, CouponTemplateValidityTypeEnum.TERM.getType())) // 3.2 领取之后
.apply(" (take_count < total_count OR total_count = -1 )"); // 4. 剩余数量大于 0或者无限领取
.apply(" (take_count < total_count OR total_count = -1)"); // 4. 剩余数量大于 0或者无限领取
}
return canTakeConsumer;
}

View File

@@ -4,9 +4,9 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -19,13 +19,21 @@ import java.util.Map;
@Mapper
public interface DiscountProductMapper extends BaseMapperX<DiscountProductDO> {
default List<DiscountProductDO> selectListByActivityId(Long activityId) {
return selectList(DiscountProductDO::getActivityId, activityId);
}
default List<DiscountProductDO> selectListByActivityId(Collection<Long> activityIds) {
return selectList(DiscountProductDO::getActivityId, activityIds);
default List<DiscountProductDO> selectListBySkuIds(Collection<Long> skuIds) {
return selectList(DiscountProductDO::getSkuId, skuIds);
}
default List<DiscountProductDO> selectListBySkuIdsAndStatusAndNow(Collection<Long> skuIds, Integer status) {
LocalDateTime now = LocalDateTime.now();
return selectList(new LambdaQueryWrapperX<DiscountProductDO>()
.in(DiscountProductDO::getSkuId, skuIds)
.eq(DiscountProductDO::getActivityStatus,status)
.lt(DiscountProductDO::getActivityStartTime, now)
.gt(DiscountProductDO::getActivityEndTime, now));
}
/**
@@ -43,19 +51,4 @@ public interface DiscountProductMapper extends BaseMapperX<DiscountProductDO> {
.groupBy("spu_id"));
}
default List<DiscountProductDO> selectListBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
return selectList(new LambdaQueryWrapperX<DiscountProductDO>()
.in(DiscountProductDO::getSpuId, spuIds)
.eq(DiscountProductDO::getActivityStatus, status));
}
default void updateByActivityId(DiscountProductDO discountProductDO) {
update(discountProductDO, new LambdaUpdateWrapper<DiscountProductDO>()
.eq(DiscountProductDO::getActivityId, discountProductDO.getActivityId()));
}
default void deleteByActivityId(Long activityId) {
delete(DiscountProductDO::getActivityId, activityId);
}
}

View File

@@ -1,14 +1,19 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 满减送活动 Mapper
@@ -25,12 +30,23 @@ public interface RewardActivityMapper extends BaseMapperX<RewardActivityDO> {
.orderByDesc(RewardActivityDO::getId));
}
default List<RewardActivityDO> selectListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) {
default List<RewardActivityDO> selectListBySpuIdAndStatusAndNow(Collection<Long> spuIds,
Collection<Long> categoryIds,
Integer status) {
LocalDateTime now = LocalDateTime.now();
Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
.map(id -> StrUtil.format("FIND_IN_SET({}, product_scope_values) ", id))
.collect(Collectors.joining(" OR "));
return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
.eq(RewardActivityDO::getStatus, status)
// 开始时间 < 指定时间dateTime < 结束时间,也就是说获取指定时间段的活动
.lt(RewardActivityDO::getStartTime, dateTime).gt(RewardActivityDO::getEndTime, dateTime)
.orderByAsc(RewardActivityDO::getStartTime)
.lt(RewardActivityDO::getStartTime, now)
.gt(RewardActivityDO::getEndTime, now)
.and(i -> i.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.SPU.getScope())
.and(i1 -> i1.apply(productScopeValuesFindInSetFunc.apply(spuIds)))
.or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.ALL.getScope()))
.or(i1 -> i1.eq(RewardActivityDO::getProductScope, PromotionProductScopeEnum.CATEGORY.getScope())
.and(i2 -> i2.apply(productScopeValuesFindInSetFunc.apply(categoryIds)))))
.orderByDesc(RewardActivityDO::getId)
);
}

View File

@@ -8,15 +8,11 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 秒杀活动 Mapper
@@ -51,7 +47,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
Assert.isTrue(count > 0);
return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
.eq(SeckillActivityDO::getId, id)
.gt(SeckillActivityDO::getStock, count)
.ge(SeckillActivityDO::getStock, count)
.setSql("stock = stock - " + count));
}
@@ -69,41 +65,21 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
.setSql("stock = stock + " + count));
}
default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) {
default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status, LocalDateTime dateTime) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
.eqIfPresent(SeckillActivityDO::getStatus, status)
.lt(SeckillActivityDO::getStartTime, dateTime)
.gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
.apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
}
/**
* 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
*
* @param spuIds spu 编号
* @param status 状态
* @return 包含 spuId 和 activityId 的 map 对象列表
*/
default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status) {
return selectMaps(new QueryWrapper<SeckillActivityDO>()
.select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
.in("spu_id", spuIds)
.eq("status", status)
.groupBy("spu_id"));
}
/**
* 获取指定活动编号的活动列表且
* 开始时间和结束时间小于给定时间 dateTime 的活动列表
*
* @param ids 活动编号
* @param dateTime 指定日期
* @return 活动列表
*/
default List<SeckillActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
return selectList(new LambdaQueryWrapperX<SeckillActivityDO>()
.in(SeckillActivityDO::getId, ids)
.lt(SeckillActivityDO::getStartTime, dateTime)
.gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
.orderByDesc(SeckillActivityDO::getCreateTime));
default SeckillActivityDO selectBySpuIdAndStatusAndNow(Long spuId, Integer status) {
LocalDateTime now = LocalDateTime.now();
return selectOne(new LambdaQueryWrapperX<SeckillActivityDO>()
.eq(SeckillActivityDO::getSpuId, spuId)
.eq(SeckillActivityDO::getStatus, status)
.lt(SeckillActivityDO::getStartTime, now)
.gt(SeckillActivityDO::getEndTime, now)); // 开始时间 < now < 结束时间,也就是说获取指定时间段的活动
}
}

View File

@@ -6,10 +6,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -108,13 +106,11 @@ public interface BargainActivityService {
List<BargainActivityDO> getBargainActivityListByCount(Integer count);
/**
* 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
* 获得 SPU 进行中的砍价活动
*
* @param spuIds spu 编号
* @param status 状态
* @param dateTime 日期时间
* @return 砍价活动列表
* @param spuId SPU 编号数组
* @return 砍价活动
*/
List<BargainActivityDO> getBargainActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
BargainActivityDO getMatchBargainActivityBySpuId(Long spuId);
}

View File

@@ -1,7 +1,5 @@
package cn.iocoder.yudao.module.promotion.service.bargain;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -15,17 +13,17 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
import cn.iocoder.yudao.module.promotion.convert.bargain.BargainActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainActivityMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -194,15 +192,8 @@ public class BargainActivityServiceImpl implements BargainActivityService {
}
@Override
public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
// 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
List<Map<String, Object>> spuIdAndActivityIdMaps = bargainActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
return Collections.emptyList();
}
// 2. 查询活动详情
return bargainActivityMapper.selectListByIdsAndDateTimeLt(
convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
public BargainActivityDO getMatchBargainActivityBySpuId(Long spuId) {
return bargainActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());
}
}

View File

@@ -7,9 +7,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activit
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -100,14 +99,6 @@ public interface CombinationActivityService {
*/
List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids);
/**
* 获取正在进行的活动分页数据
*
* @param count 需要的数量
* @return 拼团活动分页
*/
List<CombinationActivityDO> getCombinationActivityListByCount(Integer count);
/**
* 获取正在进行的活动分页数据
*
@@ -117,22 +108,20 @@ public interface CombinationActivityService {
PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam);
/**
* 获取指定活动、指定 sku 编号的商品
* 获取指定活动、指定 SKU 编号的商品
*
* @param activityId 活动编号
* @param skuId sku 编号
* @param skuId SKU 编号
* @return 活动商品信息
*/
CombinationProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId);
/**
* 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
* 获得 SPU 进行中的拼团活动
*
* @param spuIds spu 编号
* @param status 状态
* @param dateTime 日期时间
* @return 拼团活动列表
* @param spuId SPU 编号数组
* @return 拼团活动
*/
List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
CombinationActivityDO getMatchCombinationActivityBySpuId(Long spuId);
}

View File

@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -20,19 +19,18 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationA
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -178,7 +176,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
combinationProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
combinationProductMapper.deleteBatchIds(CollectionUtils.convertList(diffList.get(2), CombinationProductDO::getId));
combinationProductMapper.deleteByIds(CollectionUtils.convertList(diffList.get(2), CombinationProductDO::getId));
}
}
@@ -225,11 +223,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
return combinationActivityMapper.selectList(CombinationActivityDO::getId, ids);
}
@Override
public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) {
return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count);
}
@Override
public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) {
return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus());
@@ -243,15 +236,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
}
@Override
public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
// 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
List<Map<String, Object>> spuIdAndActivityIdMaps = combinationActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
return Collections.emptyList();
}
// 2.查询活动详情
return combinationActivityMapper.selectListByIdsAndDateTimeLt(
convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
public CombinationActivityDO getMatchCombinationActivityBySpuId(Long spuId) {
return combinationActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());
}
}

View File

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
@@ -18,26 +17,6 @@ import java.util.*;
*/
public interface CouponService {
/**
* 校验优惠劵,包括状态、有限期
* <p>
* 1. 如果校验通过,则返回优惠劵信息
* 2. 如果校验不通过,则直接抛出业务异常
*
* @param id 优惠劵编号
* @param userId 用户编号
* @return 优惠劵信息
*/
CouponDO validCoupon(Long id, Long userId);
/**
* 校验优惠劵,包括状态、有限期
*
* @param coupon 优惠劵
* @see #validCoupon(Long, Long) 逻辑相同,只是入参不同
*/
void validCoupon(CouponDO coupon);
/**
* 使用优惠劵
*
@@ -171,15 +150,6 @@ public interface CouponService {
return MapUtil.getInt(map, templateId, 0);
}
/**
* 获取用户匹配的优惠券列表
*
* @param userId 用户编号
* @param matchReqVO 匹配参数
* @return 优惠券列表
*/
List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
/**
* 获取用户是否可以领取优惠券
*

View File

@@ -12,7 +12,6 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.coupon.CouponPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.app.coupon.vo.coupon.AppCouponMatchReqVO;
import cn.iocoder.yudao.module.promotion.convert.coupon.CouponConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
@@ -56,18 +55,9 @@ public class CouponServiceImpl implements CouponService {
private MemberUserApi memberUserApi;
@Override
public CouponDO validCoupon(Long id, Long userId) {
CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
if (coupon == null) {
throw exception(COUPON_NOT_EXISTS);
}
validCoupon(coupon);
return coupon;
}
@Override
public void validCoupon(CouponDO coupon) {
public void useCoupon(Long id, Long userId, Long orderId) {
// 校验状态
CouponDO coupon = couponMapper.selectByIdAndUserId(id, userId);
if (ObjectUtil.notEqual(coupon.getStatus(), CouponStatusEnum.UNUSED.getStatus())) {
throw exception(COUPON_STATUS_NOT_UNUSED);
}
@@ -75,12 +65,6 @@ public class CouponServiceImpl implements CouponService {
if (!LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime())) {
throw exception(COUPON_VALID_TIME_NOT_NOW);
}
}
@Override
public void useCoupon(Long id, Long userId, Long orderId) {
// 校验优惠劵
validCoupon(id, userId);
// 更新状态
int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
@@ -284,9 +268,8 @@ public class CouponServiceImpl implements CouponService {
if (couponTemplate == null) {
throw exception(COUPON_TEMPLATE_NOT_EXISTS);
}
// 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
&& couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
// 校验剩余数量
if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
}
// 校验"固定日期"的有效期类型是否过期
@@ -355,16 +338,6 @@ public class CouponServiceImpl implements CouponService {
return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
}
@Override
public List<CouponDO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
CouponStatusEnum.UNUSED.getStatus(),
matchReqVO.getPrice(), matchReqVO.getSpuIds(), matchReqVO.getCategoryIds());
// 兜底逻辑:如果 CouponExpireJob 未执行status 未变成 EXPIRE ,但是 validEndTime 已经过期了,需要进行过滤
list.removeIf(coupon -> !LocalDateTimeUtils.isBetween(coupon.getValidStartTime(), coupon.getValidEndTime()));
return list;
}
@Override
public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {
// 1. 未登录时,都显示可以领取

View File

@@ -12,10 +12,10 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

View File

@@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivit
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -24,10 +23,10 @@ public interface DiscountActivityService {
*
* 注意,匹配的条件,仅仅是日期符合,并且处于开启状态
*
* @param spuIds SKU 编号数组
* @param skuIds SKU 编号数组
* @return 匹配的限时折扣商品
*/
List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> spuIds);
List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds);
/**
* 创建限时折扣活动
@@ -91,14 +90,11 @@ public interface DiscountActivityService {
List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds);
/**
* 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
* 获取指定 SPU 编号最近参加的活动,每个 spuId 只返回一条记录
*
* @param spuIds spu 编号
* @param status 状态
* @param dateTime 当前日期时间
* @param spuIds SPU 编号数组
* @return 折扣活动列表
*/
List<DiscountActivityDO> getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(
Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
List<DiscountActivityDO> getDiscountActivityListBySpuIds(Collection<Long> spuIds);
}

View File

@@ -1,14 +1,11 @@
package cn.iocoder.yudao.module.promotion.service.discount;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
@@ -18,7 +15,6 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivit
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;
import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -30,11 +26,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
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.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
/**
@@ -50,26 +44,10 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
private DiscountActivityMapper discountActivityMapper;
@Resource
private DiscountProductMapper discountProductMapper;
@Resource
private ProductSkuApi productSkuApi;
@Override
public List<DiscountProductDO> getMatchDiscountProductList(Collection<Long> spuIds) {
// 1.1 查询出 spu 对应的开启的限时折扣商品
List<DiscountProductDO> productList = discountProductMapper.selectListBySpuIdsAndStatus(spuIds,
CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isEmpty(productList)) {
return Collections.emptyList();
}
// 1.2 查询出符合的限时折扣活动
List<DiscountActivityDO> activityList = discountActivityMapper.selectListByIdsAndDateTimeLt(
convertSet(productList, DiscountProductDO::getActivityId), LocalDateTime.now());
if (CollUtil.isEmpty(productList)) {
return Collections.emptyList();
}
// 2. 获得这些活动的商品列表
return discountProductMapper.selectListByActivityId(convertList(activityList, DiscountActivityDO::getId));
public List<DiscountProductDO> getMatchDiscountProductListBySkuIds(Collection<Long> skuIds) {
return discountProductMapper.selectListBySkuIdsAndStatusAndNow(skuIds, CommonStatusEnum.ENABLE.getStatus());
}
@Override
@@ -77,8 +55,6 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
public Long createDiscountActivity(DiscountActivityCreateReqVO createReqVO) {
// 校验商品是否冲突
validateDiscountActivityProductConflicts(null, createReqVO.getProducts());
// 校验商品是否存在
validateProductExists(createReqVO.getProducts());
// 插入活动
DiscountActivityDO discountActivity = DiscountActivityConvert.INSTANCE.convert(createReqVO)
@@ -86,10 +62,10 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
discountActivityMapper.insert(discountActivity);
// 插入商品
List<DiscountProductDO> discountProducts = BeanUtils.toBean(createReqVO.getProducts(), DiscountProductDO.class,
product -> product.setActivityId(discountActivity.getId()).setActivityStatus(discountActivity.getStatus())
product -> product.setActivityId(discountActivity.getId())
.setActivityName(discountActivity.getName()).setActivityStatus(discountActivity.getStatus())
.setActivityStartTime(createReqVO.getStartTime()).setActivityEndTime(createReqVO.getEndTime()));
discountProductMapper.insertBatch(discountProducts);
// 返回
return discountActivity.getId();
}
@@ -103,40 +79,36 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
}
// 校验商品是否冲突
validateDiscountActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());
// 校验商品是否存在
validateProductExists(updateReqVO.getProducts());
// 更新活动
DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO)
.setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime()));
DiscountActivityDO updateObj = DiscountActivityConvert.INSTANCE.convert(updateReqVO);
discountActivityMapper.updateById(updateObj);
// 更新商品
updateDiscountProduct(updateObj, updateReqVO.getProducts());
updateDiscountProduct(updateReqVO);
}
private void updateDiscountProduct(DiscountActivityDO activity, List<DiscountActivityCreateReqVO.Product> products) {
// 第一步,对比新老数据,获得添加、修改、删除的列表
List<DiscountProductDO> newList = BeanUtils.toBean(products, DiscountProductDO.class,
product -> product.setActivityId(activity.getId()).setActivityStatus(activity.getStatus())
.setActivityStartTime(activity.getStartTime()).setActivityEndTime(activity.getEndTime()));
List<DiscountProductDO> oldList = discountProductMapper.selectListByActivityId(activity.getId());
List<List<DiscountProductDO>> diffList = CollectionUtils.diffList(oldList, newList, (oldVal, newVal) -> {
boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId());
if (same) {
newVal.setId(oldVal.getId());
}
return same;
});
// 第二步,批量添加、修改、删除
if (CollUtil.isNotEmpty(diffList.get(0))) {
discountProductMapper.insertBatch(diffList.get(0));
private void updateDiscountProduct(DiscountActivityUpdateReqVO updateReqVO) {
// TODO @zhangshuai这里的逻辑可以优化下哈参考 CombinationActivityServiceImpl 的 updateCombinationProduct主要是 CollectionUtils.diffList 的使用哈;
// 然后原先是使用 DiscountActivityConvert.INSTANCE.isEquals 对比,现在看看是不是简化就基于 skuId 对比就完事了;之前写的太精细,意义不大;
List<DiscountProductDO> dbDiscountProducts = discountProductMapper.selectListByActivityId(updateReqVO.getId());
// 计算要删除的记录
List<Long> deleteIds = convertList(dbDiscountProducts, DiscountProductDO::getId,
discountProductDO -> updateReqVO.getProducts().stream()
.noneMatch(product -> DiscountActivityConvert.INSTANCE.isEquals(discountProductDO, product)));
if (CollUtil.isNotEmpty(deleteIds)) {
discountProductMapper.deleteByIds(deleteIds);
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
discountProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
discountProductMapper.deleteBatchIds(convertList(diffList.get(2), DiscountProductDO::getId));
// 计算新增的记录
List<DiscountProductDO> newDiscountProducts = convertList(updateReqVO.getProducts(),
product -> DiscountActivityConvert.INSTANCE.convert(product)
.setActivityId(updateReqVO.getId())
.setActivityName(updateReqVO.getName())
.setActivityStartTime(updateReqVO.getStartTime())
.setActivityEndTime(updateReqVO.getEndTime()));
newDiscountProducts.removeIf(product -> dbDiscountProducts.stream().anyMatch(
dbProduct -> DiscountActivityConvert.INSTANCE.isEquals(dbProduct, product))); // 如果匹配到,说明是更新的
if (CollectionUtil.isNotEmpty(newDiscountProducts)) {
discountProductMapper.insertBatch(newDiscountProducts);
}
}
@@ -147,44 +119,20 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
* @param products 商品列表
*/
private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) {
// 1.1 查询所有开启的折扣活动
List<DiscountActivityDO> activityList = discountActivityMapper.selectList(DiscountActivityDO::getStatus,
CommonStatusEnum.ENABLE.getStatus());
if (id != null) { // 时排除自己
activityList.removeIf(item -> ObjectUtil.equal(item.getId(), id));
if (CollUtil.isEmpty(products)) {
return;
}
// 查询商品参加的活动
List<DiscountProductDO> list = discountProductMapper.selectListByActivityId(id);
List<DiscountProductDO> matchDiscountProductList = discountProductMapper.selectListBySkuIds(
convertSet(list, DiscountProductDO::getSkuId));
if (id != null) { // 排除自己这个活动
matchDiscountProductList.removeIf(product -> id.equals(product.getActivityId()));
}
// 如果非空,则说明冲突
if (CollUtil.isNotEmpty(matchDiscountProductList)) {
throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS);
}
// 1.2 查询活动下的所有商品
List<DiscountProductDO> productList = discountProductMapper.selectListByActivityId(
convertList(activityList, DiscountActivityDO::getId));
Map<Long, List<DiscountProductDO>> productListMap = convertMultiMap(productList, DiscountProductDO::getActivityId);
// 2. 校验商品是否冲突
activityList.forEach(item -> {
findAndThen(productListMap, item.getId(), discountProducts -> {
if (!intersectionDistinct(convertList(discountProducts, DiscountProductDO::getSpuId),
convertList(products, DiscountActivityBaseVO.Product::getSpuId)).isEmpty()) {
throw exception(DISCOUNT_ACTIVITY_SPU_CONFLICTS, item.getName());
}
});
});
}
/**
* 校验活动商品是否都存在
*
* @param products 活动商品
*/
private void validateProductExists(List<DiscountActivityBaseVO.Product> products) {
// 1.获得商品所有的 sku
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(
convertList(products, DiscountActivityBaseVO.Product::getSpuId));
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
// 2. 校验商品 sku 都存在
products.forEach(product -> {
if (!skuMap.containsKey(product.getSkuId())) {
throw exception(SKU_NOT_EXISTS);
}
});
}
@Override
@@ -195,11 +143,9 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
throw exception(DISCOUNT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
}
// 更新活动状态
discountActivityMapper.updateById(new DiscountActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus()));
// 更新活动商品状态
discountProductMapper.updateByActivityId(new DiscountProductDO().setActivityId(id).setActivityStatus(
CommonStatusEnum.DISABLE.getStatus()));
// 更新
DiscountActivityDO updateObj = new DiscountActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus());
discountActivityMapper.updateById(updateObj);
}
@Override
@@ -210,10 +156,8 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
throw exception(DISCOUNT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
}
// 删除活动
// 删除
discountActivityMapper.deleteById(id);
// 删除活动商品
discountProductMapper.deleteByActivityId(id);
}
private DiscountActivityDO validateDiscountActivityExists(Long id) {
@@ -241,20 +185,21 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
@Override
public List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds) {
return discountProductMapper.selectList(DiscountProductDO::getActivityId, activityIds);
return discountProductMapper.selectList("activity_id", activityIds);
}
@Override
public List<DiscountActivityDO> getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
public List<DiscountActivityDO> getDiscountActivityListBySpuIds(Collection<Long> spuIds) {
// 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
List<Map<String, Object>> spuIdAndActivityIdMaps = discountProductMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
List<Map<String, Object>> spuIdAndActivityIdMaps = discountProductMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(
spuIds, CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
return Collections.emptyList();
}
// 2. 查询活动详情
return discountActivityMapper.selectListByIdsAndDateTimeLt(
convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), LocalDateTime.now());
}
}

View File

@@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.promotion.service.reward;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
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 jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
@@ -63,12 +64,11 @@ public interface RewardActivityService {
PageResult<RewardActivityDO> getRewardActivityPage(RewardActivityPageReqVO pageReqVO);
/**
* 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
* 获得 spuId 商品匹配的的满减送活动列表
*
* @param status 状态
* @param dateTime 当前日期时间
* @param spuIds SPU 编号数组
* @return 满减送活动列表
*/
List<RewardActivityDO> getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime);
List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds);
}

View File

@@ -1,31 +1,33 @@
package cn.iocoder.yudao.module.promotion.service.reward;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
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.PromotionConditionTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
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.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
/**
@@ -52,9 +54,9 @@ public class RewardActivityServiceImpl implements RewardActivityService {
// 1.2 校验商品是否冲突
validateRewardActivitySpuConflicts(null, createReqVO);
// 2. 插入
// 插入
RewardActivityDO rewardActivity = BeanUtils.toBean(createReqVO, RewardActivityDO.class)
.setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getEndTime()));
.setStatus(CommonStatusEnum.ENABLE.getStatus());
rewardActivityMapper.insert(rewardActivity);
// 返回
return rewardActivity.getId();
@@ -73,8 +75,7 @@ public class RewardActivityServiceImpl implements RewardActivityService {
validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO);
// 2. 更新
RewardActivityDO updateObj = BeanUtils.toBean(updateReqVO, RewardActivityDO.class)
.setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getEndTime()));
RewardActivityDO updateObj = BeanUtils.toBean(updateReqVO, RewardActivityDO.class);
rewardActivityMapper.updateById(updateObj);
}
@@ -195,8 +196,61 @@ public class RewardActivityServiceImpl implements RewardActivityService {
}
@Override
public List<RewardActivityDO> getRewardActivityListByStatusAndDateTimeLt(Integer status, LocalDateTime dateTime) {
return rewardActivityMapper.selectListByStatusAndDateTimeLt(status, dateTime);
public List<RewardActivityMatchRespDTO> getMatchRewardActivityListBySpuIds(Collection<Long> spuIds) {
// 1. 查询商品分类
List<ProductSpuRespDTO> spuList = productSpuApi.getSpuList(spuIds);
if (CollUtil.isEmpty(spuList)) {
return Collections.emptyList();
}
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
// 2. 查询出指定 spuId 的 spu 参加的活动
List<RewardActivityDO> activityList = rewardActivityMapper.selectListBySpuIdAndStatusAndNow(
spuIds, convertSet(spuList, ProductSpuRespDTO::getCategoryId), CommonStatusEnum.ENABLE.getStatus());
if (CollUtil.isEmpty(activityList)) {
return Collections.emptyList();
}
// 3. 转换成 Response DTO
return BeanUtils.toBean(activityList, RewardActivityMatchRespDTO.class, activityDTO -> {
// 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.getRules().forEach(rule -> {
String description = "";
if (PromotionConditionTypeEnum.PRICE.getType().equals(activityDTO.getConditionType())) {
description += StrUtil.format("满 {} 元", MoneyUtils.fenToYuanStr(rule.getLimit()));
} else {
description += StrUtil.format("满 {} 件", rule.getLimit());
}
if (rule.getDiscountPrice() != null) {
description += StrUtil.format("减 {}", MoneyUtils.fenToYuanStr(rule.getDiscountPrice()));
} else if (Boolean.TRUE.equals(rule.getFreeDelivery())) {
description += "包邮";
} else if (rule.getPoint() != null && rule.getPoint() > 0) {
description += StrUtil.format("增 {} 积分", rule.getPoint());
} else if (CollUtil.isNotEmpty(rule.getGiveCouponTemplateCounts())) {
description += StrUtil.format("送 {} 张优惠券",
getSumValue(rule.getGiveCouponTemplateCounts().values(), count -> count, Integer::sum));
}
rule.setDescription(description);
});
});
}
}

View File

@@ -8,9 +8,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.Se
import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@@ -98,7 +97,7 @@ public interface SeckillActivityService {
* @param activityIds 活动编号
* @return 活动商品列表
*/
List<SeckillProductDO> getSeckillProductListByActivityId(Collection<Long> activityIds);
List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds);
/**
* 通过活动时段编号获取指定 status 的秒杀活动
@@ -110,7 +109,7 @@ public interface SeckillActivityService {
List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status);
/**
* 通过活动时段获取秒杀活动
* 通过活动时段获取开始的秒杀活动
*
* @param pageReqVO 请求
* @return 秒杀活动列表
@@ -130,13 +129,19 @@ public interface SeckillActivityService {
SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);
/**
* 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
* 获得 SPU 进行中的秒杀活动
*
* @param spuIds spu 编号
* @param status 状态
* @param dateTime 日期时间
* @return 秒杀活动列表
* @param spuId SPU 编号数组
* @return 秒杀活动
*/
List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
SeckillActivityDO getMatchSeckillActivityBySpuId(Long spuId);
/**
* 获得拼团活动列表
*
* @param ids 拼团活动编号数组
* @return 拼团活动的列表
*/
List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids);
}

View File

@@ -1,8 +1,6 @@
package cn.iocoder.yudao.module.promotion.service.seckill;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -23,14 +21,13 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -56,8 +53,10 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
private SeckillActivityMapper seckillActivityMapper;
@Resource
private SeckillProductMapper seckillProductMapper;
@Resource
private SeckillConfigService seckillConfigService;
@Resource
private ProductSpuApi productSpuApi;
@Resource
@@ -219,7 +218,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
seckillProductMapper.updateBatch(diffList.get(1));
}
if (isNotEmpty(diffList.get(2))) {
seckillProductMapper.deleteBatchIds(convertList(diffList.get(2), SeckillProductDO::getId));
seckillProductMapper.deleteByIds(convertList(diffList.get(2), SeckillProductDO::getId));
}
}
@@ -249,7 +248,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
seckillActivityMapper.deleteById(id);
// 删除活动商品
List<SeckillProductDO> products = seckillProductMapper.selectListByActivityId(id);
seckillProductMapper.deleteBatchIds(convertSet(products, SeckillProductDO::getId));
seckillProductMapper.deleteByIds(convertSet(products, SeckillProductDO::getId));
}
private SeckillActivityDO validateSeckillActivityExists(Long id) {
@@ -276,7 +275,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
}
@Override
public List<SeckillProductDO> getSeckillProductListByActivityId(Collection<Long> activityIds) {
public List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds) {
return seckillProductMapper.selectListByActivityId(activityIds);
}
@@ -289,7 +288,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
@Override
public PageResult<SeckillActivityDO> getSeckillActivityAppPageByConfigId(AppSeckillActivityPageReqVO pageReqVO) {
return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus());
return seckillActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now());
}
@Override
@@ -325,15 +324,13 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
}
@Override
public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
// 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
List<Map<String, Object>> spuIdAndActivityIdMaps = seckillActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
return Collections.emptyList();
}
// 2.查询活动详情
return seckillActivityMapper.selectListByIdsAndDateTimeLt(
convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
public SeckillActivityDO getMatchSeckillActivityBySpuId(Long spuId) {
return seckillActivityMapper.selectBySpuIdAndStatusAndNow(spuId, CommonStatusEnum.ENABLE.getStatus());
}
@Override
public List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids) {
return seckillActivityMapper.selectBatchIds(ids);
}
}

View File

@@ -1,25 +0,0 @@
package cn.iocoder.yudao.module.promotion.util;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import java.time.LocalDateTime;
/**
* 活动工具类
*
* @author 芋道源码
*/
public class PromotionUtils {
/**
* 根据时间,计算活动状态
*
* @param endTime 结束时间
* @return 活动状态
*/
public static Integer calculateActivityStatus(LocalDateTime endTime) {
return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
}
}

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper">
</mapper>

View File

@@ -18,15 +18,8 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static cn.hutool.core.collection.CollUtil.intersectionDistinct;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -34,8 +27,6 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_NOT_EXISTS;
import static com.google.common.primitives.Longs.asList;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -176,90 +167,91 @@ public class RewardActivityServiceImplTest extends BaseMockitoUnitTest {
assertPojoEquals(dbRewardActivity, pageResult.getList().get(0), "rules");
}
@Test
public void testGetRewardActivities_all() {
LocalDateTime now = LocalDateTime.now();
// mock 数据
RewardActivityDO allActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setProductScope(PromotionProductScopeEnum.ALL.getScope()).setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
rewardActivityMapper.insert(allActivity);
RewardActivityDO productActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
.setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
rewardActivityMapper.insert(productActivity);
// 准备参数
Set<Long> spuIds = asSet(1L, 2L);
// 调用
List<RewardActivityDO> activityList = rewardActivityServiceImpl.getRewardActivityListByStatusAndDateTimeLt(
CommonStatusEnum.ENABLE.getStatus(), now);
List<RewardActivityDO> matchRewardActivityList = filterMatchActivity(spuIds, activityList);
// 断言
assertEquals(matchRewardActivityList.size(), 1);
matchRewardActivityList.forEach((activity) -> {
if (activity.getId().equals(productActivity.getId())) {
assertPojoEquals(activity, productActivity);
assertEquals(activity.getProductScopeValues(), asList(1L, 2L));
} else {
fail();
}
});
}
@Test
public void testGetRewardActivities_product() {
LocalDateTime now = LocalDateTime.now();
// mock 数据
RewardActivityDO productActivity01 = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
.setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
rewardActivityMapper.insert(productActivity01);
RewardActivityDO productActivity02 = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(singletonList(3L))
.setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
rewardActivityMapper.insert(productActivity02);
// 准备参数
Set<Long> spuIds = asSet(1L, 2L, 3L);
List<RewardActivityDO> activityList = rewardActivityServiceImpl.getRewardActivityListByStatusAndDateTimeLt(
CommonStatusEnum.ENABLE.getStatus(), now);
List<RewardActivityDO> matchRewardActivityList = filterMatchActivity(spuIds, activityList);
// 断言
assertEquals(matchRewardActivityList.size(), 2);
matchRewardActivityList.forEach((activity) -> {
if (activity.getId().equals(productActivity01.getId())) {
assertPojoEquals(activity, productActivity01);
assertEquals(activity.getProductScopeValues(), asList(1L, 2L));
} else if (activity.getId().equals(productActivity02.getId())) {
assertPojoEquals(activity, productActivity02);
assertEquals(activity.getProductScopeValues(), singletonList(3L));
} else {
fail();
}
});
}
/**
* 获得满减送的订单项(商品)列表
*
* @param spuIds 商品编号
* @param activityList 活动列表
* @return 订单项(商品)列表
*/
private List<RewardActivityDO> filterMatchActivity(Collection<Long> spuIds, List<RewardActivityDO> activityList) {
List<RewardActivityDO> resultActivityList = new ArrayList<>();
for (RewardActivityDO activity : activityList) {
// 情况一:全部商品都可以参与
if (PromotionProductScopeEnum.isAll(activity.getProductScope())) {
resultActivityList.add(activity);
}
// 情况二:指定商品参与
if (PromotionProductScopeEnum.isSpu(activity.getProductScope()) &&
!intersectionDistinct(activity.getProductScopeValues(), spuIds).isEmpty()) {
resultActivityList.add(activity);
}
}
return resultActivityList;
}
// TODO 芋艿:后续完善单测
// @Test
// public void testGetRewardActivities_all() {
// LocalDateTime now = LocalDateTime.now();
// // mock 数据
// RewardActivityDO allActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
// .setProductScope(PromotionProductScopeEnum.ALL.getScope()).setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
// rewardActivityMapper.insert(allActivity);
// RewardActivityDO productActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
// .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
// .setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
// rewardActivityMapper.insert(productActivity);
// // 准备参数
// Set<Long> spuIds = asSet(1L, 2L);
//
// // 调用
// List<RewardActivityDO> activityList = rewardActivityServiceImpl.getRewardActivityListByStatusAndDateTimeLt(
// CommonStatusEnum.ENABLE.getStatus(), now);
// List<RewardActivityDO> matchRewardActivityList = filterMatchActivity(spuIds, activityList);
// // 断言
// assertEquals(matchRewardActivityList.size(), 1);
// matchRewardActivityList.forEach((activity) -> {
// if (activity.getId().equals(productActivity.getId())) {
// assertPojoEquals(activity, productActivity);
// assertEquals(activity.getProductScopeValues(), asList(1L, 2L));
// } else {
// fail();
// }
// });
// }
//
// @Test
// public void testGetRewardActivities_product() {
// LocalDateTime now = LocalDateTime.now();
// // mock 数据
// RewardActivityDO productActivity01 = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
// .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(asList(1L, 2L))
// .setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
// rewardActivityMapper.insert(productActivity01);
// RewardActivityDO productActivity02 = randomPojo(RewardActivityDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
// .setProductScope(PromotionProductScopeEnum.SPU.getScope()).setProductScopeValues(singletonList(3L))
// .setStartTime(now.minusDays(1)).setEndTime(now.plusDays(1)));
// rewardActivityMapper.insert(productActivity02);
// // 准备参数
// Set<Long> spuIds = asSet(1L, 2L, 3L);
//
// List<RewardActivityDO> activityList = rewardActivityServiceImpl.getRewardActivityListByStatusAndDateTimeLt(
// CommonStatusEnum.ENABLE.getStatus(), now);
// List<RewardActivityDO> matchRewardActivityList = filterMatchActivity(spuIds, activityList);
// // 断言
// assertEquals(matchRewardActivityList.size(), 2);
// matchRewardActivityList.forEach((activity) -> {
// if (activity.getId().equals(productActivity01.getId())) {
// assertPojoEquals(activity, productActivity01);
// assertEquals(activity.getProductScopeValues(), asList(1L, 2L));
// } else if (activity.getId().equals(productActivity02.getId())) {
// assertPojoEquals(activity, productActivity02);
// assertEquals(activity.getProductScopeValues(), singletonList(3L));
// } else {
// fail();
// }
// });
// }
//
// /**
// * 获得满减送的订单项(商品)列表
// *
// * @param spuIds 商品编号
// * @param activityList 活动列表
// * @return 订单项(商品)列表
// */
// private List<RewardActivityDO> filterMatchActivity(Collection<Long> spuIds, List<RewardActivityDO> activityList) {
// List<RewardActivityDO> resultActivityList = new ArrayList<>();
// for (RewardActivityDO activity : activityList) {
// // 情况一:全部商品都可以参与
// if (PromotionProductScopeEnum.isAll(activity.getProductScope())) {
// resultActivityList.add(activity);
// }
// // 情况二:指定商品参与
// if (PromotionProductScopeEnum.isSpu(activity.getProductScope()) &&
// !intersectionDistinct(activity.getProductScopeValues(), spuIds).isEmpty()) {
// resultActivityList.add(activity);
// }
// }
// return resultActivityList;
// }
}