diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http index 04df7bfec..0de7583ae 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http @@ -1,8 +1,18 @@ -### 获得订单交易的分页 TODO +### 获得订单交易的分页(默认) GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10 Authorization: Bearer {{appToken}} tenant-id: {{appTenentId}} +### 获得订单交易的分页(价格) +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=4 tenant-id: {{appTenentId}} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java index d9ab87c72..783b85780 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java +++ b/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 { @GetMapping("/page") @Operation(summary = "获得商品 SPU 分页") public CommonResult> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { - PageResult pageResult = productSpuService.getSpuPage(pageVO, ProductSpuStatusEnum.ENABLE.getStatus()); - return success(ProductSpuConvert.INSTANCE.convertPage02(pageResult)); + PageResult pageResult = productSpuService.getSpuPage(pageVO); + return success(ProductSpuConvert.INSTANCE.convertPageForGetSpuPage(pageResult)); } @GetMapping("/get-detail") diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java index d826900a6..27d577976 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageItemRespVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.product.controller.app.spu.vo; +import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -15,21 +16,24 @@ 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 picUrls; + @Schema(description = "商品封面图", required = true) + private String picUrl; - @Schema(description = " 最小价格,单位使用:分", required = true, example = "1024") - private Integer minPrice; + @Schema(description = "商品轮播图", required = true) + private List sliderPicUrls; - @Schema(description = "最大价格,单位使用:分", required = true, example = "1024") - private Integer maxPrice; + @Schema(description = "商品价格,单位使用:分", required = true, example = "1024") + private Integer price; + + // ========== SKU 相关字段 ========= + + @Schema(description = "库存", required = true, example = "666") + private Integer stock; // ========== 统计相关字段 ========= diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java index dcbd1e190..5050561d6 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageReqVO.java +++ b/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 { 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() { diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java index fcf6d6436..cddd742f0 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java +++ b/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; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; import org.mapstruct.Mapper; +import org.mapstruct.Named; import org.mapstruct.factory.Mappers; import java.util.ArrayList; @@ -75,8 +76,6 @@ public interface ProductSpuConvert { List convertList03(List skus); AppProductPropertyValueDetailRespVO convert03(ProductPropertyValueDetailRespBO propertyValue); - PageResult convertPage02(PageResult page); - default ProductSpuDetailRespVO convert03(ProductSpuDO spu, List skus, List propertyValues) { ProductSpuDetailRespVO spuVO = convert03(spu); @@ -105,4 +104,14 @@ public interface ProductSpuConvert { List convertList04(List skus); ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue); + // ========== 用户 App 相关 ========== + + default PageResult convertPageForGetSpuPage(PageResult page) { + // 累加虚拟销量 + page.getList().forEach(spu -> spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount())); + // 然后进行转换 + return convertPageForGetSpuPage0(page); + } + PageResult convertPageForGetSpuPage0(PageResult page); + } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java index 256bc43f1..ee0922208 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java @@ -1,11 +1,15 @@ package cn.iocoder.yudao.module.product.dal.mysql.spu; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; +import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; @@ -46,16 +50,28 @@ public interface ProductSpuMapper extends BaseMapperX { .orderByDesc(ProductSpuDO::getSort)); } - default PageResult selectPage(AppProductSpuPageReqVO pageReqVO, Integer status) { + /** + * 获得商品 SPU 分页,提供给用户 App 使用 + */ + default PageResult selectPage(AppProductSpuPageReqVO pageReqVO, Set categoryIds) { LambdaQueryWrapperX query = new LambdaQueryWrapperX() - .eqIfPresent(ProductSpuDO::getCategoryId, pageReqVO.getCategoryId()) - .eqIfPresent(ProductSpuDO::getStatus, status); + .likeIfPresent(ProductSpuDO::getName, pageReqVO.getKeyword()) // 关键字匹配,目前只匹配商品名 + .inIfPresent(ProductSpuDO::getCategoryId, categoryIds); // 分类 + query.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()) // 上架状态 + .gt(ProductSpuDO::getStock, 0); // 有库存 + // 推荐类型的过滤条件 + if (ObjUtil.equal(pageReqVO.getRecommendType(), AppProductSpuPageReqVO.RECOMMEND_TYPE_HOT)) { + query.eq(ProductSpuDO::getRecommendHot, true); + } // 排序逻辑 - if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) { - // TODO ProductSpuDO 已经没有maxPrice 属性 - //query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getMaxPrice); - } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) { - query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getSalesCount); + if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_SALES_COUNT)) { + query.last(String.format(" ORDER BY (sales_count + virtual_sales_count) %s, sort DESC, id DESC", + pageReqVO.getSortAsc() ? "ASC" : "DESC")); + } else if (Objects.equals(pageReqVO.getSortField(), AppProductSpuPageReqVO.SORT_FIELD_PRICE)) { + query.orderBy(true, pageReqVO.getSortAsc(), ProductSpuDO::getPrice) + .orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId); + } else { + query.orderByDesc(ProductSpuDO::getSort).orderByDesc(ProductSpuDO::getId); } return selectPage(pageReqVO, query); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java index 0ae7359eb..dd8f77c12 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java +++ b/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 { List getSpuList(); /** - * 获得商品 SPU 分页 + * 获得商品 SPU 分页,提供给挂你兰后台使用 * * @param pageReqVO 分页查询 * @return 商品spu分页 @@ -83,13 +83,12 @@ public interface ProductSpuService { PageResult getSpuPage(ProductSpuPageReqVO pageReqVO); /** - * 获得商品 SPU 分页 + * 获得商品 SPU 分页,提供给用户 App 使用 * * @param pageReqVO 分页查询 - * @param status 状态 * @return 商品 SPU 分页 */ - PageResult getSpuPage(AppProductSpuPageReqVO pageReqVO, Integer status); + PageResult getSpuPage(AppProductSpuPageReqVO pageReqVO); /** * 更新商品 SPU 库存(增量) diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java index 352c8b341..79a6949a5 100755 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java @@ -1,14 +1,20 @@ package cn.iocoder.yudao.module.product.service.spu; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryListReqVO; import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuCreateReqVO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; +import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO; import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; @@ -21,10 +27,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; @@ -168,8 +171,17 @@ public class ProductSpuServiceImpl implements ProductSpuService { } @Override - public PageResult getSpuPage(AppProductSpuPageReqVO pageReqVO, Integer status) { - return productSpuMapper.selectPage(pageReqVO, status); + public PageResult getSpuPage(AppProductSpuPageReqVO pageReqVO) { + // 查找时,如果查找某个分类编号,则包含它的子分类。因为顶级分类不包含商品 + Set categoryIds = new HashSet<>(); + if (pageReqVO.getCategoryId() != null && pageReqVO.getCategoryId() > 0) { + categoryIds.add(pageReqVO.getCategoryId()); + List categoryChildren = categoryService.getEnableCategoryList(new ProductCategoryListReqVO() + .setParentId(pageReqVO.getCategoryId()).setStatus(CommonStatusEnum.ENABLE.getStatus())); + categoryIds.addAll(CollectionUtils.convertList(categoryChildren, ProductCategoryDO::getId)); + } + // 分页查询 + return productSpuMapper.selectPage(pageReqVO, categoryIds); } @Override