mall:review 商品接口

This commit is contained in:
YunaiV 2023-05-23 23:27:15 +08:00
parent fb7915c917
commit c62da6a056
23 changed files with 83 additions and 80 deletions

View File

@ -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 状态
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// TODO @puhui999class 类的开始和结束都要有一个空行哈
@Schema(description = "管理后台 - 品牌精简信息 Response VO")
@Data
@NoArgsConstructor

View File

@ -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;

View File

@ -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是不是可以抽到父类里
/**
* 属性数组
*/

View File

@ -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 @tuihui999get-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);
}
}

View File

@ -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;
// ========== 统计相关字段 =========

View File

@ -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 @puhui999swagger required example 写下
@Schema(description = "商品 SPU 编号")
private Long id;
@Schema(description = "商品销量")

View File

@ -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;
}

View File

@ -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 @puhui999swagger required example 写下
@Schema(description = "spuId")
private Long id;

View File

@ -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 @puhui999swagger required example 写下
@Schema(description = "主键")
private Long id;

View File

@ -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 @puhui999swagger required example 写下
@Schema(description = "商品编号", required = true, example = "1")
@NotNull(message = "商品编号不能为空")
private Long id;

View File

@ -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);

View File

@ -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 @puhui999if 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())

View File

@ -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);
}

View File

@ -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 @puhui999IN 另外两个状态会不会好点哈尽量不用 !=
// 如果库存触发警戒库存且状态为回收站的话则不计入触发警戒库存的个数
.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)) {

View File

@ -111,6 +111,7 @@ public class ProductCategoryServiceImpl implements ProductCategoryService {
if (Objects.equals(id, ProductConstants.PARENT_ID_NULL)) {
return 0;
}
// TODO @puhui999for 的原因是因为避免脏数据导致可能的死循环一般不会超过 100 层哈
int level = 1;
// fix: 循环次数不确定改为while循环
while (true){

View File

@ -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 @puhui999vo 改成 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 @puhui999map =尽量避免出现 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 @puhui999Service 不要有 Mapper 的逻辑想想咋抽象一下哈 fix调整为在 productSpuMapper 中书写逻辑
counts.put(ProductSpuPageTabEnum.ALERT_STOCK.getType(), productSpuMapper.selectCountByStockAndStatus());
// 查询回收站中的商品数量
counts.put(ProductSpuPageTabEnum.RECYCLE_BIN.getType(), productSpuMapper.selectCount(ProductSpuDO::getStatus, ProductSpuStatusEnum.RECYCLE.getStatus()));

View File

@ -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 -> {

View File

@ -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);
}

View File

@ -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() {
// 准备参数