Commit da162853ec245d4261a2a6a3c9536dca81ba1b24

Authored by YunaiV
1 parent 2113e825

商城:完善购物车的逻辑

Showing 22 changed files with 351 additions and 432 deletions
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -46,6 +46,18 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
46 46 return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
47 47 }
48 48  
  49 + default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
  50 + SFunction<T, ?> field3, Object value3) {
  51 + return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
  52 + .eq(field3, value3));
  53 + }
  54 +
  55 + default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
  56 + SFunction<T, ?> field3, Object value3, SFunction<T, ?> field4, Object value4) {
  57 + return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
  58 + .eq(field3, value3).eq(field4, value4));
  59 + }
  60 +
49 61 default Long selectCount() {
50 62 return selectCount(new QueryWrapper<T>());
51 63 }
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/dto/ProductSkuRespDTO.java
... ... @@ -28,7 +28,7 @@ public class ProductSkuRespDTO {
28 28 private String spuName;
29 29  
30 30 /**
31   - * 属性数组,JSON 格式
  31 + * 属性数组
32 32 */
33 33 private List<Property> properties;
34 34 /**
... ... @@ -85,11 +85,19 @@ public class ProductSkuRespDTO {
85 85 */
86 86 private Long propertyId;
87 87 /**
  88 + * 属性名字
  89 + */
  90 + private String propertyName;
  91 +
  92 + /**
88 93 * 属性值编号
89 94 */
90 95 private Long valueId;
  96 + /**
  97 + * 属性值名字
  98 + */
  99 + private String valueName;
91 100  
92 101 }
93 102  
94   -
95 103 }
... ...
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
... ... @@ -29,17 +29,23 @@ public class ProductSpuRespDTO {
29 29 */
30 30 private String name;
31 31 /**
32   - * 商品编码
  32 + * 关键字
33 33 */
34   - private String code;
  34 + private String keyword;
35 35 /**
36   - * 促销语
  36 + * 商品简介
37 37 */
38   - private String sellPoint;
  38 + private String introduction;
39 39 /**
40 40 * 商品详情
41 41 */
42 42 private String description;
  43 + // TODO @芋艿:是不是要删除
  44 + /**
  45 + * 商品条码(一维码)
  46 + */
  47 + private String barCode;
  48 +
43 49 /**
44 50 * 商品分类编号
45 51 */
... ... @@ -49,13 +55,13 @@ public class ProductSpuRespDTO {
49 55 */
50 56 private Long brandId;
51 57 /**
52   - * 商品图片的数组
53   - * <p>
54   - * 1. 第一张图片将作为商品主图,支持同时上传多张图;
55   - * 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片;
56   - * 3. 至少 1 张,最多上传 10 张
  58 + * 商品封面图
57 59 */
58   - private List<String> picUrls;
  60 + private String picUrl;
  61 + /**
  62 + * 商品轮播图
  63 + */
  64 + private List<String> sliderPicUrls;
59 65 /**
60 66 * 商品视频
61 67 */
... ... @@ -76,38 +82,27 @@ public class ProductSpuRespDTO {
76 82  
77 83 /**
78 84 * 规格类型
79   - * <p>
80   - * 枚举 {@link ProductSpuSpecTypeEnum}
  85 + *
  86 + * false - 单规格
  87 + * true - 多规格
81 88 */
82 89 private Boolean specType;
83 90 /**
84   - * 最小价格,单位使用:分
85   - * <p>
86   - * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最小值
87   - */
88   - private Integer minPrice;
89   - /**
90   - * 最大价格,单位使用:分
91   - * <p>
92   - * 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最大值
  91 + * 商品价格,单位使用:分
93 92 */
94   - private Integer maxPrice;
  93 + private Integer price;
95 94 /**
96 95 * 市场价,单位使用:分
97   - * <p>
98   - * 基于其对应的 {@link ProductSkuRespDTO#getMarketPrice()} 最大值
99 96 */
100 97 private Integer marketPrice;
101 98 /**
102   - * 总库存
103   - * <p>
104   - * 基于其对应的 {@link ProductSkuRespDTO#getStock()} 求和
  99 + * 成本价,单位使用:分
105 100 */
106   - private Integer totalStock;
  101 + private Integer costPrice;
107 102 /**
108   - * 是否展示库存
  103 + * 库存
109 104 */
110   - private Boolean showStock;
  105 + private Integer stock;
111 106  
112 107 // ========== 统计相关字段 =========
113 108  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApiImpl.java
... ... @@ -15,7 +15,8 @@ import java.util.Collections;
15 15 import java.util.List;
16 16  
17 17 /**
18   - * TODO LeeYan9: 类注释;
  18 + * 商品 SKU API 实现类
  19 + *
19 20 * @author LeeYan9
20 21 * @since 2022-09-06
21 22 */
... ... @@ -28,8 +29,8 @@ public class ProductSkuApiImpl implements ProductSkuApi {
28 29  
29 30 @Override
30 31 public ProductSkuRespDTO getSku(Long id) {
31   - // TODO TODO LeeYan9: 需要实现
32   - return null;
  32 + ProductSkuDO sku = productSkuService.getSku(id);
  33 + return ProductSkuConvert.INSTANCE.convert02(sku);
33 34 }
34 35  
35 36 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java
... ... @@ -26,6 +26,9 @@ public class AppProductSpuPageItemRespVO {
26 26  
27 27 // ========== SKU 相关字段 =========
28 28  
  29 + @Schema(description = "规格类型", required = true, example = "true")
  30 + private Boolean specType;
  31 +
29 32 @Schema(description = "商品价格,单位使用:分", required = true, example = "1024")
30 33 private Integer price;
31 34  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java
... ... @@ -110,11 +110,28 @@ public class ProductSkuDO extends BaseDO {
110 110 */
111 111 private Long propertyId;
112 112 /**
  113 + * 属性名字
  114 + *
  115 + * 冗余 {@link ProductPropertyDO#getName()}
  116 + *
  117 + * 注意:每次属性名字发生变化时,需要更新该冗余
  118 + */
  119 + private String propertyName;
  120 +
  121 + /**
113 122 * 属性值编号
114 123 *
115 124 * 关联 {@link ProductPropertyValueDO#getId()}
116 125 */
117 126 private Long valueId;
  127 + /**
  128 + * 属性值名字
  129 + *
  130 + * 冗余 {@link ProductPropertyValueDO#getName()}
  131 + *
  132 + * 注意:每次属性值名字发生变化时,需要更新该冗余
  133 + */
  134 + private String valueName;
118 135  
119 136 }
120 137  
... ... @@ -139,9 +156,5 @@ public class ProductSkuDO extends BaseDO {
139 156 // TODO 芋艿:pinkStock from y
140 157 // TODO 芋艿:seckillStock from y
141 158  
142   - // TODO 芋艿:quota from c
143   - // TODO 芋艿:quotaShow from c
144   - // TODO 芋艿:attrValue from c
145   -
146 159 }
147 160  
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyServiceImpl.java
... ... @@ -67,6 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
67 67 // 更新
68 68 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO);
69 69 productPropertyMapper.updateById(updateObj);
  70 + // TODO 芋艿:更新时,需要看看 sku 表
70 71 }
71 72  
72 73 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/property/ProductPropertyValueServiceImpl.java
... ... @@ -68,6 +68,7 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
68 68 // 更新
69 69 ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO);
70 70 productPropertyValueMapper.updateById(updateObj);
  71 + // TODO 芋艿:更新时,需要看看 sku 表
71 72 }
72 73  
73 74 @Override
... ...
yudao-module-mall/yudao-module-product-biz/src/test/java/cn/iocoder/yudao/module/product/service/sku/ProductSkuServiceTest.java
... ... @@ -54,12 +54,14 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
54 54 // mock 数据
55 55 ProductSkuDO sku01 = randomPojo(ProductSkuDO.class, o -> { // 测试更新
56 56 o.setSpuId(1L);
57   - o.setProperties(singletonList(new ProductSkuDO.Property(10L, 20L)));
  57 + o.setProperties(singletonList(new ProductSkuDO.Property(
  58 + 10L, "颜色", 20L, "红色")));
58 59 });
59 60 productSkuMapper.insert(sku01);
60 61 ProductSkuDO sku02 = randomPojo(ProductSkuDO.class, o -> { // 测试删除
61 62 o.setSpuId(1L);
62   - o.setProperties(singletonList(new ProductSkuDO.Property(10L, 30L)));
  63 + o.setProperties(singletonList(new ProductSkuDO.Property(
  64 + 10L, "颜色", 30L, "蓝色")));
63 65 });
64 66 productSkuMapper.insert(sku02);
65 67 // 准备参数
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/base/spu/AppProductSpuBaseRespVO.java
... ... @@ -20,6 +20,6 @@ public class AppProductSpuBaseRespVO {
20 20 private String name;
21 21  
22 22 @Schema(description = "商品主图地址", example = "https://www.iocoder.cn/xx.png")
23   - private List<String> picUrls;
  23 + private String picUrl;
24 24  
25 25 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.http
1   -### 请求 /trade/cart/add-count 接口 => 成功
2   -POST {{appApi}}/trade/cart/add-count
  1 +### 请求 /trade/cart/add 接口 => 成功
  2 +POST {{appApi}}/trade/cart/add
3 3 tenant-id: {{appTenentId}}
4 4 Authorization: Bearer {{appToken}}
5 5 Content-Type: application/json
6 6  
7 7 {
8 8 "skuId": 1,
9   - "count": 1
  9 + "count": 10,
  10 + "addStatus": true
10 11 }
11 12  
12   -### 请求 /trade/cart/update-count 接口 => 成功
13   -PUT {{appApi}}/trade/cart/update-count
  13 +### 请求 /trade/cart/update 接口 => 成功
  14 +PUT {{appApi}}/trade/cart/update
14 15 tenant-id: {{appTenentId}}
15 16 Authorization: Bearer {{appToken}}
16 17 Content-Type: application/json
17 18  
18 19 {
19   - "skuId": 1,
  20 + "id": 35,
20 21 "count": 5
21 22 }
22 23  
23   -### 请求 /trade/cart/update-selected 接口 => 成功
24   -PUT {{appApi}}/trade/cart/update-selected
25   -tenant-id: {{appTenentId}}
26   -Authorization: Bearer {{appToken}}
27   -Content-Type: application/json
28   -
29   -{
30   - "skuIds": [1],
31   - "selected": false
32   -}
33   -
34 24 ### 请求 /trade/cart/delete 接口 => 成功
35   -DELETE {{appApi}}/trade/cart/delete?skuIds=1
  25 +DELETE {{appApi}}/trade/cart/delete?ids=1
36 26 tenant-id: {{appTenentId}}
37 27 Authorization: Bearer {{appToken}}
38 28  
... ... @@ -41,7 +31,7 @@ GET {{appApi}}/trade/cart/get-count
41 31 tenant-id: {{appTenentId}}
42 32 Authorization: Bearer {{appToken}}
43 33  
44   -### 请求 /trade/cart/get-detail 接口 => 成功
45   -GET {{appApi}}/trade/cart/get-detail
  34 +### 请求 /trade/cart/list 接口 => 成功
  35 +GET {{appApi}}/trade/cart/list
46 36 tenant-id: {{appTenentId}}
47 37 Authorization: Bearer {{appToken}}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/TradeCartController.java
... ... @@ -2,10 +2,9 @@ package cn.iocoder.yudao.module.trade.controller.app.cart;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
5   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
6   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
7   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
8   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  5 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
  6 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
  7 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
9 8 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
10 9 import io.swagger.v3.oas.annotations.Operation;
11 10 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -33,37 +32,27 @@ public class TradeCartController {
33 32 @Resource
34 33 private TradeCartService cartService;
35 34  
36   - @PostMapping("/add-count")
37   - @Operation(summary = "添加商品到购物车")
  35 + @PostMapping("/add")
  36 + @Operation(summary = "添加购物车商品")
38 37 @PreAuthenticated
39   - public CommonResult<Boolean> addCartItemCount(@Valid @RequestBody AppTradeCartItemAddCountReqVO addCountReqVO) {
40   - cartService.addCartItemCount(getLoginUserId(), addCountReqVO);
41   - return success(true);
42   - }
43   -
44   - @PutMapping("update-count")
45   - @Operation(summary = "更新购物车商品数量")
46   - @PreAuthenticated
47   - public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
48   - cartService.updateCartItemCount(getLoginUserId(), updateCountReqVO);
49   - return success(true);
  38 + public CommonResult<Long> addCart(@Valid @RequestBody AppTradeCartAddReqVO addCountReqVO) {
  39 + return success(cartService.addCart(getLoginUserId(), addCountReqVO));
50 40 }
51 41  
52   - @PutMapping("update-selected")
53   - @Operation(summary = "更新购物车商品是否选中")
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新购物车商品")
54 44 @PreAuthenticated
55   - public CommonResult<Boolean> updateCartItemSelected(@Valid @RequestBody AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
56   - cartService.updateCartItemSelected(getLoginUserId(), updateSelectedReqVO);
57   - // 获得目前购物车明细
  45 + public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartUpdateReqVO updateReqVO) {
  46 + cartService.updateCart(getLoginUserId(), updateReqVO);
58 47 return success(true);
59 48 }
60 49  
61 50 @DeleteMapping("/delete")
62 51 @Operation(summary = "删除购物车商品")
63   - @Parameter(name = "skuIds", description = "商品 SKU 编号的数组", required = true, example = "1024,2048")
  52 + @Parameter(name = "ids", description = "购物车商品编号", required = true, example = "1024,2048")
64 53 @PreAuthenticated
65   - public CommonResult<Boolean> deleteCartItem(@RequestParam("skuIds") List<Long> skuIds) {
66   - cartService.deleteCartItems(getLoginUserId(), skuIds);
  54 + public CommonResult<Boolean> deleteCart(@RequestParam("ids") List<Long> ids) {
  55 + cartService.deleteCart(getLoginUserId(), ids);
67 56 return success(true);
68 57 }
69 58  
... ... @@ -74,11 +63,11 @@ public class TradeCartController {
74 63 return success(cartService.getCartCount(getLoginUserId()));
75 64 }
76 65  
77   - @GetMapping("/get-detail")
78   - @Operation(summary = "查询用户的购物车的详情")
  66 + @GetMapping("/list")
  67 + @Operation(summary = "查询用户的购物车列表")
79 68 @PreAuthenticated
80   - public CommonResult<AppTradeCartDetailRespVO> getCartDetail() {
81   - return success(cartService.getCartDetail(getLoginUserId()));
  69 + public CommonResult<AppTradeCartListRespVO> getCartList() {
  70 + return success(cartService.getCartList(getLoginUserId()));
82 71 }
83 72  
84 73 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemAddCountReqVO.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartAddReqVO.java
... ... @@ -8,7 +8,7 @@ import javax.validation.constraints.NotNull;
8 8  
9 9 @Schema(description = "用户 App - 购物车添加购物项 Request VO")
10 10 @Data
11   -public class AppTradeCartItemAddCountReqVO {
  11 +public class AppTradeCartAddReqVO {
12 12  
13 13 @Schema(description = "商品 SKU 编号", required = true,example = "1024")
14 14 @NotNull(message = "商品 SKU 编号不能为空")
... ... @@ -19,4 +19,8 @@ public class AppTradeCartItemAddCountReqVO {
19 19 @Min(message = "数量必须大于 0", value = 1L)
20 20 private Integer count;
21 21  
  22 + @Schema(description = "是否添加到购物车", required = true, example = "true")
  23 + @NotNull(message = "是否添加购物车不能为空")
  24 + private Boolean addStatus;
  25 +
22 26 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartDetailRespVO.java deleted
1   -package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
2   -
3   -import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
4   -import io.swagger.v3.oas.annotations.media.Schema;
5   -import lombok.Data;
6   -
7   -import java.util.List;
8   -
9   -@Schema(description = "用户 App - 用户的购物车明细 Response VO")
10   -@Data
11   -public class AppTradeCartDetailRespVO {
12   -
13   - /**
14   - * 商品分组数组
15   - */
16   - private List<ItemGroup> itemGroups;
17   -
18   - /**
19   - * 费用
20   - */
21   - private Order order;
22   -
23   - @Schema(description = "商品分组") // 多个商品,参加同一个活动,从而形成分组
24   - @Data
25   - public static class ItemGroup {
26   -
27   - /**
28   - * 商品数组
29   - */
30   - private List<Sku> items;
31   - /**
32   - * 营销活动,订单级别
33   - */
34   - private Promotion promotion;
35   -
36   - }
37   -
38   - @Schema(description = "商品 SKU")
39   - @Data
40   - public static class Sku extends AppProductSkuBaseRespVO {
41   -
42   - /**
43   - * SPU 信息
44   - */
45   - private AppProductSkuBaseRespVO spu;
46   -
47   - // ========== 购物车相关的字段 ==========
48   -
49   - @Schema(description = "商品数量", required = true, example = "1")
50   - private Integer count;
51   - @Schema(description = "是否选中", required = true, example = "true")
52   - private Boolean selected;
53   -
54   - // ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ==========
55   -
56   - // TODO 芋艿:后续可以去除一些无用的字段
57   -
58   - @Schema(description = "商品原价(单)", required = true, example = "100")
59   - private Integer originalPrice;
60   - @Schema(description = "商品原价(总)", required = true, example = "200")
61   - private Integer totalOriginalPrice;
62   - @Schema(description = "商品级优惠(总)", required = true, example = "300")
63   - private Integer totalPromotionPrice;
64   - @Schema(description = "最终购买金额(总)", required = true, example = "400")
65   - private Integer totalPresentPrice;
66   - @Schema(description = "最终购买金额(单)", required = true, example = "500")
67   - private Integer presentPrice;
68   - @Schema(description = "应付金额(总)", required = true, example = "600")
69   - private Integer totalPayPrice;
70   -
71   - // ========== 营销相关的字段 ==========
72   - /**
73   - * 营销活动,商品级别
74   - */
75   - private Promotion promotion;
76   -
77   - }
78   -
79   - @Schema(description = "订单") // 对应 PriceCalculateRespDTO.Order 类,用于费用(合计)
80   - @Data
81   - public static class Order {
82   -
83   - // TODO 芋艿:后续可以去除一些无用的字段
84   -
85   - @Schema(description = "商品原价(总)", required = true, example = "100")
86   - private Integer skuOriginalPrice;
87   - @Schema(description = "商品优惠(总)", required = true, example = "200")
88   - private Integer skuPromotionPrice;
89   - @Schema(description = "订单优惠(总)", required = true, example = "300")
90   - private Integer orderPromotionPrice;
91   - @Schema(description = "运费金额", required = true, example = "400")
92   - private Integer deliveryPrice;
93   - @Schema(description = "应付金额(总)", required = true, example = "500")
94   - private Integer payPrice;
95   -
96   - }
97   -
98   - @Schema(description = "营销活动") // 对应 PriceCalculateRespDTO.Promotion 类的属性
99   - @Data
100   - public static class Promotion {
101   -
102   - @Schema(description = "营销编号", required = true, example = "1024") // 营销活动的编号、优惠劵的编号
103   - private Long id;
104   - @Schema(description = "营销名字", required = true, example = "xx 活动")
105   - private String name;
106   - @Schema(description = "营销类型", required = true, example = "1")
107   - private Integer type;
108   -
109   - // ========== 匹配情况 ==========
110   - @Schema(description = "是否满足优惠条件", required = true, example = "true")
111   - private Boolean meet;
112   - @Schema(description = "满足条件的提示", required = true, example = "圣诞价:省 150.00 元")
113   - private String meetTip;
114   -
115   - }
116   -
117   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartListRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
  2 +
  3 +import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
  4 +import cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.Data;
  7 +
  8 +import java.util.List;
  9 +
  10 +@Schema(description = "用户 App - 用户的购物列表 Response VO")
  11 +@Data
  12 +public class AppTradeCartListRespVO {
  13 +
  14 + /**
  15 + * 有效的购物项数组
  16 + */
  17 + private List<Cart> validList;
  18 +
  19 + /**
  20 + * 无效的购物项数组
  21 + */
  22 + private List<Cart> invalidList;
  23 +
  24 + @Schema(description = "购物项")
  25 + @Data
  26 + public static class Cart {
  27 +
  28 + @Schema(description = "购物项的编号", required = true, example = "1024")
  29 + private Long id;
  30 +
  31 + @Schema(description = "商品数量", required = true, example = "1")
  32 + private Integer count;
  33 +
  34 + /**
  35 + * 商品 SPU
  36 + */
  37 + private AppProductSpuBaseRespVO spu;
  38 + /**
  39 + * 商品 SKU
  40 + */
  41 + private AppProductSkuBaseRespVO sku;
  42 +
  43 + }
  44 +
  45 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartItemUpdateCountReqVO.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/vo/AppTradeCartUpdateReqVO.java
... ... @@ -6,13 +6,13 @@ import lombok.Data;
6 6 import javax.validation.constraints.Min;
7 7 import javax.validation.constraints.NotNull;
8 8  
9   -@Schema(description = "用户 App - 购物车更新数量 Request VO")
  9 +@Schema(description = "用户 App - 购物车更新 Request VO")
10 10 @Data
11   -public class AppTradeCartItemUpdateCountReqVO {
  11 +public class AppTradeCartUpdateReqVO {
12 12  
13   - @Schema(description = "商品 SKU 编号", required = true, example = "1024")
14   - @NotNull(message = "商品 SKU 编号不能为空")
15   - private Long skuId;
  13 + @Schema(description = "编号", required = true, example = "1024")
  14 + @NotNull(message = "编号不能为空")
  15 + private Long id;
16 16  
17 17 @Schema(description = "商品数量", required = true, example = "1")
18 18 @NotNull(message = "数量不能为空")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
1 1 package cn.iocoder.yudao.module.trade.convert.cart;
2 2  
3   -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
4   -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
5   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
6   -import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
  3 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  4 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  5 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
  6 +import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
  7 +import cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;
  8 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
  9 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
7 10 import org.mapstruct.Mapper;
8   -import org.mapstruct.Mapping;
9   -import org.mapstruct.Mappings;
10 11 import org.mapstruct.factory.Mappers;
11 12  
12   -import java.util.Collections;
  13 +import java.util.ArrayList;
13 14 import java.util.List;
  15 +import java.util.Map;
14 16  
15   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  17 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
16 18  
17 19 @Mapper
18 20 public interface TradeCartConvert {
19 21  
20 22 TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class);
21 23  
22   - default AppTradeCartDetailRespVO buildEmptyAppTradeCartDetailRespVO() {
23   - return new AppTradeCartDetailRespVO().setItemGroups(Collections.emptyList())
24   - .setOrder(new AppTradeCartDetailRespVO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0)
25   - .setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0));
  24 + default AppTradeCartListRespVO convertList(List<TradeCartDO> carts,
  25 + List<ProductSpuRespDTO> spus, List<ProductSkuRespDTO> skus) {
  26 + Map<Long, ProductSpuRespDTO> spuMap = convertMap(spus, ProductSpuRespDTO::getId);
  27 + Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
  28 + // 遍历,开始转换
  29 + List<AppTradeCartListRespVO.Cart> validList = new ArrayList<>(carts.size());
  30 + List<AppTradeCartListRespVO.Cart> invalidList = new ArrayList<>();
  31 + carts.forEach(cart -> {
  32 + AppTradeCartListRespVO.Cart cartVO = new AppTradeCartListRespVO.Cart();
  33 + cartVO.setId(cart.getId()).setCount(cart.getCount());
  34 + ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
  35 + ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
  36 + cartVO.setSpu(convert(spu)).setSku(convert(sku));
  37 + // 如果 spu 或 sku 不存在,或者 spu 被禁用,说明是非法的,或者 sku 库存不足
  38 + if (spu == null
  39 + || sku == null
  40 + || !ProductSpuStatusEnum.isEnable(spu.getStatus())
  41 + || sku.getStock() <= 0) {
  42 + invalidList.add(cartVO);
  43 + } else {
  44 + validList.add(cartVO);
  45 + }
  46 + });
  47 + return new AppTradeCartListRespVO().setValidList(validList).setValidList(invalidList);
26 48 }
27   -
28   - default PriceCalculateReqDTO convert(Long userId, List<TradeCartItemDO> cartItems) {
29   - return new PriceCalculateReqDTO().setUserId(userId)
30   - .setItems(convertList(cartItems, cartItem -> new PriceCalculateReqDTO.Item().setSkuId(cartItem.getSkuId())
31   - .setCount(cartItem.getSelected() ? cartItem.getCount() : 0)));
32   - }
33   -
34   - // ========== AppTradeCartDetailRespVO 相关 ==========
35   -
36   - AppTradeCartDetailRespVO.Promotion convert(PriceCalculateRespDTO.Promotion bean);
37   -
38   - @Mappings({
39   - @Mapping(source = "cartItem.count", target = "count")
40   - })
41   - AppTradeCartDetailRespVO.Sku convert(PriceCalculateRespDTO.OrderItem orderItem, TradeCartItemDO cartItem);
42   -
43   - AppTradeCartDetailRespVO.Order convert(PriceCalculateRespDTO.Order bean);
  49 + AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);
  50 + AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);
44 51  
45 52 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartItemDO.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/cart/TradeCartDO.java
... ... @@ -9,13 +9,15 @@ import lombok.experimental.Accessors;
9 9 /**
10 10 * 购物车的商品信息 DO
11 11 *
  12 + * 每个商品,对应一条记录,通过 {@link #spuId} 和 {@link #skuId} 关联
  13 + *
12 14 * @author 芋道源码
13 15 */
14   -@TableName("trade_cart_item")
  16 +@TableName("trade_cart")
15 17 @Data
16 18 @EqualsAndHashCode(callSuper = true)
17 19 @Accessors(chain = true)
18   -public class TradeCartItemDO extends BaseDO {
  20 +public class TradeCartDO extends BaseDO {
19 21  
20 22 // ========= 基础字段 BEGIN =========
21 23  
... ... @@ -23,14 +25,6 @@ public class TradeCartItemDO extends BaseDO {
23 25 * 编号,唯一自增
24 26 */
25 27 private Long id;
26   - /**
27   - * 是否选中
28   - */
29   - private Boolean selected;
30   -
31   - // ========= 基础字段 END =========
32   -
33   - // ========= 买家信息 BEGIN =========
34 28  
35 29 /**
36 30 * 用户编号
... ... @@ -39,7 +33,25 @@ public class TradeCartItemDO extends BaseDO {
39 33 */
40 34 private Long userId;
41 35  
42   - // ========= 买家信息 END =========
  36 + /**
  37 + * 是否添加到购物车
  38 + *
  39 + * false - 未添加:用户点击【立即购买】
  40 + * true - 已添加:用户点击【添加购物车】
  41 + *
  42 + * 为什么要设计这个字段?
  43 + * 配合 orderStatus 字段,可以知道有多少商品,用户点击了【立即购买】,最终多少【确认下单】
  44 + */
  45 + private Boolean addStatus;
  46 + /**
  47 + * 是否提交订单
  48 + *
  49 + * false - 未下单:立即购买,或者添加到购物车,此时设置为 false
  50 + * true - 已下单:确认下单,此时设置为 true
  51 + */
  52 + private Boolean orderStatus;
  53 +
  54 + // ========= 基础字段 END =========
43 55  
44 56 // ========= 商品信息 BEGIN =========
45 57  
... ... @@ -64,27 +76,11 @@ public class TradeCartItemDO extends BaseDO {
64 76  
65 77 // ========= 优惠信息 BEGIN =========
66 78  
67   -// /**
68   -// * 商品营销活动编号
69   -// */
70   -// private Long activityId; // discount_id
71   -// /**
72   -// * 商品营销活动类型
73   -// */
74   -// private Integer activityType;
75 79 // TODO 芋艿:combination_id 拼团 ID
76 80 // TODO 芋艿:seckill_id 秒杀产品 ID
77 81 // TODO 芋艿:bargain_id 砍价 ID
  82 + // TODO 芋艿:pinkId 团长拼团 ID
78 83  
79 84 // ========= 优惠信息 END =========
80 85  
81   - // TODO 待确定字段:mf
82   - // TODO 芋艿:distribution_card_no 推广员
83   - // TODO 芋艿:is_pay 未购买、已购买
84   - // TODO 芋艿:is_new 是否立即购买
85   -
86   - // TODO 待确定字段: yv
87   - // TODO isPay: 是否购买
88   - // TODO isNew:是否立即购买
89   -
90 86 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartItemMapper.java deleted
1   -package cn.iocoder.yudao.module.trade.dal.mysql.cart;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.map.MapUtil;
5   -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7   -import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
8   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
9   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
10   -import org.apache.ibatis.annotations.Mapper;
11   -
12   -import java.util.Collection;
13   -import java.util.List;
14   -import java.util.Map;
15   -
16   -@Mapper
17   -public interface TradeCartItemMapper extends BaseMapperX<TradeCartItemDO> {
18   -
19   - default TradeCartItemDO selectByUserIdAndSkuId(Long userId, Long skuId) {
20   - return selectOne(TradeCartItemDO::getUserId, userId,
21   - TradeCartItemDO::getSkuId, skuId);
22   - }
23   -
24   - default List<TradeCartItemDO> selectListByUserIdAndSkuIds(Long userId, Collection<Long> skuIds) {
25   - return selectList(new LambdaQueryWrapper<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
26   - .in(TradeCartItemDO::getSkuId, skuIds));
27   - }
28   -
29   - default void updateByIds(Collection<Long> ids, TradeCartItemDO updateObject) {
30   - update(updateObject, new LambdaQueryWrapper<TradeCartItemDO>().in(TradeCartItemDO::getId, ids));
31   - }
32   -
33   - default Integer selectSumByUserId(Long userId) {
34   - // SQL sum 查询
35   - List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartItemDO>()
36   - .select("SUM(count) AS sumCount")
37   - .eq("user_id", userId));
38   - // 获得数量
39   - return CollUtil.isNotEmpty(result) ? MapUtil.getInt(result.get(0), "sumCount") : 0;
40   - }
41   -
42   - default List<TradeCartItemDO> selectListByUserId(Long userId, Boolean selected) {
43   - return selectList(new LambdaQueryWrapperX<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
44   - .eqIfPresent(TradeCartItemDO::getSelected, selected));
45   - }
46   -
47   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/cart/TradeCartMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.dal.mysql.cart;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
  7 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  8 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +import java.util.Collection;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +@Mapper
  16 +public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
  17 +
  18 + default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId,
  19 + Boolean addStatus, Boolean orderStatus) {
  20 + return selectOne(TradeCartDO::getUserId, userId,
  21 + TradeCartDO::getSkuId, skuId,
  22 + TradeCartDO::getAddStatus, addStatus,
  23 + TradeCartDO::getOrderStatus, orderStatus);
  24 + }
  25 +
  26 + default Integer selectSumByUserId(Long userId) {
  27 + // SQL sum 查询
  28 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartDO>()
  29 + .select("SUM(count) AS sumCount")
  30 + .eq("user_id", userId)
  31 + .eq("add_status", true) // 只计算添加到购物车中的
  32 + .eq("order_status", false)); // 必须未下单
  33 + // 获得数量
  34 + return CollUtil.getFirst(result) != null ? MapUtil.getInt(result.get(0), "sumCount") : 0;
  35 + }
  36 +
  37 + default TradeCartDO selectById(Long id, Long userId) {
  38 + return selectOne(TradeCartDO::getId, id,
  39 + TradeCartDO::getUserId, userId);
  40 + }
  41 +
  42 + default List<TradeCartDO> selectListByIds(Collection<Long> ids, Long userId) {
  43 + return selectList(new LambdaQueryWrapper<TradeCartDO>()
  44 + .in(TradeCartDO::getId, ids)
  45 + .eq(TradeCartDO::getUserId, userId));
  46 + }
  47 +
  48 + default List<TradeCartDO> selectListByUserId(Long userId) {
  49 + return selectList(TradeCartDO::getUserId, userId);
  50 + }
  51 +
  52 + default void updateByIds(Collection<Long> ids, TradeCartDO updateObject) {
  53 + update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids));
  54 + }
  55 +
  56 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartService.java
1 1 package cn.iocoder.yudao.module.trade.service.cart;
2 2  
3   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
4   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
5   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
6   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  3 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
  4 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
  5 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
7 6  
8 7 import javax.validation.Valid;
9 8 import java.util.Collection;
... ... @@ -19,9 +18,10 @@ public interface TradeCartService {
19 18 * 添加商品到购物车
20 19 *
21 20 * @param userId 用户编号
22   - * @param addCountReqVO 添加信息
  21 + * @param addReqVO 添加信息
  22 + * @return 购物项的编号
23 23 */
24   - void addCartItemCount(Long userId, @Valid AppTradeCartItemAddCountReqVO addCountReqVO);
  24 + Long addCart(Long userId, @Valid AppTradeCartAddReqVO addReqVO);
25 25  
26 26 /**
27 27 * 更新购物车商品数量
... ... @@ -29,23 +29,15 @@ public interface TradeCartService {
29 29 * @param userId 用户编号
30 30 * @param updateCountReqVO 更新信息
31 31 */
32   - void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO);
33   -
34   - /**
35   - * 更新购物车商品是否选中
36   - *
37   - * @param userId 用户编号
38   - * @param updateSelectedReqVO 更新信息
39   - */
40   - void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO);
  32 + void updateCart(Long userId, AppTradeCartUpdateReqVO updateCountReqVO);
41 33  
42 34 /**
43 35 * 删除购物车商品
44 36 *
45 37 * @param userId 用户编号
46   - * @param skuIds SKU 编号的数组
  38 + * @param ids 购物项的编号
47 39 */
48   - void deleteCartItems(Long userId, Collection<Long> skuIds);
  40 + void deleteCart(Long userId, Collection<Long> ids);
49 41  
50 42 /**
51 43 * 查询用户在购物车中的商品数量
... ... @@ -56,11 +48,11 @@ public interface TradeCartService {
56 48 Integer getCartCount(Long userId);
57 49  
58 50 /**
59   - * 查询用户的购物车详情
  51 + * 查询用户的购物车列表
60 52 *
61 53 * @param userId 用户编号
62   - * @return 购物车详情
  54 + * @return 购物车列表
63 55 */
64   - AppTradeCartDetailRespVO getCartDetail(Long userId);
  56 + AppTradeCartListRespVO getCartList(Long userId);
65 57  
66 58 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/cart/TradeCartServiceImpl.java
1 1 package cn.iocoder.yudao.module.trade.service.cart;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.lang.Assert;
5 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7   -import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
8   -import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
9   -import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum;
10 5 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
11 6 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
12   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
13   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
14   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
15   -import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
  7 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  8 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  9 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
  10 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
  11 +import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
16 12 import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert;
17   -import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
18   -import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper;
  13 +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
  14 +import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartMapper;
19 15 import org.springframework.stereotype.Service;
20 16 import org.springframework.validation.annotation.Validated;
21 17  
22 18 import javax.annotation.Resource;
23   -import java.util.ArrayList;
24 19 import java.util.Collection;
  20 +import java.util.Comparator;
25 21 import java.util.List;
26   -import java.util.Map;
27 22  
28 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
29   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
30 25 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
31 26 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
32 27 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND;
  28 +import static java.util.Collections.emptyList;
33 29  
34 30 /**
35 31 * 购物车 Service 实现类
36 32 *
  33 + * // TODO 芋艿:秒杀、拼团、砍价对购物车的影响
  34 + * // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息
  35 + *
37 36 * @author 芋道源码
38 37 */
39 38 @Service
... ... @@ -41,123 +40,92 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_N
41 40 public class TradeCartServiceImpl implements TradeCartService {
42 41  
43 42 @Resource
44   - private TradeCartItemMapper cartItemMapper;
  43 + private TradeCartMapper cartMapper;
45 44  
46 45 @Resource
47   - private ProductSkuApi productSkuApi;
  46 + private ProductSpuApi productSpuApi;
48 47 @Resource
49   - private PriceApi priceApi;
  48 + private ProductSkuApi productSkuApi;
50 49  
51 50 @Override
52   - public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) {
53   - Long skuId = addCountReqVO.getSkuId();
54   - Integer count = addCountReqVO.getCount();
55   - // 查询 CartItemDO
56   - TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId());
57   -
58   - // 存在,则进行数量更新
59   - if (tradeItem != null) {
60   - checkProductSku(skuId, tradeItem.getCount() + count);
61   - cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId())
62   - .setSelected(true).setCount(tradeItem.getCount() + count));
63   - return;
  51 + public Long addCart(Long userId, AppTradeCartAddReqVO addReqVO) {
  52 + // 查询 TradeCartDO
  53 + TradeCartDO cart = cartMapper.selectByUserIdAndSkuId(userId, addReqVO.getSkuId(),
  54 + addReqVO.getAddStatus(), false);
  55 + // 校验 SKU
  56 + Integer count = cart != null && addReqVO.getAddStatus() ?
  57 + cart.getCount() + addReqVO.getCount() : addReqVO.getCount();
  58 + ProductSkuRespDTO sku = checkProductSku(addReqVO.getSkuId(), count);
  59 +
  60 + // 情况一:存在,则进行数量更新
  61 + if (cart != null) {
  62 + cartMapper.updateById(new TradeCartDO().setId(cart.getId()).setCount(count));
  63 + return cart.getId();
  64 + // 情况二:不存在,则进行插入
  65 + } else {
  66 + cart = new TradeCartDO().setUserId(userId)
  67 + .setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count)
  68 + .setAddStatus(addReqVO.getAddStatus()).setOrderStatus(false);
  69 + cartMapper.insert(cart);
64 70 }
65   -
66   - // 不存在,则进行插入
67   - ProductSkuRespDTO sku = checkProductSku(skuId, count);
68   - cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId())
69   - .setSelected(true).setCount(count));
  71 + return cart.getId();
70 72 }
71 73  
72 74 @Override
73   - public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
74   - // 校验 TradeCartItemDO 存在
75   - TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId());
76   - if (tradeItem == null) {
  75 + public void updateCart(Long userId, AppTradeCartUpdateReqVO updateReqVO) {
  76 + // 校验 TradeCartDO 存在
  77 + TradeCartDO cart = cartMapper.selectById(updateReqVO.getId(), userId);
  78 + if (cart == null) {
77 79 throw exception(CARD_ITEM_NOT_FOUND);
78 80 }
79 81 // 校验商品 SKU
80   - checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount());
  82 + checkProductSku(cart.getSkuId(), updateReqVO.getCount());
81 83  
82 84 // 更新数量
83   - cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount()));
84   - }
85   -
86   - @Override
87   - public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
88   - // 查询 CartItemDO 列表
89   - List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds());
90   - if (CollUtil.isEmpty(cartItems)) {
91   - return;
92   - }
93   -
94   - // 更新选中
95   - cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId),
96   - new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected()));
  85 + cartMapper.updateById(new TradeCartDO().setId(cart.getId())
  86 + .setCount(updateReqVO.getCount()));
