Commit 3ced6a3240022dcdaadd1c9807495ab3378c5b8f
1 parent
a5f01810
完善 App 商品搜索的逻辑
Showing
8 changed files
with
87 additions
and
32 deletions
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<ProductSpuDO> { |
| 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 | ... | ... |