Commit 3ced6a3240022dcdaadd1c9807495ab3378c5b8f

Authored by YunaiV
1 parent a5f01810

完善 App 商品搜索的逻辑

yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http
1 -### 获得订单交易的分页 TODO 1 +### 获得订单交易的分页(默认)
2 GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10 2 GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10
3 Authorization: Bearer {{appToken}} 3 Authorization: Bearer {{appToken}}
4 tenant-id: {{appTenentId}} 4 tenant-id: {{appTenentId}}
5 5
  6 +### 获得订单交易的分页(价格)
  7 +GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=price&sortAsc=true
  8 +Authorization: Bearer {{appToken}}
  9 +tenant-id: {{appTenentId}}
  10 +
  11 +### 获得订单交易的分页(销售)
  12 +GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=salesCount&sortAsc=true
  13 +Authorization: Bearer {{appToken}}
  14 +tenant-id: {{appTenentId}}
  15 +
6 ### 获得商品 SPU 明细 16 ### 获得商品 SPU 明细
7 GET {{appApi}}/product/spu/get-detail?id=4 17 GET {{appApi}}/product/spu/get-detail?id=4
8 tenant-id: {{appTenentId}} 18 tenant-id: {{appTenentId}}
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
@@ -49,8 +49,8 @@ public class AppProductSpuController { @@ -49,8 +49,8 @@ public class AppProductSpuController {
49 @GetMapping("/page") 49 @GetMapping("/page")
50 @Operation(summary = "获得商品 SPU 分页") 50 @Operation(summary = "获得商品 SPU 分页")
51 public CommonResult<PageResult<AppProductSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { 51 public CommonResult<PageResult<AppProductSpuPageItemRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) {
52 - PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus());  
53 - return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult)); 52 + PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
  53 + return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult));
54 } 54 }
55 55
56 @GetMapping("/get-detail") 56 @GetMapping("/get-detail")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java
1 package cn.iocoder.yudao.module.product.controller.app.spu.vo; 1 package cn.iocoder.yudao.module.product.controller.app.spu.vo;
2 2
  3 +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 6