97 87 }
98 88  
99 89 /**
100 90 * 购物车删除商品
101 91 *
102 92 * @param userId 用户编号
103   - * @param skuIds 商品 SKU 编号的数组
  93 + * @param ids 商品 SKU 编号的数组
104 94 */
105 95 @Override
106   - public void deleteCartItems(Long userId, Collection<Long> skuIds) {
107   - // 查询 CartItemDO 列表
108   - List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
109   - if (CollUtil.isEmpty(cartItems)) {
  96 + public void deleteCart(Long userId, Collection<Long> ids) {
  97 + // 查询 TradeCartDO 列表
  98 + List<TradeCartDO> carts = cartMapper.selectListByIds(ids, userId);
  99 + if (CollUtil.isEmpty(carts)) {
110 100 return;
111 101 }
112 102  
113 103 // 批量标记删除
114   - cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId));
  104 + cartMapper.deleteBatchIds(ids);
115 105 }
116 106  
117 107 @Override
118 108 public Integer getCartCount(Long userId) {
119   - return cartItemMapper.selectSumByUserId(userId);
  109 + return cartMapper.selectSumByUserId(userId);
120 110 }
121 111  
122 112 @Override
123   - public AppTradeCartDetailRespVO getCartDetail(Long userId) {
  113 + public AppTradeCartListRespVO getCartList(Long userId) {
124 114 // 获得购物车的商品
125   - List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserId(userId, null);
  115 + List<TradeCartDO> carts = cartMapper.selectListByUserId(userId);
  116 + carts.sort(Comparator.comparing(TradeCartDO::getId).reversed());
126 117 // 如果未空,则返回空结果
127   - if (CollUtil.isEmpty(cartItems)) {
128   - return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO();
  118 + if (CollUtil.isEmpty(carts)) {
  119 + return new AppTradeCartListRespVO().setValidList(emptyList())
  120 + .setInvalidList(emptyList());
129 121 }
130 122  
131   - // 调用价格服务,计算价格
132   - PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems));
133   -
134   - // 转换返回
135   - Map<Long, TradeCartItemDO> cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId);
136   - Map<Long, PriceCalculateRespDTO.OrderItem> orderItemMap = convertMap(priceCalculate.getOrder().getItems(),
137   - PriceCalculateRespDTO.OrderItem::getSkuId);
138   - List<AppTradeCartDetailRespVO.ItemGroup> itemGroups = new ArrayList<>(cartItems.size());
139   - // ① 场景一,营销活动,订单级别 TODO 芋艿:待测试
140   - priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel()))
141   - .forEach(promotion -> {
142   - AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>())
143   - .setPromotion(TradeCartConvert.INSTANCE.convert(promotion));
144   - itemGroups.add(itemGroup);
145   - promotion.getItems().forEach(promotionItem -> {
146   - PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId());
147   - Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId());
148   - TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
149   - itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
150   - });
151   - });
152   - // ② 场景二,营销活动,商品级别
153   - orderItemMap.values().forEach(orderItem -> {
154   - AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null);
155   - itemGroups.add(itemGroup);
156   - TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
157   - itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
158   - });
159   - return new AppTradeCartDetailRespVO().setItemGroups(itemGroups)
160   - .setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder()));
  123 + // 查询 SPU、SKU 列表
  124 + List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(carts, TradeCartDO::getSpuId));
  125 + List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(carts, TradeCartDO::getSpuId));
  126 +
  127 + // 拼接数据
  128 + return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
161 129 }
162 130  
163 131 /**
... ...