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,6 +46,18 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
46 return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)); 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 default Long selectCount() { 61 default Long selectCount() {
50 return selectCount(new QueryWrapper<T>()); 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,7 +28,7 @@ public class ProductSkuRespDTO {
28 private String spuName; 28 private String spuName;
29 29
30 /** 30 /**
31 - * 属性数组,JSON 格式 31 + * 属性数组
32 */ 32 */
33 private List<Property> properties; 33 private List<Property> properties;
34 /** 34 /**
@@ -85,11 +85,19 @@ public class ProductSkuRespDTO { @@ -85,11 +85,19 @@ public class ProductSkuRespDTO {
85 */ 85 */
86 private Long propertyId; 86 private Long propertyId;
87 /** 87 /**
  88 + * 属性名字
  89 + */
  90 + private String propertyName;
  91 +
  92 + /**
88 * 属性值编号 93 * 属性值编号
89 */ 94 */
90 private Long valueId; 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,17 +29,23 @@ public class ProductSpuRespDTO {
29 */ 29 */
30 private String name; 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 private String description; 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,13 +55,13 @@ public class ProductSpuRespDTO {
49 */ 55 */
50 private Long brandId; 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,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 private Boolean specType; 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 private Integer marketPrice; 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,7 +15,8 @@ import java.util.Collections;
15 import java.util.List; 15 import java.util.List;
16 16
17 /** 17 /**
18 - * TODO LeeYan9: 类注释; 18 + * 商品 SKU API 实现类
  19 + *
19 * @author LeeYan9 20 * @author LeeYan9
20 * @since 2022-09-06 21 * @since 2022-09-06
21 */ 22 */
@@ -28,8 +29,8 @@ public class ProductSkuApiImpl implements ProductSkuApi { @@ -28,8 +29,8 @@ public class ProductSkuApiImpl implements ProductSkuApi {
28 29
29 @Override 30 @Override
30 public ProductSkuRespDTO getSku(Long id) { 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 @Override 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,6 +26,9 @@ public class AppProductSpuPageItemRespVO {
26 26
27 // ========== SKU 相关字段 ========= 27 // ========== SKU 相关字段 =========
28 28
  29 + @Schema(description = "规格类型", required = true, example = "true")
  30 + private Boolean specType;
  31 +
29 @Schema(description = "商品价格,单位使用:分", required = true, example = "1024") 32 @Schema(description = "商品价格,单位使用:分", required = true, example = "1024")
30 private Integer price; 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,11 +110,28 @@ public class ProductSkuDO extends BaseDO {
110 */ 110 */
111 private Long propertyId; 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 * 关联 {@link ProductPropertyValueDO#getId()} 124 * 关联 {@link ProductPropertyValueDO#getId()}
116 */ 125 */
117 private Long valueId; 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,9 +156,5 @@ public class ProductSkuDO extends BaseDO {
139 // TODO 芋艿:pinkStock from y 156 // TODO 芋艿:pinkStock from y
140 // TODO 芋艿:seckillStock from y 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,6 +67,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
67 // 更新 67 // 更新
68 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO); 68 ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO);
69 productPropertyMapper.updateById(updateObj); 69 productPropertyMapper.updateById(updateObj);
  70 + // TODO 芋艿:更新时,需要看看 sku 表
70 } 71 }
71 72
72 @Override 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,6 +68,7 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
68 // 更新 68 // 更新
69 ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO); 69 ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO);
70 productPropertyValueMapper.updateById(updateObj); 70 productPropertyValueMapper.updateById(updateObj);
  71 + // TODO 芋艿:更新时,需要看看 sku 表
71 } 72 }
72 73
73 @Override 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,12 +54,14 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
54 // mock 数据 54 // mock 数据
55 ProductSkuDO sku01 = randomPojo(ProductSkuDO.class, o -> { // 测试更新 55 ProductSkuDO sku01 = randomPojo(ProductSkuDO.class, o -> { // 测试更新
56 o.setSpuId(1L); 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 productSkuMapper.insert(sku01); 60 productSkuMapper.insert(sku01);
60 ProductSkuDO sku02 = randomPojo(ProductSkuDO.class, o -> { // 测试删除 61 ProductSkuDO sku02 = randomPojo(ProductSkuDO.class, o -> { // 测试删除
61 o.setSpuId(1L); 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 productSkuMapper.insert(sku02); 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,6 +20,6 @@ public class AppProductSpuBaseRespVO {
20 private String name; 20 private String name;
21 21
22 @Schema(description = "商品主图地址", example = "https://www.iocoder.cn/xx.png") 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 tenant-id: {{appTenentId}} 3 tenant-id: {{appTenentId}}
4 Authorization: Bearer {{appToken}} 4 Authorization: Bearer {{appToken}}
5 Content-Type: application/json 5 Content-Type: application/json
6 6
7 { 7 {
8 "skuId": 1, 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 tenant-id: {{appTenentId}} 15 tenant-id: {{appTenentId}}
15 Authorization: Bearer {{appToken}} 16 Authorization: Bearer {{appToken}}
16 Content-Type: application/json 17 Content-Type: application/json
17 18
18 { 19 {
19 - "skuId": 1, 20 + "id": 35,
20 "count": 5 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 ### 请求 /trade/cart/delete 接口 => 成功 24 ### 请求 /trade/cart/delete 接口 => 成功
35 -DELETE {{appApi}}/trade/cart/delete?skuIds=1 25 +DELETE {{appApi}}/trade/cart/delete?ids=1
36 tenant-id: {{appTenentId}} 26 tenant-id: {{appTenentId}}
37 Authorization: Bearer {{appToken}} 27 Authorization: Bearer {{appToken}}
38 28
@@ -41,7 +31,7 @@ GET {{appApi}}/trade/cart/get-count @@ -41,7 +31,7 @@ GET {{appApi}}/trade/cart/get-count
41 tenant-id: {{appTenentId}} 31 tenant-id: {{appTenentId}}
42 Authorization: Bearer {{appToken}} 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 tenant-id: {{appTenentId}} 36 tenant-id: {{appTenentId}}
47 Authorization: Bearer {{appToken}} 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,10 +2,9 @@ package cn.iocoder.yudao.module.trade.controller.app.cart;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 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 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService; 8 import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
10 import io.swagger.v3.oas.annotations.Operation; 9 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 10 import io.swagger.v3.oas.annotations.Parameter;
@@ -33,37 +32,27 @@ public class TradeCartController { @@ -33,37 +32,27 @@ public class TradeCartController {
33 @Resource 32 @Resource
34 private TradeCartService cartService; 33 private TradeCartService cartService;
35 34
36 - @PostMapping("/add-count")  
37 - @Operation(summary = "添加商品到购物车") 35 + @PostMapping("/add")
  36 + @Operation(summary = "添加购物车商品")
38 @PreAuthenticated 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 @PreAuthenticated 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 return success(true); 47 return success(true);
59 } 48 }
60 49
61 @DeleteMapping("/delete") 50 @DeleteMapping("/delete")
62 @Operation(summary = "删除购物车商品") 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 @PreAuthenticated 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 return success(true); 56 return success(true);
68 } 57 }
69 58
@@ -74,11 +63,11 @@ public class TradeCartController { @@ -74,11 +63,11 @@ public class TradeCartController {
74 return success(cartService.getCartCount(getLoginUserId())); 63 return success(cartService.getCartCount(getLoginUserId()));
75 } 64 }
76 65
77 - @GetMapping("/get-detail")  
78 - @Operation(summary = "查询用户的购物车的详情") 66 + @GetMapping("/list")
  67 + @Operation(summary = "查询用户的购物车列表")
79 @PreAuthenticated 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,7 +8,7 @@ import javax.validation.constraints.NotNull;
8 8
9 @Schema(description = "用户 App - 购物车添加购物项 Request VO") 9 @Schema(description = "用户 App - 购物车添加购物项 Request VO")
10 @Data 10 @Data
11 -public class AppTradeCartItemAddCountReqVO { 11 +public class AppTradeCartAddReqVO {
12 12
13 @Schema(description = "商品 SKU 编号", required = true,example = "1024") 13 @Schema(description = "商品 SKU 编号", required = true,example = "1024")
14 @NotNull(message = "商品 SKU 编号不能为空") 14 @NotNull(message = "商品 SKU 编号不能为空")
@@ -19,4 +19,8 @@ public class AppTradeCartItemAddCountReqVO { @@ -19,4 +19,8 @@ public class AppTradeCartItemAddCountReqVO {
19 @Min(message = "数量必须大于 0", value = 1L) 19 @Min(message = "数量必须大于 0", value = 1L)
20 private Integer count; 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,13 +6,13 @@ import lombok.Data;
6 import javax.validation.constraints.Min; 6 import javax.validation.constraints.Min;
7 import javax.validation.constraints.NotNull; 7 import javax.validation.constraints.NotNull;
8 8
9 -@Schema(description = "用户 App - 购物车更新数量 Request VO") 9 +@Schema(description = "用户 App - 购物车更新 Request VO")
10 @Data 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 @Schema(description = "商品数量", required = true, example = "1") 17 @Schema(description = "商品数量", required = true, example = "1")
18 @NotNull(message = "数量不能为空") 18 @NotNull(message = "数量不能为空")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/cart/TradeCartConvert.java
1 package cn.iocoder.yudao.module.trade.convert.cart; 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 import org.mapstruct.Mapper; 10 import org.mapstruct.Mapper;
8 -import org.mapstruct.Mapping;  
9 -import org.mapstruct.Mappings;  
10 import org.mapstruct.factory.Mappers; 11 import org.mapstruct.factory.Mappers;
11 12
12 -import java.util.Collections; 13 +import java.util.ArrayList;
13 import java.util.List; 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 @Mapper 19 @Mapper
18 public interface TradeCartConvert { 20 public interface TradeCartConvert {
19 21
20 TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class); 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,13 +9,15 @@ import lombok.experimental.Accessors;
9 /** 9 /**
10 * 购物车的商品信息 DO 10 * 购物车的商品信息 DO
11 * 11 *
  12 + * 每个商品,对应一条记录,通过 {@link #spuId} 和 {@link #skuId} 关联
  13 + *
12 * @author 芋道源码 14 * @author 芋道源码
13 */ 15 */
14 -@TableName("trade_cart_item") 16 +@TableName("trade_cart")
15 @Data 17 @Data
16 @EqualsAndHashCode(callSuper = true) 18 @EqualsAndHashCode(callSuper = true)
17 @Accessors(chain = true) 19 @Accessors(chain = true)
18 -public class TradeCartItemDO extends BaseDO { 20 +public class TradeCartDO extends BaseDO {
19 21
20 // ========= 基础字段 BEGIN ========= 22 // ========= 基础字段 BEGIN =========
21 23
@@ -23,14 +25,6 @@ public class TradeCartItemDO extends BaseDO { @@ -23,14 +25,6 @@ public class TradeCartItemDO extends BaseDO {
23 * 编号,唯一自增 25 * 编号,唯一自增
24 */ 26 */
25 private Long id; 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,7 +33,25 @@ public class TradeCartItemDO extends BaseDO {
39 */ 33 */
40 private Long userId; 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 // ========= 商品信息 BEGIN ========= 56 // ========= 商品信息 BEGIN =========
45 57
@@ -64,27 +76,11 @@ public class TradeCartItemDO extends BaseDO { @@ -64,27 +76,11 @@ public class TradeCartItemDO extends BaseDO {
64 76
65 // ========= 优惠信息 BEGIN ========= 77 // ========= 优惠信息 BEGIN =========
66 78
67 -// /**  
68 -// * 商品营销活动编号  
69 -// */  
70 -// private Long activityId; // discount_id  
71 -// /**  
72 -// * 商品营销活动类型  
73 -// */  
74 -// private Integer activityType;  
75 // TODO 芋艿:combination_id 拼团 ID 79 // TODO 芋艿:combination_id 拼团 ID
76 // TODO 芋艿:seckill_id 秒杀产品 ID 80 // TODO 芋艿:seckill_id 秒杀产品 ID
77 // TODO 芋艿:bargain_id 砍价 ID 81 // TODO 芋艿:bargain_id 砍价 ID
  82 + // TODO 芋艿:pinkId 团长拼团 ID
78 83
79 // ========= 优惠信息 END ========= 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 package cn.iocoder.yudao.module.trade.service.cart; 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 import javax.validation.Valid; 7 import javax.validation.Valid;
9 import java.util.Collection; 8 import java.util.Collection;
@@ -19,9 +18,10 @@ public interface TradeCartService { @@ -19,9 +18,10 @@ public interface TradeCartService {
19 * 添加商品到购物车 18 * 添加商品到购物车
20 * 19 *
21 * @param userId 用户编号 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,23 +29,15 @@ public interface TradeCartService {
29 * @param userId 用户编号 29 * @param userId 用户编号
30 * @param updateCountReqVO 更新信息 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 * @param userId 用户编号 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,11 +48,11 @@ public interface TradeCartService {
56 Integer getCartCount(Long userId); 48 Integer getCartCount(Long userId);
57 49
58 /** 50 /**
59 - * 查询用户的购物车详情 51 + * 查询用户的购物车列表
60 * 52 *
61 * @param userId 用户编号 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 package cn.iocoder.yudao.module.trade.service.cart; 1 package cn.iocoder.yudao.module.trade.service.cart;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.lang.Assert;  
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 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 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; 5 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
11 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 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 import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert; 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 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
21 17
22 import javax.annotation.Resource; 18 import javax.annotation.Resource;
23 -import java.util.ArrayList;  
24 import java.util.Collection; 19 import java.util.Collection;
  20 +import java.util.Comparator;
25 import java.util.List; 21 import java.util.List;
26 -import java.util.Map;  
27 22
28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; 25 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
31 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH; 26 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
32 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND; 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 * 购物车 Service 实现类 31 * 购物车 Service 实现类
36 * 32 *
  33 + * // TODO 芋艿:秒杀、拼团、砍价对购物车的影响
  34 + * // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息
  35 + *
37 * @author 芋道源码 36 * @author 芋道源码
38 */ 37 */
39 @Service 38 @Service
@@ -41,123 +40,92 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_N @@ -41,123 +40,92 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_N
41 public class TradeCartServiceImpl implements TradeCartService { 40 public class TradeCartServiceImpl implements TradeCartService {
42 41
43 @Resource 42 @Resource
44 - private TradeCartItemMapper cartItemMapper; 43 + private TradeCartMapper cartMapper;
45 44
46 @Resource 45 @Resource
47 - private ProductSkuApi productSkuApi; 46 + private ProductSpuApi productSpuApi;
48 @Resource 47 @Resource
49 - private PriceApi priceApi; 48 + private ProductSkuApi productSkuApi;
50 49
51 @Override 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 @Override 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 throw exception(CARD_ITEM_NOT_FOUND); 79 throw exception(CARD_ITEM_NOT_FOUND);
78 } 80 }
79 // 校验商品 SKU 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 * @param userId 用户编号 92 * @param userId 用户编号
103 - * @param skuIds 商品 SKU 编号的数组 93 + * @param ids 商品 SKU 编号的数组
104 */ 94 */
105 @Override 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 return; 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 @Override 107 @Override
118 public Integer getCartCount(Long userId) { 108 public Integer getCartCount(Long userId) {
119 - return cartItemMapper.selectSumByUserId(userId); 109 + return cartMapper.selectSumByUserId(userId);
120 } 110 }
121 111
122 @Override 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 /**