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 2 GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10
3 3 Authorization: Bearer {{appToken}}
4 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 16 ### 获得商品 SPU 明细
7 17 GET {{appApi}}/product/spu/get-detail?id=4
8 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 49 @GetMapping("/page")
50 50 @Operation(summary = "获得商品 SPU 分页")
51 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 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 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 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5 import lombok.Data;
5 6  
... ... @@ -15,21 +16,24 @@ public class AppProductSpuPageItemRespVO {
15 16 private Long id;
16 17  
17 18 @Schema(description = "商品名称", required = true, example = "芋道")
18   - @NotEmpty(message = "商品名称不能为空")
19 19 private String name;
20 20  
21 21 @Schema(description = "分类编号", required = true)
22   - @NotNull(message = "分类编号不能为空")
23 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 19 public static final String SORT_FIELD_PRICE = "price";
20 20 public static final String SORT_FIELD_SALES_COUNT = "salesCount";
21 21  
  22 + public static final String RECOMMEND_TYPE_HOT = "hot";
  23 +
22 24 @Schema(description = "分类编号", example = "1")
23 25 private Long categoryId;
24 26  
25 27 @Schema(description = "关键字", example = "好看")
26 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 31 private String sortField;
30 32  
31 33 @Schema(description = "排序方式", example = "true")
32 34 private Boolean sortAsc;
33 35  
  36 + @Schema(description = "推荐类型", example = "hot") // 参见 AppProductSpuPageReqVO.RECOMMEND_TYPE_XXX 常亮
  37 + private String recommendType;
  38 +
34 39 @AssertTrue(message = "排序字段不合法")
35 40 @JsonIgnore
36 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 13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
14 14 import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO;
15 15 import org.mapstruct.Mapper;
  16 +import org.mapstruct.Named;
16 17 import org.mapstruct.factory.Mappers;
17 18  
18 19 import java.util.ArrayList;
... ... @@ -75,8 +76,6 @@ public interface ProductSpuConvert {
75 76 List<AppProductSpuDetailRespVO.Sku> convertList03(List<ProductSkuDO> skus);
76 77 AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue);
77 78  
78   - PageResult<AppProductSpuPageItemRespVO> convertPage02(PageResult<ProductSpuDO> page);
79   -
80 79 default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List<ProductSkuDO> skus,
81 80 List<ProductPropertyValueDetailRespBO> propertyValues) {
82 81 ProductSpuDetailRespVO spuVO = convert03(spu);
... ... @@ -105,4 +104,14 @@ public interface ProductSpuConvert {
105 104 List<ProductSpuDetailRespVO.Sku> convertList04(List<ProductSkuDO> skus);
106 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 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 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 9 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
7 10 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
8 11 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
  12 +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
9 13 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 14 import org.apache.ibatis.annotations.Mapper;
11 15  
... ... @@ -46,16 +50,28 @@ public interface ProductSpuMapper extends BaseMapperX&lt;ProductSpuDO&gt; {
46 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 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 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 75 List<ProductSpuDO> getSpuList();
76 76  
77 77 /**
78   - * 获得商品 SPU 分页
  78 + * 获得商品 SPU 分页,提供给挂你兰后台使用
79 79 *
80 80 * @param pageReqVO 分页查询
81 81 * @return 商品spu分页
... ... @@ -83,13 +83,12 @@ public interface ProductSpuService {
83 83 PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);
84 84  
85 85 /**
86   - * 获得商品 SPU 分页
  86 + * 获得商品 SPU 分页,提供给用户 App 使用
87 87 *
88 88 * @param pageReqVO 分页查询
89   - * @param status 状态
90 89 * @return 商品 SPU 分页
91 90 */
92   - PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO, Integer status);
  91 + PageResult<ProductSpuDO> getSpuPage(AppProductSpuPageReqVO pageReqVO);
93 92  
94 93 /**
95 94 * 更新商品 SPU 库存(增量)
... ...
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
1 1 package cn.iocoder.yudao.module.product.service.spu;
2 2  
3 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 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 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 11 import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO;
7 12 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO;
8 13 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO;
9 14 import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO;
10 15 import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO;
11 16 import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert;
  17 +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
12 18 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
13 19 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
14 20 import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
... ... @@ -21,10 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
21 27 import org.springframework.validation.annotation.Validated;
22 28  
23 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 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
... ... @@ -168,8 +171,17 @@ public class ProductSpuServiceImpl implements ProductSpuService {
168 171 }
169 172  
170 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 187 @Override
... ...