@@ -15,21 +16,24 @@ public class AppProductSpuPageItemRespVO { @@ -15,21 +16,24 @@ public class AppProductSpuPageItemRespVO {
15 private Long id; 16 private Long id;
16 17
17 @Schema(description = "商品名称", required = true, example = "芋道") 18 @Schema(description = "商品名称", required = true, example = "芋道")
18 - @NotEmpty(message = "商品名称不能为空")  
19 private String name; 19 private String name;
20 20
21 @Schema(description = "分类编号", required = true) 21 @Schema(description = "分类编号", required = true)
22 - @NotNull(message = "分类编号不能为空")  
23 private Long categoryId; 22 private Long categoryId;
24 23
25 - @Schema(description = "商品图片的数组", required = true)  
26 - private List<String> picUrls; 24 + @Schema(description = "商品封面图", required = true)
  25 + private String picUrl;
27 26
28 - @Schema(description = " 最小价格,单位使用:分", required = true, example = "1024")  
29 - private Integer minPrice; 27 + @Schema(description = "商品轮播图", required = true)
  28 + private List<String> sliderPicUrls;
30 29
31 - @Schema(description = "最大价格,单位使用:分", required = true, example = "1024")  
32 - private Integer maxPrice; 30 + @Schema(description = "商品价格,单位使用:分", required = true, example = "1024")
  31 + private Integer price;
  32 +
  33 + // ========== SKU 相关字段 =========
  34 +
  35 + @Schema(description = "库存", required = true, example = "666")
  36 + private Integer stock;
33 37
34 // ========== 统计相关字段 ========= 38 // ========== 统计相关字段 =========
35 39
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java
@@ -19,18 +19,23 @@ public class AppProductSpuPageReqVO extends PageParam { @@ -19,18 +19,23 @@ public class AppProductSpuPageReqVO extends PageParam {
19 public static final String SORT_FIELD_PRICE = "price"; 19 public static final String SORT_FIELD_PRICE = "price";
20 public static final String SORT_FIELD_SALES_COUNT = "salesCount"; 20 public static final String SORT_FIELD_SALES_COUNT = "salesCount";
21 21
  22 + public static final String RECOMMEND_TYPE_HOT = "hot";
  23 +
22 @Schema(description = "分类编号", example = "1") 24 @Schema(description = "分类编号", example = "1")
23 private Long categoryId; 25 private Long categoryId;
24 26
25 @Schema(description = "关键字", example = "好看") 27 @Schema(description = "关键字", example = "好看")
26 private String keyword; 28 private String keyword;
27 29
28 - @Schema(description = "排序字段", example = "price") // 参见 AppSpuPageReqVO.SORT_FIELD_XXX 常量 30 + @Schema(description = "排序字段", example = "price") // 参见 AppProductSpuPageReqVO.SORT_FIELD_XXX 常量
29 private String sortField; 31 private String sortField;
30 32
31 @Schema(description = "排序方式", example = "true") 33 @Schema(description = "排序方式", example = "true")
32 private Boolean sortAsc; 34 private Boolean sortAsc;
33 35
  36 + @Schema(description = "推荐类型", example = "hot") // 参见 AppProductSpuPageReqVO.RECOMMEND_TYPE_XXX 常亮
  37 + private String recommendType;
  38 +
34 @AssertTrue(message = "排序字段不合法") 39 @AssertTrue(message = "排序字段不合法")
35 @JsonIgnore 40 @JsonIgnore
36 public boolean isSortFieldValid() { 41 public boolean isSortFieldValid() {
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
14 import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; 14 import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
15 import org.mapstruct.Mapper; 15 import org.mapstruct.Mapper;
  16 +import org.mapstruct.Named;
16 import org.mapstruct.factory.Mappers; 17 import org.mapstruct.factory.Mappers;
17 18
18 import java.util.ArrayList; 19 import java.util.ArrayList;
@@ -75,8 +76,6 @@ public interface ProductSpuConvert { @@ -75,8 +76,6 @@ public interface ProductSpuConvert {
75 List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus); 76 List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
76 AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue); 77 AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
77 78
78 - PageResult<AppProductSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);  
79 -  
80 default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus, 79 default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
81 List<ProductPropertyValueDetailRespBO> propertyValues) { 80 List<ProductPropertyValueDetailRespBO> propertyValues) {
82 ProductSpuDetailRespVO spuVO = convert03(spu); 81 ProductSpuDetailRespVO spuVO = convert03(spu);
@@ -105,4 +104,14 @@ public interface ProductSpuConvert { @@ -105,4 +104,14 @@ public interface ProductSpuConvert {
105 List<ProductSpuDetailRespVO.Sku> convertList04(List<ProductSkuDO> skus); 104 List<ProductSpuDetailRespVO.Sku> convertList04(List<ProductSkuDO> skus);
106 ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue); 105 ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue);
107 106
  107 + // ========== 用户 App 相关 ==========
  108 +
  109 + default PageResult<AppProductSpuPageItemRespVO> convertPageForGetSpuPage(PageResult<ProductSpuDO> page) {
  110 + // 累加虚拟销量
  111 + page.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount()));
  112 + // 然后进行转换
  113 + return convertPageForGetSpuPage0(page);
  114 + }
  115 + PageResult<AppProductSpuPageItemRespVO> convertPageForGetSpuPage0(PageResult<ProductSpuDO> page);
  116 +
108 } 117 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java
1 package cn.iocoder.yudao.module.product.dal.mysql.spu; 1 package cn.iocoder.yudao.module.product.dal.mysql.spu;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; 9 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
7 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; 10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
8 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 11 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  12 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 13 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 import org.apache.ibatis.annotations.Mapper; 14 import org.apache.ibatis.annotations.Mapper;
11 15
@@ -46,16 +50,28 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; { @@ -46,16 +50,28 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; {
46 .orderByDesc(ProductSpuDO::getSort)); 50 .orderByDesc(ProductSpuDO::getSort));
47 } 51 }
48 52
49 - default PageResult<ProductSpuDO> selectPage(AppProductSpuPageReqVO pageReqVO, Integer status) { 53 + /**
  54 + * 获得商品 SPU 分页,提供给用户 App 使用
  55 + */
  56 + default PageResult<ProductSpuDO> selectPage(AppProductSpuPageReqVO pageReqVO, Set<Long> categoryIds) {
50 LambdaQueryWrapperX<ProductSpuDO> query = new LambdaQueryWrapperX<ProductSpuDO>() 57 LambdaQueryWrapperX<ProductSpuDO> query = new LambdaQueryWrapperX<ProductSpuDO>()
51 - .eqIfPresent(ProductSpuDO::getCategoryId, pageReqVO.getCategoryId())  
52 - .eqIfPresent(ProductSpuDO::getStatus, status); 58 + .likeIfPresent(ProductSpuDO::getName, pageReqVO.getKeyword()) // 关键字匹配,目前只匹配商品名
  59 + .inIfPresent(ProductSpuDO::getCategoryId, categoryIds); // 分类
  60 + query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()) // 上架状态
  61 + .gt(ProductSpuDO::getStock, 0); // 有库存
  62 + // 推荐类型的过滤条件
  63 + if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_HOT)) {
  64 + query.eq(ProductSpuDO::getRecommendHot, true);
  65 + }
