Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop-tmp

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/coupon/AppCouponController.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponMapper.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
This commit is contained in:
YunaiV
2024-09-15 11:24:32 +08:00
70 changed files with 674 additions and 893 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,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

@@ -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

@@ -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<AppCouponMatchRespVO> list = couponService.getMatchCouponList(getLoginUserId(), matchReqVO);
return success(list);
}
@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

@@ -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

@@ -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

@@ -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;

View File

@@ -100,14 +100,6 @@ public interface CombinationActivityService {
*/
List<CombinationActivityDO> getCombinationActivityListByIds(Collection<Long> ids);
/**
* 获取正在进行的活动分页数据
*
* @param count 需要的数量
* @return 拼团活动分页
*/
List<CombinationActivityDO> getCombinationActivityListByCount(Integer count);
/**
* 获取正在进行的活动分页数据
*

View File

@@ -225,11 +225,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());

View File

@@ -4,8 +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.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
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;
@@ -19,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);
/**
* 使用优惠劵
*
@@ -172,15 +150,6 @@ public interface CouponService {
return MapUtil.getInt(map, templateId, 0);
}
/**
* 获取用户匹配的优惠券列表
*
* @param userId 用户编号
* @param matchReqVO 匹配参数
* @return 优惠券列表
*/
List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO);
/**
* 获取用户是否可以领取优惠券
*

View File

@@ -12,13 +12,10 @@ 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.controller.app.coupon.vo.coupon.AppCouponMatchRespVO;
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;
import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityTypeEnum;
@@ -58,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);
}
@@ -77,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(),
@@ -286,10 +268,8 @@ public class CouponServiceImpl implements CouponService {
if (couponTemplate == null) {
throw exception(COUPON_TEMPLATE_NOT_EXISTS);
}
// 校验剩余数量(仅在 CouponTakeTypeEnum.USER 用户领取时)
if (CouponTakeTypeEnum.isUser(couponTemplate.getTakeCount())
&& couponTemplate.getTotalCount() != null
&& couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
// 校验剩余数量
if (couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) {
throw exception(COUPON_TEMPLATE_NOT_ENOUGH);
}
// 校验"固定日期"的有效期类型是否过期
@@ -299,7 +279,7 @@ public class CouponServiceImpl implements CouponService {
}
}
// 校验领取方式
if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) {
if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getValue())) {
throw exception(COUPON_TEMPLATE_CANNOT_TAKE);
}
}
@@ -311,7 +291,7 @@ public class CouponServiceImpl implements CouponService {
* @param couponTemplate 优惠劵模版
*/
private void removeTakeLimitUser(Set<Long> userIds, CouponTemplateDO couponTemplate) {
if (couponTemplate.getTakeLimitCount() == null || couponTemplate.getTakeLimitCount() <= 0) {
if (couponTemplate.getTakeLimitCount() <= 0) {
return;
}
// 查询已领过券的用户
@@ -358,48 +338,6 @@ public class CouponServiceImpl implements CouponService {
return couponMapper.selectCountByUserIdAndTemplateIdIn(userId, templateIds);
}
@Override
public List<AppCouponMatchRespVO> getMatchCouponList(Long userId, AppCouponMatchReqVO matchReqVO) {
List<AppCouponMatchRespVO> couponMatchist = new ArrayList<>();
List<CouponDO> list = couponMapper.selectListByUserIdAndStatusAndUsePriceLeAndProductScope(userId,
CouponStatusEnum.UNUSED.getStatus());
for (CouponDO couponDO : list) {
AppCouponMatchRespVO appCouponMatchRespVO = CouponConvert.INSTANCE.convert2(couponDO);
Integer productScope = appCouponMatchRespVO.getProductScope();
List<Long> productScopeValues = appCouponMatchRespVO.getProductScopeValues();
Integer usePrice = appCouponMatchRespVO.getUsePrice();
if(matchReqVO.getPrice() < usePrice){
// 价格小于等于,满足价格使用条件
appCouponMatchRespVO.setMatch(false);
appCouponMatchRespVO.setDescription("未达到使用门槛");
}else if(!LocalDateTimeUtils.isBetween(appCouponMatchRespVO.getValidStartTime(), appCouponMatchRespVO.getValidEndTime())) {
//判断时间
appCouponMatchRespVO.setMatch(false);
appCouponMatchRespVO.setDescription("使用时间未到");
}else if (PromotionProductScopeEnum.ALL.getScope().equals(productScope)){
appCouponMatchRespVO.setMatch(true);
}else if (PromotionProductScopeEnum.SPU.getScope().equals(productScope)){
boolean spu = new HashSet<>(productScopeValues).containsAll(matchReqVO.getSpuIds());
if(spu){
appCouponMatchRespVO.setMatch(true);
}else {
appCouponMatchRespVO.setMatch(false);
appCouponMatchRespVO.setDescription("与商品不匹配");
}
}else if (PromotionProductScopeEnum.CATEGORY.getScope().equals(productScope)){
boolean category = new HashSet<>(productScopeValues).containsAll(matchReqVO.getCategoryIds());
if(category){
appCouponMatchRespVO.setMatch(true);
}else {
appCouponMatchRespVO.setMatch(false);
appCouponMatchRespVO.setDescription("与商品类型不匹配");
}
}
couponMatchist.add(appCouponMatchRespVO);
}
return couponMatchist;
}
@Override
public Map<Long, Boolean> getUserCanCanTakeMap(Long userId, List<CouponTemplateDO> templates) {
// 1. 未登录时,都显示可以领取

View File

@@ -8,8 +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 +98,7 @@ public interface SeckillActivityService {
* @param activityIds 活动编号
* @return 活动商品列表
*/
List<SeckillProductDO> getSeckillProductListByActivityId(Collection<Long> activityIds);
List<SeckillProductDO> getSeckillProductListByActivityIds(Collection<Long> activityIds);
/**
* 通过活动时段编号获取指定 status 的秒杀活动
@@ -139,4 +139,12 @@ public interface SeckillActivityService {
*/
List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
/**
* 获得拼团活动列表
*
* @param ids 拼团活动编号数组
* @return 拼团活动的列表
*/
List<SeckillActivityDO> getSeckillActivityListByIds(Collection<Long> ids);
}