Commit f7296d18b90ee68d9ba120c7d09b5a2b20098ed7
Merge remote-tracking branch 'origin/feature/mall_product' into brokerate
# Conflicts: # yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java # yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java # yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
Showing
73 changed files
with
908 additions
and
420 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
| ... | ... | @@ -43,17 +43,6 @@ public class LocalDateTimeUtils { |
| 43 | 43 | return LocalDateTime.of(year, mouth, day, 0, 0, 0); |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | - /** | |
| 47 | - * 创建指定时间 | |
| 48 | - * | |
| 49 | - * @param timeStr 时间字符串 | |
| 50 | - * @return 指定时间 | |
| 51 | - */ | |
| 52 | - public static LocalDateTime buildTime(String timeStr) { | |
| 53 | - // TODO @puhui999:这个方法的实现,和 LocalDateTimeUtil.parse() 的差异点是啥呀 | |
| 54 | - return LocalDateTime.of(LocalDate.now(), LocalTime.parse(timeStr)); | |
| 55 | - } | |
| 56 | - | |
| 57 | 46 | public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1, |
| 58 | 47 | int year2, int mouth2, int day2) { |
| 59 | 48 | return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; | ... | ... |
yudao-module-mall/yudao-module-product-biz/pom.xml
| ... | ... | @@ -23,12 +23,6 @@ |
| 23 | 23 | <artifactId>yudao-module-product-api</artifactId> |
| 24 | 24 | <version>${revision}</version> |
| 25 | 25 | </dependency> |
| 26 | - <!-- TODO 芋艿:看看~~~ --> | |
| 27 | - <dependency> | |
| 28 | - <groupId>cn.iocoder.boot</groupId> | |
| 29 | - <artifactId>yudao-module-trade-api</artifactId> | |
| 30 | - <version>${revision}</version> | |
| 31 | - </dependency> | |
| 32 | 26 | <dependency> |
| 33 | 27 | <groupId>cn.iocoder.boot</groupId> |
| 34 | 28 | <artifactId>yudao-module-member-api</artifactId> | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/property/ProductPropertyValueApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.product.api.property; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; |
| 4 | -import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; | |
| 4 | +import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; | |
| 5 | 5 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; |
| 6 | 6 | import org.springframework.stereotype.Service; |
| 7 | 7 | import org.springframework.validation.annotation.Validated; | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/property/ProductPropertyValueController.java
| 1 | 1 | package cn.iocoder.yudao.module.product.controller.admin.property; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollectionUtil; | |
| 4 | -import cn.hutool.crypto.symmetric.AES; | |
| 5 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 8 | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 9 | 7 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueRespVO; |
| 10 | 8 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 11 | -import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; | |
| 9 | +import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; | |
| 12 | 10 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; |
| 13 | 11 | import io.swagger.v3.oas.annotations.Operation; |
| 14 | 12 | import io.swagger.v3.oas.annotations.Parameter; |
| ... | ... | @@ -20,9 +18,6 @@ import org.springframework.web.bind.annotation.*; |
| 20 | 18 | import javax.annotation.Resource; |
| 21 | 19 | import javax.validation.Valid; |
| 22 | 20 | |
| 23 | -import java.util.Arrays; | |
| 24 | -import java.util.List; | |
| 25 | - | |
| 26 | 21 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 27 | 22 | |
| 28 | 23 | @Tag(name = "管理后台 - 商品属性值") | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
| 1 | 1 | package cn.iocoder.yudao.module.product.controller.app.spu; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; | |
| 7 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 9 | +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | |
| 5 | 10 | import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO; |
| 6 | -import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageRespVO; | |
| 7 | 11 | import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; |
| 12 | +import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageRespVO; | |
| 8 | 13 | import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; |
| 9 | 14 | import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; |
| 10 | 15 | import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; |
| ... | ... | @@ -23,10 +28,12 @@ import org.springframework.web.bind.annotation.RestController; |
| 23 | 28 | |
| 24 | 29 | import javax.annotation.Resource; |
| 25 | 30 | import javax.validation.Valid; |
| 31 | +import java.util.Collections; | |
| 26 | 32 | import java.util.List; |
| 27 | 33 | |
| 28 | 34 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 29 | 35 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 36 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | |
| 30 | 37 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_ENABLE; |
| 31 | 38 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; |
| 32 | 39 | |
| ... | ... | @@ -41,6 +48,11 @@ public class AppProductSpuController { |
| 41 | 48 | @Resource |
| 42 | 49 | private ProductSkuService productSkuService; |
| 43 | 50 | |
| 51 | + @Resource | |
| 52 | + private MemberLevelApi memberLevelApi; | |
| 53 | + @Resource | |
| 54 | + private MemberUserApi memberUserApi; | |
| 55 | + | |
| 44 | 56 | @GetMapping("/list") |
| 45 | 57 | @Operation(summary = "获得商品 SPU 列表") |
| 46 | 58 | @Parameters({ |
| ... | ... | @@ -51,14 +63,32 @@ public class AppProductSpuController { |
| 51 | 63 | @RequestParam("recommendType") String recommendType, |
| 52 | 64 | @RequestParam(value = "count", defaultValue = "10") Integer count) { |
| 53 | 65 | List<ProductSpuDO> list = productSpuService.getSpuList(recommendType, count); |
| 54 | - return success(ProductSpuConvert.INSTANCE.convertListForGetSpuList(list)); | |
| 66 | + if (CollUtil.isEmpty(list)) { | |
| 67 | + return success(Collections.emptyList()); | |
| 68 | + } | |
| 69 | + | |
| 70 | + // 拼接返回 | |
| 71 | + List<AppProductSpuPageRespVO> voList = ProductSpuConvert.INSTANCE.convertListForGetSpuList(list); | |
| 72 | + // 处理 vip 价格 | |
| 73 | + MemberLevelRespDTO memberLevel = getMemberLevel(); | |
| 74 | + voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel))); | |
| 75 | + return success(voList); | |
| 55 | 76 | } |
| 56 | 77 | |
| 57 | 78 | @GetMapping("/page") |
| 58 | 79 | @Operation(summary = "获得商品 SPU 分页") |
| 59 | 80 | public CommonResult<PageResult<AppProductSpuPageRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { |
| 60 | 81 | PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO); |
| 61 | - return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult)); | |
| 82 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 83 | + return success(PageResult.empty(pageResult.getTotal())); | |
| 84 | + } | |
| 85 | + | |
| 86 | + // 拼接返回 | |
| 87 | + PageResult<AppProductSpuPageRespVO> voPageResult = ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult); | |
| 88 | + // 处理 vip 价格 | |
| 89 | + MemberLevelRespDTO memberLevel = getMemberLevel(); | |
| 90 | + voPageResult.getList().forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel))); | |
| 91 | + return success(voPageResult); | |
| 62 | 92 | } |
| 63 | 93 | |
| 64 | 94 | @GetMapping("/get-detail") |
| ... | ... | @@ -74,10 +104,40 @@ public class AppProductSpuController { |
| 74 | 104 | throw exception(SPU_NOT_ENABLE); |
| 75 | 105 | } |
| 76 | 106 | |
| 77 | - // 查询商品 SKU | |
| 107 | + // 拼接返回 | |
| 78 | 108 | List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId()); |
| 79 | - // 拼接 | |
| 80 | - return success(ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus)); | |
| 109 | + AppProductSpuDetailRespVO detailVO = ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus); | |
| 110 | + // 处理 vip 价格 | |
| 111 | + MemberLevelRespDTO memberLevel = getMemberLevel(); | |
| 112 | + detailVO.setVipPrice(calculateVipPrice(detailVO.getPrice(), memberLevel)); | |
| 113 | + return success(detailVO); | |
| 114 | + } | |
| 115 | + | |
| 116 | + private MemberLevelRespDTO getMemberLevel() { | |
| 117 | + Long userId = getLoginUserId(); | |
| 118 | + if (userId == null) { | |
| 119 | + return null; | |
| 120 | + } | |
| 121 | + MemberUserRespDTO user = memberUserApi.getUser(userId); | |
| 122 | + if (user.getLevelId() == null || user.getLevelId() <= 0) { | |
| 123 | + return null; | |
| 124 | + } | |
| 125 | + return memberLevelApi.getMemberLevel(user.getLevelId()); | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * 计算会员 VIP 优惠价格 | |
| 130 | + * | |
| 131 | + * @param price 原价 | |
| 132 | + * @param memberLevel 会员等级 | |
| 133 | + * @return 优惠价格 | |
| 134 | + */ | |
| 135 | + public Integer calculateVipPrice(Integer price, MemberLevelRespDTO memberLevel) { | |
| 136 | + if (memberLevel == null || memberLevel.getDiscountPercent() == null) { | |
| 137 | + return 0; | |
| 138 | + } | |
| 139 | + Integer newPrice = price * memberLevel.getDiscountPercent() / 100; | |
| 140 | + return price - newPrice; | |
| 81 | 141 | } |
| 82 | 142 | |
| 83 | 143 | } | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/propertyvalue/ProductPropertyValueConvert.java renamed to yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/property/ProductPropertyValueConvert.java
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
| ... | ... | @@ -79,8 +79,6 @@ public interface ProductSpuConvert { |
| 79 | 79 | ProductSpuDO spu = list.get(i); |
| 80 | 80 | AppProductSpuPageRespVO spuVO = voList.get(i); |
| 81 | 81 | spuVO.setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit())); |
| 82 | - // 计算 vip 价格 TODO 芋艿:临时的逻辑,等 vip 支持后 | |
| 83 | - spuVO.setVipPrice((int) (spuVO.getPrice() * 0.9)); | |
| 84 | 82 | } |
| 85 | 83 | return voList; |
| 86 | 84 | } |
| ... | ... | @@ -95,11 +93,6 @@ public interface ProductSpuConvert { |
| 95 | 93 | .setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit())); |
| 96 | 94 | // 处理 SKU |
| 97 | 95 | spuVO.setSkus(convertListForGetSpuDetail(skus)); |
| 98 | - // 计算 vip 价格 TODO 芋艿:临时的逻辑,等 vip 支持后 | |
| 99 | - if (true) { | |
| 100 | - spuVO.setVipPrice((int) (spuVO.getPrice() * 0.9)); | |
| 101 | - spuVO.getSkus().forEach(sku -> sku.setVipPrice((int) (sku.getPrice() * 0.9))); | |
| 102 | - } | |
| 103 | 96 | return spuVO; |
| 104 | 97 | } |
| 105 | 98 | ... | ... |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/package-info.java
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
| ... | ... | @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 5 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueCreateReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValuePageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.product.controller.admin.property.vo.value.ProductPropertyValueUpdateReqVO; |
| 8 | -import cn.iocoder.yudao.module.product.convert.propertyvalue.ProductPropertyValueConvert; | |
| 8 | +import cn.iocoder.yudao.module.product.convert.property.ProductPropertyValueConvert; | |
| 9 | 9 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; |
| 10 | 10 | import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; |
| 11 | 11 | import cn.iocoder.yudao.module.product.dal.mysql.property.ProductPropertyValueMapper; | ... | ... |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/bargain/BargainActivityApi.java
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java
| ... | ... | @@ -7,14 +7,13 @@ package cn.iocoder.yudao.module.promotion.api.seckill; |
| 7 | 7 | */ |
| 8 | 8 | public interface SeckillActivityApi { |
| 9 | 9 | |
| 10 | - // TODO @puhui999:activityId 改成 id 好点哈; | |
| 11 | 10 | /** |
| 12 | 11 | * 更新秒杀库存 |
| 13 | 12 | * |
| 14 | - * @param activityId 活动编号 | |
| 13 | + * @param id 活动编号 | |
| 15 | 14 | * @param skuId sku 编号 |
| 16 | 15 | * @param count 数量 |
| 17 | 16 | */ |
| 18 | - void updateSeckillStock(Long activityId, Long skuId, Integer count); | |
| 17 | + void updateSeckillStock(Long id, Long skuId, Integer count); | |
| 19 | 18 | |
| 20 | 19 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java
| ... | ... | @@ -56,7 +56,6 @@ public interface ErrorCodeConstants { |
| 56 | 56 | ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013008004, "秒杀活动未关闭或未结束,不能删除"); |
| 57 | 57 | ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013008005, "秒杀活动已关闭,不能重复关闭"); |
| 58 | 58 | ErrorCode SECKILL_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1013008006, "秒杀失败,原因秒杀库存不足"); |
| 59 | - ErrorCode SECKILL_ACTIVITY_FAIL_STATUS_CLOSED = new ErrorCode(1013008007, "秒杀活动已关闭"); | |
| 60 | 59 | |
| 61 | 60 | // ========== 秒杀时段 1013009000 ========== |
| 62 | 61 | ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1013009000, "秒杀时段不存在"); | ... | ... |
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
| ... | ... | @@ -17,8 +17,8 @@ public class SeckillActivityApiImpl implements SeckillActivityApi { |
| 17 | 17 | private SeckillActivityService activityService; |
| 18 | 18 | |
| 19 | 19 | @Override |
| 20 | - public void updateSeckillStock(Long activityId, Long skuId, Integer count) { | |
| 21 | - activityService.updateSeckillStock(activityId, skuId, count); | |
| 20 | + public void updateSeckillStock(Long id, Long skuId, Integer count) { | |
| 21 | + activityService.updateSeckillStock(id, skuId, count); | |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainActivityController.java
| ... | ... | @@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController; |
| 23 | 23 | import javax.annotation.Resource; |
| 24 | 24 | import java.util.List; |
| 25 | 25 | |
| 26 | +import static cn.hutool.core.util.ObjectUtil.defaultIfNull; | |
| 26 | 27 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 27 | 28 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 28 | 29 | |
| ... | ... | @@ -39,7 +40,7 @@ public class AppBargainActivityController { |
| 39 | 40 | @GetMapping("/page") |
| 40 | 41 | @Operation(summary = "获得砍价活动分页") |
| 41 | 42 | public CommonResult<PageResult<AppBargainActivityRespVO>> getBargainActivityPage(PageParam pageReqVO) { |
| 42 | - PageResult<BargainActivityDO> result = bargainActivityService.getBargainActivityPageForApp(pageReqVO); | |
| 43 | + PageResult<BargainActivityDO> result = bargainActivityService.getBargainActivityPage(pageReqVO); | |
| 43 | 44 | if (CollUtil.isEmpty(result.getList())) { |
| 44 | 45 | return success(PageResult.empty(result.getTotal())); |
| 45 | 46 | } |
| ... | ... | @@ -54,7 +55,7 @@ public class AppBargainActivityController { |
| 54 | 55 | @Parameter(name = "count", description = "需要展示的数量", example = "6") |
| 55 | 56 | public CommonResult<List<AppBargainActivityRespVO>> getBargainActivityList( |
| 56 | 57 | @RequestParam(name = "count", defaultValue = "6") Integer count) { |
| 57 | - List<BargainActivityDO> list = bargainActivityService.getBargainActivityListForApp(count); | |
| 58 | + List<BargainActivityDO> list = bargainActivityService.getBargainActivityListByCount(defaultIfNull(count, 6)); | |
| 58 | 59 | if (CollUtil.isEmpty(list)) { |
| 59 | 60 | return success(BargainActivityConvert.INSTANCE.convertAppList(list)); |
| 60 | 61 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationActivityController.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.controller.app.combination; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | |
| 10 | +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; | |
| 6 | 11 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO; |
| 7 | 12 | import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO; |
| 13 | +import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; | |
| 14 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; | |
| 15 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; | |
| 16 | +import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; | |
| 8 | 17 | import io.swagger.v3.oas.annotations.Operation; |
| 9 | 18 | import io.swagger.v3.oas.annotations.Parameter; |
| 10 | 19 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -14,11 +23,13 @@ import org.springframework.web.bind.annotation.RequestMapping; |
| 14 | 23 | import org.springframework.web.bind.annotation.RequestParam; |
| 15 | 24 | import org.springframework.web.bind.annotation.RestController; |
| 16 | 25 | |
| 17 | -import java.time.LocalDateTime; | |
| 18 | -import java.util.ArrayList; | |
| 26 | +import javax.annotation.Resource; | |
| 27 | +import java.util.Collections; | |
| 19 | 28 | import java.util.List; |
| 20 | 29 | |
| 30 | +import static cn.hutool.core.util.ObjectUtil.defaultIfNull; | |
| 21 | 31 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 32 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 22 | 33 | |
| 23 | 34 | @Tag(name = "用户 APP - 拼团活动") |
| 24 | 35 | @RestController |
| ... | ... | @@ -26,104 +37,51 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 26 | 37 | @Validated |
| 27 | 38 | public class AppCombinationActivityController { |
| 28 | 39 | |
| 40 | + @Resource | |
| 41 | + private CombinationActivityService activityService; | |
| 42 | + @Resource | |
| 43 | + private ProductSpuApi spuApi; | |
| 44 | + | |
| 45 | + // TODO 芋艿:增加 Spring Cache | |
| 29 | 46 | @GetMapping("/list") |
| 30 | 47 | @Operation(summary = "获得拼团活动列表", description = "用于小程序首页") |
| 31 | - // TODO 芋艿:增加 Spring Cache | |
| 32 | - // TODO 芋艿:缺少 swagger 注解 | |
| 48 | + @Parameter(name = "count", description = "需要展示的数量", example = "6") | |
| 33 | 49 | public CommonResult<List<AppCombinationActivityRespVO>> getCombinationActivityList( |
| 34 | 50 | @RequestParam(name = "count", defaultValue = "6") Integer count) { |
| 35 | - List<AppCombinationActivityRespVO> activityList = new ArrayList<>(); | |
| 36 | - AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO(); | |
| 37 | - activity1.setId(1L); | |
| 38 | - activity1.setName("618 大拼团"); | |
| 39 | - activity1.setUserSize(3); | |
| 40 | - activity1.setSpuId(2048L); | |
| 41 | - activity1.setPicUrl("https://static.iocoder.cn/mall/a79f5d2ea6bf0c3c11b2127332dfe2df.jpg"); | |
| 42 | - activity1.setMarketPrice(50); | |
| 43 | - activity1.setCombinationPrice(100); | |
| 44 | - activityList.add(activity1); | |
| 45 | - | |
| 46 | - AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO(); | |
| 47 | - activity2.setId(2L); | |
| 48 | - activity2.setName("双十一拼团"); | |
| 49 | - activity2.setUserSize(5); | |
| 50 | - activity2.setSpuId(4096L); | |
| 51 | - activity2.setPicUrl("https://static.iocoder.cn/mall/132.jpeg"); | |
| 52 | - activity2.setMarketPrice(100); | |
| 53 | - activity2.setCombinationPrice(200); | |
| 54 | - activityList.add(activity2); | |
| 55 | - | |
| 56 | - return success(activityList); | |
| 51 | + List<CombinationActivityDO> list = activityService.getCombinationActivityListByCount(defaultIfNull(count, 6)); | |
| 52 | + if (CollUtil.isEmpty(list)) { | |
| 53 | + return success(Collections.emptyList()); | |
| 54 | + } | |
| 55 | + // 拼接返回 | |
| 56 | + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(list, CombinationActivityDO::getSpuId)); | |
| 57 | + return success(CombinationActivityConvert.INSTANCE.convertAppList(list, spuList)); | |
| 57 | 58 | } |
| 58 | 59 | |
| 59 | 60 | @GetMapping("/page") |
| 60 | 61 | @Operation(summary = "获得拼团活动分页") |
| 61 | 62 | public CommonResult<PageResult<AppCombinationActivityRespVO>> getCombinationActivityPage(PageParam pageParam) { |
| 62 | - List<AppCombinationActivityRespVO> activityList = new ArrayList<>(); | |
| 63 | - AppCombinationActivityRespVO activity1 = new AppCombinationActivityRespVO(); | |
| 64 | - activity1.setId(1L); | |
| 65 | - activity1.setName("618 大拼团"); | |
| 66 | - activity1.setUserSize(3); | |
| 67 | - activity1.setSpuId(2048L); | |
| 68 | - activity1.setPicUrl("商品图片地址"); | |
| 69 | - activity1.setMarketPrice(50); | |
| 70 | - activity1.setCombinationPrice(100); | |
| 71 | - activityList.add(activity1); | |
| 72 | - | |
| 73 | - AppCombinationActivityRespVO activity2 = new AppCombinationActivityRespVO(); | |
| 74 | - activity2.setId(2L); | |
| 75 | - activity2.setName("双十一拼团"); | |
| 76 | - activity2.setUserSize(5); | |
| 77 | - activity2.setSpuId(4096L); | |
| 78 | - activity2.setPicUrl("商品图片地址"); | |
| 79 | - activity2.setMarketPrice(100); | |
| 80 | - activity2.setCombinationPrice(200); | |
| 81 | - activityList.add(activity2); | |
| 82 | - | |
| 83 | - return success(new PageResult<>(activityList, 2L)); | |
| 63 | + PageResult<CombinationActivityDO> result = activityService.getCombinationActivityPage(pageParam); | |
| 64 | + if (CollUtil.isEmpty(result.getList())) { | |
| 65 | + return success(PageResult.empty(result.getTotal())); | |
| 66 | + } | |
| 67 | + // 拼接返回 | |
| 68 | + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(result.getList(), CombinationActivityDO::getSpuId)); | |
| 69 | + return success(CombinationActivityConvert.INSTANCE.convertAppPage(result, spuList)); | |
| 84 | 70 | } |
| 85 | 71 | |
| 86 | 72 | @GetMapping("/get-detail") |
| 87 | 73 | @Operation(summary = "获得拼团活动明细") |
| 88 | 74 | @Parameter(name = "id", description = "活动编号", required = true, example = "1024") |
| 89 | 75 | public CommonResult<AppCombinationActivityDetailRespVO> getCombinationActivityDetail(@RequestParam("id") Long id) { |
| 90 | - // TODO 芋艿:如果禁用的时候,需要抛出异常; | |
| 91 | - AppCombinationActivityDetailRespVO obj = new AppCombinationActivityDetailRespVO(); | |
| 92 | - // 设置其属性的值 | |
| 93 | - obj.setId(id); | |
| 94 | - obj.setName("晚九点限时秒杀"); | |
| 95 | - obj.setStatus(1); | |
| 96 | - obj.setStartTime(LocalDateTime.of(2023, 6, 15, 0, 0, 0)); | |
| 97 | - obj.setEndTime(LocalDateTime.of(2023, 6, 20, 23, 59, 0)); | |
| 98 | - obj.setUserSize(2); | |
| 99 | - obj.setSuccessCount(100); | |
| 100 | - obj.setSpuId(633L); | |
| 101 | - obj.setSingleLimitCount(2); | |
| 102 | - obj.setTotalLimitCount(3); | |
| 103 | - | |
| 104 | - // 创建一个Product对象的列表 | |
| 105 | - List<AppCombinationActivityDetailRespVO.Product> productList = new ArrayList<>(); | |
| 106 | - // 创建三个新的Product对象并设置其属性的值 | |
| 107 | - AppCombinationActivityDetailRespVO.Product product1 = new AppCombinationActivityDetailRespVO.Product(); | |
| 108 | - product1.setSkuId(1L); | |
| 109 | - product1.setCombinationPrice(100); | |
| 110 | - // 将第一个Product对象添加到列表中 | |
| 111 | - productList.add(product1); | |
| 112 | - // 创建第二个Product对象并设置其属性的值 | |
| 113 | - AppCombinationActivityDetailRespVO.Product product2 = new AppCombinationActivityDetailRespVO.Product(); | |
| 114 | - product2.setSkuId(2L); | |
| 115 | - product2.setCombinationPrice(200); | |
| 116 | - // 将第二个Product对象添加到列表中 | |
| 117 | - productList.add(product2); | |
| 118 | - // 创建第三个Product对象并设置其属性的值 | |
| 119 | - AppCombinationActivityDetailRespVO.Product product3 = new AppCombinationActivityDetailRespVO.Product(); | |
| 120 | - product3.setSkuId(3L); | |
| 121 | - product3.setCombinationPrice(300); | |
| 122 | - // 将第三个Product对象添加到列表中 | |
| 123 | - productList.add(product3); | |
| 124 | - // 将Product列表设置为对象的属性值 | |
| 125 | - obj.setProducts(productList); | |
| 126 | - return success(obj); | |
| 76 | + // 1、获取活动 | |
| 77 | + CombinationActivityDO combinationActivity = activityService.getCombinationActivity(id); | |
| 78 | + if (combinationActivity == null | |
| 79 | + || ObjectUtil.equal(combinationActivity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { | |
| 80 | + return success(null); | |
| 81 | + } | |
| 82 | + // 2、获取活动商品 | |
| 83 | + List<CombinationProductDO> products = activityService.getCombinationProductsByActivityId(combinationActivity.getId()); | |
| 84 | + return success(CombinationActivityConvert.INSTANCE.convert3(combinationActivity, products)); | |
| 127 | 85 | } |
| 128 | 86 | |
| 129 | 87 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/vo/activity/AppCombinationActivityRespVO.java
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillActivityController.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.controller.app.seckill; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| ... | ... | @@ -27,14 +28,10 @@ import org.springframework.web.bind.annotation.RequestParam; |
| 27 | 28 | import org.springframework.web.bind.annotation.RestController; |
| 28 | 29 | |
| 29 | 30 | import javax.annotation.Resource; |
| 30 | -import java.util.Arrays; | |
| 31 | 31 | import java.util.List; |
| 32 | 32 | |
| 33 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 34 | 33 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 35 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | |
| 36 | -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween; | |
| 37 | -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.SECKILL_ACTIVITY_FAIL_STATUS_CLOSED; | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 38 | 35 | |
| 39 | 36 | @Tag(name = "用户 App - 秒杀活动") |
| 40 | 37 | @RestController |
| ... | ... | @@ -50,27 +47,21 @@ public class AppSeckillActivityController { |
| 50 | 47 | @Resource |
| 51 | 48 | private ProductSpuApi spuApi; |
| 52 | 49 | |
| 53 | - @GetMapping("/get-now") | |
| 54 | - @Operation(summary = "获得当前秒杀活动") // 提供给首页使用 | |
| 55 | 50 | // TODO 芋艿:需要增加 spring cache |
| 51 | + @GetMapping("/get-now") | |
| 52 | + @Operation(summary = "获得当前秒杀活动", description = "获取当前正在进行的活动,提供给首页使用") | |
| 56 | 53 | public CommonResult<AppSeckillActivityNowRespVO> getNowSeckillActivity() { |
| 57 | 54 | // 1. 获取当前时间处在哪个秒杀阶段 |
| 58 | - // TODO @puhui999:可以考虑在 service 写个方法;这样 controller 不用关注过多逻辑 | |
| 59 | - List<SeckillConfigDO> configList = configService.getSeckillConfigList(); | |
| 60 | - SeckillConfigDO filteredConfig = findFirst(configList, config -> ObjectUtil.equal(config.getStatus(), | |
| 61 | - CommonStatusEnum.ENABLE.getStatus()) && isBetween(config.getStartTime(), config.getEndTime())); | |
| 62 | - if (filteredConfig == null) { // 时段不存在直接返回 null | |
| 55 | + SeckillConfigDO configList = configService.getSeckillConfigListByStatusOnCurrentTime(CommonStatusEnum.ENABLE.getStatus()); | |
| 56 | + if (configList == null) { // 时段不存在直接返回 null | |
| 63 | 57 | return success(null); |
| 64 | 58 | } |
| 65 | 59 | |
| 66 | 60 | // 2. 查询满足当前阶段的活动 |
| 67 | - // TODO @puhui999:最好直接返回开启的;不多查询数据 | |
| 68 | - List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIds(Arrays.asList(filteredConfig.getId())); | |
| 69 | - List<SeckillActivityDO> filteredList = filterList(activityList, item -> ObjectUtil.equal(item.getStatus(), CommonStatusEnum.ENABLE.getStatus())); | |
| 70 | - | |
| 71 | - // 3. 拼接数据 | |
| 72 | - List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(filteredList, SeckillActivityDO::getSpuId)); | |
| 73 | - return success(SeckillActivityConvert.INSTANCE.convert(filteredConfig, filteredList, spuList)); | |
| 61 | + List<SeckillActivityDO> activityList = activityService.getSeckillActivityListByConfigIdAndStatus(configList.getId(), CommonStatusEnum.ENABLE.getStatus()); | |
| 62 | + // 3 获取 spu 信息 | |
| 63 | + List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(activityList, SeckillActivityDO::getSpuId)); | |
| 64 | + return success(SeckillActivityConvert.INSTANCE.convert(configList, activityList, spuList)); | |
| 74 | 65 | } |
| 75 | 66 | |
| 76 | 67 | @GetMapping("/page") |
| ... | ... | @@ -78,7 +69,9 @@ public class AppSeckillActivityController { |
| 78 | 69 | public CommonResult<PageResult<AppSeckillActivityRespVO>> getSeckillActivityPage(AppSeckillActivityPageReqVO pageReqVO) { |
| 79 | 70 | // 1. 查询满足当前阶段的活动 |
| 80 | 71 | PageResult<SeckillActivityDO> pageResult = activityService.getSeckillActivityAppPageByConfigId(pageReqVO); |
| 81 | - | |
| 72 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 73 | + return success(PageResult.empty(pageResult.getTotal())); | |
| 74 | + } | |
| 82 | 75 | // 2. 拼接数据 |
| 83 | 76 | List<ProductSpuRespDTO> spuList = spuApi.getSpuList(convertList(pageResult.getList(), SeckillActivityDO::getSpuId)); |
| 84 | 77 | return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult, spuList)); |
| ... | ... | @@ -88,28 +81,22 @@ public class AppSeckillActivityController { |
| 88 | 81 | @Operation(summary = "获得秒杀活动明细") |
| 89 | 82 | @Parameter(name = "id", description = "活动编号", required = true, example = "1024") |
| 90 | 83 | public CommonResult<AppSeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) { |
| 91 | - // 1、获取当前时间处在哪个秒杀阶段 | |
| 92 | - // TODO puhui999:这里,和 58 行是雷同的 | |
| 93 | - List<SeckillConfigDO> configList = configService.getSeckillConfigList(); | |
| 94 | - SeckillConfigDO filteredConfig = findFirst(configList, config -> ObjectUtil.equal(config.getStatus(), | |
| 95 | - CommonStatusEnum.ENABLE.getStatus()) && isBetween(config.getStartTime(), config.getEndTime())); | |
| 96 | - if (filteredConfig == null) { // 时段不存在直接返回 null | |
| 84 | + // 1. 获取当前时间处在哪个秒杀阶段 | |
| 85 | + SeckillConfigDO configList = configService.getSeckillConfigListByStatusOnCurrentTime(CommonStatusEnum.ENABLE.getStatus()); | |
| 86 | + if (configList == null) { // 时段不存在直接返回 null | |
| 97 | 87 | return success(null); |
| 98 | 88 | } |
| 99 | 89 | |
| 100 | 90 | // 2. 获取活动 |
| 101 | 91 | SeckillActivityDO seckillActivity = activityService.getSeckillActivity(id); |
| 102 | - if (seckillActivity == null) { | |
| 92 | + if (seckillActivity == null | |
| 93 | + || ObjectUtil.equal(seckillActivity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { | |
| 103 | 94 | return success(null); |
| 104 | 95 | } |
| 105 | - // TODO 芋艿:如果禁用的时候,需要抛出异常; | |
| 106 | - if (ObjectUtil.equal(seckillActivity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { | |
| 107 | - throw exception(SECKILL_ACTIVITY_FAIL_STATUS_CLOSED); | |
| 108 | - } | |
| 109 | 96 | |
| 110 | 97 | // 3. 拼接数据 |
| 111 | - List<SeckillProductDO> products = activityService.getSeckillProductListByActivityId(seckillActivity.getId()); | |
| 112 | - return success(SeckillActivityConvert.INSTANCE.convert3(seckillActivity, products, filteredConfig)); | |
| 98 | + List<SeckillProductDO> productList = activityService.getSeckillProductListByActivityId(seckillActivity.getId()); | |
| 99 | + return success(SeckillActivityConvert.INSTANCE.convert3(seckillActivity, productList, configList)); | |
| 113 | 100 | } |
| 114 | 101 | |
| 115 | 102 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/AppSeckillConfigController.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.controller.app.seckill; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.CollectionUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | 5 | import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.config.AppSeckillConfigRespVO; |
| 7 | 6 | import cn.iocoder.yudao.module.promotion.convert.seckill.seckillconfig.SeckillConfigConvert; |
| 8 | -import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillconfig.SeckillConfigDO; | |
| 9 | 7 | import cn.iocoder.yudao.module.promotion.service.seckill.SeckillConfigService; |
| 10 | 8 | import io.swagger.v3.oas.annotations.Operation; |
| 11 | 9 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -15,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping; |
| 15 | 13 | import org.springframework.web.bind.annotation.RestController; |
| 16 | 14 | |
| 17 | 15 | import javax.annotation.Resource; |
| 18 | -import java.util.Collections; | |
| 19 | 16 | import java.util.List; |
| 20 | 17 | |
| 21 | 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| ... | ... | @@ -31,13 +28,8 @@ public class AppSeckillConfigController { |
| 31 | 28 | @GetMapping("/list") |
| 32 | 29 | @Operation(summary = "获得秒杀时间段列表") |
| 33 | 30 | public CommonResult<List<AppSeckillConfigRespVO>> getSeckillConfigList() { |
| 34 | - List<SeckillConfigDO> list = configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 35 | - // TODO @puhui999:如果这种,不用判空也问题不大; | |
| 36 | - if (CollectionUtil.isEmpty(list)) { | |
| 37 | - return success(Collections.emptyList()); | |
| 38 | - } | |
| 39 | - | |
| 40 | - return success(SeckillConfigConvert.INSTANCE.convertList2(list)); | |
| 31 | + return success(SeckillConfigConvert.INSTANCE.convertList2( | |
| 32 | + configService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus()))); | |
| 41 | 33 | } |
| 42 | 34 | |
| 43 | 35 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/seckill/vo/activity/AppSeckillActivityRespVO.java
| ... | ... | @@ -25,6 +25,9 @@ public class AppSeckillActivityRespVO { |
| 25 | 25 | // 从 SPU 的 marketPrice 读取 |
| 26 | 26 | private Integer marketPrice; |
| 27 | 27 | |
| 28 | + @Schema(description = "秒杀活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 29 | + private Integer status; | |
| 30 | + | |
| 28 | 31 | @Schema(description = "秒杀库存(剩余)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| 29 | 32 | private Integer stock; |
| 30 | 33 | @Schema(description = "秒杀库存(总共)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200") | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/bargain/BargainActivityConvert.java
| ... | ... | @@ -46,9 +46,7 @@ public interface BargainActivityConvert { |
| 46 | 46 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 47 | 47 | List<BargainActivityRespVO> list = CollectionUtils.convertList(result.getList(), item -> { |
| 48 | 48 | findAndThen(spuMap, item.getSpuId(), spu -> { |
| 49 | - // TODO @puhui999:这里可以使用链式哈 | |
| 50 | - item.setPicUrl(spu.getPicUrl()); | |
| 51 | - item.setSpuName(spu.getName()); | |
| 49 | + item.setPicUrl(spu.getPicUrl()).setSpuName(spu.getName()); | |
| 52 | 50 | }); |
| 53 | 51 | return item; |
| 54 | 52 | }); |
| ... | ... | @@ -75,11 +73,7 @@ public interface BargainActivityConvert { |
| 75 | 73 | // 拼接关联属性 |
| 76 | 74 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 77 | 75 | List<AppBargainActivityRespVO> list = CollectionUtils.convertList(result.getList(), item -> { |
| 78 | - findAndThen(spuMap, item.getSpuId(), spu -> { | |
| 79 | - // TODO @puhui999:这里可以使用链式哈 | |
| 80 | - item.setPicUrl(spu.getPicUrl()); | |
| 81 | - item.setMarketPrice(spu.getMarketPrice()); | |
| 82 | - }); | |
| 76 | + findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); | |
| 83 | 77 | return item; |
| 84 | 78 | }); |
| 85 | 79 | result.setList(list); |
| ... | ... | @@ -92,11 +86,7 @@ public interface BargainActivityConvert { |
| 92 | 86 | List<AppBargainActivityRespVO> activityList = convertAppList(list); |
| 93 | 87 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 94 | 88 | return CollectionUtils.convertList(activityList, item -> { |
| 95 | - findAndThen(spuMap, item.getSpuId(), spu -> { | |
| 96 | - // TODO @puhui999:这里可以使用链式哈 | |
| 97 | - item.setPicUrl(spu.getPicUrl()); | |
| 98 | - item.setMarketPrice(spu.getMarketPrice()); | |
| 99 | - }); | |
| 89 | + findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); | |
| 100 | 90 | return item; |
| 101 | 91 | }); |
| 102 | 92 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
| ... | ... | @@ -13,6 +13,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activit |
| 13 | 13 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO; |
| 14 | 14 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO; |
| 15 | 15 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO; |
| 16 | +import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityDetailRespVO; | |
| 17 | +import cn.iocoder.yudao.module.promotion.controller.app.combination.vo.activity.AppCombinationActivityRespVO; | |
| 16 | 18 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; |
| 17 | 19 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; |
| 18 | 20 | import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; |
| ... | ... | @@ -25,6 +27,7 @@ import java.util.List; |
| 25 | 27 | import java.util.Map; |
| 26 | 28 | |
| 27 | 29 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| 30 | +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; | |
| 28 | 31 | |
| 29 | 32 | /** |
| 30 | 33 | * 拼团活动 Convert |
| ... | ... | @@ -58,10 +61,7 @@ public interface CombinationActivityConvert { |
| 58 | 61 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 59 | 62 | PageResult<CombinationActivityRespVO> pageResult = convertPage(page); |
| 60 | 63 | pageResult.getList().forEach(item -> { |
| 61 | - MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> { | |
| 62 | - item.setSpuName(spu.getName()); | |
| 63 | - item.setPicUrl(spu.getPicUrl()); | |
| 64 | - }); | |
| 64 | + MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl())); | |
| 65 | 65 | item.setProducts(convertList2(productList)); |
| 66 | 66 | }); |
| 67 | 67 | return pageResult; |
| ... | ... | @@ -97,18 +97,51 @@ public interface CombinationActivityConvert { |
| 97 | 97 | default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO, |
| 98 | 98 | CombinationActivityDO activity, MemberUserRespDTO user, |
| 99 | 99 | ProductSpuRespDTO spu, ProductSkuRespDTO sku) { |
| 100 | - // TODO @puhui999:搞成链式的 set;这样会更规整一点; | |
| 101 | - CombinationRecordDO record = convert(reqDTO); | |
| 102 | - record.setVirtualGroup(false); | |
| 103 | - record.setExpireTime(record.getStartTime().plusHours(activity.getLimitDuration())); | |
| 104 | - record.setUserSize(activity.getUserSize()); | |
| 105 | - record.setNickname(user.getNickname()); | |
| 106 | - record.setAvatar(user.getAvatar()); | |
| 107 | - record.setSpuName(spu.getName()); | |
| 108 | - record.setPicUrl(sku.getPicUrl()); | |
| 109 | - return record; | |
| 100 | + // TODO @puhui999:订单付款后需要设置开始时间和结束时间; | |
| 101 | + return convert(reqDTO) | |
| 102 | + .setVirtualGroup(false) | |
| 103 | + .setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration())) | |
| 104 | + .setUserSize(activity.getUserSize()) | |
| 105 | + .setNickname(user.getNickname()) | |
| 106 | + .setAvatar(user.getAvatar()) | |
| 107 | + .setSpuName(spu.getName()) | |
| 108 | + .setPicUrl(sku.getPicUrl()); | |
| 110 | 109 | } |
| 111 | 110 | |
| 112 | 111 | List<CombinationRecordRespDTO> convert(List<CombinationRecordDO> bean); |
| 113 | 112 | |
| 113 | + List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list); | |
| 114 | + | |
| 115 | + default List<AppCombinationActivityRespVO> convertAppList(List<CombinationActivityDO> list, List<ProductSpuRespDTO> spuList) { | |
| 116 | + List<AppCombinationActivityRespVO> activityList = convertAppList(list); | |
| 117 | + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); | |
| 118 | + return CollectionUtils.convertList(activityList, item -> { | |
| 119 | + findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); | |
| 120 | + return item; | |
| 121 | + }); | |
| 122 | + } | |
| 123 | + | |
| 124 | + PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result); | |
| 125 | + | |
| 126 | + default PageResult<AppCombinationActivityRespVO> convertAppPage(PageResult<CombinationActivityDO> result, List<ProductSpuRespDTO> spuList) { | |
| 127 | + PageResult<AppCombinationActivityRespVO> appPage = convertAppPage(result); | |
| 128 | + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); | |
| 129 | + List<AppCombinationActivityRespVO> list = CollectionUtils.convertList(appPage.getList(), item -> { | |
| 130 | + findAndThen(spuMap, item.getSpuId(), spu -> { | |
| 131 | + item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()); | |
| 132 | + }); | |
| 133 | + return item; | |
| 134 | + }); | |
| 135 | + appPage.setList(list); | |
| 136 | + return appPage; | |
| 137 | + } | |
| 138 | + | |
| 139 | + AppCombinationActivityDetailRespVO convert2(CombinationActivityDO combinationActivity); | |
| 140 | + | |
| 141 | + List<AppCombinationActivityDetailRespVO.Product> convertList1(List<CombinationProductDO> products); | |
| 142 | + | |
| 143 | + default AppCombinationActivityDetailRespVO convert3(CombinationActivityDO combinationActivity, List<CombinationProductDO> products) { | |
| 144 | + return convert2(combinationActivity).setProducts(convertList1(products)); | |
| 145 | + } | |
| 146 | + | |
| 114 | 147 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/seckill/seckillactivity/SeckillActivityConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| ... | ... | @@ -29,7 +30,6 @@ import java.util.Map; |
| 29 | 30 | |
| 30 | 31 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| 31 | 32 | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| 32 | -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | |
| 33 | 33 | |
| 34 | 34 | /** |
| 35 | 35 | * 秒杀活动 Convert |
| ... | ... | @@ -98,10 +98,9 @@ public interface SeckillActivityConvert { |
| 98 | 98 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 99 | 99 | respVO.setActivities(CollectionUtils.convertList(convertList3(activityList), item -> { |
| 100 | 100 | findAndThen(spuMap, item.getSpuId(), spu -> { |
| 101 | - // TODO @puhui999:可以尝试链式 set 哈; | |
| 102 | - item.setPicUrl(spu.getPicUrl()); | |
| 103 | - item.setMarketPrice(spu.getMarketPrice()); | |
| 104 | - item.setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit())); | |
| 101 | + item.setPicUrl(spu.getPicUrl()) | |
| 102 | + .setMarketPrice(spu.getMarketPrice()) | |
| 103 | + .setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit())); | |
| 105 | 104 | }); |
| 106 | 105 | return item; |
| 107 | 106 | })); |
| ... | ... | @@ -114,12 +113,8 @@ public interface SeckillActivityConvert { |
| 114 | 113 | PageResult<AppSeckillActivityRespVO> result = convertPage1(pageResult); |
| 115 | 114 | Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId); |
| 116 | 115 | List<AppSeckillActivityRespVO> list = CollectionUtils.convertList(result.getList(), item -> { |
| 117 | - findAndThen(spuMap, item.getSpuId(), spu -> { | |
| 118 | - // TODO @puhui999:可以尝试链式 set 哈; | |
| 119 | - item.setPicUrl(spu.getPicUrl()); | |
| 120 | - item.setMarketPrice(spu.getMarketPrice()); | |
| 121 | - item.setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit())); | |
| 122 | - }); | |
| 116 | + findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice()) | |
| 117 | + .setUnitName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.PRODUCT_UNIT, spu.getUnit()))); | |
| 123 | 118 | return item; |
| 124 | 119 | }); |
| 125 | 120 | result.setList(list); |
| ... | ... | @@ -131,12 +126,13 @@ public interface SeckillActivityConvert { |
| 131 | 126 | List<AppSeckillActivityDetailRespVO.Product> convertList1(List<SeckillProductDO> products); |
| 132 | 127 | |
| 133 | 128 | default AppSeckillActivityDetailRespVO convert3(SeckillActivityDO seckillActivity, List<SeckillProductDO> products, SeckillConfigDO filteredConfig) { |
| 134 | - AppSeckillActivityDetailRespVO respVO = convert2(seckillActivity); | |
| 135 | - respVO.setProducts(convertList1(products)); | |
| 136 | - // TODO @puhui999:可以尝试链式 set 哈; | |
| 137 | - respVO.setStartTime(buildTime(filteredConfig.getStartTime())); | |
| 138 | - respVO.setEndTime(buildTime(filteredConfig.getEndTime())); | |
| 139 | - return respVO; | |
| 129 | + return convert2(seckillActivity) | |
| 130 | + .setProducts(convertList1(products)) | |
| 131 | + // TODO @puhui999:要不要在里面 default 一个方法,处理这个事件;简洁一点; | |
| 132 | + .setStartTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getStartTime(), "yyyy-MM-dd") + " " + filteredConfig.getStartTime(), | |
| 133 | + "yyyy-MM-dd HH:mm:ss")) // 活动开始日期和时段结合 | |
| 134 | + .setEndTime(LocalDateTimeUtil.parse(LocalDateTimeUtil.format(seckillActivity.getEndTime(), "yyyy-MM-dd") + " " + filteredConfig.getEndTime(), | |
| 135 | + "yyyy-MM-dd HH:mm:ss")); // 活动结束日期和时段结合 | |
| 140 | 136 | } |
| 141 | 137 | |
| 142 | 138 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
| ... | ... | @@ -38,18 +38,41 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> { |
| 38 | 38 | * @param count 扣减的库存数量 |
| 39 | 39 | * @return 影响的行数 |
| 40 | 40 | */ |
| 41 | - default int updateActivityStock(Long id, int count) { | |
| 41 | + default int updateStock(Long id, int count) { | |
| 42 | 42 | return update(null, new LambdaUpdateWrapper<BargainActivityDO>() |
| 43 | 43 | .eq(BargainActivityDO::getId, id) |
| 44 | 44 | .ge(BargainActivityDO::getStock, count) |
| 45 | 45 | .setSql("stock = stock - " + count)); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | - default PageResult<BargainActivityDO> selectAppPage(PageParam pageReqVO, Integer status, LocalDateTime now) { | |
| 48 | + /** | |
| 49 | + * 查询处在 now 日期时间且是 status 状态的活动分页 | |
| 50 | + * | |
| 51 | + * @param pageReqVO 分页参数 | |
| 52 | + * @param status 状态 | |
| 53 | + * @param now 当前日期时间 | |
| 54 | + * @return 活动分页 | |
| 55 | + */ | |
| 56 | + default PageResult<BargainActivityDO> selectPage(PageParam pageReqVO, Integer status, LocalDateTime now) { | |
| 49 | 57 | return selectPage(pageReqVO, new LambdaQueryWrapperX<BargainActivityDO>() |
| 50 | 58 | .eq(BargainActivityDO::getStatus, status) |
| 51 | 59 | .le(BargainActivityDO::getStartTime, now) |
| 52 | 60 | .ge(BargainActivityDO::getEndTime, now)); |
| 53 | 61 | } |
| 54 | 62 | |
| 63 | + /** | |
| 64 | + * 查询处在 now 日期时间且是 status 状态的活动分页 | |
| 65 | + * | |
| 66 | + * @param status 状态 | |
| 67 | + * @param now 当前日期时间 | |
| 68 | + * @return 活动分页 | |
| 69 | + */ | |
| 70 | + default List<BargainActivityDO> selectList(Integer count, Integer status, LocalDateTime now) { | |
| 71 | + return selectList(new LambdaQueryWrapperX<BargainActivityDO>() | |
| 72 | + .eq(BargainActivityDO::getStatus, status) | |
| 73 | + .le(BargainActivityDO::getStartTime, now) | |
| 74 | + .ge(BargainActivityDO::getEndTime, now) | |
| 75 | + .last("LIMIT " + count)); | |
| 76 | + } | |
| 77 | + | |
| 55 | 78 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.dal.mysql.combination; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| ... | ... | @@ -28,4 +29,15 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi |
| 28 | 29 | return selectList(CombinationActivityDO::getStatus, status); |
| 29 | 30 | } |
| 30 | 31 | |
| 32 | + default PageResult<CombinationActivityDO> selectPage(PageParam pageParam, Integer status) { | |
| 33 | + return selectPage(pageParam, new LambdaQueryWrapperX<CombinationActivityDO>() | |
| 34 | + .eq(CombinationActivityDO::getStatus, status)); | |
| 35 | + } | |
| 36 | + | |
| 37 | + default List<CombinationActivityDO> selectListByStatus(Integer status, Integer count) { | |
| 38 | + return selectList(new LambdaQueryWrapperX<CombinationActivityDO>() | |
| 39 | + .eq(CombinationActivityDO::getStatus, status) | |
| 40 | + .last("LIMIT " + count)); | |
| 41 | + } | |
| 42 | + | |
| 31 | 43 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
| ... | ... | @@ -41,7 +41,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> { |
| 41 | 41 | * @param count 扣减的库存数量 |
| 42 | 42 | * @return 影响的行数 |
| 43 | 43 | */ |
| 44 | - default int updateActivityStock(Long id, int count) { | |
| 44 | + default int updateStock(Long id, int count) { | |
| 45 | 45 | return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() |
| 46 | 46 | .eq(SeckillActivityDO::getId, id) |
| 47 | 47 | .gt(SeckillActivityDO::getTotalStock, 0) | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
| ... | ... | @@ -16,8 +16,13 @@ import java.util.List; |
| 16 | 16 | @Mapper |
| 17 | 17 | public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> { |
| 18 | 18 | |
| 19 | - default List<SeckillProductDO> selectListByActivityId(Long id) { | |
| 20 | - return selectList(SeckillProductDO::getActivityId, id); | |
| 19 | + default List<SeckillProductDO> selectListByActivityId(Long activityId) { | |
| 20 | + return selectList(SeckillProductDO::getActivityId, activityId); | |
| 21 | + } | |
| 22 | + | |
| 23 | + default SeckillProductDO selectByActivityIdAndSkuId(Long activityId, Long skuId) { | |
| 24 | + return selectOne(SeckillProductDO::getActivityId, activityId, | |
| 25 | + SeckillProductDO::getSkuId, skuId); | |
| 21 | 26 | } |
| 22 | 27 | |
| 23 | 28 | default List<SeckillProductDO> selectListByActivityId(Collection<Long> ids) { |
| ... | ... | @@ -31,7 +36,7 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> { |
| 31 | 36 | * @param count 扣减的库存数量 |
| 32 | 37 | * @return 影响的行数 |
| 33 | 38 | */ |
| 34 | - default int updateActivityStock(Long id, int count) { | |
| 39 | + default int updateStock(Long id, int count) { | |
| 35 | 40 | return update(null, new LambdaUpdateWrapper<SeckillProductDO>() |
| 36 | 41 | .eq(SeckillProductDO::getId, id) |
| 37 | 42 | .gt(SeckillProductDO::getStock, count) | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
| ... | ... | @@ -63,22 +63,20 @@ public interface BargainActivityService { |
| 63 | 63 | */ |
| 64 | 64 | PageResult<BargainActivityDO> getBargainActivityPage(BargainActivityPageReqVO pageReqVO); |
| 65 | 65 | |
| 66 | - // TODO @puhui999:这里可以改成进行中的活动;尽量避免专门为 app 定制,或者类似的名字哈;mapper 那也是 | |
| 67 | - | |
| 68 | 66 | /** |
| 69 | - * 获取 APP 端活动分页数据 | |
| 67 | + * 获取正在进行的活动分页数据 | |
| 70 | 68 | * |
| 71 | 69 | * @param pageReqVO 分页请求 |
| 72 | 70 | * @return 砍价活动分页 |
| 73 | 71 | */ |
| 74 | - PageResult<BargainActivityDO> getBargainActivityPageForApp(PageParam pageReqVO); | |
| 72 | + PageResult<BargainActivityDO> getBargainActivityPage(PageParam pageReqVO); | |
| 75 | 73 | |
| 76 | 74 | /** |
| 77 | - * 获取 APP 端活动展示数据 | |
| 75 | + * 获取正在进行的活动分页数据 | |
| 78 | 76 | * |
| 79 | 77 | * @param count 需要的数量 |
| 80 | 78 | * @return 砍价活动分页 |
| 81 | 79 | */ |
| 82 | - List<BargainActivityDO> getBargainActivityListForApp(Integer count); | |
| 80 | + List<BargainActivityDO> getBargainActivityListByCount(Integer count); | |
| 83 | 81 | |
| 84 | 82 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
| ... | ... | @@ -75,16 +75,18 @@ public class BargainActivityServiceImpl implements BargainActivityService { |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | @Override |
| 78 | - @Transactional(rollbackFor = Exception.class) | |
| 79 | 78 | public void updateBargainActivityStock(Long id, Integer count) { |
| 80 | 79 | // 查询砍价活动 |
| 81 | 80 | BargainActivityDO activity = getBargainActivity(id); |
| 82 | 81 | if (activity == null) { |
| 83 | 82 | throw exception(BARGAIN_ACTIVITY_NOT_EXISTS); |
| 84 | 83 | } |
| 84 | + if (count > activity.getStock()) { | |
| 85 | + throw exception(BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL); | |
| 86 | + } | |
| 85 | 87 | |
| 86 | 88 | // 更新砍价库存 |
| 87 | - int updateCount = bargainActivityMapper.updateActivityStock(id, count); | |
| 89 | + int updateCount = bargainActivityMapper.updateStock(id, count); | |
| 88 | 90 | if (updateCount == 0) { |
| 89 | 91 | throw exception(BARGAIN_ACTIVITY_UPDATE_STOCK_FAIL); |
| 90 | 92 | } |
| ... | ... | @@ -114,7 +116,7 @@ public class BargainActivityServiceImpl implements BargainActivityService { |
| 114 | 116 | public void deleteBargainActivity(Long id) { |
| 115 | 117 | // 校验存在 |
| 116 | 118 | BargainActivityDO activityDO = validateBargainActivityExists(id); |
| 117 | - // 校验状态 | |
| 119 | + // 校验状态 TODO puhui: 测试完成后需要恢复校验 | |
| 118 | 120 | //if (ObjectUtil.equal(activityDO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { |
| 119 | 121 | // throw exception(BARGAIN_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END); |
| 120 | 122 | //} |
| ... | ... | @@ -142,21 +144,14 @@ public class BargainActivityServiceImpl implements BargainActivityService { |
| 142 | 144 | } |
| 143 | 145 | |
| 144 | 146 | @Override |
| 145 | - public PageResult<BargainActivityDO> getBargainActivityPageForApp(PageParam pageReqVO) { | |
| 147 | + public PageResult<BargainActivityDO> getBargainActivityPage(PageParam pageReqVO) { | |
| 146 | 148 | // 只查询进行中,且在时间范围内的 |
| 147 | - return bargainActivityMapper.selectAppPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); | |
| 149 | + return bargainActivityMapper.selectPage(pageReqVO, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); | |
| 148 | 150 | } |
| 149 | 151 | |
| 150 | 152 | @Override |
| 151 | - public List<BargainActivityDO> getBargainActivityListForApp(Integer count) { | |
| 152 | - // TODO @puhui999:这种 default count 的逻辑,可以放到 controller 哈;然后可以使用 ObjectUtils.default 方法 | |
| 153 | - if (count == null) { | |
| 154 | - count = 6; | |
| 155 | - } | |
| 156 | - // TODO @puhui999:这种不要用 page;会浪费一次 count; | |
| 157 | - PageResult<BargainActivityDO> result = bargainActivityMapper.selectAppPage(new PageParam().setPageSize(count), | |
| 158 | - CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); | |
| 159 | - return result.getList(); | |
| 153 | + public List<BargainActivityDO> getBargainActivityListByCount(Integer count) { | |
| 154 | + return bargainActivityMapper.selectList(count, CommonStatusEnum.ENABLE.getStatus(), LocalDateTime.now()); | |
| 160 | 155 | } |
| 161 | 156 | |
| 162 | 157 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
| 1 | 1 | package cn.iocoder.yudao.module.promotion.service.combination; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; |
| ... | ... | @@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationP |
| 9 | 10 | |
| 10 | 11 | import javax.validation.Valid; |
| 11 | 12 | import java.util.Collection; |
| 13 | +import java.util.Collections; | |
| 12 | 14 | import java.util.List; |
| 13 | 15 | |
| 14 | 16 | /** |
| ... | ... | @@ -67,6 +69,16 @@ public interface CombinationActivityService { |
| 67 | 69 | /** |
| 68 | 70 | * 获得拼团活动商品列表 |
| 69 | 71 | * |
| 72 | + * @param activityId 拼团活动 id | |
| 73 | + * @return 拼团活动的商品列表 | |
| 74 | + */ | |
| 75 | + default List<CombinationProductDO> getCombinationProductsByActivityId(Long activityId) { | |
| 76 | + return getCombinationProductsByActivityIds(Collections.singletonList(activityId)); | |
| 77 | + } | |
| 78 | + | |
| 79 | + /** | |
| 80 | + * 获得拼团活动商品列表 | |
| 81 | + * | |
| 70 | 82 | * @param activityIds 拼团活动 ids |
| 71 | 83 | * @return 拼团活动的商品列表 |
| 72 | 84 | */ |
| ... | ... | @@ -83,4 +95,20 @@ public interface CombinationActivityService { |
| 83 | 95 | */ |
| 84 | 96 | void validateCombination(Long activityId, Long userId, Long skuId, Integer count); |
| 85 | 97 | |
| 98 | + /** | |
| 99 | + * 获取正在进行的活动分页数据 | |
| 100 | + * | |
| 101 | + * @param count 需要的数量 | |
| 102 | + * @return 拼团活动分页 | |
| 103 | + */ | |
| 104 | + List<CombinationActivityDO> getCombinationActivityListByCount(Integer count); | |
| 105 | + | |
| 106 | + /** | |
| 107 | + * 获取正在进行的活动分页数据 | |
| 108 | + * | |
| 109 | + * @param pageParam 分页请求 | |
| 110 | + * @return 拼团活动分页 | |
| 111 | + */ | |
| 112 | + PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam); | |
| 113 | + | |
| 86 | 114 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.service.combination; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 6 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 8 | 9 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| ... | ... | @@ -223,8 +224,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic |
| 223 | 224 | throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE); |
| 224 | 225 | } |
| 225 | 226 | // 1.3 校验是否超出单次限购数量 |
| 226 | - // TODO puhui999:count > activity.getSingleLimitCount() 会更好理解点; | |
| 227 | - if (activity.getSingleLimitCount() < count) { | |
| 227 | + if (count > activity.getSingleLimitCount()) { | |
| 228 | 228 | throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED); |
| 229 | 229 | } |
| 230 | 230 | |
| ... | ... | @@ -245,4 +245,14 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic |
| 245 | 245 | } |
| 246 | 246 | } |
| 247 | 247 | |
| 248 | + @Override | |
| 249 | + public List<CombinationActivityDO> getCombinationActivityListByCount(Integer count) { | |
| 250 | + return combinationActivityMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus(), count); | |
| 251 | + } | |
| 252 | + | |
| 253 | + @Override | |
| 254 | + public PageResult<CombinationActivityDO> getCombinationActivityPage(PageParam pageParam) { | |
| 255 | + return combinationActivityMapper.selectPage(pageParam, CommonStatusEnum.ENABLE.getStatus()); | |
| 256 | + } | |
| 257 | + | |
| 248 | 258 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
| ... | ... | @@ -96,12 +96,15 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 96 | 96 | return recordDO; |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | + // TODO @puhui999:有一个应该在创建那要做下;就是当前 activityId 已经有未支付的订单,不允许在发起新的;要么支付,要么去掉先; | |
| 100 | + | |
| 99 | 101 | @Override |
| 100 | 102 | @Transactional(rollbackFor = Exception.class) |
| 101 | 103 | public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { |
| 102 | 104 | // 1.1 校验拼团活动 |
| 103 | 105 | CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(reqDTO.getActivityId()); |
| 104 | 106 | // 1.2 需要校验下,他当前是不是已经参加了该拼团; |
| 107 | + // TODO @puhui999:拼团应该可以重复参加;应该去校验总共的上限哈,就是 activity.totalLimitCount | |
| 105 | 108 | CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(reqDTO.getUserId(), reqDTO.getOrderId()); |
| 106 | 109 | if (recordDO != null) { |
| 107 | 110 | throw exception(COMBINATION_RECORD_EXISTS); |
| ... | ... | @@ -111,6 +114,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 111 | 114 | if (CollUtil.isNotEmpty(recordDOList)) { |
| 112 | 115 | throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED); |
| 113 | 116 | } |
| 117 | + // TODO @puhui999:有个开始时间未校验 | |
| 114 | 118 | // 1.4 校验当前活动是否过期 |
| 115 | 119 | if (LocalDateTime.now().isAfter(activity.getEndTime())) { |
| 116 | 120 | throw exception(COMBINATION_RECORD_FAILED_TIME_END); |
| ... | ... | @@ -128,6 +132,8 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { |
| 128 | 132 | } |
| 129 | 133 | } |
| 130 | 134 | |
| 135 | + // TODO @puhui999:单次限购 | |
| 136 | + | |
| 131 | 137 | // 2. 创建拼团记录 |
| 132 | 138 | MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()); |
| 133 | 139 | ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()); | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
| ... | ... | @@ -37,11 +37,11 @@ public interface SeckillActivityService { |
| 37 | 37 | /** |
| 38 | 38 | * 更新秒杀库存 |
| 39 | 39 | * |
| 40 | - * @param activityId 活动编号 | |
| 40 | + * @param id 活动编号 | |
| 41 | 41 | * @param skuId sku 编号 |
| 42 | 42 | * @param count 数量 |
| 43 | 43 | */ |
| 44 | - void updateSeckillStock(Long activityId, Long skuId, Integer count); | |
| 44 | + void updateSeckillStock(Long id, Long skuId, Integer count); | |
| 45 | 45 | |
| 46 | 46 | /** |
| 47 | 47 | * 关闭秒杀活动 |
| ... | ... | @@ -98,6 +98,15 @@ public interface SeckillActivityService { |
| 98 | 98 | List<SeckillActivityDO> getSeckillActivityListByConfigIds(Collection<Long> ids); |
| 99 | 99 | |
| 100 | 100 | /** |
| 101 | + * 通过活动时段编号获取指定 status 的秒杀活动 | |
| 102 | + * | |
| 103 | + * @param configId 时段配置编号 | |
| 104 | + * @param status 状态 | |
| 105 | + * @return 秒杀活动列表 | |
| 106 | + */ | |
| 107 | + List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status); | |
| 108 | + | |
| 109 | + /** | |
| 101 | 110 | * 通过活动时段获取秒杀活动 |
| 102 | 111 | * |
| 103 | 112 | * @param pageReqVO 请求 | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
| ... | ... | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.service.seckill; |
| 3 | 3 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 7 | 6 | import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; |
| 8 | 7 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 9 | 8 | import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; |
| ... | ... | @@ -31,6 +30,7 @@ import java.util.Map; |
| 31 | 30 | import static cn.hutool.core.collection.CollUtil.isNotEmpty; |
| 32 | 31 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 33 | 32 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 33 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween; | |
| 34 | 34 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; |
| 35 | 35 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; |
| 36 | 36 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; |
| ... | ... | @@ -148,32 +148,26 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { |
| 148 | 148 | |
| 149 | 149 | @Override |
| 150 | 150 | @Transactional(rollbackFor = Exception.class) |
| 151 | - public void updateSeckillStock(Long activityId, Long skuId, Integer count) { | |
| 152 | - // 1、校验秒杀活动是否存在 | |
| 153 | - SeckillActivityDO seckillActivity = getSeckillActivity(activityId); | |
| 154 | - // 1.1、校验库存是否充足 | |
| 155 | - if (seckillActivity.getTotalStock() < count) { | |
| 151 | + public void updateSeckillStock(Long id, Long skuId, Integer count) { | |
| 152 | + // 1.1 校验活动库存是否充足 | |
| 153 | + SeckillActivityDO seckillActivity = getSeckillActivity(id); | |
| 154 | + if (count > seckillActivity.getTotalStock()) { | |
| 156 | 155 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 157 | 156 | } |
| 158 | - | |
| 159 | - // 2、获取活动商品 | |
| 160 | - List<SeckillProductDO> products = getSeckillProductListByActivityId(activityId); | |
| 161 | - // 2.1、过滤出购买的商品 | |
| 162 | - SeckillProductDO product = findFirst(products, item -> ObjectUtil.equal(skuId, item.getSkuId())); | |
| 163 | - // 2.2、检查活动商品库存是否充足 | |
| 164 | - boolean isSufficient = product == null || (product.getStock() == 0 || (product.getStock() < count) || (product.getStock() - count) < 0); | |
| 165 | - if (isSufficient) { | |
| 157 | + // 1.2 校验商品库存是否充足 | |
| 158 | + SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId); | |
| 159 | + if (product == null || count > product.getStock()) { | |
| 166 | 160 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 167 | 161 | } |
| 168 | 162 | |
| 169 | - // 3、更新活动商品库存 | |
| 170 | - int updateCount = seckillProductMapper.updateActivityStock(product.getId(), count); | |
| 163 | + // 2.1 更新活动商品库存 | |
| 164 | + int updateCount = seckillProductMapper.updateStock(product.getId(), count); | |
| 171 | 165 | if (updateCount == 0) { |
| 172 | 166 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 173 | 167 | } |
| 174 | 168 | |
| 175 | - // 4、更新活动库存 | |
| 176 | - updateCount = seckillActivityMapper.updateActivityStock(seckillActivity.getId(), count); | |
| 169 | + // 2.2 更新活动库存 | |
| 170 | + updateCount = seckillActivityMapper.updateStock(seckillActivity.getId(), count); | |
| 177 | 171 | if (updateCount == 0) { |
| 178 | 172 | throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); |
| 179 | 173 | } |
| ... | ... | @@ -268,8 +262,15 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { |
| 268 | 262 | |
| 269 | 263 | @Override |
| 270 | 264 | public List<SeckillActivityDO> getSeckillActivityListByConfigIds(Collection<Long> ids) { |
| 271 | - return CollectionUtils.filterList(seckillActivityMapper.selectList(), | |
| 272 | - item -> CollectionUtils.anyMatch(item.getConfigIds(), ids::contains)); | |
| 265 | + return filterList(seckillActivityMapper.selectList(), | |
| 266 | + item -> anyMatch(item.getConfigIds(), ids::contains)); | |
| 267 | + } | |
| 268 | + | |
| 269 | + @Override | |
| 270 | + public List<SeckillActivityDO> getSeckillActivityListByConfigIdAndStatus(Long configId, Integer status) { | |
| 271 | + return filterList(seckillActivityMapper.selectList(SeckillActivityDO::getStatus, status), | |
| 272 | + item -> anyMatch(item.getConfigIds(), id -> ObjectUtil.equal(id, configId)) // 校验时段 | |
| 273 | + && isBetween(item.getStartTime(), item.getEndTime())); // 追加当前日期是否处在活动日期之间的校验条件 | |
| 273 | 274 | } |
| 274 | 275 | |
| 275 | 276 | @Override | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigService.java
| ... | ... | @@ -54,6 +54,7 @@ public interface SeckillConfigService { |
| 54 | 54 | */ |
| 55 | 55 | List<SeckillConfigDO> getSeckillConfigList(); |
| 56 | 56 | |
| 57 | + | |
| 57 | 58 | /** |
| 58 | 59 | * 校验秒杀时段是否存在 |
| 59 | 60 | * |
| ... | ... | @@ -85,4 +86,12 @@ public interface SeckillConfigService { |
| 85 | 86 | */ |
| 86 | 87 | void updateSeckillConfigStatus(Long id, Integer status); |
| 87 | 88 | |
| 89 | + /** | |
| 90 | + * 获取当前日期时间处于的秒杀时段且状态为 status | |
| 91 | + * | |
| 92 | + * @param status 状态 | |
| 93 | + * @return 时段 | |
| 94 | + */ | |
| 95 | + SeckillConfigDO getSeckillConfigListByStatusOnCurrentTime(Integer status); | |
| 96 | + | |
| 88 | 97 | } | ... | ... |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillConfigServiceImpl.java
| ... | ... | @@ -20,6 +20,8 @@ import java.util.Collection; |
| 20 | 20 | import java.util.List; |
| 21 | 21 | |
| 22 | 22 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 23 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst; | |
| 24 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.isBetween; | |
| 23 | 25 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; |
| 24 | 26 | |
| 25 | 27 | /** |
| ... | ... | @@ -68,6 +70,12 @@ public class SeckillConfigServiceImpl implements SeckillConfigService { |
| 68 | 70 | } |
| 69 | 71 | |
| 70 | 72 | @Override |
| 73 | + public SeckillConfigDO getSeckillConfigListByStatusOnCurrentTime(Integer status) { | |
| 74 | + return findFirst(seckillConfigMapper.selectList(SeckillConfigDO::getStatus, status), | |
| 75 | + config -> isBetween(config.getStartTime(), config.getEndTime())); | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Override | |
| 71 | 79 | public void deleteSeckillConfig(Long id) { |
| 72 | 80 | // 校验存在 |
| 73 | 81 | validateSeckillConfigExists(id); | ... | ... |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
| ... | ... | @@ -12,12 +12,6 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; |
| 12 | 12 | public interface ErrorCodeConstants { |
| 13 | 13 | |
| 14 | 14 | // ========== Order 模块 1011000000 ========== |
| 15 | - ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在"); | |
| 16 | - ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖"); | |
| 17 | - ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足"); | |
| 18 | - ErrorCode ORDER_CREATE_SPU_NOT_FOUND = new ErrorCode(1011000005, "商品 SPU 不可售卖"); | |
| 19 | - ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在"); | |
| 20 | - | |
| 21 | 15 | ErrorCode ORDER_ITEM_NOT_FOUND = new ErrorCode(1011000010, "交易订单项不存在"); |
| 22 | 16 | ErrorCode ORDER_NOT_FOUND = new ErrorCode(1011000011, "交易订单不存在"); |
| 23 | 17 | ErrorCode ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL = new ErrorCode(1011000012, "交易订单项更新售后状态失败,请重试"); |
| ... | ... | @@ -37,6 +31,7 @@ public interface ErrorCodeConstants { |
| 37 | 31 | ErrorCode ORDER_UPDATE_PRICE_FAIL_PAID = new ErrorCode(1011000026, "支付订单调价失败,原因:支付订单已付款,不能调价"); |
| 38 | 32 | ErrorCode ORDER_UPDATE_PRICE_FAIL_EQUAL = new ErrorCode(1011000027, "支付订单调价失败,原因:价格没有变化"); |
| 39 | 33 | ErrorCode ORDER_UPDATE_PRICE_FAIL_NOT_ITEM = new ErrorCode(1011000028, "支付订单调价失败,原因:订单项不存在"); |
| 34 | + ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1011000029, "交易订单删除失败,订单不是【已取消】状态"); | |
| 40 | 35 | |
| 41 | 36 | // ========== After Sale 模块 1011000100 ========== |
| 42 | 37 | ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在"); |
| ... | ... | @@ -58,9 +53,8 @@ public interface ErrorCodeConstants { |
| 58 | 53 | |
| 59 | 54 | // ========== Price 相关 1011003000 ============ |
| 60 | 55 | ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0"); |
| 61 | - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空"); | |
| 62 | 56 | ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板"); |
| 63 | - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY = new ErrorCode(1011003003, "计算快递运费异常,自提点为空"); | |
| 57 | + ErrorCode PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER = new ErrorCode(1011003004, "参与秒杀、拼团、砍价的营销商品,无法使用优惠劵"); | |
| 64 | 58 | |
| 65 | 59 | // ========== 物流 Express 模块 1011004000 ========== |
| 66 | 60 | ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在"); | ... | ... |
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/order/TradeOrderOperateTypeEnum.java
| ... | ... | @@ -14,7 +14,10 @@ import lombok.RequiredArgsConstructor; |
| 14 | 14 | public enum TradeOrderOperateTypeEnum { |
| 15 | 15 | |
| 16 | 16 | MEMBER_CREATE(1, "用户下单"), |
| 17 | - TEST(2, "用户({nickname})做了({thing})"), | |
| 17 | + MEMBER_RECEIVE(30, "用户已收货"), | |
| 18 | + MEMBER_COMMENT(31, "用户评价"), | |
| 19 | + MEMBER_CANCEL(40, "取消订单"), | |
| 20 | + MEMBER_DELETE(41, "删除订单"), | |
| 18 | 21 | ; |
| 19 | 22 | |
| 20 | 23 | /** | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.java
| ... | ... | @@ -8,11 +8,11 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 8 | 8 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 9 | 9 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; |
| 10 | 10 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*; |
| 11 | +import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO; | |
| 11 | 12 | import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert; |
| 12 | 13 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO; |
| 13 | 14 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 14 | 15 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| 15 | -import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO; | |
| 16 | 16 | import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService; |
| 17 | 17 | import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService; |
| 18 | 18 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| ... | ... | @@ -87,13 +87,13 @@ public class TradeAfterSaleController { |
| 87 | 87 | // 拼接数据 |
| 88 | 88 | MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId()); |
| 89 | 89 | // 获取售后日志 |
| 90 | - List<TradeAfterSaleLogRespDTO> logs = afterSaleLogService.getLog(afterSale.getId()); | |
| 90 | + List<TradeAfterSaleLogRespVO> logs = afterSaleLogService.getLog(afterSale.getId()); | |
| 91 | 91 | // TODO 方便测试看效果,review 后移除 |
| 92 | 92 | if (logs == null) { |
| 93 | 93 | logs = new ArrayList<>(); |
| 94 | 94 | } |
| 95 | 95 | for (int i = 1; i <= 6; i++) { |
| 96 | - TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO(); | |
| 96 | + TradeAfterSaleLogRespVO respVO = new TradeAfterSaleLogRespVO(); | |
| 97 | 97 | respVO.setId((long) i); |
| 98 | 98 | respVO.setUserId((long) i); |
| 99 | 99 | respVO.setUserType(i % 2 == 0 ? 2 : 1); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
| ... | ... | @@ -156,7 +156,7 @@ public class AppTradeOrderController { |
| 156 | 156 | @Operation(summary = "删除交易订单") |
| 157 | 157 | @Parameter(name = "id", description = "交易订单编号") |
| 158 | 158 | public CommonResult<Boolean> deleteOrder(@RequestParam("id") Long id) { |
| 159 | - // TODO @芋艿:未实现,mock 用 | |
| 159 | + tradeOrderUpdateService.deleteOrder(getLoginUserId(), id); | |
| 160 | 160 | return success(true); |
| 161 | 161 | } |
| 162 | 162 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementRespVO.java
| ... | ... | @@ -82,6 +82,9 @@ public class AppTradeOrderSettlementRespVO { |
| 82 | 82 | @Schema(description = "积分抵扣的金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") |
| 83 | 83 | private Integer pointPrice; |
| 84 | 84 | |
| 85 | + @Schema(description = "VIP 减免金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") | |
| 86 | + private Integer vipPrice; | |
| 87 | + | |
| 85 | 88 | @Schema(description = "实际支付金额(总),单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "450") |
| 86 | 89 | private Integer payPrice; |
| 87 | 90 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/aftersale/TradeAfterSaleConvert.java
| ... | ... | @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO; |
| 16 | 16 | import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO; |
| 17 | 17 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 18 | 18 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| 19 | -import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO; | |
| 20 | 19 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 21 | 20 | import org.mapstruct.Mapper; |
| 22 | 21 | import org.mapstruct.Mapping; |
| ... | ... | @@ -68,10 +67,10 @@ public interface TradeAfterSaleConvert { |
| 68 | 67 | |
| 69 | 68 | PageResult<AppTradeAfterSaleRespVO> convertPage02(PageResult<TradeAfterSaleDO> page); |
| 70 | 69 | |
| 71 | - List<TradeAfterSaleLogRespDTO> convertList(List<TradeAfterSaleLogDO> list); | |
| 72 | - | |
| 70 | + List<TradeAfterSaleLogRespVO> convertList(List<TradeAfterSaleLogDO> list); | |
| 71 | + | |
| 73 | 72 | default TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, TradeOrderDO order, List<TradeOrderItemDO> orderItems, |
| 74 | - MemberUserRespDTO user, List<TradeAfterSaleLogRespDTO> logs) { | |
| 73 | + MemberUserRespDTO user, List<TradeAfterSaleLogRespVO> logs) { | |
| 75 | 74 | TradeAfterSaleDetailRespVO respVO = convert(afterSale, orderItems); |
| 76 | 75 | // 处理用户信息 |
| 77 | 76 | respVO.setUser(convert(user)); |
| ... | ... | @@ -81,7 +80,8 @@ public interface TradeAfterSaleConvert { |
| 81 | 80 | respVO.setLogs(convertList1(logs)); |
| 82 | 81 | return respVO; |
| 83 | 82 | } |
| 84 | - List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list); | |
| 83 | + | |
| 84 | + List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespVO> list); | |
| 85 | 85 | @Mapping(target = "id", source = "afterSale.id") |
| 86 | 86 | TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, List<TradeOrderItemDO> orderItems); |
| 87 | 87 | TradeOrderBaseVO convert(TradeOrderDO order); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
| ... | ... | @@ -65,6 +65,7 @@ public interface TradeOrderConvert { |
| 65 | 65 | @Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"), |
| 66 | 66 | @Mapping(source = "calculateRespBO.price.couponPrice", target = "couponPrice"), |
| 67 | 67 | @Mapping(source = "calculateRespBO.price.pointPrice", target = "pointPrice"), |
| 68 | + @Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"), | |
| 68 | 69 | @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice") |
| 69 | 70 | }) |
| 70 | 71 | TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
| ... | ... | @@ -168,6 +168,7 @@ public class TradeOrderDO extends BaseDO { |
| 168 | 168 | * - {@link #discountPrice} |
| 169 | 169 | * + {@link #deliveryPrice} |
| 170 | 170 | * + {@link #adjustPrice} |
| 171 | + * - {@link #vipPrice} | |
| 171 | 172 | */ |
| 172 | 173 | private Integer payPrice; |
| 173 | 174 | |
| ... | ... | @@ -273,5 +274,9 @@ public class TradeOrderDO extends BaseDO { |
| 273 | 274 | * 退还的使用的积分 |
| 274 | 275 | */ |
| 275 | 276 | private Integer refundPoint; |
| 277 | + /** | |
| 278 | + * VIP 减免金额,单位:分 | |
| 279 | + */ | |
| 280 | + private Integer vipPrice; | |
| 276 | 281 | |
| 277 | 282 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
| ... | ... | @@ -126,6 +126,7 @@ public class TradeOrderItemDO extends BaseDO { |
| 126 | 126 | * - {@link #discountPrice} |
| 127 | 127 | * + {@link #deliveryPrice} |
| 128 | 128 | * + {@link #adjustPrice} |
| 129 | + * - {@link #vipPrice} | |
| 129 | 130 | */ |
| 130 | 131 | private Integer payPrice; |
| 131 | 132 | |
| ... | ... | @@ -151,6 +152,10 @@ public class TradeOrderItemDO extends BaseDO { |
| 151 | 152 | * 赠送的积分 |
| 152 | 153 | */ |
| 153 | 154 | private Integer givePoint; |
| 155 | + /** | |
| 156 | + * VIP 减免金额,单位:分 | |
| 157 | + */ | |
| 158 | + private Integer vipPrice; | |
| 154 | 159 | // TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice,记录 vip 的优惠;还是直接使用 vipPrice; |
| 155 | 160 | // 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的; |
| 156 | 161 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/aftersalelog/core/dto/TradeAfterSaleLogRespDTO.java deleted
| 1 | -package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto; | |
| 2 | - | |
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | -import lombok.AllArgsConstructor; | |
| 5 | -import lombok.Data; | |
| 6 | -import lombok.NoArgsConstructor; | |
| 7 | - | |
| 8 | -import javax.validation.constraints.NotNull; | |
| 9 | -import java.time.LocalDateTime; | |
| 10 | - | |
| 11 | -// TODO @puhui999:这个是不是应该搞成 vo 啊? | |
| 12 | -/** | |
| 13 | - * 贸易售后日志详情 DTO | |
| 14 | - * | |
| 15 | - * @author HUIHUI | |
| 16 | - */ | |
| 17 | -@Data | |
| 18 | -@NoArgsConstructor | |
| 19 | -@AllArgsConstructor | |
| 20 | -public class TradeAfterSaleLogRespDTO { | |
| 21 | - | |
| 22 | - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20669") | |
| 23 | - private Long id; | |
| 24 | - | |
| 25 | - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22634") | |
| 26 | - @NotNull(message = "用户编号不能为空") | |
| 27 | - private Long userId; | |
| 28 | - | |
| 29 | - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 30 | - @NotNull(message = "用户类型不能为空") | |
| 31 | - private Integer userType; | |
| 32 | - | |
| 33 | - @Schema(description = "售后编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3023") | |
| 34 | - @NotNull(message = "售后编号不能为空") | |
| 35 | - private Long afterSaleId; | |
| 36 | - | |
| 37 | - @Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25870") | |
| 38 | - @NotNull(message = "订单编号不能为空") | |
| 39 | - private Long orderId; | |
| 40 | - | |
| 41 | - @Schema(description = "订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23154") | |
| 42 | - @NotNull(message = "订单项编号不能为空") | |
| 43 | - private Long orderItemId; | |
| 44 | - | |
| 45 | - @Schema(description = "售后状态(之前)", example = "2") | |
| 46 | - private Integer beforeStatus; | |
| 47 | - | |
| 48 | - @Schema(description = "售后状态(之后)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 49 | - @NotNull(message = "售后状态(之后)不能为空") | |
| 50 | - private Integer afterStatus; | |
| 51 | - | |
| 52 | - @Schema(description = "操作明细", requiredMode = Schema.RequiredMode.REQUIRED, example = "维权完成,退款金额:¥37776.00") | |
| 53 | - @NotNull(message = "操作明细不能为空") | |
| 54 | - private String content; | |
| 55 | - | |
| 56 | - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 57 | - private LocalDateTime createTime; | |
| 58 | - | |
| 59 | -} |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/aftersalelog/core/service/AfterSaleLogService.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service; |
| 2 | 2 | |
| 3 | 3 | |
| 4 | +import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO; | |
| 4 | 5 | import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO; |
| 5 | -import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO; | |
| 6 | 6 | |
| 7 | 7 | import java.util.List; |
| 8 | 8 | |
| ... | ... | @@ -29,6 +29,6 @@ public interface AfterSaleLogService { |
| 29 | 29 | * @param afterSaleId 售后编号 |
| 30 | 30 | * @return 售后日志 |
| 31 | 31 | */ |
| 32 | - List<TradeAfterSaleLogRespDTO> getLog(Long afterSaleId); | |
| 32 | + List<TradeAfterSaleLogRespVO> getLog(Long afterSaleId); | |
| 33 | 33 | |
| 34 | 34 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/core/aop/TradeOrderLogAspect.java
| ... | ... | @@ -63,6 +63,9 @@ public class TradeOrderLogAspect { |
| 63 | 63 | Long userId = getUserId(); |
| 64 | 64 | // 1.2 订单信息 |
| 65 | 65 | Long orderId = ORDER_ID.get(); |
| 66 | + if (orderId == null) { // 如果未设置,只有注解,说明不需要记录订单日志 | |
| 67 | + return; | |
| 68 | + } | |
| 66 | 69 | Integer beforeStatus = BEFORE_STATUS.get(); |
| 67 | 70 | Integer afterStatus = AFTER_STATUS.get(); |
| 68 | 71 | Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap()); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
| ... | ... | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 11 | 11 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO; |
| 12 | 12 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO; |
| 13 | 13 | import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO; |
| 14 | +import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO; | |
| 14 | 15 | import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO; |
| 15 | 16 | import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO; |
| 16 | 17 | import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert; |
| ... | ... | @@ -26,7 +27,6 @@ import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum; |
| 26 | 27 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; |
| 27 | 28 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 28 | 29 | import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO; |
| 29 | -import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO; | |
| 30 | 30 | import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService; |
| 31 | 31 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 32 | 32 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| ... | ... | @@ -449,8 +449,9 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa |
| 449 | 449 | } |
| 450 | 450 | } |
| 451 | 451 | |
| 452 | + // TODO @puhui999:应该返回 do 哈。 | |
| 452 | 453 | @Override |
| 453 | - public List<TradeAfterSaleLogRespDTO> getLog(Long afterSaleId) { | |
| 454 | + public List<TradeAfterSaleLogRespVO> getLog(Long afterSaleId) { | |
| 454 | 455 | // TODO 不熟悉流程先这么滴 |
| 455 | 456 | List<TradeAfterSaleLogDO> saleLogDOs = tradeAfterSaleLogMapper.selectList(TradeAfterSaleLogDO::getAfterSaleId, afterSaleId); |
| 456 | 457 | return TradeAfterSaleConvert.INSTANCE.convertList(saleLogDOs); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
| ... | ... | @@ -106,7 +106,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { |
| 106 | 106 | if (order == null) { |
| 107 | 107 | throw exception(ORDER_NOT_FOUND); |
| 108 | 108 | } |
| 109 | - | |
| 109 | + // 查询物流 | |
| 110 | 110 | return getExpressTrackList(order); |
| 111 | 111 | } |
| 112 | 112 | |
| ... | ... | @@ -117,7 +117,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { |
| 117 | 117 | if (order == null) { |
| 118 | 118 | throw exception(ORDER_NOT_FOUND); |
| 119 | 119 | } |
| 120 | - | |
| 120 | + // 查询物流 | |
| 121 | 121 | return getExpressTrackList(order); |
| 122 | 122 | } |
| 123 | 123 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
| ... | ... | @@ -63,6 +63,22 @@ public interface TradeOrderUpdateService { |
| 63 | 63 | void receiveOrder(Long userId, Long id); |
| 64 | 64 | |
| 65 | 65 | /** |
| 66 | + * 【会员】取消订单 | |
| 67 | + * | |
| 68 | + * @param userId 用户编号 | |
| 69 | + * @param id 订单编号 | |
| 70 | + */ | |
| 71 | + void cancelOrder(Long userId, Long id); | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 【会员】删除订单 | |
| 75 | + * | |
| 76 | + * @param userId 用户编号 | |
| 77 | + * @param id 订单编号 | |
| 78 | + */ | |
| 79 | + void deleteOrder(Long userId, Long id); | |
| 80 | + | |
| 81 | + /** | |
| 66 | 82 | * 【管理员】交易订单备注 |
| 67 | 83 | * |
| 68 | 84 | * @param reqVO 请求 |
| ... | ... | @@ -117,11 +133,4 @@ public interface TradeOrderUpdateService { |
| 117 | 133 | */ |
| 118 | 134 | Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO); |
| 119 | 135 | |
| 120 | - /** | |
| 121 | - * 【会员】取消订单 | |
| 122 | - * | |
| 123 | - * @param userId 用户ID | |
| 124 | - * @param id 订单编号 | |
| 125 | - */ | |
| 126 | - void cancelOrder(Long userId, Long id); | |
| 127 | 136 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
| ... | ... | @@ -49,6 +49,7 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 49 | 49 | import cn.iocoder.yudao.module.trade.enums.order.*; |
| 50 | 50 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 51 | 51 | import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; |
| 52 | +import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils; | |
| 52 | 53 | import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; |
| 53 | 54 | import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; |
| 54 | 55 | import cn.iocoder.yudao.module.trade.service.cart.CartService; |
| ... | ... | @@ -299,6 +300,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 299 | 300 | // 5. 生成预支付 |
| 300 | 301 | createPayOrder(order, orderItems, calculateRespBO); |
| 301 | 302 | |
| 303 | + // 6. 插入订单日志 | |
| 304 | + TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus()); | |
| 305 | + | |
| 302 | 306 | // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! |
| 303 | 307 | } |
| 304 | 308 | |
| ... | ... | @@ -482,6 +486,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 482 | 486 | |
| 483 | 487 | @Override |
| 484 | 488 | @Transactional(rollbackFor = Exception.class) |
| 489 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE) | |
| 485 | 490 | public void receiveOrder(Long userId, Long id) { |
| 486 | 491 | // 校验并获得交易订单(可收货) |
| 487 | 492 | TradeOrderDO order = validateOrderReceivable(userId, id); |
| ... | ... | @@ -492,7 +497,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 492 | 497 | if (updateCount == 0) { |
| 493 | 498 | throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); |
| 494 | 499 | } |
| 495 | - // TODO 芋艿:OrderLog | |
| 496 | 500 | |
| 497 | 501 | // TODO 芋艿:lili 发送订单变化的消息 |
| 498 | 502 | |
| ... | ... | @@ -500,7 +504,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 500 | 504 | |
| 501 | 505 | // TODO 芋艿:销售佣金的记录; |
| 502 | 506 | |
| 503 | - // TODO 芋艿:获得积分; | |
| 507 | + // 插入订单日志 | |
| 508 | + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); | |
| 504 | 509 | } |
| 505 | 510 | |
| 506 | 511 | @Override |
| ... | ... | @@ -541,41 +546,49 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 541 | 546 | tradeOrderMapper.updateById(update); |
| 542 | 547 | // TODO @芋艿:改价时,赠送的积分,要不要做改动??? |
| 543 | 548 | |
| 544 | - // TODO @puhui999:应该是按照 payPrice 分配;并且要考虑取余问题;payPrice 也要考虑,item 里的 | |
| 545 | - // TODO:先按 adjustPrice 实现,没明白 payPrice 怎么搞哈哈哈 | |
| 546 | - // TODO @puhui999:就是对比新老 adjustPrice 的差值,然后计算补充的 adjustPrice 最终值;另外,可以不用区分 items.size 是不是 > 1 哈;应该是一致的逻辑;分摊的逻辑,有点类似 dividePrice 方法噢; | |
| 547 | 549 | // 5、更新 TradeOrderItem |
| 548 | - if (items.size() > 1) { | |
| 549 | - // TradeOrderItemDO 需要做 adjustPrice 的分摊 | |
| 550 | - int price = reqVO.getAdjustPrice() / items.size(); | |
| 551 | - int remainderPrice = reqVO.getAdjustPrice() % items.size(); | |
| 552 | - List<TradeOrderItemDO> orders = new ArrayList<>(); | |
| 553 | - for (int i = 0; i < items.size(); i++) { | |
| 554 | - // 把平摊后剩余的金额加到第一个订单项 | |
| 555 | - if (remainderPrice != 0 && i == 0) { | |
| 556 | - orders.add(convertOrderItemPrice(items.get(i), price + remainderPrice)); | |
| 557 | - } | |
| 558 | - orders.add(convertOrderItemPrice(items.get(i), price)); | |
| 559 | - } | |
| 560 | - tradeOrderItemMapper.updateBatch(orders); | |
| 561 | - } else { | |
| 562 | - TradeOrderItemDO orderItem = items.get(0); | |
| 563 | - TradeOrderItemDO updateItem = convertOrderItemPrice(orderItem, reqVO.getAdjustPrice()); | |
| 564 | - tradeOrderItemMapper.updateById(updateItem); | |
| 550 | + // TradeOrderItemDO 需要做 adjustPrice 的分摊 | |
| 551 | + List<Integer> dividePrices = dividePrice(items, orderPayPrice); | |
| 552 | + List<TradeOrderItemDO> updateItems = new ArrayList<>(); | |
| 553 | + for (int i = 0; i < items.size(); i++) { | |
| 554 | + TradeOrderItemDO item = items.get(i); | |
| 555 | + Integer adjustPrice = item.getPrice() - dividePrices.get(i); // 计算调整的金额 | |
| 556 | + updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(adjustPrice) | |
| 557 | + .setPayPrice(item.getPayPrice() - adjustPrice)); | |
| 565 | 558 | } |
| 559 | + tradeOrderItemMapper.updateBatch(updateItems); | |
| 560 | + | |
| 566 | 561 | |
| 567 | 562 | // 6、更新支付订单 |
| 568 | 563 | payOrderApi.updatePayOrderPrice(order.getPayOrderId(), update.getPayPrice()); |
| 569 | 564 | } |
| 570 | 565 | |
| 571 | - private TradeOrderItemDO convertOrderItemPrice(TradeOrderItemDO orderItem, Integer price) { | |
| 572 | - TradeOrderItemDO newOrderItem = new TradeOrderItemDO(); | |
| 573 | - newOrderItem.setId(orderItem.getId()); | |
| 574 | - newOrderItem.setAdjustPrice(price); | |
| 575 | - int payPrice = orderItem.getAdjustPrice() != null ? (orderItem.getPayPrice() - orderItem.getAdjustPrice()) | |
| 576 | - + price : orderItem.getPayPrice() + price; | |
| 577 | - newOrderItem.setPayPrice(payPrice); | |
| 578 | - return newOrderItem; | |
| 566 | + /** | |
| 567 | + * 计算订单调价价格分摊 | |
| 568 | + * | |
| 569 | + * @param items 订单项 | |
| 570 | + * @param orderPayPrice 订单支付金额 | |
| 571 | + * @return 分摊金额数组,和传入的 orderItems 一一对应 | |
| 572 | + */ | |
| 573 | + private List<Integer> dividePrice(List<TradeOrderItemDO> items, Integer orderPayPrice) { | |
| 574 | + Integer total = getSumValue(items, TradeOrderItemDO::getPrice, Integer::sum); | |
| 575 | + assert total != null; | |
| 576 | + // 遍历每一个,进行分摊 | |
| 577 | + List<Integer> prices = new ArrayList<>(items.size()); | |
| 578 | + int remainPrice = orderPayPrice; | |
| 579 | + for (int i = 0; i < items.size(); i++) { | |
| 580 | + TradeOrderItemDO orderItem = items.get(i); | |
| 581 | + int partPrice; | |
| 582 | + if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减 | |
| 583 | + partPrice = (int) (orderPayPrice * (1.0D * orderItem.getPayPrice() / total)); | |
| 584 | + remainPrice -= partPrice; | |
| 585 | + } else { | |
| 586 | + partPrice = remainPrice; | |
| 587 | + } | |
| 588 | + Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0"); | |
| 589 | + prices.add(partPrice); | |
| 590 | + } | |
| 591 | + return prices; | |
| 579 | 592 | } |
| 580 | 593 | |
| 581 | 594 | @Override |
| ... | ... | @@ -671,6 +684,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 671 | 684 | |
| 672 | 685 | @Override |
| 673 | 686 | @Transactional(rollbackFor = Exception.class) |
| 687 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT) | |
| 674 | 688 | public Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) { |
| 675 | 689 | // 先通过订单项 ID,查询订单项是否存在 |
| 676 | 690 | TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId); |
| ... | ... | @@ -698,24 +712,27 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 698 | 712 | List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId()); |
| 699 | 713 | if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) { |
| 700 | 714 | tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)); |
| 701 | - // TODO 待实现:已完成评价,要不要写一条订单日志?目前 crmeb 会写,有赞可以研究下 | |
| 715 | + // 增加订单日志 | |
| 716 | + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); | |
| 702 | 717 | } |
| 703 | 718 | return comment; |
| 704 | 719 | } |
| 705 | 720 | |
| 706 | 721 | @Override |
| 722 | + @Transactional(rollbackFor = Exception.class) | |
| 723 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL) | |
| 707 | 724 | public void cancelOrder(Long userId, Long id) { |
| 708 | - // 校验存在 | |
| 725 | + // 1.1 校验存在 | |
| 709 | 726 | TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId); |
| 710 | 727 | if (order == null) { |
| 711 | 728 | throw exception(ORDER_NOT_FOUND); |
| 712 | 729 | } |
| 713 | - // 校验状态 | |
| 730 | + // 1.2 校验状态 | |
| 714 | 731 | if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) { |
| 715 | 732 | throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); |
| 716 | 733 | } |
| 717 | 734 | |
| 718 | - // 1.更新 TradeOrderDO 状态为已取消 | |
| 735 | + // 2. 更新 TradeOrderDO 状态为已取消 | |
| 719 | 736 | int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(), |
| 720 | 737 | new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) |
| 721 | 738 | .setCancelTime(LocalDateTime.now()) |
| ... | ... | @@ -724,22 +741,43 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 724 | 741 | throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); |
| 725 | 742 | } |
| 726 | 743 | |
| 727 | - // TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来 | |
| 744 | + // 3. TODO 活动相关库存回滚需要活动 id,活动 id 怎么获取?app 端能否传过来;回复:从订单里拿呀 | |
| 728 | 745 | tradeOrderHandlers.forEach(handler -> handler.rollback()); |
| 729 | 746 | |
| 730 | - // 2.回滚库存 | |
| 747 | + // 4. 回滚库存 | |
| 731 | 748 | List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); |
| 732 | 749 | productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems)); |
| 733 | 750 | |
| 734 | - // 3.回滚优惠券 | |
| 735 | - couponApi.returnUsedCoupon(order.getCouponId()); | |
| 751 | + // 5. 回滚优惠券 | |
| 752 | + if (order.getCouponId() > 0) { | |
| 753 | + couponApi.returnUsedCoupon(order.getCouponId()); | |
| 754 | + } | |
| 736 | 755 | |
| 737 | - // 4.回滚积分(抵扣的) | |
| 756 | + // 6. 回滚积分(抵扣的) | |
| 738 | 757 | addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId()); |
| 739 | 758 | |
| 740 | - // TODO 芋艿:OrderLog | |
| 759 | + // 7. 增加订单日志 | |
| 760 | + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus()); | |
| 761 | + } | |
| 741 | 762 | |
| 742 | - // TODO 芋艿:lili 发送订单变化的消息 | |
| 763 | + @Override | |
| 764 | + @Transactional(rollbackFor = Exception.class) | |
| 765 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE) | |
| 766 | + public void deleteOrder(Long userId, Long id) { | |
| 767 | + // 1.1 校验存在 | |
| 768 | + TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId); | |
| 769 | + if (order == null) { | |
| 770 | + throw exception(ORDER_NOT_FOUND); | |
| 771 | + } | |
| 772 | + // 1.2 校验状态 | |
| 773 | + if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) { | |
| 774 | + throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL); | |
| 775 | + } | |
| 776 | + // 2. 删除订单 | |
| 777 | + tradeOrderMapper.deleteById(id); | |
| 778 | + | |
| 779 | + // 3. 记录日志 | |
| 780 | + TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus()); | |
| 743 | 781 | } |
| 744 | 782 | |
| 745 | 783 | /** |
| ... | ... | @@ -766,6 +804,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 766 | 804 | memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); |
| 767 | 805 | } |
| 768 | 806 | |
| 807 | + /** | |
| 808 | + * 添加用户积分 | |
| 809 | + * <p> | |
| 810 | + * 目前是支付成功后,就会创建积分记录。 | |
| 811 | + * <p> | |
| 812 | + * 业内还有两种做法,可以根据自己的业务调整: | |
| 813 | + * 1. 确认收货后,才创建积分记录 | |
| 814 | + * 2. 支付 or 下单成功时,创建积分记录(冻结),确认收货解冻或者 n 天后解冻 | |
| 815 | + * | |
| 816 | + * @param userId 用户编号 | |
| 817 | + * @param point 增加积分数量 | |
| 818 | + * @param bizType 业务编号 | |
| 819 | + * @param bizId 业务编号 | |
| 820 | + */ | |
| 769 | 821 | protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) { |
| 770 | 822 | if (point != null && point > 0) { |
| 771 | 823 | memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId)); |
| ... | ... | @@ -778,15 +830,27 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 778 | 830 | } |
| 779 | 831 | } |
| 780 | 832 | |
| 833 | + /** | |
| 834 | + * 创建分销记录 | |
| 835 | + * | |
| 836 | + * 目前是支付成功后,就会创建分销记录。 | |
| 837 | + * | |
| 838 | + * 业内还有两种做法,可以根据自己的业务调整: | |
| 839 | + * 1. 确认收货后,才创建分销记录 | |
| 840 | + * 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻 | |
| 841 | + * | |
| 842 | + * @param userId 用户编号 | |
| 843 | + * @param orderId 订单编号 | |
| 844 | + */ | |
| 781 | 845 | @Async |
| 782 | 846 | protected void addBrokerageAsync(Long userId, Long orderId) { |
| 783 | 847 | MemberUserRespDTO user = memberUserApi.getUser(userId); |
| 784 | 848 | Assert.notNull(user); |
| 785 | - | |
| 849 | + // 每一个订单项,都会去生成分销记录 | |
| 786 | 850 | List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId); |
| 787 | - List<BrokerageAddReqBO> list = convertList(orderItems, | |
| 851 | + List<BrokerageAddReqBO> addList = convertList(orderItems, | |
| 788 | 852 | item -> TradeOrderConvert.INSTANCE.convert(user, item, productSkuApi.getSku(item.getSkuId()))); |
| 789 | - brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, list); | |
| 853 | + brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList); | |
| 790 | 854 | } |
| 791 | 855 | |
| 792 | 856 | @Async | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/bo/TradeBeforeOrderCreateReqBO.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.order.bo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| ... | ... | @@ -14,36 +15,73 @@ import javax.validation.constraints.NotNull; |
| 14 | 15 | @Data |
| 15 | 16 | public class TradeBeforeOrderCreateReqBO { |
| 16 | 17 | |
| 17 | - // TODO @puhui999:注释也写下哈;bo 还是写注释噢 | |
| 18 | - | |
| 18 | + /** | |
| 19 | + * 订单类型 | |
| 20 | + * | |
| 21 | + * 枚举 {@link TradeOrderTypeEnum} | |
| 22 | + */ | |
| 19 | 23 | @NotNull(message = "订单类型不能为空") |
| 20 | 24 | private Integer orderType; |
| 21 | 25 | |
| 26 | + /** | |
| 27 | + * 用户编号 | |
| 28 | + * | |
| 29 | + * 关联 MemberUserDO 的 id 编号 | |
| 30 | + */ | |
| 31 | + @NotNull(message = "用户编号不能为空") | |
| 32 | + private Long userId; | |
| 33 | + | |
| 22 | 34 | // ========== 秒杀活动相关字段 ========== |
| 23 | 35 | |
| 36 | + /** | |
| 37 | + * | |
| 38 | + */ | |
| 24 | 39 | @Schema(description = "秒杀活动编号", example = "1024") |
| 25 | 40 | private Long seckillActivityId; |
| 26 | 41 | |
| 27 | 42 | // ========== 拼团活动相关字段 ========== |
| 28 | 43 | |
| 44 | + /** | |
| 45 | + * 拼团活动编号 | |
| 46 | + */ | |
| 29 | 47 | @Schema(description = "拼团活动编号", example = "1024") |
| 30 | 48 | private Long combinationActivityId; |
| 31 | 49 | |
| 50 | + /** | |
| 51 | + * 拼团团长编号 | |
| 52 | + */ | |
| 32 | 53 | @Schema(description = "拼团团长编号", example = "2048") |
| 33 | 54 | private Long combinationHeadId; |
| 34 | 55 | |
| 56 | + // ========== 砍价活动相关字段 ========== | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * 砍价活动编号 | |
| 60 | + */ | |
| 35 | 61 | @Schema(description = "砍价活动编号", example = "123") |
| 36 | 62 | private Long bargainActivityId; |
| 37 | 63 | |
| 64 | + // ========== 活动购买商品相关字段 ========== | |
| 65 | + | |
| 66 | + /** | |
| 67 | + * 商品 SPU 编号 | |
| 68 | + * | |
| 69 | + * 关联 ProductSkuDO 的 spuId 编号 | |
| 70 | + */ | |
| 38 | 71 | @NotNull(message = "SPU 编号不能为空") |
| 39 | 72 | private Long spuId; |
| 40 | 73 | |
| 74 | + /** | |
| 75 | + * 商品 SKU 编号 | |
| 76 | + * | |
| 77 | + * 关联 ProductSkuDO 的 id 编号 | |
| 78 | + */ | |
| 41 | 79 | @NotNull(message = "SKU 编号活动商品不能为空") |
| 42 | 80 | private Long skuId; |
| 43 | 81 | |
| 44 | - @NotNull(message = "用户编号不能为空") | |
| 45 | - private Long userId; | |
| 46 | - | |
| 82 | + /** | |
| 83 | + * 购买的商品数量 | |
| 84 | + */ | |
| 47 | 85 | @NotNull(message = "购买数量不能为空") |
| 48 | 86 | private Integer count; |
| 49 | 87 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBargainHandler.java
| ... | ... | @@ -20,14 +20,17 @@ public class TradeBargainHandler implements TradeOrderHandler { |
| 20 | 20 | @Resource |
| 21 | 21 | private BargainActivityApi bargainActivityApi; |
| 22 | 22 | |
| 23 | + // TODO @puhui999:先临时写在这里;在价格计算时,如果是秒杀商品,需要校验如下条件: | |
| 24 | + // 1. 商品存在、库存充足、单次限购; | |
| 25 | + // 2. 活动进行中、时间段符合 | |
| 26 | + | |
| 23 | 27 | @Override |
| 24 | 28 | public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) { |
| 25 | 29 | // 如果是砍价订单 |
| 26 | 30 | if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), reqBO.getOrderType())) { |
| 27 | 31 | return; |
| 28 | 32 | } |
| 29 | - | |
| 30 | - // 额外扣减砍价的库存 | |
| 33 | + // 扣减砍价活动的库存 | |
| 31 | 34 | bargainActivityApi.updateBargainActivityStock(reqBO.getBargainActivityId(), reqBO.getCount()); |
| 32 | 35 | } |
| 33 | 36 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeSeckillHandler.java
| ... | ... | @@ -20,13 +20,17 @@ public class TradeSeckillHandler implements TradeOrderHandler { |
| 20 | 20 | @Resource |
| 21 | 21 | private SeckillActivityApi seckillActivityApi; |
| 22 | 22 | |
| 23 | + // TODO @puhui999:先临时写在这里;在价格计算时,如果是秒杀商品,需要校验如下条件: | |
| 24 | + // 1. 商品存在、库存充足、单次限购; | |
| 25 | + // 2. 活动进行中、时间段符合 | |
| 26 | + | |
| 23 | 27 | @Override |
| 24 | 28 | public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) { |
| 25 | 29 | // 如果是秒杀订单:额外扣减秒杀的库存; |
| 26 | 30 | if (ObjectUtil.notEqual(TradeOrderTypeEnum.SECKILL.getType(), reqBO.getOrderType())) { |
| 27 | 31 | return; |
| 28 | 32 | } |
| 29 | - | |
| 33 | + // 扣减秒杀活动的库存 | |
| 30 | 34 | seckillActivityApi.updateSeckillStock(reqBO.getSeckillActivityId(), reqBO.getSkuId(), reqBO.getCount()); |
| 31 | 35 | } |
| 32 | 36 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
| ... | ... | @@ -95,6 +95,10 @@ public class TradePriceCalculateRespBO { |
| 95 | 95 | */ |
| 96 | 96 | private Integer pointPrice; |
| 97 | 97 | /** |
| 98 | + * VIP 减免金额,单位:分 | |
| 99 | + */ | |
| 100 | + private Integer vipPrice; | |
| 101 | + /** | |
| 98 | 102 | * 最终购买金额(总),单位:分 |
| 99 | 103 | * |
| 100 | 104 | * = {@link #totalPrice} |
| ... | ... | @@ -102,6 +106,7 @@ public class TradePriceCalculateRespBO { |
| 102 | 106 | * - {@link #pointPrice} |
| 103 | 107 | * - {@link #discountPrice} |
| 104 | 108 | * + {@link #deliveryPrice} |
| 109 | + * - {@link #vipPrice} | |
| 105 | 110 | */ |
| 106 | 111 | private Integer payPrice; |
| 107 | 112 | |
| ... | ... | @@ -168,6 +173,10 @@ public class TradePriceCalculateRespBO { |
| 168 | 173 | */ |
| 169 | 174 | private Integer usePoint; |
| 170 | 175 | /** |
| 176 | + * VIP 减免金额,单位:分 | |
| 177 | + */ | |
| 178 | + private Integer vipPrice; | |
| 179 | + /** | |
| 171 | 180 | * 应付金额(总),单位:分 |
| 172 | 181 | * |
| 173 | 182 | * = {@link #price} * {@link #count} |
| ... | ... | @@ -175,6 +184,7 @@ public class TradePriceCalculateRespBO { |
| 175 | 184 | * - {@link #pointPrice} |
| 176 | 185 | * - {@link #discountPrice} |
| 177 | 186 | * + {@link #deliveryPrice} |
| 187 | + * - {@link #vipPrice} | |
| 178 | 188 | */ |
| 179 | 189 | private Integer payPrice; |
| 180 | 190 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculator.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| 5 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 6 | import cn.hutool.core.util.StrUtil; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO; |
| ... | ... | @@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; |
| 9 | 10 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 10 | 11 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; |
| 11 | 12 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 13 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 12 | 14 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 13 | 15 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 14 | 16 | import org.springframework.core.annotation.Order; |
| ... | ... | @@ -22,6 +24,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU |
| 22 | 24 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; |
| 23 | 25 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE; |
| 24 | 26 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU; |
| 27 | +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER; | |
| 25 | 28 | |
| 26 | 29 | /** |
| 27 | 30 | * 优惠劵的 {@link TradePriceCalculator} 实现类 |
| ... | ... | @@ -44,6 +47,10 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator { |
| 44 | 47 | CouponRespDTO coupon = couponApi.validateCoupon(new CouponValidReqDTO() |
| 45 | 48 | .setId(param.getCouponId()).setUserId(param.getUserId())); |
| 46 | 49 | Assert.notNull(coupon, "校验通过的优惠劵({}),不能为空", param.getCouponId()); |
| 50 | + // 1.2 只有【普通】订单,才允许使用优惠劵 | |
| 51 | + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { | |
| 52 | + throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER); | |
| 53 | + } | |
| 47 | 54 | |
| 48 | 55 | // 2.1 获得匹配的商品 SKU 数组 |
| 49 | 56 | List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
| ... | ... | @@ -64,7 +64,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 64 | 64 | |
| 65 | 65 | private void calculateByPickUp(TradePriceCalculateReqBO param) { |
| 66 | 66 | if (param.getPickUpStoreId() == null) { |
| 67 | - throw exception(PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY); | |
| 67 | + // 价格计算时,如果为空就不算~最终下单,会校验该字段不允许空 | |
| 68 | + return; | |
| 68 | 69 | } |
| 69 | 70 | DeliveryPickUpStoreDO pickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(param.getPickUpStoreId()); |
| 70 | 71 | if (pickUpStore == null || CommonStatusEnum.DISABLE.getStatus().equals(pickUpStore.getStatus())) { |
| ... | ... | @@ -77,7 +78,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 77 | 78 | private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 78 | 79 | // 0. 得到收件地址区域 |
| 79 | 80 | if (param.getAddressId() == null) { |
| 80 | - throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY); | |
| 81 | + // 价格计算时,如果为空就不算~最终下单,会校验该字段不允许空 | |
| 82 | + return; | |
| 81 | 83 | } |
| 82 | 84 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |
| 83 | 85 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 8 | 9 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 9 | 11 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 10 | 12 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 11 | 13 | import org.springframework.core.annotation.Order; |
| ... | ... | @@ -33,6 +35,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato |
| 33 | 35 | |
| 34 | 36 | @Override |
| 35 | 37 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 38 | + // 0. 只有【普通】订单,才计算该优惠 | |
| 39 | + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { | |
| 40 | + return; | |
| 41 | + } | |
| 36 | 42 | // 获得 SKU 对应的限时折扣活动 |
| 37 | 43 | List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList( |
| 38 | 44 | convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId)); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.price.calculator; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 4 | +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; | |
| 5 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 6 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 7 | +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 10 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | |
| 11 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | |
| 12 | +import org.springframework.core.annotation.Order; | |
| 13 | +import org.springframework.stereotype.Component; | |
| 14 | + | |
| 15 | +import javax.annotation.Resource; | |
| 16 | + | |
| 17 | +import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * 会员 VIP 折扣的 {@link TradePriceCalculator} 实现类 | |
| 21 | + * | |
| 22 | + * @author 芋道源码 | |
| 23 | + */ | |
| 24 | +@Component | |
| 25 | +@Order(TradePriceCalculator.ORDER_MEMBER_LEVEL) | |
| 26 | +public class TradeMemberLevelPriceCalculator implements TradePriceCalculator { | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + private MemberLevelApi memberLevelApi; | |
| 30 | + @Resource | |
| 31 | + private MemberUserApi memberUserApi; | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { | |
| 35 | + // 0. 只有【普通】订单,才计算该优惠 | |
| 36 | + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { | |
| 37 | + return; | |
| 38 | + } | |
| 39 | + // 1. 获得用户的会员等级 | |
| 40 | + MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()); | |
| 41 | + if (user.getLevelId() == null || user.getLevelId() <= 0) { | |
| 42 | + return; | |
| 43 | + } | |
| 44 | + MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId()); | |
| 45 | + if (level == null || level.getDiscountPercent() == null) { | |
| 46 | + return; | |
| 47 | + } | |
| 48 | + | |
| 49 | + // 2. 计算每个 SKU 的优惠金额 | |
| 50 | + result.getItems().forEach(orderItem -> { | |
| 51 | + // 2.1 计算优惠金额 | |
| 52 | + Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent()); | |
| 53 | + if (vipPrice <= 0) { | |
| 54 | + return; | |
| 55 | + } | |
| 56 | + | |
| 57 | + // 2.2 记录优惠明细 | |
| 58 | + if (orderItem.getSelected()) { | |
| 59 | + // 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示 | |
| 60 | + TradePriceCalculatorHelper.addPromotion(result, orderItem, | |
| 61 | + level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(), | |
| 62 | + String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)), | |
| 63 | + vipPrice); | |
| 64 | + } | |
| 65 | + | |
| 66 | + // 2.3 更新 SKU 的优惠金额 | |
| 67 | + orderItem.setVipPrice(vipPrice); | |
| 68 | + TradePriceCalculatorHelper.recountPayPrice(orderItem); | |
| 69 | + }); | |
| 70 | + TradePriceCalculatorHelper.recountAllPrice(result); | |
| 71 | + } | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 计算会员 VIP 优惠价格 | |
| 75 | + * | |
| 76 | + * @param price 原价 | |
| 77 | + * @param discountPercent 折扣 | |
| 78 | + * @return 优惠价格 | |
| 79 | + */ | |
| 80 | + public Integer calculateVipPrice(Integer price, Integer discountPercent) { | |
| 81 | + if (discountPercent == null) { | |
| 82 | + return 0; | |
| 83 | + } | |
| 84 | + Integer newPrice = price * discountPercent / 100; | |
| 85 | + return price - newPrice; | |
| 86 | + } | |
| 87 | + | |
| 88 | +} | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
| ... | ... | @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 13 | 13 | */ |
| 14 | 14 | public interface TradePriceCalculator { |
| 15 | 15 | |
| 16 | + int ORDER_MEMBER_LEVEL = 5; | |
| 16 | 17 | int ORDER_DISCOUNT_ACTIVITY = 10; |
| 17 | 18 | int ORDER_REWARD_ACTIVITY = 20; |
| 18 | 19 | int ORDER_COUPON = 30; | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
| ... | ... | @@ -52,7 +52,7 @@ public class TradePriceCalculatorHelper { |
| 52 | 52 | .setCount(item.getCount()).setCartId(item.getCartId()).setSelected(item.getSelected()); |
| 53 | 53 | // sku 价格 |
| 54 | 54 | orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount()) |
| 55 | - .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0); | |
| 55 | + .setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0).setVipPrice(0); | |
| 56 | 56 | // sku 信息 |
| 57 | 57 | orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties()) |
| 58 | 58 | .setWeight(sku.getWeight()).setVolume(sku.getVolume()); |
| ... | ... | @@ -98,7 +98,7 @@ public class TradePriceCalculatorHelper { |
| 98 | 98 | // 先重置 |
| 99 | 99 | TradePriceCalculateRespBO.Price price = result.getPrice(); |
| 100 | 100 | price.setTotalPrice(0).setDiscountPrice(0).setDeliveryPrice(0) |
| 101 | - .setCouponPrice(0).setPointPrice(0).setPayPrice(0); | |
| 101 | + .setCouponPrice(0).setPointPrice(0).setVipPrice(0).setPayPrice(0); | |
| 102 | 102 | // 再合计 item |
| 103 | 103 | result.getItems().forEach(item -> { |
| 104 | 104 | if (!item.getSelected()) { |
| ... | ... | @@ -109,6 +109,7 @@ public class TradePriceCalculatorHelper { |
| 109 | 109 | price.setDeliveryPrice(price.getDeliveryPrice() + item.getDeliveryPrice()); |
| 110 | 110 | price.setCouponPrice(price.getCouponPrice() + item.getCouponPrice()); |
| 111 | 111 | price.setPointPrice(price.getPointPrice() + item.getPointPrice()); |
| 112 | + price.setVipPrice(price.getVipPrice() + item.getVipPrice()); | |
| 112 | 113 | price.setPayPrice(price.getPayPrice() + item.getPayPrice()); |
| 113 | 114 | }); |
| 114 | 115 | } |
| ... | ... | @@ -132,7 +133,9 @@ public class TradePriceCalculatorHelper { |
| 132 | 133 | - orderItem.getDiscountPrice() |
| 133 | 134 | + orderItem.getDeliveryPrice() |
| 134 | 135 | - orderItem.getCouponPrice() |
| 135 | - - orderItem.getPointPrice()); | |
| 136 | + - orderItem.getPointPrice() | |
| 137 | + - orderItem.getVipPrice() | |
| 138 | + ); | |
| 136 | 139 | } |
| 137 | 140 | |
| 138 | 141 | /** |
| ... | ... | @@ -162,6 +165,9 @@ public class TradePriceCalculatorHelper { |
| 162 | 165 | if (orderItem.getGivePoint() == null) { |
| 163 | 166 | orderItem.setGivePoint(0); |
| 164 | 167 | } |
| 168 | + if (orderItem.getVipPrice() == null) { | |
| 169 | + orderItem.setVipPrice(0); | |
| 170 | + } | |
| 165 | 171 | recountPayPrice(orderItem); |
| 166 | 172 | }); |
| 167 | 173 | } | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculator.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; |
| 8 | 9 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 9 | 11 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 10 | 12 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 11 | 13 | import org.springframework.core.annotation.Order; |
| ... | ... | @@ -32,6 +34,10 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator |
| 32 | 34 | |
| 33 | 35 | @Override |
| 34 | 36 | public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { |
| 37 | + // 0. 只有【普通】订单,才计算该优惠 | |
| 38 | + if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) { | |
| 39 | + return; | |
| 40 | + } | |
| 35 | 41 | // 获得 SKU 对应的满减送活动 |
| 36 | 42 | List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getMatchRewardActivityList( |
| 37 | 43 | convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId)); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeCouponPriceCalculatorTest.java
| ... | ... | @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO; |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 10 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 10 | 11 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 11 | 12 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 12 | 13 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -47,6 +48,7 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest { |
| 47 | 48 | new TradePriceCalculateReqBO.Item().setSkuId(40L).setCount(5).setSelected(false) // 匹配优惠劵,但是未选中 |
| 48 | 49 | )); |
| 49 | 50 | TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() |
| 51 | + .setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 50 | 52 | .setPrice(new TradePriceCalculateRespBO.Price()) |
| 51 | 53 | .setPromotions(new ArrayList<>()) |
| 52 | 54 | .setItems(asList( | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculatorTest.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum; |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 8 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 8 | 9 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 9 | 10 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 10 | 11 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -42,6 +43,7 @@ public class TradeDiscountActivityPriceCalculatorTest extends BaseMockitoUnitTes |
| 42 | 43 | new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中 |
| 43 | 44 | )); |
| 44 | 45 | TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() |
| 46 | + .setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 45 | 47 | .setPrice(new TradePriceCalculateRespBO.Price()) |
| 46 | 48 | .setPromotions(new ArrayList<>()) |
| 47 | 49 | .setItems(asList( | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeMemberLevelPriceCalculatorTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.price.calculator; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; | |
| 5 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 6 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 7 | +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 10 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; | |
| 11 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | |
| 12 | +import org.junit.jupiter.api.Test; | |
| 13 | +import org.mockito.InjectMocks; | |
| 14 | +import org.mockito.Mock; | |
| 15 | + | |
| 16 | +import java.util.ArrayList; | |
| 17 | + | |
| 18 | +import static java.util.Arrays.asList; | |
| 19 | +import static org.junit.jupiter.api.Assertions.*; | |
| 20 | +import static org.mockito.ArgumentMatchers.eq; | |
| 21 | +import static org.mockito.Mockito.when; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * {@link TradeMemberLevelPriceCalculator} 的单元测试类 | |
| 25 | + * | |
| 26 | + * @author 芋道源码 | |
| 27 | + */ | |
| 28 | +public class TradeMemberLevelPriceCalculatorTest extends BaseMockitoUnitTest { | |
| 29 | + | |
| 30 | + @InjectMocks | |
| 31 | + private TradeMemberLevelPriceCalculator memberLevelPriceCalculator; | |
| 32 | + | |
| 33 | + @Mock | |
| 34 | + private MemberLevelApi memberLevelApi; | |
| 35 | + @Mock | |
| 36 | + private MemberUserApi memberUserApi; | |
| 37 | + | |
| 38 | + @Test | |
| 39 | + public void testCalculate() { | |
| 40 | + // 准备参数 | |
| 41 | + TradePriceCalculateReqBO param = new TradePriceCalculateReqBO() | |
| 42 | + .setUserId(1024L) | |
| 43 | + .setItems(asList( | |
| 44 | + new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动,且已选中 | |
| 45 | + new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中 | |
| 46 | + )); | |
| 47 | + TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() | |
| 48 | + .setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 49 | + .setPrice(new TradePriceCalculateRespBO.Price()) | |
| 50 | + .setPromotions(new ArrayList<>()) | |
| 51 | + .setItems(asList( | |
| 52 | + new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true) | |
| 53 | + .setPrice(100), | |
| 54 | + new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(false) | |
| 55 | + .setPrice(50) | |
| 56 | + )); | |
| 57 | + // 保证价格被初始化上 | |
| 58 | + TradePriceCalculatorHelper.recountPayPrice(result.getItems()); | |
| 59 | + TradePriceCalculatorHelper.recountAllPrice(result); | |
| 60 | + | |
| 61 | + // mock 方法(会员等级) | |
| 62 | + when(memberUserApi.getUser(eq(1024L))).thenReturn(new MemberUserRespDTO().setLevelId(2048L)); | |
| 63 | + when(memberLevelApi.getMemberLevel(eq(2048L))).thenReturn( | |
| 64 | + new MemberLevelRespDTO().setId(2048L).setName("VIP 会员").setDiscountPercent(60)); | |
| 65 | + | |
| 66 | + // 调用 | |
| 67 | + memberLevelPriceCalculator.calculate(param, result); | |
| 68 | + // 断言:Price 部分 | |
| 69 | + TradePriceCalculateRespBO.Price price = result.getPrice(); | |
| 70 | + assertEquals(price.getTotalPrice(), 200); | |
| 71 | + assertEquals(price.getDiscountPrice(), 0); | |
| 72 | + assertEquals(price.getPointPrice(), 0); | |
| 73 | + assertEquals(price.getDeliveryPrice(), 0); | |
| 74 | + assertEquals(price.getCouponPrice(), 0); | |
| 75 | + assertEquals(price.getVipPrice(), 80); | |
| 76 | + assertEquals(price.getPayPrice(), 120); | |
| 77 | + assertNull(result.getCouponId()); | |
| 78 | + // 断言:SKU 1 | |
| 79 | + assertEquals(result.getItems().size(), 2); | |
| 80 | + TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0); | |
| 81 | + assertEquals(orderItem01.getSkuId(), 10L); | |
| 82 | + assertEquals(orderItem01.getCount(), 2); | |
| 83 | + assertEquals(orderItem01.getPrice(), 100); | |
| 84 | + assertEquals(orderItem01.getDiscountPrice(), 0); | |
| 85 | + assertEquals(orderItem01.getDeliveryPrice(), 0); | |
| 86 | + assertEquals(orderItem01.getCouponPrice(), 0); | |
| 87 | + assertEquals(orderItem01.getPointPrice(), 0); | |
| 88 | + assertEquals(orderItem01.getVipPrice(), 80); | |
| 89 | + assertEquals(orderItem01.getPayPrice(), 120); | |
| 90 | + // 断言:SKU 2 | |
| 91 | + TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1); | |
| 92 | + assertEquals(orderItem02.getSkuId(), 20L); | |
| 93 | + assertEquals(orderItem02.getCount(), 3); | |
| 94 | + assertEquals(orderItem02.getPrice(), 50); | |
| 95 | + assertEquals(orderItem02.getDiscountPrice(), 0); | |
| 96 | + assertEquals(orderItem02.getDeliveryPrice(), 0); | |
| 97 | + assertEquals(orderItem02.getCouponPrice(), 0); | |
| 98 | + assertEquals(orderItem02.getPointPrice(), 0); | |
| 99 | + assertEquals(orderItem02.getVipPrice(), 60); | |
| 100 | + assertEquals(orderItem02.getPayPrice(), 90); | |
| 101 | + // 断言:Promotion 部分 | |
| 102 | + assertEquals(result.getPromotions().size(), 1); | |
| 103 | + TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0); | |
| 104 | + assertEquals(promotion01.getId(), 2048L); | |
| 105 | + assertEquals(promotion01.getName(), "VIP 会员"); | |
| 106 | + assertEquals(promotion01.getType(), PromotionTypeEnum.MEMBER_LEVEL.getType()); | |
| 107 | + assertEquals(promotion01.getTotalPrice(), 200); | |
| 108 | + assertEquals(promotion01.getDiscountPrice(), 80); | |
| 109 | + assertTrue(promotion01.getMatch()); | |
| 110 | + assertEquals(promotion01.getDescription(), "会员等级折扣:省 0.80 元"); | |
| 111 | + TradePriceCalculateRespBO.PromotionItem promotionItem01 = promotion01.getItems().get(0); | |
| 112 | + assertEquals(promotion01.getItems().size(), 1); | |
| 113 | + assertEquals(promotionItem01.getSkuId(), 10L); | |
| 114 | + assertEquals(promotionItem01.getTotalPrice(), 200); | |
| 115 | + assertEquals(promotionItem01.getDiscountPrice(), 80); | |
| 116 | + } | |
| 117 | + | |
| 118 | +} | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeRewardActivityPriceCalculatorTest.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum; |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; |
| 8 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 8 | 9 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 9 | 10 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 10 | 11 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -44,6 +45,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest |
| 44 | 45 | new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true) // 匹配活动 2 |
| 45 | 46 | )); |
| 46 | 47 | TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() |
| 48 | + .setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 47 | 49 | .setPrice(new TradePriceCalculateRespBO.Price()) |
| 48 | 50 | .setPromotions(new ArrayList<>()) |
| 49 | 51 | .setItems(asList( |
| ... | ... | @@ -157,6 +159,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest |
| 157 | 159 | new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true) |
| 158 | 160 | )); |
| 159 | 161 | TradePriceCalculateRespBO result = new TradePriceCalculateRespBO() |
| 162 | + .setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 160 | 163 | .setPrice(new TradePriceCalculateRespBO.Price()) |
| 161 | 164 | .setPromotions(new ArrayList<>()) |
| 162 | 165 | .setItems(asList( | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java
| 1 | 1 | package cn.iocoder.yudao.module.member.api.level; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 3 | 4 | import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; |
| 4 | 5 | |
| 5 | 6 | /** |
| ... | ... | @@ -10,6 +11,14 @@ import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; |
| 10 | 11 | public interface MemberLevelApi { |
| 11 | 12 | |
| 12 | 13 | /** |
| 14 | + * 获得会员等级 | |
| 15 | + * | |
| 16 | + * @param id 会员等级编号 | |
| 17 | + * @return 会员等级 | |
| 18 | + */ | |
| 19 | + MemberLevelRespDTO getMemberLevel(Long id); | |
| 20 | + | |
| 21 | + /** | |
| 13 | 22 | * 增加会员经验 |
| 14 | 23 | * |
| 15 | 24 | * @param userId 会员ID | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/dto/MemberLevelRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.member.api.level.dto; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 会员等级 Resp DTO | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Data | |
| 12 | +public class MemberLevelRespDTO { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 编号 | |
| 16 | + */ | |
| 17 | + private Long id; | |
| 18 | + /** | |
| 19 | + * 等级名称 | |
| 20 | + */ | |
| 21 | + private String name; | |
| 22 | + /** | |
| 23 | + * 等级 | |
| 24 | + */ | |
| 25 | + private Integer level; | |
| 26 | + /** | |
| 27 | + * 升级经验 | |
| 28 | + */ | |
| 29 | + private Integer experience; | |
| 30 | + /** | |
| 31 | + * 享受折扣 | |
| 32 | + */ | |
| 33 | + private Integer discountPercent; | |
| 34 | + /** | |
| 35 | + * 状态 | |
| 36 | + * | |
| 37 | + * 枚举 {@link CommonStatusEnum} | |
| 38 | + */ | |
| 39 | + private Integer status; | |
| 40 | + | |
| 41 | +} | ... | ... |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.member.api.level; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 4 | +import cn.iocoder.yudao.module.member.convert.level.MemberLevelConvert; | |
| 3 | 5 | import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; |
| 4 | 6 | import cn.iocoder.yudao.module.member.service.level.MemberLevelService; |
| 5 | 7 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -23,6 +25,11 @@ public class MemberLevelApiImpl implements MemberLevelApi { |
| 23 | 25 | private MemberLevelService memberLevelService; |
| 24 | 26 | |
| 25 | 27 | @Override |
| 28 | + public MemberLevelRespDTO getMemberLevel(Long id) { | |
| 29 | + return MemberLevelConvert.INSTANCE.convert02(memberLevelService.getLevel(id)); | |
| 30 | + } | |
| 31 | + | |
| 32 | + @Override | |
| 26 | 33 | public void addExperience(Long userId, Integer experience, Integer bizType, String bizId) { |
| 27 | 34 | MemberExperienceBizTypeEnum bizTypeEnum = MemberExperienceBizTypeEnum.getByType(bizType); |
| 28 | 35 | if (bizTypeEnum == null) { | ... | ... |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/level/MemberLevelConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.member.convert.level; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; | |
| 3 | 4 | import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelCreateReqVO; |
| 4 | 5 | import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelRespVO; |
| 5 | 6 | import cn.iocoder.yudao.module.member.controller.admin.level.vo.level.MemberLevelSimpleRespVO; |
| ... | ... | @@ -33,4 +34,6 @@ public interface MemberLevelConvert { |
| 33 | 34 | |
| 34 | 35 | List<AppMemberLevelRespVO> convertList02(List<MemberLevelDO> list); |
| 35 | 36 | |
| 37 | + MemberLevelRespDTO convert02(MemberLevelDO bean); | |
| 38 | + | |
| 36 | 39 | } | ... | ... |