53 // 排序逻辑 66 // 排序逻辑
54 - if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) {  
55 - // TODO ProductSpuDO 已经没有maxPrice 属性  
56 - //query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getMaxPrice);  
57 - } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {  
58 - query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getSalesCount); 67 + if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) {
  68 + query.last(String.format(" ORDER BY (sales_count + virtual_sales_count) %s, sort DESC, id DESC",
  69 + pageReqVO.getSortAsc() ? "ASC" : "DESC"));
  70 + } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) {
  71 + query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getPrice)
  72 + .orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId);
  73 + } else {
  74 + query.orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId);
59 } 75 }
60 return selectPage(pageReqVO, query); 76 return selectPage(pageReqVO, query);
61 } 77 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java
@@ -75,7 +75,7 @@ public interface ProductSpuService { @@ -75,7 +75,7 @@ public interface ProductSpuService {
75 List<ProductSpuDO> getSpuList(); 75 List<ProductSpuDO> getSpuList();
76 76
77 /** 77 /**
78 - * 获得商品 SPU 分页 78 + * 获得商品 SPU 分页,提供给挂你兰后台使用
79 * 79 *
80 * @param pageReqVO 分页查询 80 * @param pageReqVO 分页查询
81 * @return 商品spu分页 81 * @return 商品spu分页
@@ -83,13 +83,12 @@ public interface ProductSpuService { @@ -83,13 +83,12 @@ public interface ProductSpuService {
83 PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO); 83 PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);
84 84
85 /** 85 /**
86 - * 获得商品 SPU 分页 86 + * 获得商品 SPU 分页,提供给用户 App 使用
87 * 87 *
88 * @param pageReqVO 分页查询 88 * @param pageReqVO 分页查询
89 - * @param status 状态  
90 * @return 商品 SPU 分页 89 * @return 商品 SPU 分页
91 */ 90 */
92 - PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO, Integer status); 91 + PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO);
93 92
94 /** 93 /**
95 * 更新商品 SPU 库存(增量) 94 * 更新商品 SPU 库存(增量)
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 package cn.iocoder.yudao.module.product.service.spu; 1 package cn.iocoder.yudao.module.product.service.spu;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.NumberUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  10 +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO;
6 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; 11 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
7 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; 12 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
8 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; 13 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
9 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; 14 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; 15 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
11 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; 16 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
  17 +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 18 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 19 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
14 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; 20 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
@@ -21,10 +27,7 @@ import org.springframework.transaction.annotation.Transactional; @@ -21,10 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
21 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
22 28
23 import javax.annotation.Resource; 29 import javax.annotation.Resource;
24 -import java.util.Collection;  
25 -import java.util.List;  
26 -import java.util.Map;  
27 -import java.util.Set; 30 +import java.util.*;
28 31
29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -168,8 +171,17 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -168,8 +171,17 @@ public class ProductSpuServiceImpl implements ProductSpuService {
168 } 171 }
169 172
170 @Override 173 @Override
171 - public PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO, Integer status) {  
172 - return productSpuMapper.selectPage(pageReqVO, status); 174 + public PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO) {
  175 + // 查找时,如果查找某个分类编号,则包含它的子分类。因为顶级分类不包含商品
  176 + Set<Long> categoryIds = new HashSet<>();
  177 + if (pageReqVO.getCategoryId() != null && pageReqVO.getCategoryId() > 0) {
  178 + categoryIds.add(pageReqVO.getCategoryId());
  179 + List<ProductCategoryDO> categoryChildren = categoryService.getEnableCategoryList(new ProductCategoryListReqVO()
  180 + .setParentId(pageReqVO.getCategoryId()).setStatus(CommonStatusEnum.ENABLE.getStatus()));
  181 + categoryIds.addAll(CollectionUtils.convertList(categoryChildren, ProductCategoryDO::getId));
  182 + }
  183 + // 分页查询
  184 + return productSpuMapper.selectPage(pageReqVO, categoryIds);
173 } 185 }
174 186
175 @Override 187 @Override