mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 01:01:52 +08:00
合并分支修复差异
This commit is contained in:
commit
c233ce1a20
@ -46,6 +46,18 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
|
||||
}
|
||||
|
||||
default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
|
||||
SFunction<T, ?> field3, Object value3) {
|
||||
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
|
||||
.eq(field3, value3));
|
||||
}
|
||||
|
||||
default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
|
||||
SFunction<T, ?> field3, Object value3, SFunction<T, ?> field4, Object value4) {
|
||||
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
|
||||
.eq(field3, value3).eq(field4, value4));
|
||||
}
|
||||
|
||||
default Long selectCount() {
|
||||
return selectCount(new QueryWrapper<T>());
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@ -22,13 +21,9 @@ public class ProductSkuRespDTO {
|
||||
* SPU 编号
|
||||
*/
|
||||
private Long spuId;
|
||||
/**
|
||||
* SPU 名字
|
||||
*/
|
||||
private String spuName;
|
||||
|
||||
/**
|
||||
* 属性数组,JSON 格式
|
||||
* 属性数组
|
||||
*/
|
||||
private List<Property> properties;
|
||||
/**
|
||||
@ -51,12 +46,6 @@ public class ProductSkuRespDTO {
|
||||
* 图片地址
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* SKU 状态
|
||||
* <p>
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 库存
|
||||
*/
|
||||
@ -84,12 +73,20 @@ public class ProductSkuRespDTO {
|
||||
* 属性编号
|
||||
*/
|
||||
private Long propertyId;
|
||||
/**
|
||||
* 属性名字
|
||||
*/
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* 属性值编号
|
||||
*/
|
||||
private Long valueId;
|
||||
/**
|
||||
* 属性值名字
|
||||
*/
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -28,17 +28,23 @@ public class ProductSpuRespDTO {
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 商品编码
|
||||
* 关键字
|
||||
*/
|
||||
private String code;
|
||||
private String keyword;
|
||||
/**
|
||||
* 促销语
|
||||
* 商品简介
|
||||
*/
|
||||
private String sellPoint;
|
||||
private String introduction;
|
||||
/**
|
||||
* 商品详情
|
||||
*/
|
||||
private String description;
|
||||
// TODO @芋艿:是不是要删除
|
||||
/**
|
||||
* 商品条码(一维码)
|
||||
*/
|
||||
private String barCode;
|
||||
|
||||
/**
|
||||
* 商品分类编号
|
||||
*/
|
||||
@ -48,13 +54,13 @@ public class ProductSpuRespDTO {
|
||||
*/
|
||||
private Long brandId;
|
||||
/**
|
||||
* 商品图片的数组
|
||||
* <p>
|
||||
* 1. 第一张图片将作为商品主图,支持同时上传多张图;
|
||||
* 2. 建议使用尺寸 800x800 像素以上、大小不超过 1M 的正方形图片;
|
||||
* 3. 至少 1 张,最多上传 10 张
|
||||
* 商品封面图
|
||||
*/
|
||||
private List<String> picUrls;
|
||||
private String picUrl;
|
||||
/**
|
||||
* 商品轮播图
|
||||
*/
|
||||
private List<String> sliderPicUrls;
|
||||
/**
|
||||
* 商品视频
|
||||
*/
|
||||
@ -75,36 +81,27 @@ public class ProductSpuRespDTO {
|
||||
|
||||
/**
|
||||
* 规格类型
|
||||
*
|
||||
* false - 单规格
|
||||
* true - 多规格
|
||||
*/
|
||||
private Boolean specType;
|
||||
/**
|
||||
* 最小价格,单位使用:分
|
||||
* <p>
|
||||
* 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最小值
|
||||
* 商品价格,单位使用:分
|
||||
*/
|
||||
private Integer minPrice;
|
||||
/**
|
||||
* 最大价格,单位使用:分
|
||||
* <p>
|
||||
* 基于其对应的 {@link ProductSkuRespDTO#getPrice()} 最大值
|
||||
*/
|
||||
private Integer maxPrice;
|
||||
private Integer price;
|
||||
/**
|
||||
* 市场价,单位使用:分
|
||||
* <p>
|
||||
* 基于其对应的 {@link ProductSkuRespDTO#getMarketPrice()} 最大值
|
||||
*/
|
||||
private Integer marketPrice;
|
||||
/**
|
||||
* 总库存
|
||||
* <p>
|
||||
* 基于其对应的 {@link ProductSkuRespDTO#getStock()} 求和
|
||||
* 成本价,单位使用:分
|
||||
*/
|
||||
private Integer totalStock;
|
||||
private Integer costPrice;
|
||||
/**
|
||||
* 是否展示库存
|
||||
* 库存
|
||||
*/
|
||||
private Boolean showStock;
|
||||
private Integer stock;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
|
@ -15,7 +15,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TODO LeeYan9: 类注释;
|
||||
* 商品 SKU API 实现类
|
||||
*
|
||||
* @author LeeYan9
|
||||
* @since 2022-09-06
|
||||
*/
|
||||
@ -28,8 +29,8 @@ public class ProductSkuApiImpl implements ProductSkuApi {
|
||||
|
||||
@Override
|
||||
public ProductSkuRespDTO getSku(Long id) {
|
||||
// TODO TODO LeeYan9: 需要实现
|
||||
return null;
|
||||
ProductSkuDO sku = productSkuService.getSku(id);
|
||||
return ProductSkuConvert.INSTANCE.convert02(sku);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,8 +1,18 @@
|
||||
### 获得订单交易的分页 TODO
|
||||
### 获得订单交易的分页(默认)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 获得商品 SPU 明细
|
||||
GET {{appApi}}/product/spu/get-detail?id=4
|
||||
### 获得订单交易的分页(价格)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=price&sortAsc=true
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 获得订单交易的分页(销售)
|
||||
GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=salesCount&sortAsc=true
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### 获得商品 SPU 明细
|
||||
GET {{appApi}}/product/spu/get-detail?id=102
|
||||
tenant-id: {{appTenentId}}
|
||||
|
@ -49,8 +49,8 @@ public class AppProductSpuController {
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品 SPU 分页")
|
||||
public CommonResult<PageResult<AppProductSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
|
||||
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus());
|
||||
return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult));
|
||||
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
|
||||
return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@ -73,7 +73,7 @@ public class AppProductSpuController {
|
||||
List<ProductPropertyValueDetailRespBO> propertyValues = productPropertyValueService
|
||||
.getPropertyValueDetailList(ProductSkuConvert.INSTANCE.convertPropertyValueIds(skus));
|
||||
// 拼接
|
||||
return success(ProductSpuConvert.INSTANCE.convert(spu, skus, propertyValues));
|
||||
return success(ProductSpuConvert.INSTANCE.convertForGetSpuDetail(spu, skus, propertyValues));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,17 +18,17 @@ public class AppProductSpuDetailRespVO {
|
||||
@Schema(description = "商品名称", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "促销语", example = "好吃!")
|
||||
private String sellPoint;
|
||||
|
||||
@Schema(description = "商品详情", required = true, example = "我是商品描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "商品分类编号", required = true, example = "1")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品图片的数组", required = true)
|
||||
private List<String> picUrls;
|
||||
@Schema(description = "商品封面图", required = true)
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "商品轮播图", required = true)
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
@Schema(description = "商品视频", required = true)
|
||||
private String videoUrl;
|
||||
@ -38,14 +38,14 @@ public class AppProductSpuDetailRespVO {
|
||||
@Schema(description = "规格类型", required = true, example = "true")
|
||||
private Boolean specType;
|
||||
|
||||
@Schema(description = "是否展示库存", required = true, example = "true")
|
||||
private Boolean showStock;
|
||||
@Schema(description = "商品价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer minPrice;
|
||||
@Schema(description = "市场价,单位使用:分", required = true, example = "1024")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "最大价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer maxPrice;
|
||||
@Schema(description = "库存", required = true, example = "666")
|
||||
private Integer stock;
|
||||
|
||||
/**
|
||||
* SKU 数组
|
||||
@ -72,7 +72,7 @@ public class AppProductSpuDetailRespVO {
|
||||
@Schema(description = "销售价格,单位:分", required = true, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "市场价", example = "1024")
|
||||
@Schema(description = "市场价,单位使用:分", required = true, example = "1024")
|
||||
private Integer marketPrice;
|
||||
|
||||
@Schema(description = "图片地址", required = true, example = "https://www.iocoder.cn/xx.png")
|
||||
|
@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.product.controller.app.spu.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 商品 SPU 分页项 Response VO")
|
||||
@ -15,25 +13,31 @@ public class AppProductSpuPageItemRespVO {
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品名称", required = true, example = "芋道")
|
||||
@NotEmpty(message = "商品名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "分类编号", required = true)
|
||||
@NotNull(message = "分类编号不能为空")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "商品图片的数组", required = true)
|
||||
private List<String> picUrls;
|
||||
@Schema(description = "商品封面图", required = true)
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer minPrice;
|
||||
@Schema(description = "商品轮播图", required = true)
|
||||
private List<String> sliderPicUrls;
|
||||
|
||||
@Schema(description = "最大价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer maxPrice;
|
||||
// ========== SKU 相关字段 =========
|
||||
|
||||
@Schema(description = "规格类型", required = true, example = "true")
|
||||
private Boolean specType;
|
||||
|
||||
@Schema(description = "商品价格,单位使用:分", required = true, example = "1024")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "库存", required = true, example = "666")
|
||||
private Integer stock;
|
||||
|
||||
// ========== 统计相关字段 =========
|
||||
|
||||
@Schema(description = "商品销量", example = "1024")
|
||||
@Schema(description = "商品销量", required = true, example = "1024")
|
||||
private Integer salesCount;
|
||||
|
||||
}
|
||||
|
@ -19,18 +19,23 @@ public class AppProductSpuPageReqVO extends PageParam {
|
||||
public static final String SORT_FIELD_PRICE = "price";
|
||||
public static final String SORT_FIELD_SALES_COUNT = "salesCount";
|
||||
|
||||
public static final String RECOMMEND_TYPE_HOT = "hot";
|
||||
|
||||
@Schema(description = "分类编号", example = "1")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "关键字", example = "好看")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "排序字段", example = "price") // 参见 AppSpuPageReqVO.SORT_FIELD_XXX 常量
|
||||
@Schema(description = "排序字段", example = "price") // 参见 AppProductSpuPageReqVO.SORT_FIELD_XXX 常量
|
||||
private String sortField;
|
||||
|
||||
@Schema(description = "排序方式", example = "true")
|
||||
private Boolean sortAsc;
|
||||
|
||||
@Schema(description = "推荐类型", example = "hot") // 参见 AppProductSpuPageReqVO.RECOMMEND_TYPE_XXX 常亮
|
||||
private String recommendType;
|
||||
|
||||
@AssertTrue(message = "排序字段不合法")
|
||||
@JsonIgnore
|
||||
public boolean isSortFieldValid() {
|
||||
|
@ -34,7 +34,7 @@ public interface ProductSpuConvert {
|
||||
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
|
||||
|
||||
ProductSpuDO convert(ProductSpuCreateReqVO bean);
|
||||
|
||||
// TODO 还是使用convert,重命名改动太多
|
||||
ProductSpuDO convert(ProductSpuUpdateReqVO bean);
|
||||
|
||||
List<ProductSpuDO> convertList(List<ProductSpuDO> list);
|
||||
@ -47,36 +47,6 @@ public interface ProductSpuConvert {
|
||||
|
||||
List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list);
|
||||
|
||||
default AppProductSpuDetailRespVO convert(ProductSpuDO spu, List<ProductSkuDO> skus,
|
||||
List<ProductPropertyValueDetailRespBO> propertyValues) {
|
||||
AppProductSpuDetailRespVO spuVO = convert02(spu)
|
||||
.setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0));
|
||||
spuVO.setSkus(convertList03(skus));
|
||||
// 处理商品属性
|
||||
Map<Long, ProductPropertyValueDetailRespBO> propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
|
||||
for (int i = 0; i < skus.size(); i++) {
|
||||
List<ProductSkuDO.Property> properties = skus.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
AppProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
|
||||
sku.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
|
||||
if (propertyValue == null) {
|
||||
return;
|
||||
}
|
||||
sku.getProperties().add(convert03(propertyValue));
|
||||
});
|
||||
}
|
||||
return spuVO;
|
||||
}
|
||||
AppProductSpuDetailRespVO convert02(ProductSpuDO spu);
|
||||
List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
|
||||
AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
|
||||
|
||||
PageResult<AppProductSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);
|
||||
|
||||
default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
|
||||
List<ProductPropertyValueDetailRespBO> propertyValues) {
|
||||
@ -106,4 +76,43 @@ public interface ProductSpuConvert {
|
||||
List<ProductSkuRespVO> convertList04(List<ProductSkuDO> skus);
|
||||
ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
|
||||
|
||||
// ========== 用户 App 相关 ==========
|
||||
|
||||
default PageResult<AppProductSpuPageItemRespVO> convertPageForGetSpuPage(PageResult<ProductSpuDO> page) {
|
||||
// 累加虚拟销量
|
||||
page.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
|
||||
// 然后进行转换
|
||||
return convertPageForGetSpuPage0(page);
|
||||
}
|
||||
PageResult<AppProductSpuPageItemRespVO> convertPageForGetSpuPage0(PageResult<ProductSpuDO> page);
|
||||
|
||||
default AppProductSpuDetailRespVO convertForGetSpuDetail(ProductSpuDO spu, List<ProductSkuDO> skus,
|
||||
List<ProductPropertyValueDetailRespBO> propertyValues) {
|
||||
AppProductSpuDetailRespVO spuVO = convertForGetSpuDetail(spu)
|
||||
.setSalesCount(spu.getSalesCount() + defaultIfNull(spu.getVirtualSalesCount(), 0));
|
||||
spuVO.setSkus(convertListForGetSpuDetail(skus));
|
||||
// 处理商品属性
|
||||
Map<Long, ProductPropertyValueDetailRespBO> propertyValueMap = convertMap(propertyValues, ProductPropertyValueDetailRespBO::getValueId);
|
||||
for (int i = 0; i < skus.size(); i++) {
|
||||
List<ProductSkuDO.Property> properties = skus.get(i).getProperties();
|
||||
if (CollUtil.isEmpty(properties)) {
|
||||
continue;
|
||||
}
|
||||
AppProductSpuDetailRespVO.Sku sku = spuVO.getSkus().get(i);
|
||||
sku.setProperties(new ArrayList<>(properties.size()));
|
||||
// 遍历每个 properties,设置到 AppSpuDetailRespVO.Sku 中
|
||||
properties.forEach(property -> {
|
||||
ProductPropertyValueDetailRespBO propertyValue = propertyValueMap.get(property.getValueId());
|
||||
if (propertyValue == null) {
|
||||
return;
|
||||
}
|
||||
sku.getProperties().add(convertForGetSpuDetail(propertyValue));
|
||||
});
|
||||
}
|
||||
return spuVO;
|
||||
}
|
||||
AppProductSpuDetailRespVO convertForGetSpuDetail(ProductSpuDO spu);
|
||||
List<AppProductSpuDetailRespVO.Sku> convertListForGetSpuDetail(List<ProductSkuDO> skus);
|
||||
AppProductPropertyValueDetailRespVO convertForGetSpuDetail(ProductPropertyValueDetailRespBO propertyValue);
|
||||
|
||||
}
|
||||
|
@ -110,12 +110,29 @@ public class ProductSkuDO extends TenantBaseDO {
|
||||
* 关联 {@link ProductPropertyDO#getId()}
|
||||
*/
|
||||
private Long propertyId;
|
||||
///**
|
||||
// * 属性名字
|
||||
// *
|
||||
// * 冗余 {@link ProductPropertyDO#getName()}
|
||||
// *
|
||||
// * 注意:每次属性名字发生变化时,需要更新该冗余
|
||||
// */ TODO 与已有代码逻辑存在冲突
|
||||
//private String propertyName;
|
||||
|
||||
/**
|
||||
* 属性值编号
|
||||
*
|
||||
* 关联 {@link ProductPropertyValueDO#getId()}
|
||||
*/
|
||||
private Long valueId;
|
||||
///**
|
||||
// * 属性值名字
|
||||
// *
|
||||
// * 冗余 {@link ProductPropertyValueDO#getName()}
|
||||
// *
|
||||
// * 注意:每次属性值名字发生变化时,需要更新该冗余
|
||||
// */ TODO 与已有代码逻辑存在冲突
|
||||
//private String valueName;
|
||||
|
||||
}
|
||||
|
||||
@ -140,9 +157,5 @@ public class ProductSkuDO extends TenantBaseDO {
|
||||
// TODO 芋艿:pinkStock from y
|
||||
// TODO 芋艿:seckillStock from y
|
||||
|
||||
// TODO 芋艿:quota from c
|
||||
// TODO 芋艿:quotaShow from c
|
||||
// TODO 芋艿:attrValue from c
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ public class ProductPropertyServiceImpl implements ProductPropertyService {
|
||||
// 更新
|
||||
ProductPropertyDO updateObj = ProductPropertyConvert.INSTANCE.convert(updateReqVO);
|
||||
productPropertyMapper.updateById(updateObj);
|
||||
// TODO 芋艿:更新时,需要看看 sku 表
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,6 +68,7 @@ public class ProductPropertyValueServiceImpl implements ProductPropertyValueServ
|
||||
// 更新
|
||||
ProductPropertyValueDO updateObj = ProductPropertyValueConvert.INSTANCE.convert(updateReqVO);
|
||||
productPropertyValueMapper.updateById(updateObj);
|
||||
// TODO 芋艿:更新时,需要看看 sku 表
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,7 +169,7 @@ public class ProductSkuServiceImpl implements ProductSkuService {
|
||||
// 拆分三个集合,新插入的、需要更新的、需要删除的
|
||||
List<ProductSkuDO> insertSkus = new ArrayList<>();
|
||||
List<ProductSkuDO> updateSkus = new ArrayList<>();
|
||||
List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertList06(skus, null);
|
||||
List<ProductSkuDO> allUpdateSkus = ProductSkuConvert.INSTANCE.convertList06(skus, spuId);
|
||||
allUpdateSkus.forEach(sku -> {
|
||||
String propertiesKey = ProductSkuConvert.INSTANCE.buildPropertyKey(sku);
|
||||
// 1、找得到的,进行更新
|
||||
|
@ -54,12 +54,18 @@ public class ProductSkuServiceTest extends BaseDbUnitTest {
|
||||
// mock 数据
|
||||
ProductSkuDO sku01 = randomPojo(ProductSkuDO.class, o -> { // 测试更新
|
||||
o.setSpuId(1L);
|
||||
o.setProperties(singletonList(new ProductSkuDO.Property(10L, 20L)));
|
||||
//o.setProperties(singletonList(new ProductSkuDO.Property(
|
||||
// 10L, "颜色", 20L, "红色"))); TODO 新增字段已注释
|
||||
o.setProperties(singletonList(new ProductSkuDO.Property(
|
||||
10L, 20L)));
|
||||
});
|
||||
productSkuMapper.insert(sku01);
|
||||
ProductSkuDO sku02 = randomPojo(ProductSkuDO.class, o -> { // 测试删除
|
||||
o.setSpuId(1L);
|
||||
o.setProperties(singletonList(new ProductSkuDO.Property(10L, 30L)));
|
||||
//o.setProperties(singletonList(new ProductSkuDO.Property(
|
||||
// 10L, "颜色", 30L, "蓝色"))); TODO 新增字段已注释
|
||||
o.setProperties(singletonList(new ProductSkuDO.Property(
|
||||
10L, 30L)));
|
||||
});
|
||||
productSkuMapper.insert(sku02);
|
||||
// 准备参数
|
||||
|
@ -14,7 +14,6 @@ public interface ErrorCodeConstants {
|
||||
// ========== Order 模块 1-011-000-000 ==========
|
||||
ErrorCode ORDER_CREATE_SKU_NOT_FOUND = new ErrorCode(1011000001, "商品 SKU 不存在");
|
||||
ErrorCode ORDER_CREATE_SPU_NOT_SALE = new ErrorCode(1011000002, "商品 SPU 不可售卖");
|
||||
ErrorCode ORDER_CREATE_SKU_NOT_SALE = new ErrorCode(1011000003, "商品 SKU 不可售卖");
|
||||
ErrorCode ORDER_CREATE_SKU_STOCK_NOT_ENOUGH = new ErrorCode(1011000004, "商品 SKU 库存不足");
|
||||
ErrorCode ORDER_CREATE_SPU_NOT_FOUND = new ErrorCode(1011000005, "商品 SPU 不可售卖");
|
||||
ErrorCode ORDER_CREATE_ADDRESS_NOT_FOUND = new ErrorCode(1011000006, "收货地址不存在");
|
||||
|
@ -17,12 +17,12 @@ public class AppProductSkuBaseRespVO {
|
||||
@Schema(description = "主键", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SKU 名字", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "图片地址", example = "https://www.iocoder.cn/xx.png")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "销售价格,单位:分", required = true, example = "100")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "库存", required = true, example = "1")
|
||||
private Integer stock;
|
||||
|
||||
|
@ -20,6 +20,6 @@ public class AppProductSpuBaseRespVO {
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品主图地址", example = "https://www.iocoder.cn/xx.png")
|
||||
private List<String> picUrls;
|
||||
private String picUrl;
|
||||
|
||||
}
|
||||
|
@ -1,38 +1,28 @@
|
||||
### 请求 /trade/cart/add-count 接口 => 成功
|
||||
POST {{appApi}}/trade/cart/add-count
|
||||
### 请求 /trade/cart/add 接口 => 成功
|
||||
POST {{appApi}}/trade/cart/add
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuId": 1,
|
||||
"count": 1
|
||||
"count": 10,
|
||||
"addStatus": true
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/update-count 接口 => 成功
|
||||
PUT {{appApi}}/trade/cart/update-count
|
||||
### 请求 /trade/cart/update 接口 => 成功
|
||||
PUT {{appApi}}/trade/cart/update
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuId": 1,
|
||||
"id": 35,
|
||||
"count": 5
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/update-selected 接口 => 成功
|
||||
PUT {{appApi}}/trade/cart/update-selected
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"skuIds": [1],
|
||||
"selected": false
|
||||
}
|
||||
|
||||
### 请求 /trade/cart/delete 接口 => 成功
|
||||
DELETE {{appApi}}/trade/cart/delete?skuIds=1
|
||||
DELETE {{appApi}}/trade/cart/delete?ids=1
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
@ -41,7 +31,7 @@ GET {{appApi}}/trade/cart/get-count
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /trade/cart/get-detail 接口 => 成功
|
||||
GET {{appApi}}/trade/cart/get-detail
|
||||
### 请求 /trade/cart/list 接口 => 成功
|
||||
GET {{appApi}}/trade/cart/list
|
||||
tenant-id: {{appTenentId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.trade.controller.app.cart;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -33,37 +33,35 @@ public class TradeCartController {
|
||||
@Resource
|
||||
private TradeCartService cartService;
|
||||
|
||||
@PostMapping("/add-count")
|
||||
@Operation(summary = "添加商品到购物车")
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "添加购物车商品")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> addCartItemCount(@Valid @RequestBody AppTradeCartItemAddCountReqVO addCountReqVO) {
|
||||
cartService.addCartItemCount(getLoginUserId(), addCountReqVO);
|
||||
public CommonResult<Long> addCart(@Valid @RequestBody AppTradeCartAddReqVO addCountReqVO) {
|
||||
return success(cartService.addCart(getLoginUserId(), addCountReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新购物车商品")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCart(@Valid @RequestBody AppTradeCartUpdateReqVO updateReqVO) {
|
||||
cartService.updateCart(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("update-count")
|
||||
@Operation(summary = "更新购物车商品数量")
|
||||
@PutMapping("/reset")
|
||||
@Operation(summary = "重置购物车商品")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartItemQuantity(@Valid @RequestBody AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
|
||||
cartService.updateCartItemCount(getLoginUserId(), updateCountReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("update-selected")
|
||||
@Operation(summary = "更新购物车商品是否选中")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> updateCartItemSelected(@Valid @RequestBody AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
|
||||
cartService.updateCartItemSelected(getLoginUserId(), updateSelectedReqVO);
|
||||
// 获得目前购物车明细
|
||||
public CommonResult<Boolean> resetCart(@Valid @RequestBody AppTradeCartResetReqVO updateReqVO) {
|
||||
cartService.resetCart(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除购物车商品")
|
||||
@Parameter(name = "skuIds", description = "商品 SKU 编号的数组", required = true, example = "1024,2048")
|
||||
@Parameter(name = "ids", description = "购物车商品编号", required = true, example = "1024,2048")
|
||||
@PreAuthenticated
|
||||
public CommonResult<Boolean> deleteCartItem(@RequestParam("skuIds") List<Long> skuIds) {
|
||||
cartService.deleteCartItems(getLoginUserId(), skuIds);
|
||||
public CommonResult<Boolean> deleteCart(@RequestParam("ids") List<Long> ids) {
|
||||
cartService.deleteCart(getLoginUserId(), ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@ -74,11 +72,11 @@ public class TradeCartController {
|
||||
return success(cartService.getCartCount(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/get-detail")
|
||||
@Operation(summary = "查询用户的购物车的详情")
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询用户的购物车列表")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeCartDetailRespVO> getCartDetail() {
|
||||
return success(cartService.getCartDetail(getLoginUserId()));
|
||||
public CommonResult<AppTradeCartListRespVO> getCartList() {
|
||||
return success(cartService.getCartList(getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,12 +3,11 @@ package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 购物车添加购物项 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemAddCountReqVO {
|
||||
public class AppTradeCartAddReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true,example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
@ -16,7 +15,10 @@ public class AppTradeCartItemAddCountReqVO {
|
||||
|
||||
@Schema(description = "新增商品数量", required = true, example = "1")
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(message = "数量必须大于 0", value = 1L)
|
||||
private Integer count;
|
||||
|
||||
@Schema(description = "是否添加到购物车", required = true, example = "true")
|
||||
@NotNull(message = "是否添加购物车不能为空")
|
||||
private Boolean addStatus;
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 用户的购物车明细 Response VO")
|
||||
@Data
|
||||
public class AppTradeCartDetailRespVO {
|
||||
|
||||
/**
|
||||
* 商品分组数组
|
||||
*/
|
||||
private List<ItemGroup> itemGroups;
|
||||
|
||||
/**
|
||||
* 费用
|
||||
*/
|
||||
private Order order;
|
||||
|
||||
@Schema(description = "商品分组") // 多个商品,参加同一个活动,从而形成分组
|
||||
@Data
|
||||
public static class ItemGroup {
|
||||
|
||||
/**
|
||||
* 商品数组
|
||||
*/
|
||||
private List<Sku> items;
|
||||
/**
|
||||
* 营销活动,订单级别
|
||||
*/
|
||||
private Promotion promotion;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "商品 SKU")
|
||||
@Data
|
||||
public static class Sku extends AppProductSkuBaseRespVO {
|
||||
|
||||
/**
|
||||
* SPU 信息
|
||||
*/
|
||||
private AppProductSkuBaseRespVO spu;
|
||||
|
||||
// ========== 购物车相关的字段 ==========
|
||||
|
||||
@Schema(description = "商品数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
@Schema(description = "是否选中", required = true, example = "true")
|
||||
private Boolean selected;
|
||||
|
||||
// ========== 价格相关的字段,对应 PriceCalculateRespDTO.OrderItem 的属性 ==========
|
||||
|
||||
// TODO 芋艿:后续可以去除一些无用的字段
|
||||
|
||||
@Schema(description = "商品原价(单)", required = true, example = "100")
|
||||
private Integer originalPrice;
|
||||
@Schema(description = "商品原价(总)", required = true, example = "200")
|
||||
private Integer totalOriginalPrice;
|
||||
@Schema(description = "商品级优惠(总)", required = true, example = "300")
|
||||
private Integer totalPromotionPrice;
|
||||
@Schema(description = "最终购买金额(总)", required = true, example = "400")
|
||||
private Integer totalPresentPrice;
|
||||
@Schema(description = "最终购买金额(单)", required = true, example = "500")
|
||||
private Integer presentPrice;
|
||||
@Schema(description = "应付金额(总)", required = true, example = "600")
|
||||
private Integer totalPayPrice;
|
||||
|
||||
// ========== 营销相关的字段 ==========
|
||||
/**
|
||||
* 营销活动,商品级别
|
||||
*/
|
||||
private Promotion promotion;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "订单") // 对应 PriceCalculateRespDTO.Order 类,用于费用(合计)
|
||||
@Data
|
||||
public static class Order {
|
||||
|
||||
// TODO 芋艿:后续可以去除一些无用的字段
|
||||
|
||||
@Schema(description = "商品原价(总)", required = true, example = "100")
|
||||
private Integer skuOriginalPrice;
|
||||
@Schema(description = "商品优惠(总)", required = true, example = "200")
|
||||
private Integer skuPromotionPrice;
|
||||
@Schema(description = "订单优惠(总)", required = true, example = "300")
|
||||
private Integer orderPromotionPrice;
|
||||
@Schema(description = "运费金额", required = true, example = "400")
|
||||
private Integer deliveryPrice;
|
||||
@Schema(description = "应付金额(总)", required = true, example = "500")
|
||||
private Integer payPrice;
|
||||
|
||||
}
|
||||
|
||||
@Schema(description = "营销活动") // 对应 PriceCalculateRespDTO.Promotion 类的属性
|
||||
@Data
|
||||
public static class Promotion {
|
||||
|
||||
@Schema(description = "营销编号", required = true, example = "1024") // 营销活动的编号、优惠劵的编号
|
||||
private Long id;
|
||||
@Schema(description = "营销名字", required = true, example = "xx 活动")
|
||||
private String name;
|
||||
@Schema(description = "营销类型", required = true, example = "1")
|
||||
private Integer type;
|
||||
|
||||
// ========== 匹配情况 ==========
|
||||
@Schema(description = "是否满足优惠条件", required = true, example = "true")
|
||||
private Boolean meet;
|
||||
@Schema(description = "满足条件的提示", required = true, example = "圣诞价:省 150.00 元")
|
||||
private String meetTip;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
@Schema(description = "用户 App - 购物车更新是否选中 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemUpdateSelectedReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号列表", required = true, example = "1024,2048")
|
||||
@NotNull(message = "商品 SKU 编号列表不能为空")
|
||||
private Collection<Long> skuIds;
|
||||
|
||||
@Schema(description = "是否选中", required = true, example = "true")
|
||||
@NotNull(message = "是否选中不能为空")
|
||||
private Boolean selected;
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 用户的购物列表 Response VO")
|
||||
@Data
|
||||
public class AppTradeCartListRespVO {
|
||||
|
||||
/**
|
||||
* 有效的购物项数组
|
||||
*/
|
||||
private List<Cart> validList;
|
||||
|
||||
/**
|
||||
* 无效的购物项数组
|
||||
*/
|
||||
private List<Cart> invalidList;
|
||||
|
||||
@Schema(description = "购物项")
|
||||
@Data
|
||||
public static class Cart {
|
||||
|
||||
@Schema(description = "购物项的编号", required = true, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品数量", required = true, example = "1")
|
||||
private Integer count;
|
||||
|
||||
/**
|
||||
* 商品 SPU
|
||||
*/
|
||||
private AppProductSpuBaseRespVO spu;
|
||||
/**
|
||||
* 商品 SKU
|
||||
*/
|
||||
private AppProductSkuBaseRespVO sku;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -6,11 +6,15 @@ import lombok.Data;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 购物车更新数量 Request VO")
|
||||
@Schema(description = "用户 App - 购物车重置 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartItemUpdateCountReqVO {
|
||||
public class AppTradeCartResetReqVO {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true, example = "1024")
|
||||
@Schema(description = "编号", required = true, example = "1024")
|
||||
@NotNull(message = "编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品 SKU 编号", required = true,example = "1024")
|
||||
@NotNull(message = "商品 SKU 编号不能为空")
|
||||
private Long skuId;
|
||||
|
@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.cart.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "用户 App - 购物车更新 Request VO")
|
||||
@Data
|
||||
public class AppTradeCartUpdateReqVO {
|
||||
|
||||
@Schema(description = "编号", required = true, example = "1024")
|
||||
@NotNull(message = "编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "商品数量", required = true, example = "1")
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(message = "数量必须大于 0", value = 1L)
|
||||
private Integer count;
|
||||
|
||||
}
|
@ -1,45 +1,52 @@
|
||||
package cn.iocoder.yudao.module.trade.convert.cart;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.sku.AppProductSkuBaseRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.spu.AppProductSpuBaseRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartConvert {
|
||||
|
||||
TradeCartConvert INSTANCE = Mappers.getMapper(TradeCartConvert.class);
|
||||
|
||||
default AppTradeCartDetailRespVO buildEmptyAppTradeCartDetailRespVO() {
|
||||
return new AppTradeCartDetailRespVO().setItemGroups(Collections.emptyList())
|
||||
.setOrder(new AppTradeCartDetailRespVO.Order().setSkuOriginalPrice(0).setSkuPromotionPrice(0)
|
||||
.setOrderPromotionPrice(0).setDeliveryPrice(0).setPayPrice(0));
|
||||
default AppTradeCartListRespVO convertList(List<TradeCartDO> carts,
|
||||
List<ProductSpuRespDTO> spus, List<ProductSkuRespDTO> skus) {
|
||||
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spus, ProductSpuRespDTO::getId);
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
// 遍历,开始转换
|
||||
List<AppTradeCartListRespVO.Cart> validList = new ArrayList<>(carts.size());
|
||||
List<AppTradeCartListRespVO.Cart> invalidList = new ArrayList<>();
|
||||
carts.forEach(cart -> {
|
||||
AppTradeCartListRespVO.Cart cartVO = new AppTradeCartListRespVO.Cart();
|
||||
cartVO.setId(cart.getId()).setCount(cart.getCount());
|
||||
ProductSpuRespDTO spu = spuMap.get(cart.getSpuId());
|
||||
ProductSkuRespDTO sku = skuMap.get(cart.getSkuId());
|
||||
cartVO.setSpu(convert(spu)).setSku(convert(sku));
|
||||
// 如果 SPU 不存在,或者下架,或者库存不足,说明是无效的
|
||||
if (spu == null
|
||||
|| !ProductSpuStatusEnum.isEnable(spu.getStatus())
|
||||
|| spu.getStock() <= 0) {
|
||||
invalidList.add(cartVO);
|
||||
} else {
|
||||
// 虽然 SKU 可能也会不存在,但是可以通过购物车重新选择
|
||||
validList.add(cartVO);
|
||||
}
|
||||
});
|
||||
return new AppTradeCartListRespVO().setValidList(validList).setInvalidList(invalidList);
|
||||
}
|
||||
|
||||
default PriceCalculateReqDTO convert(Long userId, List<TradeCartItemDO> cartItems) {
|
||||
return new PriceCalculateReqDTO().setUserId(userId)
|
||||
.setItems(convertList(cartItems, cartItem -> new PriceCalculateReqDTO.Item().setSkuId(cartItem.getSkuId())
|
||||
.setCount(cartItem.getSelected() ? cartItem.getCount() : 0)));
|
||||
}
|
||||
|
||||
// ========== AppTradeCartDetailRespVO 相关 ==========
|
||||
|
||||
AppTradeCartDetailRespVO.Promotion convert(PriceCalculateRespDTO.Promotion bean);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "cartItem.count", target = "count")
|
||||
})
|
||||
AppTradeCartDetailRespVO.Sku convert(PriceCalculateRespDTO.OrderItem orderItem, TradeCartItemDO cartItem);
|
||||
|
||||
AppTradeCartDetailRespVO.Order convert(PriceCalculateRespDTO.Order bean);
|
||||
AppProductSpuBaseRespVO convert(ProductSpuRespDTO spu);
|
||||
AppProductSkuBaseRespVO convert(ProductSkuRespDTO sku);
|
||||
|
||||
}
|
||||
|
@ -9,13 +9,15 @@ import lombok.experimental.Accessors;
|
||||
/**
|
||||
* 购物车的商品信息 DO
|
||||
*
|
||||
* 每个商品,对应一条记录,通过 {@link #spuId} 和 {@link #skuId} 关联
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("trade_cart_item")
|
||||
@TableName("trade_cart")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class TradeCartItemDO extends BaseDO {
|
||||
public class TradeCartDO extends BaseDO {
|
||||
|
||||
// ========= 基础字段 BEGIN =========
|
||||
|
||||
@ -23,14 +25,6 @@ public class TradeCartItemDO extends BaseDO {
|
||||
* 编号,唯一自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 是否选中
|
||||
*/
|
||||
private Boolean selected;
|
||||
|
||||
// ========= 基础字段 END =========
|
||||
|
||||
// ========= 买家信息 BEGIN =========
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
@ -39,7 +33,25 @@ public class TradeCartItemDO extends BaseDO {
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
// ========= 买家信息 END =========
|
||||
/**
|
||||
* 是否添加到购物车
|
||||
*
|
||||
* false - 未添加:用户点击【立即购买】
|
||||
* true - 已添加:用户点击【添加购物车】
|
||||
*
|
||||
* 为什么要设计这个字段?
|
||||
* 配合 orderStatus 字段,可以知道有多少商品,用户点击了【立即购买】,最终多少【确认下单】
|
||||
*/
|
||||
private Boolean addStatus;
|
||||
/**
|
||||
* 是否提交订单
|
||||
*
|
||||
* false - 未下单:立即购买,或者添加到购物车,此时设置为 false
|
||||
* true - 已下单:确认下单,此时设置为 true
|
||||
*/
|
||||
private Boolean orderStatus;
|
||||
|
||||
// ========= 基础字段 END =========
|
||||
|
||||
// ========= 商品信息 BEGIN =========
|
||||
|
||||
@ -64,27 +76,11 @@ public class TradeCartItemDO extends BaseDO {
|
||||
|
||||
// ========= 优惠信息 BEGIN =========
|
||||
|
||||
// /**
|
||||
// * 商品营销活动编号
|
||||
// */
|
||||
// private Long activityId; // discount_id
|
||||
// /**
|
||||
// * 商品营销活动类型
|
||||
// */
|
||||
// private Integer activityType;
|
||||
// TODO 芋艿:combination_id 拼团 ID
|
||||
// TODO 芋艿:seckill_id 秒杀产品 ID
|
||||
// TODO 芋艿:bargain_id 砍价 ID
|
||||
// TODO 芋艿:pinkId 团长拼团 ID
|
||||
|
||||
// ========= 优惠信息 END =========
|
||||
|
||||
// TODO 待确定字段:mf
|
||||
// TODO 芋艿:distribution_card_no 推广员
|
||||
// TODO 芋艿:is_pay 未购买、已购买
|
||||
// TODO 芋艿:is_new 是否立即购买
|
||||
|
||||
// TODO 待确定字段: yv
|
||||
// TODO isPay: 是否购买
|
||||
// TODO isNew:是否立即购买
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.cart;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartItemMapper extends BaseMapperX<TradeCartItemDO> {
|
||||
|
||||
default TradeCartItemDO selectByUserIdAndSkuId(Long userId, Long skuId) {
|
||||
return selectOne(TradeCartItemDO::getUserId, userId,
|
||||
TradeCartItemDO::getSkuId, skuId);
|
||||
}
|
||||
|
||||
default List<TradeCartItemDO> selectListByUserIdAndSkuIds(Long userId, Collection<Long> skuIds) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
|
||||
.in(TradeCartItemDO::getSkuId, skuIds));
|
||||
}
|
||||
|
||||
default void updateByIds(Collection<Long> ids, TradeCartItemDO updateObject) {
|
||||
update(updateObject, new LambdaQueryWrapper<TradeCartItemDO>().in(TradeCartItemDO::getId, ids));
|
||||
}
|
||||
|
||||
default Integer selectSumByUserId(Long userId) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartItemDO>()
|
||||
.select("SUM(count) AS sumCount")
|
||||
.eq("user_id", userId));
|
||||
// 获得数量
|
||||
return CollUtil.isNotEmpty(result) ? MapUtil.getInt(result.get(0), "sumCount") : 0;
|
||||
}
|
||||
|
||||
default List<TradeCartItemDO> selectListByUserId(Long userId, Boolean selected) {
|
||||
return selectList(new LambdaQueryWrapperX<TradeCartItemDO>().eq(TradeCartItemDO::getUserId, userId)
|
||||
.eqIfPresent(TradeCartItemDO::getSelected, selected));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.mysql.cart;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
|
||||
|
||||
default TradeCartDO selectByUserIdAndSkuId(Long userId, Long skuId,
|
||||
Boolean addStatus, Boolean orderStatus) {
|
||||
return selectOne(TradeCartDO::getUserId, userId,
|
||||
TradeCartDO::getSkuId, skuId,
|
||||
TradeCartDO::getAddStatus, addStatus,
|
||||
TradeCartDO::getOrderStatus, orderStatus);
|
||||
}
|
||||
|
||||
default Integer selectSumByUserId(Long userId) {
|
||||
// SQL sum 查询
|
||||
List<Map<String, Object>> result = selectMaps(new QueryWrapper<TradeCartDO>()
|
||||
.select("SUM(count) AS sumCount")
|
||||
.eq("user_id", userId)
|
||||
.eq("add_status", true) // 只计算添加到购物车中的
|
||||
.eq("order_status", false)); // 必须未下单
|
||||
// 获得数量
|
||||
return CollUtil.getFirst(result) != null ? MapUtil.getInt(result.get(0), "sumCount") : 0;
|
||||
}
|
||||
|
||||
default TradeCartDO selectById(Long id, Long userId) {
|
||||
return selectOne(TradeCartDO::getId, id,
|
||||
TradeCartDO::getUserId, userId);
|
||||
}
|
||||
|
||||
default List<TradeCartDO> selectListByIds(Collection<Long> ids, Long userId) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartDO>()
|
||||
.in(TradeCartDO::getId, ids)
|
||||
.eq(TradeCartDO::getUserId, userId));
|
||||
}
|
||||
|
||||
default List<TradeCartDO> selectListByUserId(Long userId, Boolean addStatus, Boolean orderStatus) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartDO>()
|
||||
.eq(TradeCartDO::getUserId, userId)
|
||||
.eq(TradeCartDO::getAddStatus, addStatus)
|
||||
.eq(TradeCartDO::getOrderStatus, orderStatus));
|
||||
}
|
||||
|
||||
default void updateByIds(Collection<Long> ids, TradeCartDO updateObject) {
|
||||
update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids));
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package cn.iocoder.yudao.module.trade.service.cart;
|
||||
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
@ -19,9 +19,10 @@ public interface TradeCartService {
|
||||
* 添加商品到购物车
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param addCountReqVO 添加信息
|
||||
* @param addReqVO 添加信息
|
||||
* @return 购物项的编号
|
||||
*/
|
||||
void addCartItemCount(Long userId, @Valid AppTradeCartItemAddCountReqVO addCountReqVO);
|
||||
Long addCart(Long userId, @Valid AppTradeCartAddReqVO addReqVO);
|
||||
|
||||
/**
|
||||
* 更新购物车商品数量
|
||||
@ -29,23 +30,25 @@ public interface TradeCartService {
|
||||
* @param userId 用户编号
|
||||
* @param updateCountReqVO 更新信息
|
||||
*/
|
||||
void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO);
|
||||
void updateCart(Long userId, AppTradeCartUpdateReqVO updateCountReqVO);
|
||||
|
||||
/**
|
||||
* 更新购物车商品是否选中
|
||||
* 重置购物车商品
|
||||
*
|
||||
* 使用场景:在一个购物车项对应的商品失效(例如说 SPU 被下架),可以重新选择对应的 SKU
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param updateSelectedReqVO 更新信息
|
||||
* @param updateReqVO 重置信息
|
||||
*/
|
||||
void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO);
|
||||
void resetCart(Long userId, AppTradeCartResetReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除购物车商品
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param skuIds SKU 编号的数组
|
||||
* @param ids 购物项的编号
|
||||
*/
|
||||
void deleteCartItems(Long userId, Collection<Long> skuIds);
|
||||
void deleteCart(Long userId, Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 查询用户在购物车中的商品数量
|
||||
@ -56,11 +59,11 @@ public interface TradeCartService {
|
||||
Integer getCartCount(Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户的购物车详情
|
||||
* 查询用户的购物车列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 购物车详情
|
||||
* @return 购物车列表
|
||||
*/
|
||||
AppTradeCartDetailRespVO getCartDetail(Long userId);
|
||||
AppTradeCartListRespVO getCartList(Long userId);
|
||||
|
||||
}
|
||||
|
@ -1,39 +1,39 @@
|
||||
package cn.iocoder.yudao.module.trade.service.cart;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.PriceApi;
|
||||
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionLevelEnum;
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemAddCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateCountReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartItemUpdateSelectedReqVO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.cart.TradeCartConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartItemMapper;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.mysql.cart.TradeCartMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_NOT_FOUND;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* 购物车 Service 实现类
|
||||
*
|
||||
* // TODO 芋艿:秒杀、拼团、砍价对购物车的影响
|
||||
* // TODO 芋艿:未来优化:购物车的价格计算,支持营销信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@ -41,123 +41,134 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.CARD_ITEM_N
|
||||
public class TradeCartServiceImpl implements TradeCartService {
|
||||
|
||||
@Resource
|
||||
private TradeCartItemMapper cartItemMapper;
|
||||
private TradeCartMapper cartMapper;
|
||||
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private PriceApi priceApi;
|
||||
|
||||
@Override
|
||||
public void addCartItemCount(Long userId, AppTradeCartItemAddCountReqVO addCountReqVO) {
|
||||
Long skuId = addCountReqVO.getSkuId();
|
||||
Integer count = addCountReqVO.getCount();
|
||||
// 查询 CartItemDO
|
||||
TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, addCountReqVO.getSkuId());
|
||||
public Long addCart(Long userId, AppTradeCartAddReqVO addReqVO) {
|
||||
// 查询 TradeCartDO
|
||||
TradeCartDO cart = cartMapper.selectByUserIdAndSkuId(userId, addReqVO.getSkuId(),
|
||||
addReqVO.getAddStatus(), false);
|
||||
// 校验 SKU
|
||||
Integer count = cart != null && addReqVO.getAddStatus() ?
|
||||
cart.getCount() + addReqVO.getCount() : addReqVO.getCount();
|
||||
ProductSkuRespDTO sku = checkProductSku(addReqVO.getSkuId(), count);
|
||||
|
||||
// 存在,则进行数量更新
|
||||
if (tradeItem != null) {
|
||||
checkProductSku(skuId, tradeItem.getCount() + count);
|
||||
cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId())
|
||||
.setSelected(true).setCount(tradeItem.getCount() + count));
|
||||
return;
|
||||
// 情况零:特殊,count 小于等于 0,说明前端项目删除
|
||||
// 情况一:存在,则进行数量更新
|
||||
if (cart != null) {
|
||||
// 特殊情况,如果 count 小于等于 0,说明前端想要删除
|
||||
if (count <= 0) {
|
||||
cartMapper.deleteById(cart.getId());
|
||||
} else {
|
||||
cartMapper.updateById(new TradeCartDO().setId(cart.getId()).setCount(count));
|
||||
}
|
||||
return cart.getId();
|
||||
// 情况二:不存在,则进行插入
|
||||
} else {
|
||||
cart = new TradeCartDO().setUserId(userId)
|
||||
.setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setCount(count)
|
||||
.setAddStatus(addReqVO.getAddStatus()).setOrderStatus(false);
|
||||
cartMapper.insert(cart);
|
||||
}
|
||||
|
||||
// 不存在,则进行插入
|
||||
ProductSkuRespDTO sku = checkProductSku(skuId, count);
|
||||
cartItemMapper.insert(new TradeCartItemDO().setUserId(userId).setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.setSelected(true).setCount(count));
|
||||
return cart.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCartItemCount(Long userId, AppTradeCartItemUpdateCountReqVO updateCountReqVO) {
|
||||
// 校验 TradeCartItemDO 存在
|
||||
TradeCartItemDO tradeItem = cartItemMapper.selectByUserIdAndSkuId(userId, updateCountReqVO.getSkuId());
|
||||
if (tradeItem == null) {
|
||||
public void updateCart(Long userId, AppTradeCartUpdateReqVO updateReqVO) {
|
||||
// 校验 TradeCartDO 存在
|
||||
TradeCartDO cart = cartMapper.selectById(updateReqVO.getId(), userId);
|
||||
if (cart == null) {
|
||||
throw exception(CARD_ITEM_NOT_FOUND);
|
||||
}
|
||||
// 校验商品 SKU
|
||||
checkProductSku(updateCountReqVO.getSkuId(), updateCountReqVO.getCount());
|
||||
checkProductSku(cart.getSkuId(), updateReqVO.getCount());
|
||||
|
||||
// 更新数量
|
||||
cartItemMapper.updateById(new TradeCartItemDO().setId(tradeItem.getId()).setCount(updateCountReqVO.getCount()));
|
||||
cartMapper.updateById(new TradeCartDO().setId(cart.getId())
|
||||
.setCount(updateReqVO.getCount()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCartItemSelected(Long userId, AppTradeCartItemUpdateSelectedReqVO updateSelectedReqVO) {
|
||||
// 查询 CartItemDO 列表
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, updateSelectedReqVO.getSkuIds());
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
return;
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void resetCart(Long userId, AppTradeCartResetReqVO resetReqVO) {
|
||||
// 第一步:删除原本的购物项
|
||||
TradeCartDO oldCart = cartMapper.selectById(resetReqVO.getId(), userId);
|
||||
if (oldCart == null) {
|
||||
throw exception(CARD_ITEM_NOT_FOUND);
|
||||
}
|
||||
cartMapper.deleteById(oldCart.getId());
|
||||
|
||||
// 更新选中
|
||||
cartItemMapper.updateByIds(CollectionUtils.convertList(cartItems, TradeCartItemDO::getId),
|
||||
new TradeCartItemDO().setSelected(updateSelectedReqVO.getSelected()));
|
||||
// 第二步:添加新的购物项
|
||||
TradeCartDO newCart = cartMapper.selectByUserIdAndSkuId(userId, resetReqVO.getSkuId(),
|
||||
true, false);
|
||||
if (newCart != null) {
|
||||
updateCart(userId, new AppTradeCartUpdateReqVO()
|
||||
.setId(newCart.getId()).setCount(resetReqVO.getCount()));
|
||||
} else {
|
||||
addCart(userId, new AppTradeCartAddReqVO().setAddStatus(true)
|
||||
.setSkuId(resetReqVO.getSkuId()).setCount(resetReqVO.getCount()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 购物车删除商品
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param skuIds 商品 SKU 编号的数组
|
||||
* @param ids 商品 SKU 编号的数组
|
||||
*/
|
||||
@Override
|
||||
public void deleteCartItems(Long userId, Collection<Long> skuIds) {
|
||||
// 查询 CartItemDO 列表
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
public void deleteCart(Long userId, Collection<Long> ids) {
|
||||
// 查询 TradeCartDO 列表
|
||||
List<TradeCartDO> carts = cartMapper.selectListByIds(ids, userId);
|
||||
if (CollUtil.isEmpty(carts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 批量标记删除
|
||||
cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(cartItems, TradeCartItemDO::getId));
|
||||
cartMapper.deleteBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCartCount(Long userId) {
|
||||
return cartItemMapper.selectSumByUserId(userId);
|
||||
return cartMapper.selectSumByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppTradeCartDetailRespVO getCartDetail(Long userId) {
|
||||
// 获得购物车的商品
|
||||
List<TradeCartItemDO> cartItems = cartItemMapper.selectListByUserId(userId, null);
|
||||
public AppTradeCartListRespVO getCartList(Long userId) {
|
||||
// 获得购物车的商品,只查询未下单的
|
||||
List<TradeCartDO> carts = cartMapper.selectListByUserId(userId, true, false);
|
||||
carts.sort(Comparator.comparing(TradeCartDO::getId).reversed());
|
||||
// 如果未空,则返回空结果
|
||||
if (CollUtil.isEmpty(cartItems)) {
|
||||
return TradeCartConvert.INSTANCE.buildEmptyAppTradeCartDetailRespVO();
|
||||
if (CollUtil.isEmpty(carts)) {
|
||||
return new AppTradeCartListRespVO().setValidList(emptyList())
|
||||
.setInvalidList(emptyList());
|
||||
}
|
||||
|
||||
// 调用价格服务,计算价格
|
||||
PriceCalculateRespDTO priceCalculate = priceApi.calculatePrice(TradeCartConvert.INSTANCE.convert(userId, cartItems));
|
||||
// 查询 SPU、SKU 列表
|
||||
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(carts, TradeCartDO::getSpuId));
|
||||
List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(carts, TradeCartDO::getSkuId));
|
||||
|
||||
// 转换返回
|
||||
Map<Long, TradeCartItemDO> cartItemMap = convertMap(cartItems, TradeCartItemDO::getSkuId);
|
||||
Map<Long, PriceCalculateRespDTO.OrderItem> orderItemMap = convertMap(priceCalculate.getOrder().getItems(),
|
||||
PriceCalculateRespDTO.OrderItem::getSkuId);
|
||||
List<AppTradeCartDetailRespVO.ItemGroup> itemGroups = new ArrayList<>(cartItems.size());
|
||||
// ① 场景一,营销活动,订单级别 TODO 芋艿:待测试
|
||||
priceCalculate.getPromotions().stream().filter(promotion -> PromotionLevelEnum.ORDER.getLevel().equals(promotion.getLevel()))
|
||||
.forEach(promotion -> {
|
||||
AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>())
|
||||
.setPromotion(TradeCartConvert.INSTANCE.convert(promotion));
|
||||
itemGroups.add(itemGroup);
|
||||
promotion.getItems().forEach(promotionItem -> {
|
||||
PriceCalculateRespDTO.OrderItem orderItem = orderItemMap.remove(promotionItem.getSkuId());
|
||||
Assert.notNull(orderItem, "商品 SKU({}) 对应的订单项不能为空", promotionItem.getSkuId());
|
||||
TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
||||
itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
||||
});
|
||||
});
|
||||
// ② 场景二,营销活动,商品级别
|
||||
orderItemMap.values().forEach(orderItem -> {
|
||||
AppTradeCartDetailRespVO.ItemGroup itemGroup = new AppTradeCartDetailRespVO.ItemGroup().setItems(new ArrayList<>(1)).setPromotion(null);
|
||||
itemGroups.add(itemGroup);
|
||||
TradeCartItemDO cartItem = cartItemMap.get(orderItem.getSkuId());
|
||||
itemGroup.getItems().add(TradeCartConvert.INSTANCE.convert(orderItem, cartItem)); // TODO spu
|
||||
// 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
||||
deleteCartIfSpuDeleted(carts, spus);
|
||||
|
||||
// 拼接数据
|
||||
return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
|
||||
}
|
||||
|
||||
private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) {
|
||||
// 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
||||
carts.removeIf(cart -> {
|
||||
if (spus.stream().noneMatch(spu -> spu.getId().equals(cart.getSpuId()))) {
|
||||
cartMapper.deleteById(cart.getId());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return new AppTradeCartDetailRespVO().setItemGroups(itemGroups)
|
||||
.setOrder(TradeCartConvert.INSTANCE.convert(priceCalculate.getOrder()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,7 +183,7 @@ public class TradeCartServiceImpl implements TradeCartService {
|
||||
*/
|
||||
private ProductSkuRespDTO checkProductSku(Long skuId, Integer count) {
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
|
||||
if (sku == null || CommonStatusEnum.DISABLE.getStatus().equals(sku.getStatus())) {
|
||||
if (sku == null) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
if (count > sku.getStock()) {
|
||||
|
@ -5,7 +5,6 @@ import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
@ -125,15 +124,10 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
if (items.size() != skus.size()) {
|
||||
throw exception(ORDER_CREATE_SKU_NOT_FOUND);
|
||||
}
|
||||
// 校验是否禁用 or 库存不足
|
||||
// 校验库存不足
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
items.forEach(item -> {
|
||||
ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
// SKU 禁用
|
||||
if (ObjectUtil.notEqual(CommonStatusEnum.ENABLE.getStatus(), sku.getStatus())) {
|
||||
throw exception(ORDER_CREATE_SKU_NOT_SALE);
|
||||
}
|
||||
// SKU 库存不足
|
||||
if (item.getCount() > sku.getStock()) {
|
||||
throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);
|
||||
}
|
||||
|
@ -99,10 +99,10 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
new AppTradeOrderCreateReqVO.Item().setSkuId(2L).setCount(4)));
|
||||
// mock 方法(商品 SKU 检查)
|
||||
ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L)
|
||||
.setPrice(50).setStock(100).setStatus(CommonStatusEnum.ENABLE.getStatus())
|
||||
.setPrice(50).setStock(100)
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(111L).setValueId(222L))));
|
||||
ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L)
|
||||
.setPrice(20).setStock(50).setStatus(CommonStatusEnum.ENABLE.getStatus()))
|
||||
.setPrice(20).setStock(50))
|
||||
.setProperties(singletonList(new ProductSkuRespDTO.Property().setPropertyId(333L).setValueId(444L)));
|
||||
when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02));
|
||||
// mock 方法(商品 SPU 检查)
|
||||
@ -201,7 +201,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
assertEquals(tradeOrderItemDO01.getProperties().size(), 1);
|
||||
assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L);
|
||||
assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L);
|
||||
assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName());
|
||||
//assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName()); TODO 找不到spuName
|
||||
assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl());
|
||||
assertEquals(tradeOrderItemDO01.getCount(), 3);
|
||||
assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150);
|
||||
@ -221,7 +221,7 @@ public class TradeOrderServiceTest extends BaseDbUnitTest {
|
||||
assertEquals(tradeOrderItemDO02.getProperties().size(), 1);
|
||||
assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L);
|
||||
assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L);
|
||||
assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName());
|
||||
//assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName()); TODO 找不到spuName
|
||||
assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl());
|
||||
assertEquals(tradeOrderItemDO02.getCount(), 4);
|
||||
assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80);
|
||||
|
Loading…
Reference in New Issue
Block a user