mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-29 01:08:43 +08:00 
			
		
		
		
	mall:review 商品接口
This commit is contained in:
		| @@ -2,9 +2,12 @@ package cn.iocoder.yudao.module.product.enums; | ||||
|  | ||||
| /** | ||||
|  * product 字典类型的枚举类 | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
| public interface DictTypeConstants { | ||||
|  | ||||
|     String PRODUCT_UNIT = "product_unit";  // 商品单位 | ||||
|     String PRODUCT_SPU_STATUS = "product_spu_status"; // 商品 SPU 状态 | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,9 @@ package cn.iocoder.yudao.module.product.enums; | ||||
|  * | ||||
|  * @author HUIHUI | ||||
|  */ | ||||
|  | ||||
| public interface ProductConstants { | ||||
|  | ||||
|     // TODO @puhui999:这个变量,可以放到 CategoryDO 的实体里 | ||||
|     /** | ||||
|      * 父分类编号 - 根分类 | ||||
|      */ | ||||
| @@ -15,14 +16,18 @@ public interface ProductConstants { | ||||
|      * 限定分类层级 | ||||
|      */ | ||||
|     int CATEGORY_LEVEL = 2; | ||||
|  | ||||
|     // TODO @puhui999:这个变量,必要项不大哈 | ||||
|     /** | ||||
|      * SPU 分页 tab 个数 | ||||
|      */ | ||||
|     int SPU_TAB_COUNTS = 5; | ||||
|  | ||||
|     /** | ||||
|      * 警戒库存 TODO 警戒库存暂时为 10,后期需要使用常量或者数据库配置替换 | ||||
|      */ | ||||
|     int ALERT_STOCK = 10; | ||||
|  | ||||
|     /** | ||||
|      * 默认商品销量  TODO 默认商品销量为零 | ||||
|      */ | ||||
| @@ -31,4 +36,5 @@ public interface ProductConstants { | ||||
|      * 默认善品浏览量  TODO 默认浏览量为零 | ||||
|      */ | ||||
|     Integer BROWSE_COUNT = 0; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import lombok.Getter; | ||||
|  | ||||
| import java.util.Arrays; | ||||
|  | ||||
| // TODO @puhui999:中英文之间要有空格; 商品 spu Tab 标签枚举;这个类可以改成 ProductSpuPageTabEnum 会更好一点哈;分页 Tab 的意思; | ||||
| // TODO @puhui999:这种非关键的枚举,要不直接写在 ProductSpuPageReqVO 里。类似 public static final Integer TAB_TYPE_FOR_SALE = 0; // 出售中商品 | ||||
| /** | ||||
|  * 商品 spu Tabs 标签枚举类型 | ||||
|  * | ||||
| @@ -21,6 +21,7 @@ public enum ProductSpuPageTabEnum implements IntArrayValuable { | ||||
|     SOLD_OUT(2,"已售空商品"), | ||||
|     ALERT_STOCK(3,"警戒库存"), | ||||
|     RECYCLE_BIN(4,"商品回收站"); | ||||
|  | ||||
|     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ProductSpuPageTabEnum::getType).toArray(); | ||||
|     /** | ||||
|      * 状态 | ||||
| @@ -35,4 +36,5 @@ public enum ProductSpuPageTabEnum implements IntArrayValuable { | ||||
|     public int[] array() { | ||||
|         return ARRAYS; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -61,7 +61,7 @@ public class ProductBrandController { | ||||
|     public CommonResult<ProductBrandRespVO> getBrand(@RequestParam("id") Long id) { | ||||
|         ProductBrandDO brand = brandService.getBrand(id); | ||||
|         return success(ProductBrandConvert.INSTANCE.convert(brand)); | ||||
|     } | ||||
|     } // TODO @puhui999:方法和方法之间,要有空行。 | ||||
|     @GetMapping("/list-all-simple") | ||||
|     @Operation(summary = "获取品牌精简信息列表", description = "主要用于前端的下拉选项") | ||||
|     public CommonResult<List<ProductBrandSimpleRespVO>> getSimpleUserList() { | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| // TODO @puhui999:class 类的开始和结束,都要有一个空行哈。 | ||||
| @Schema(description = "管理后台 - 品牌精简信息 Response VO") | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| package cn.iocoder.yudao.module.product.controller.admin.sku.vo; | ||||
|  | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO; | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| @@ -57,6 +55,7 @@ public class ProductSkuBaseVO { | ||||
|     @Schema(description = "二级分销的佣金,单位:分", example = "1024") | ||||
|     private Integer subCommissionSecondPrice; | ||||
|  | ||||
|     // TODO @puhui999:这里要写 swagger 注解哈 | ||||
|     /** | ||||
|      * 商品属性 | ||||
|      */ | ||||
| @@ -67,27 +66,19 @@ public class ProductSkuBaseVO { | ||||
|  | ||||
|         /** | ||||
|          * 属性编号 | ||||
|          * 关联 {@link ProductPropertyDO#getId()} | ||||
|          */ | ||||
|         private Long propertyId; | ||||
|         /** | ||||
|          * 属性名字 | ||||
|          * 冗余 {@link ProductPropertyDO#getName()} | ||||
|          * | ||||
|          * 注意:每次属性名字发生变化时,需要更新该冗余 | ||||
|          */ | ||||
|         private String propertyName; | ||||
|  | ||||
|         /** | ||||
|          * 属性值编号 | ||||
|          * 关联 {@link ProductPropertyValueDO#getId()} | ||||
|          */ | ||||
|         private Long valueId; | ||||
|         /** | ||||
|          * 属性值名字 | ||||
|          * 冗余 {@link ProductPropertyValueDO#getName()} | ||||
|          * | ||||
|          * 注意:每次属性值名字发生变化时,需要更新该冗余 | ||||
|          */ | ||||
|         private String valueName; | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| package cn.iocoder.yudao.module.product.controller.admin.sku.vo; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.*; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
|  | ||||
| import javax.validation.constraints.NotNull; | ||||
| import java.util.List; | ||||
|  | ||||
| @Schema(description = "管理后台 - 商品 SKU 创建/更新 Request VO") | ||||
| @@ -12,6 +13,7 @@ import java.util.List; | ||||
| @ToString(callSuper = true) | ||||
| public class ProductSkuCreateOrUpdateReqVO extends ProductSkuBaseVO { | ||||
|  | ||||
|     // TODO @puhui999:是不是可以抽到父类里? | ||||
|     /** | ||||
|      * 属性数组 | ||||
|      */ | ||||
|   | ||||
| @@ -2,14 +2,11 @@ package cn.iocoder.yudao.module.product.controller.admin.spu; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||||
| import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | ||||
| import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; | ||||
| import cn.iocoder.yudao.module.product.convert.spu.ProductSpuConvert; | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | ||||
| import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | ||||
| import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; | ||||
| import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||
| @@ -22,14 +19,10 @@ import javax.annotation.Resource; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| import javax.validation.Valid; | ||||
| import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||||
| import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; | ||||
|  | ||||
| /** | ||||
| @@ -101,7 +94,7 @@ public class ProductSpuController { | ||||
|         return success(ProductSpuConvert.INSTANCE.convertPage(productSpuService.getSpuPage(pageVO))); | ||||
|     } | ||||
|  | ||||
|     // TODO @tuihui999:get-count;另外,url 使用 - 拆分  fix | ||||
|     // TODO @puhui999:方法名改成 getSpuCount,只是用于 tab 哈,这样更抽象一点。 | ||||
|     @GetMapping("/get-count") | ||||
|     @Operation(summary = "获得商品 SPU 分页 tab count") | ||||
|     @PreAuthorize("@ss.hasPermission('product:spu:query')") | ||||
| @@ -110,7 +103,7 @@ public class ProductSpuController { | ||||
|     } | ||||
|  | ||||
|     @GetMapping("/export") | ||||
|     @Operation(summary = "导出用户") | ||||
|     @Operation(summary = "导出商品") | ||||
|     @PreAuthorize("@ss.hasPermission('product:spu:export')") | ||||
|     @OperateLog(type = EXPORT) | ||||
|     public void exportUserList(@Validated ProductSpuExportReqVO reqVO, | ||||
| @@ -118,6 +111,7 @@ public class ProductSpuController { | ||||
|         List<ProductSpuDO> spuList = productSpuService.getSpuList(reqVO); | ||||
|         // 导出 Excel | ||||
|         List<ProductSpuExcelVO> datas = ProductSpuConvert.INSTANCE.convertList03(spuList); | ||||
|         ExcelUtils.write(response, "商品spu.xls", "数据", ProductSpuExcelVO.class, datas); | ||||
|         ExcelUtils.write(response, "商品列表.xls", "数据", ProductSpuExcelVO.class, datas); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package cn.iocoder.yudao.module.product.controller.admin.spu.vo; | ||||
|  | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
|  | ||||
| @@ -97,14 +96,14 @@ public class ProductSpuBaseVO { | ||||
|     @NotNull(message = "商品赠送积分不能为空") | ||||
|     private Integer giveIntegral; | ||||
|  | ||||
|     @Schema(description = "赠送的优惠劵编号的数组") // TODO 这块前端还未实现 | ||||
|     @Schema(description = "赠送的优惠劵编号的数组", example = "[1, 10]") // TODO 这块前端还未实现 | ||||
|     private List<Long> giveCouponTemplateIds; | ||||
|  | ||||
|     @Schema(description = "分销类型", example = "true") | ||||
|     @Schema(description = "分销类型", required = true, example = "true") | ||||
|     @NotNull(message = "商品分销类型不能为空") | ||||
|     private Boolean subCommissionType; | ||||
|  | ||||
|     @Schema(description = "活动展示顺序", example = "[1、3、2、4、5]") // TODO 这块前端还未实现 | ||||
|     @Schema(description = "活动展示顺序", example = "[1, 3, 2, 4, 5]") // TODO 这块前端还未实现 | ||||
|     private List<Integer> activityOrders; | ||||
|  | ||||
|     // ========== 统计相关字段 ========= | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
|  | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
| @@ -21,7 +20,9 @@ import java.util.List; | ||||
| @ToString(callSuper = true) | ||||
| public class ProductSpuDetailRespVO extends ProductSpuBaseVO { | ||||
|  | ||||
|     @Schema(description = "spuId") | ||||
|     // TODO @puhui999:swagger 的 required 和 example 写下 | ||||
|  | ||||
|     @Schema(description = "商品 SPU 编号") | ||||
|     private Long id; | ||||
|  | ||||
|     @Schema(description = "商品销量") | ||||
|   | ||||
| @@ -11,11 +11,13 @@ import org.springframework.format.annotation.DateTimeFormat; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||||
|  | ||||
| @Schema(description = "管理后台 - 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的") | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class ProductSpuExportReqVO { | ||||
|  | ||||
|     @Schema(description = "商品名称", example = "yutou") | ||||
|     private String name; | ||||
|  | ||||
| @@ -29,4 +31,5 @@ public class ProductSpuExportReqVO { | ||||
|     @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private LocalDateTime[] createTime; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package cn.iocoder.yudao.module.product.controller.admin.spu.vo; | ||||
|  | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| @@ -19,6 +18,8 @@ import java.time.LocalDateTime; | ||||
| @ToString(callSuper = true) | ||||
| public class ProductSpuRespVO extends ProductSpuBaseVO { | ||||
|  | ||||
|     // TODO @puhui999:swagger 的 required 和 example 写下 | ||||
|  | ||||
|     @Schema(description = "spuId") | ||||
|     private Long id; | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| package cn.iocoder.yudao.module.product.controller.admin.spu.vo; | ||||
|  | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.ToString; | ||||
|  | ||||
| /** | ||||
| @@ -16,6 +14,8 @@ import lombok.ToString; | ||||
| @ToString(callSuper = true) | ||||
| public class ProductSpuSimpleRespVO { | ||||
|  | ||||
|     // TODO @puhui999:swagger 的 required 和 example 写下 | ||||
|  | ||||
|     @Schema(description = "主键") | ||||
|     private Long id; | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.product.controller.admin.spu.vo; | ||||
|  | ||||
| import cn.iocoder.yudao.framework.common.validation.InEnum; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import lombok.Data; | ||||
| @@ -11,7 +10,6 @@ import lombok.ToString; | ||||
|  | ||||
| import javax.validation.Valid; | ||||
| import javax.validation.constraints.NotNull; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
| @@ -25,6 +23,8 @@ import java.util.List; | ||||
| @ToString(callSuper = true) | ||||
| public class ProductSpuUpdateReqVO extends ProductSpuBaseVO { | ||||
|  | ||||
|     // TODO @puhui999:swagger 的 required 和 example 写下 | ||||
|  | ||||
|     @Schema(description = "商品编号", required = true, example = "1") | ||||
|     @NotNull(message = "商品编号不能为空") | ||||
|     private Long id; | ||||
|   | ||||
| @@ -26,7 +26,9 @@ public interface ProductBrandConvert { | ||||
|     ProductBrandDO convert(ProductBrandUpdateReqVO bean); | ||||
|  | ||||
|     ProductBrandRespVO convert(ProductBrandDO bean); | ||||
|  | ||||
|     List<ProductBrandSimpleRespVO> convertList1(List<ProductBrandDO> list); | ||||
|  | ||||
|     List<ProductBrandRespVO> convertList(List<ProductBrandDO> list); | ||||
|  | ||||
|     PageResult<ProductBrandRespVO> convertPage(PageResult<ProductBrandDO> page); | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| package cn.iocoder.yudao.module.product.convert.spu; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| @@ -10,8 +9,8 @@ import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; | ||||
| import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProductPropertyValueDetailRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuDetailRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageItemRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; | ||||
| import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; | ||||
| import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | ||||
| @@ -52,6 +51,8 @@ public interface ProductSpuConvert { | ||||
|     List<ProductSpuRespDTO> convertList2(List<ProductSpuDO> list); | ||||
|  | ||||
|     List<ProductSpuSimpleRespVO> convertList02(List<ProductSpuDO> list); | ||||
|  | ||||
|     // TODO @puhui999:部分属性,可以通过 mapstruct 的 @Mapping(source = , target = , ) 映射转换,可以查下文档 | ||||
|     default List<ProductSpuExcelVO> convertList03(List<ProductSpuDO> list){ | ||||
|         ArrayList<ProductSpuExcelVO> spuExcelVOs = new ArrayList<>(); | ||||
|         list.forEach((spu)->{ | ||||
| @@ -95,6 +96,7 @@ public interface ProductSpuConvert { | ||||
|     } | ||||
|     ProductSpuDetailRespVO convert03(ProductSpuDO spu); | ||||
|  | ||||
|     // TODO @puhui999:下面两个没用到,是不是删除呀? | ||||
|     List<ProductSkuRespVO> convertList04(List<ProductSkuDO> skus); | ||||
|  | ||||
|     ProductPropertyValueDetailRespVO convert04(ProductPropertyValueDetailRespBO propertyValue); | ||||
| @@ -142,11 +144,13 @@ public interface ProductSpuConvert { | ||||
|  | ||||
|     AppProductPropertyValueDetailRespVO convertForGetSpuDetail(ProductPropertyValueDetailRespBO propertyValue); | ||||
|  | ||||
|     default ProductSpuDetailRespVO convertForSpuDetailRespVO(ProductSpuDO spu, List<ProductSkuDO> skus, Function<Set<Long>, List<ProductPropertyValueDetailRespBO>> func) { | ||||
|     default ProductSpuDetailRespVO convertForSpuDetailRespVO(ProductSpuDO spu, List<ProductSkuDO> skus, | ||||
|                                                              Function<Set<Long>, List<ProductPropertyValueDetailRespBO>> func) { | ||||
|         ProductSpuDetailRespVO productSpuDetailRespVO = convert03(spu); | ||||
|         // TODO @puhui999:if return 哈,减少嵌套层数。 | ||||
|         if (CollUtil.isNotEmpty(skus)) { | ||||
|             List<ProductSkuRespVO> skuVOs = ProductSkuConvert.INSTANCE.convertList(skus); | ||||
|             // fix:统一模型,即使是单规格,也查询下,如若Properties为空报错则为单属性不做处理 | ||||
|             // fix:统一模型,即使是单规格,也查询下,如若 Properties 为空报错则为单属性不做处理 | ||||
|             try { | ||||
|                 // 获取所有的属性值 id | ||||
|                 Set<Long> valueIds = skus.stream().flatMap(p -> p.getProperties().stream()) | ||||
|   | ||||
| @@ -31,6 +31,7 @@ public interface ProductBrandMapper extends BaseMapperX<ProductBrandDO> { | ||||
|         return selectOne(ProductBrandDO::getName, name); | ||||
|     } | ||||
|  | ||||
|     // TODO @puhui999:) { 中间要有空格哈。 | ||||
|     default List<ProductBrandDO> selectListByStatus(Integer status){ | ||||
|         return selectList(ProductBrandDO::getStatus,status); | ||||
|     } | ||||
|   | ||||
| @@ -10,13 +10,10 @@ import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReq | ||||
| 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.ProductConstants; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||||
| import com.baomidou.mybatisplus.core.toolkit.support.SFunction; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
| import org.apache.poi.ss.formula.functions.T; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| @@ -26,7 +23,7 @@ import java.util.Set; | ||||
| public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||||
|  | ||||
|     /** | ||||
|      * 获取 商品 SPU 分页列表数据 | ||||
|      * 获取商品 SPU 分页列表数据 | ||||
|      * | ||||
|      * @param reqVO 分页请求参数 | ||||
|      * @return 商品 SPU 分页列表数据 | ||||
| @@ -43,11 +40,14 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取库存小于value且状态不等于status的的个数 | ||||
|      * 获取库存小于 value ,且状态不等于 status 的的个数 | ||||
|      * | ||||
|      * @return 个数 | ||||
|      */ | ||||
|     default Long selectCountByStockAndStatus() { | ||||
|         LambdaQueryWrapperX<ProductSpuDO> queryWrapper = new LambdaQueryWrapperX<>(); | ||||
|         queryWrapper.le(ProductSpuDO::getStock, ProductConstants.ALERT_STOCK) | ||||
|                 // TODO @puhui999:IN 另外两个状态,会不会好点哈。尽量不用 != | ||||
|                 // 如果库存触发警戒库存且状态为回收站的话则不计入触发警戒库存的个数 | ||||
|                 .and(q -> q.ne(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus())); | ||||
|         return selectCount(queryWrapper); | ||||
| @@ -111,6 +111,7 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||||
|         return selectList(queryWrapper); | ||||
|     } | ||||
|  | ||||
|     // TODO @puhui999:应该不太适合 validate 验证,应该是补充条件,例如说 appendTabQuery | ||||
|     /** | ||||
|      * 验证选项卡类型构建条件 | ||||
|      * | ||||
| @@ -118,8 +119,8 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> { | ||||
|      * @param queryWrapper 查询条件 | ||||
|      */ | ||||
|     static void validateTabType(Integer tabType, LambdaQueryWrapperX<ProductSpuDO> queryWrapper) { | ||||
|         // 出售中商品 TODO puhui999:这样好点 | ||||
|         if (ObjectUtil.equals(ProductSpuPageTabEnum.FOR_SALE.getType(), tabType)) { | ||||
|             // 出售中商品 | ||||
|             queryWrapper.eqIfPresent(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus()); | ||||
|         } | ||||
|         if (ObjectUtil.equals(ProductSpuPageTabEnum.IN_WAREHOUSE.getType(), tabType)) { | ||||
|   | ||||
| @@ -111,6 +111,7 @@ public class ProductCategoryServiceImpl implements ProductCategoryService { | ||||
|         if (Objects.equals(id, ProductConstants.PARENT_ID_NULL)) { | ||||
|             return 0; | ||||
|         } | ||||
|         // TODO @puhui999:for 的原因,是因为避免脏数据,导致可能的死循环。一般不会超过 100 层哈 | ||||
|         int level = 1; | ||||
|         // fix: 循环次数不确定改为while循环 | ||||
|         while (true){ | ||||
|   | ||||
| @@ -1,29 +1,24 @@ | ||||
| package cn.iocoder.yudao.module.product.service.spu; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| 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.mybatis.core.query.LambdaQueryWrapperX; | ||||
| 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.sku.vo.ProductSkuRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; | ||||
| import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; | ||||
| 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; | ||||
| import cn.iocoder.yudao.module.product.enums.ProductConstants; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; | ||||
| import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; | ||||
| import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; | ||||
| import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | ||||
| import cn.iocoder.yudao.module.product.service.property.bo.ProductPropertyValueDetailRespBO; | ||||
| import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; | ||||
| import org.springframework.context.annotation.Lazy; | ||||
| import org.springframework.stereotype.Service; | ||||
| @@ -32,7 +27,6 @@ import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.*; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; | ||||
| @@ -63,11 +57,11 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|     @Override | ||||
|     @Transactional(rollbackFor = Exception.class) | ||||
|     public Long createSpu(ProductSpuCreateReqVO createReqVO) { | ||||
|         // 校验分类 TODO puhui999:暂不清楚为什么只能选择第三层的结点;芋艿:改成二级分类,因为商品只能放在叶子节点级别;fix | ||||
|         // 校验分类、品牌 | ||||
|         validateCategory(createReqVO.getCategoryId()); | ||||
|         brandService.validateProductBrand(createReqVO.getBrandId()); | ||||
|         List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus(); | ||||
|         // 校验 SKU | ||||
|         List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus(); | ||||
|         productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType()); | ||||
|         ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); | ||||
|         // 初始化 SPU 中 SKU 相关属性 | ||||
| @@ -86,13 +80,13 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|     public void updateSpu(ProductSpuUpdateReqVO updateReqVO) { | ||||
|         // 校验 SPU 是否存在 | ||||
|         validateSpuExists(updateReqVO.getId()); | ||||
|         // 校验分类 | ||||
|         // 校验分类、品牌 | ||||
|         validateCategory(updateReqVO.getCategoryId()); | ||||
|         // 校验品牌 | ||||
|         brandService.validateProductBrand(updateReqVO.getBrandId()); | ||||
|         // 校验SKU | ||||
|         List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = updateReqVO.getSkus(); | ||||
|         productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType()); | ||||
|         // TODO @puhui999:可以校验逻辑,和更新逻辑,中间有个空行,这样会发现,哟 这里到了关键逻辑啦,更有层次感 | ||||
|         // 更新 SPU | ||||
|         ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); | ||||
|         initSpuFromSkus(updateObj, skuSaveReqList); | ||||
| @@ -111,17 +105,19 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|     private void initSpuFromSkus(ProductSpuDO spu, List<ProductSkuCreateOrUpdateReqVO> skus) { | ||||
|         // 断言,避免告警 | ||||
|         assert skus.size() > 0; | ||||
|         // 获取sku单价最低的商品 | ||||
|         // 获取 sku 单价最低的商品 | ||||
|         // TODO @puhui999:vo 改成 sku 会更好。vo dto 只是我们用来区分的,如果能区分的情况下,用更明确的名字会更好。 | ||||
| //        CollectionUtils.getMinValue(); TODO @puhui999:可以用这个方法,常见的 stream 操作,封装成方法,让逻辑更简洁 | ||||
|         ProductSkuCreateOrUpdateReqVO vo = skus.stream().min(Comparator.comparing(ProductSkuCreateOrUpdateReqVO::getPrice)).get(); | ||||
|         // sku单价最低的商品的价格 | ||||
|         // sku 单价最低的商品的价格 | ||||
|         spu.setPrice(vo.getPrice()); | ||||
|         // sku单价最低的商品的市场价格 | ||||
|         // sku 单价最低的商品的市场价格 | ||||
|         spu.setMarketPrice(vo.getMarketPrice()); | ||||
|         // sku单价最低的商品的成本价格 | ||||
|         spu.setCostPrice(vo.getCostPrice()); | ||||
|         // sku单价最低的商品的条形码 | ||||
|         spu.setBarCode(vo.getBarCode()); | ||||
|         // skus库存总数 | ||||
|         // skus 库存总数 | ||||
|         spu.setStock(getSumValue(skus, ProductSkuCreateOrUpdateReqVO::getStock, Integer::sum)); | ||||
|         // 若是 spu 已有状态则不处理 | ||||
|         if (spu.getStatus() == null) { | ||||
| @@ -233,6 +229,7 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|         } | ||||
|         // 查询商品 SKU | ||||
|         List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId()); | ||||
|         // TODO @puhui999:感觉还是查询好 productPropertyValueService,然后 propertyId 可以交给 convert 处理下即可。 | ||||
|         return ProductSpuConvert.INSTANCE.convertForSpuDetailRespVO(spu, skus, productPropertyValueService::getPropertyValueDetailList); | ||||
|     } | ||||
|  | ||||
| @@ -249,7 +246,6 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|  | ||||
|     @Override | ||||
|     public Map<Integer, Long> getTabsCount() { | ||||
|         // TODO @puhui999:map =》;尽量避免出现 map 这种命名,无命名含义哈 fix | ||||
|         Map<Integer, Long> counts = new HashMap<>(ProductConstants.SPU_TAB_COUNTS); | ||||
|         // 查询销售中的商品数量 | ||||
|         counts.put(ProductSpuPageTabEnum.FOR_SALE.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus())); | ||||
| @@ -258,8 +254,6 @@ public class ProductSpuServiceImpl implements ProductSpuService { | ||||
|         // 查询售空的商品数量 | ||||
|         counts.put(ProductSpuPageTabEnum.SOLD_OUT.getType(), productSpuMapper.selectCount(ProductSpuDO::getStock, 0)); | ||||
|         // 查询触发警戒库存的商品数量 | ||||
|         // TODO @puhui999:要有空格;, productSpuMapper fix | ||||
|         // TODO @puhui999:Service 不要有 Mapper 的逻辑;想想咋抽象一下哈 fix:调整为在 productSpuMapper 中书写逻辑 | ||||
|         counts.put(ProductSpuPageTabEnum.ALERT_STOCK.getType(), productSpuMapper.selectCountByStockAndStatus()); | ||||
|         // 查询回收站中的商品数量 | ||||
|         counts.put(ProductSpuPageTabEnum.RECYCLE_BIN.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus())); | ||||
|   | ||||
| @@ -40,10 +40,7 @@ public class ProductCategoryServiceImplTest extends BaseDbUnitTest { | ||||
|     @Test | ||||
|     public void testCreateCategory_success() { | ||||
|         // 准备参数 | ||||
|         ProductCategoryCreateReqVO reqVO = randomPojo(ProductCategoryCreateReqVO.class,o -> { | ||||
|             // 设置PC端图片可为空 TODO 数据库没有这个字段 | ||||
|             //o.setBigPicUrl(null); | ||||
|         }); | ||||
|         ProductCategoryCreateReqVO reqVO = randomPojo(ProductCategoryCreateReqVO.class); | ||||
|  | ||||
|         // mock 父类 | ||||
|         ProductCategoryDO parentProductCategory = randomPojo(ProductCategoryDO.class, o -> { | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package cn.iocoder.yudao.module.product.service.sku; | ||||
|  | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.framework.test.core.util.AssertUtils; | ||||
| import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; | ||||
| @@ -50,6 +49,7 @@ public class ProductSkuServiceTest extends BaseDbUnitTest { | ||||
|     @MockBean | ||||
|     private ProductPropertyValueService productPropertyValueService; | ||||
|  | ||||
|     // TODO @puhui999:是不是可以删除这 2 方法 | ||||
|     public Long generateId() { | ||||
|         return RandomUtil.randomLong(100000, 999999); | ||||
|     } | ||||
|   | ||||
| @@ -6,13 +6,12 @@ import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||
| import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.category.vo.ProductCategoryCreateReqVO; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.sku.vo.ProductSkuCreateOrUpdateReqVO; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; | ||||
| import cn.iocoder.yudao.module.product.controller.app.spu.vo.AppProductSpuPageReqVO; | ||||
| 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.ProductSpuRespVO; | ||||
| import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuUpdateReqVO; | ||||
| 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; | ||||
| import cn.iocoder.yudao.module.product.enums.spu.ProductSpuPageTabEnum; | ||||
| @@ -23,21 +22,20 @@ import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; | ||||
| import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | ||||
| import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; | ||||
| import com.google.common.collect.Lists; | ||||
| import org.apache.poi.ss.formula.functions.T; | ||||
| import org.junit.jupiter.api.Assertions; | ||||
| import org.junit.jupiter.api.Disabled; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.Mockito; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
| import org.springframework.context.annotation.Import; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.math.RoundingMode; | ||||
| import java.util.*; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | ||||
| import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||||
| @@ -83,6 +81,8 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { | ||||
|  | ||||
|     public int generaInt(){return RandomUtil.randomInt(1,9999999);} | ||||
|  | ||||
|     // TODO @芋艿:单测后续 review 哈 | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateSpu_success() { | ||||
|         // 准备参数 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV