首页 > 其他分享 >谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)

时间:2023-01-27 10:31:44浏览次数:51  
标签:SKU return String -- List SPU new 采购 id


分组与属性关联、发布商品、仓库服务

  • ​​分组与属性关联​​
  • ​​显示属性​​
  • ​​移除属性​​
  • ​​查询分组未关联的属性​​
  • ​​添加属性关联​​
  • ​​发布商品​​
  • ​​调试会员等级接口​​
  • ​​获取分类关联的品牌​​
  • ​​获取分类下所有分组&关联属性​​
  • ​​商品新增vo抽取​​
  • ​​商品新增业务流程分析​​
  • ​​检索功能​​
  • ​​仓库服务​​
  • ​​整合ware服务&获取仓库列表​​
  • ​​查询库存&创建采购需求​​
  • ​​合并采购需求​​
  • ​​领取采购单​​
  • ​​完成采购​​
  • ​​显示商品库存中的sku_name​​

分组与属性关联

显示属性

这里其实就是一个分布查询,流程如下:

  1. 点击分组属性的时候获取到分组id,
  2. 拿分组id去关联表查分组id对应的attr_id
  3. 拿​​attr_id​​去pms_attr表中获取属性

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_java

controller

/**
* 3.获取属性分组的关联的所有属性
*/
@RequestMapping("/{attrgroupId}/attr/relation")
public R attrRelation(@PathVariable("attrgroupId") Long attrgroupId) {
List<AttrEntity> entities = attrService.getRelationAttr(attrgroupId);
return R.ok().put("data", entities);
}

service

@Override
public List<AttrEntity> getRelationAttr(Long attrgroupId) {
//分布查询,第一步去关联表中查出所有的组和属性id
List<AttrAttrgroupRelationEntity> entities = relationService.list(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id",attrgroupId));

//第二收集属性id
List<Long> attrIds = entities.stream().map((attr) -> {
return attr.getAttrId();
}).collect(Collectors.toList());

List<AttrEntity> list = this.listByIds(attrIds);
return list;
}

测试

属性显示成功

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_java_02

移除属性

这里为了方便,我们直接写一个批量删除的接口

controller

  • /product/attrgroup/attr/relation/delete
  • post请求会带来json数据,要封装成自定义对象vos需要@RequestBody注解
  • 意思就是将请求体中的数据封装成vos
/**
* 4.移除属性分组和属性的关系
*/
@PostMapping("/attr/relation/delete")
public R deleteRelation(@RequestBody AttrGroupRelationVo[] vos) {
attrService.deleteRelation(vos);
return R.ok();
}

service

@Override
public void deleteRelation(AttrGroupRelationVo[] vos) {
List<AttrAttrgroupRelationEntity> entities = Arrays.asList(vos).stream().map((item) -> {
AttrAttrgroupRelationEntity entity = new AttrAttrgroupRelationEntity();
BeanUtils.copyProperties(item, entity);
return entity;
}).collect(Collectors.toList());
relation.deleteBatchRelation(entities);
}

mapper

void deleteBatchRelation(@Param("entities") List<AttrAttrgroupRelationEntity> entities);

<delete id="deleteBatchRelation">
DELETE FROM `pms_attr_attrgroup_relation` where
<foreach collection="entities" item="item" separator="OR">
(attr_id = #{item.attrId} AND attr_group_id = #{item.attrGroupId})
</foreach>
</delete>

查询分组未关联的属性

逻辑分析

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_封装_03

controller

/**
* 5.获取属性分组没有关联的所有属性
* /product/attrgroup/{attrgroupId}/noattr/relation
*/
@RequestMapping("/{attrgroupId}/noattr/relation")
public R attrNoRelation(@RequestParam Map<String, Object> params,
@PathVariable("attrgroupId") Long attrgroupId) {
PageUtils page = attrService.getNoRelationAttr(params,attrgroupId);
return R.ok().put("page", page);
}

service

认真看注释,认真理解,还是很绕的

查询分组未关联的数据三步!

  1. 获得当前分类下的所有分组
  2. 获得这些分组下所有已添加的属性
  3. 添加新属性时移除这些已添加的属性
@Override
public PageUtils getNoRelationAttr(Map<String, Object> params, Long attrgroupId) {

/**
* 1.当前分组只能关联自己所属的分类里面的所有属性
*/
AttrGroupEntity attrGroupEntity = attrGroupService.getById(attrgroupId);
Long catelogId = attrGroupEntity.getCatelogId();

/**
* 2 .当前分组只能引用别的分组没有引用的属性
* 2.1 当前分类下的所有分组
* 2.2 这些分组关联的属性
* 2.3 从当前分类的所有属性中移除这些属性
*/

/**
* 2.1 当前分类下的所有分组。收集到他们的组id
*/
List<AttrGroupEntity> group = attrGroupService.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));

List<Long> collectGroupIds = group.stream().map((item) -> {
return item.getAttrGroupId();
}).collect(Collectors.toList());

/**
* 2.2 收集到分组的所有属性
* (1)拿着上一步收集到的组id到关系表中查找关系表实体类对象,
* (2)通过关系表实体类对象获得所有分组下的所有属性id
*/
List<AttrAttrgroupRelationEntity> groupId = relationService.list(new QueryWrapper<AttrAttrgroupRelationEntity>().in("attr_group_id", collectGroupIds));
List<Long> attrIds = groupId.stream().map((item) -> {
return item.getAttrId();
}).collect(Collectors.toList());

/**
* 2.3 从当前分类的所有属性中移除这些属性并筛选出基本属性(where attr_type = 1)
*/
QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>().eq("catelog_id", catelogId).eq("attr_type",ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());
//如果其他分组也没关联属性,那么就不加这个条件
if (attrIds != null && attrIds.size() > 0){
wrapper.notIn("attr_id", attrIds);
}

/**
* 分页多条件查询
* where (`attr_id` = ? or `attr_name` like ?)
*/
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
wrapper.and((w) -> {
w.eq("attr_id", key).or().like("attr_name", key);
});
}


/**
* page方法需要两个参数
* 1.IPage对象(通过工具类Query获取并通过.getPage(params)封装页面传来分页参数)
* 2.wrapper(自己生成)
*/
IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), wrapper);
PageUtils pageUtils = new PageUtils(page);
return pageUtils;
}

​tips:​

注意非空判断

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_List_04

测试

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_封装_05

给销售属性绑定分组,把9号属性绑定给1号分组

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_java_06

查询分组未关联的属性

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_07

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_json_08

添加属性关联

常规的调用,注意点是saveBatch传的参数是数据对应的实体类

我们想传其他​​vo​​​时,需要对这个方法进行一个​​重写​

最后也是通过把vo的值赋给对应实体类,在调用相应批量保存

controller

/**
* 6.添加属性与分组关联关系
* /product/attrgroup/attr/relation
*/
@PostMapping("/attr/relation")
public R addRelation(@RequestBody List<AttrGroupRelationVo> vos) {
relationService.saveBatch(vos);
return R.ok();
}

service

@Override
public void saveBatch(List<AttrGroupRelationVo> vos) {
List<AttrAttrgroupRelationEntity> collect = vos.stream().map((item) -> {
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
BeanUtils.copyProperties(item, relationEntity);
return relationEntity;
}).collect(Collectors.toList());
this.saveBatch(collect);
}

发布商品

调试会员等级接口

启动会员微服务,添加网关,添加前端页面…

添加如下会员:

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_09

获取分类关联的品牌

controller

/**
* 1.获取分类关联的品牌
* /product/categorybrandrelation/brands/list
*/
@GetMapping("/brands/list")
public R relationBrandList(@RequestParam(value = "catId", required = true) Long catId) {
List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);
//品牌对象集合在进行筛选,赋予品牌对象id和name,返回封装的vo给前端
List<BrandVo> collect = vos.stream().map(item -> {
BrandVo brandVo = new BrandVo();
brandVo.setBrandId(item.getBrandId());
brandVo.setBrandName(item.getName());
return brandVo;
}).collect(Collectors.toList());
return R.ok().put("data",collect);
}

service

@Override
public List<BrandEntity> getBrandsByCatId(Long catId) {
//获得CategoryBrandRelationEntity集合对象
List<CategoryBrandRelationEntity> catelogId = relationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));
//获得所有集合对象中brandid,通过brandService查询所有品牌,封装成品牌对象集合
List<BrandEntity> collect = catelogId.stream().map(item -> {
Long brandId = item.getBrandId();
BrandEntity entity = brandService.getById(brandId);
return entity;
}).collect(Collectors.toList());
//返回品牌对象集合
return collect;
}

测试

开发规范

  1. Controller:处理请求,接受和校验数据
  2. Service接受controlLer传来的数据,进行业务处理
  3. Controller接受service处理完的数据,封装页面指定的vo

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_java_10

获取分类下所有分组&关联属性

接口功能如下

也就是说当我们选择手机分类时,那就查出手机相关的分组信息,并查出每个分组相应属性信息

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_json_11

vo

@Data
public class AttrGroupWithAttrsVo {
/**
* 分组id
*/
@TableId
private Long attrGroupId;
/**
* 组名
*/
private String attrGroupName;
/**
* 排序
*/
private Integer sort;
/**
* 描述
*/
private String descript;
/**
* 组图标
*/
private String icon;
/**
* 所属分类id
*/
private Long catelogId;

private List<AttrEntity> attrs;
}

controller

/**
* 7.获取分类下所有分组&关联属性
* /product/attrgroup/{catelogId}/withattr
*/
@GetMapping("/{catelogId}/withattr")
public R getAttrGroupWithAttrs(@PathVariable("catelogId") Long catelogId) {
List<AttrGroupWithAttrsVo> vos = attrGroupService.getAttrGroupWithAttrsByCatelogId(catelogId);
return R.ok().put("data",vos);
}

service

vo的重要性:

  1. vo(value object)当相应数据需要自定义时,用vo是最好的选择,不需要对实体类字段进行修改

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_12

/**
* 获取分类下的所有分组及属性
* @param catelogId
* @return
*/
@Override
public List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {
/** 1.获取分类下的所有分组,封装成集合
* 分类和组的关系在pms_group表中,所以(where catelog_id = ?)即可查出分类对应的组
* 由于这是mp,它会得出所有的这种关系,并把结果封装成集合
*/
List<AttrGroupEntity> list = this.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));

/** 2.获得分组下的属性
* 要第三张关联表,直接调用关联表的service即查询分组对应的属性id
* 获得属性id在去调用属性表的service即可查询属性名
* 以上两步前面已经写好逻辑了直接调用即可attrService.getRelationAttr(groupId)
*/
List<AttrGroupWithAttrsVo> collect = list.stream().map((item) -> {
AttrGroupWithAttrsVo attrGroupWithAttrsVo = new AttrGroupWithAttrsVo();
BeanUtils.copyProperties(item, attrGroupWithAttrsVo);
List<AttrEntity> attrs = attrService.getRelationAttr(attrGroupWithAttrsVo.getAttrGroupId());
if (attrs != null) {
attrGroupWithAttrsVo.setAttrs(attrs);
}
return attrGroupWithAttrsVo;
}).filter((attrvo) -> {
return attrvo.getAttrs() != null && attrvo.getAttrs().size() > 0;
}).collect(Collectors.toList());
return collect;
}

测试

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_List_13

商品新增vo抽取

设置完属性,点击保存之后取消保存,复制控制台输出

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_14

​[在线JSON字符串转Java实体类(JavaBean、Entity)-BeJSON.com](https://www.bejson.com/json2javapojo/new/)​

直接解析json数据封装成实体类

这里我简单截取一个主要的Vo

此Vo包括每个步骤所携带的数据,有的是单个字段有的是一个集合

逻辑不难,难点是要理清逻辑,注意细节!

@Data
public class SpuSaveVo {

@NotEmpty(groups = {AddGroup.class})
private String spuName;
private String spuDescription;
@NotEmpty(groups = {AddGroup.class})
private Long catalogId;
@NotEmpty(groups = {AddGroup.class})
private Long brandId;
private double weight;
private int publishStatus;
private List<String> decript;
private List<String> images;
private Bounds bounds;
@NotEmpty(groups = {AddGroup.class})
private List<BaseAttrs> baseAttrs;
@NotEmpty(groups = {AddGroup.class})
private List<Skus> skus;

}

商品新增业务流程分析

逻辑很简单那,就是把​​数据​​​保存到​​多张表​​中

因为这个​​Vo​​收集的数据很多,包括每个步骤你所选择的数据

1.保存spu基本信息 pms_spu_info

因为所有传来的信息都在vo里,所以我们把信息拷贝到对应的实体类中,如果vo没有的那就可以自己赋值

表结构如下:

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_java_15

这里的infoEntity.setCreateTime(new Date());infoEntity.setUpdateTime(new Date());是因为前端传入的是没有这两个字段的,我们自己赋值即可

SpuInfoEntity infoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(vo, infoEntity);
infoEntity.setCreateTime(new Date());
infoEntity.setUpdateTime(new Date());
this.saveBaseInfo(infoEntity);

2.保存spu的描述图片 pms_spu_info_desc
保存哪个数据到哪个表,就注入那个service

String.join()的作用是把集合中的元素通过","分割形成一个一个的字符串

List<String> decript = vo.getDecript();
SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
descEntity.setSpuId(infoEntity.getId());
descEntity.setDecript(String.join(",", decript));
spuInfoDescService.saveSpuInfoDesc(descEntity);

3.保存spu的图片集 pms_spu_images
从vo中获取所有图片集合
调用图片service进行保存,保存只需要两个点
图片id和url地址,传入对象即可

List<String> images = vo.getImages();
imagesService.saveImages(infoEntity.getId(), images);

4.保存spu的规格参数 pms_product_attr_value
从vo中获取所有规格参数集合
对规格参数集合进行遍历,设置每项的属性

List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
List<ProductAttrValueEntity> collect = baseAttrs.stream().map((attr) -> {
ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
valueEntity.setAttrId(attr.getAttrId());
AttrEntity id = attrService.getById(attr.getAttrId());
valueEntity.setAttrName(id.getAttrName());
valueEntity.setAttrValue(attr.getAttrValues());
valueEntity.setQuickShow(attr.getShowDesc());
valueEntity.setSpuId(infoEntity.getId());
return valueEntity;
}).collect(Collectors.toList());
attrValueService.saveProductAttr(collect);

5.保存spu的积分信息 mall_sms -> sms_spu_bounds

Bounds bounds = vo.getBounds();
SpuBoundTo spuBoundTo = new SpuBoundTo();
BeanUtils.copyProperties(bounds, spuBoundTo);
spuBoundTo.setSpuId(infoEntity.getId());
R r0 = couponFeignService.saveSpuBounds(spuBoundTo);
if (r0.getCode() != 0) {
log.error("远程保存spu积分信息异常");
}
couponFeignService.saveSpuBounds(spuBoundTo);

6.保存当前spu对应的所有sku信息;

//6.1sku的基本信息;pms_sku_info
List<Skus> skus = vo.getSkus();
if (skus != null && skus.size() > 0) {
skus.forEach(item -> {
String defalutImg = "";
for (Images image : item.getImages()) {
if (image.getDefaultImg() == 1) {
defalutImg = image.getImgUrl();
}
}
SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
BeanUtils.copyProperties(item, skuInfoEntity);
//添加vo中没有的信息
skuInfoEntity.setBrandId(infoEntity.getBrandId());
skuInfoEntity.setCatalogId(infoEntity.getCatalogId());
skuInfoEntity.setSaleCount(0L);
skuInfoEntity.setSpuId(infoEntity.getId());
skuInfoEntity.setSkuDefaultImg(defalutImg);
skuInfoService.saveSkuInfo(skuInfoEntity);

//6.2sku图片信息;pms_sku_images
//没有图片路径的无需保存
Long skuId = skuInfoEntity.getSkuId();
List<SkuImagesEntity> imageEntities = item.getImages().stream().map(img -> {
SkuImagesEntity skuImagesEntity = new SkuImagesEntity();

skuImagesEntity.setSkuId(skuId);
skuImagesEntity.setImgUrl(img.getImgUrl());
skuImagesEntity.setDefaultImg(img.getDefaultImg());

return skuImagesEntity;
}).filter(entity -> {
return !StringUtils.isEmpty(entity.getImgUrl());
}).collect(Collectors.toList());
skuImagesService.saveBatch(imageEntities);

//6.3sku的销售属性;pms_sku_sale_attr_value
List<Attr> attr = item.getAttr();
List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {
SkuSaleAttrValueEntity attrValueEntity = new SkuSaleAttrValueEntity();
BeanUtils.copyProperties(a, attrValueEntity);
attrValueEntity.setSkuId(skuId);

return attrValueEntity;
}).collect(Collectors.toList());
skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);

//6.4sku的优惠满减信息(跨服务);
SkuReductionTo skuReductionTo = new SkuReductionTo();
BeanUtils.copyProperties(item, skuReductionTo);
skuReductionTo.setSkuId(skuId);
if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1) {
R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
if (r1.getCode() != 0) {
log.error("远程保存spu积分信息异常");
}
}

});
}

测试

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_16

检索功能

也就是多条件分页查询,很常见的功能!

spu检索

controller

/**
* 列表
*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = spuInfoService.queryPageByCondition(params);

return R.ok().put("page", page);
}

service

@Override
public PageUtils queryPageByCondition(Map<String, Object> params) {
QueryWrapper<SpuInfoEntity> queryWrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
//等价sql: status=1 and (id=1 or spu_name like xxx)
queryWrapper.and((w) -> {
w.eq("id", key).or().like("spu_name", key);
});
}
String status = (String) params.get("status");
if (!StringUtils.isEmpty(status)) {
queryWrapper.eq("publish_status", status);
}
String brandId = (String) params.get("brandId");
if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)) {
queryWrapper.eq("brand_id", brandId);
}
String catelogId = (String) params.get("catelogId");
if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {
queryWrapper.eq("catalog_id", catelogId);
}
IPage<SpuInfoEntity> page = this.page(
new Query<SpuInfoEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}

sku检索

controller

/**
* 列表
*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = skuInfoService.queryPageByParams(params);

return R.ok().put("page", page);
}

service

@Override
public PageUtils queryPageByParams(Map<String, Object> params) {
QueryWrapper<SkuInfoEntity> queryWrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
queryWrapper.and((w) -> {
w.eq("sku_id", key).or().like("sku_name", key);
});
}
String catelogId = (String) params.get("catelogId");
if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {
queryWrapper.eq("catalog_id", catelogId);
}
String brandId = (String) params.get("brandId");
if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)) {
queryWrapper.eq("brand_id", brandId);
}
String max = (String) params.get("max");
if (!StringUtils.isEmpty(max)) {
try {
BigDecimal bigDecimal = new BigDecimal(max);
if (bigDecimal.compareTo(new BigDecimal("0")) == 1) {
queryWrapper.le("price", max);
}
} catch (Exception e) {
}
}
String min = (String) params.get("min");
if (!StringUtils.isEmpty(min)) {
queryWrapper.ge("price", min);
}

IPage<SkuInfoEntity> page = this.page(
new Query<SkuInfoEntity>().getPage(params),
queryWrapper
);
return new PageUtils((page));
}

仓库服务

整合ware服务&获取仓库列表

  1. 加入微服务注册中心
  2. 加入网关

获取仓库列表就是对仓库表的简单查询,逆向生成代码以帮我们生成好,只要配置好网关就可以直接显示

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_List_17

我们只要记住,反是单表操作的逆向生成以帮我们生成好了,我们能拿来直接用,就像增加仓库、删除、修改都是可以直接用的!

多条件分页查询

@Override
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<WareInfoEntity> queryWrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)) {
queryWrapper.eq("id", key)
.or().like("name", key)
.or().like("address", key)
.or().like("areacode", key);
}
IPage<WareInfoEntity> page = this.page(
new Query<WareInfoEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}

多条件查询都是一样的套路,获得你搜索的key,然后拿这个key去模糊匹配多个字段

比如这里拿你输入的key会在name、address、areacode做模糊查询,条件直接通过or来拼接

查询库存&创建采购需求

查询库存

查询库存也是单表操作,CRUD都帮我们做好了,我们就在分页的基础上加上多条件查询即可

//多条件分页查询
@Override
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
String skuId = (String) params.get("skuId");
if (!StringUtils.isEmpty(skuId)) {
queryWrapper.eq("sku_id", skuId);
}
String wareId = (String) params.get("wareId");
if (!StringUtils.isEmpty(wareId)) {
queryWrapper.eq("ware_id", wareId);
}
IPage<WareSkuEntity> page = this.page(
new Query<WareSkuEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}

创建采购需求

同上都是单表操作,我们只需要做采购需求的多条件分页查询

@Override
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<PurchaseDetailEntity> queryWrapper = new QueryWrapper<PurchaseDetailEntity>();
String key = (String)params.get("key");
if(!StringUtils.isEmpty(key)){
queryWrapper.and(w->{
w.eq("purchase_id",key).or().eq("sku_id",key);
});
}
String status = (String)params.get("status");
if(!StringUtils.isEmpty(status)) {
queryWrapper.eq("status",status);
}
String wareId = (String)params.get("wareId");
if(!StringUtils.isEmpty(wareId)) {
queryWrapper.eq("ware_id",wareId);
}
IPage<PurchaseDetailEntity> page = this.page(
new Query<PurchaseDetailEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}

合并采购需求

合并逻辑:

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_json_18

1.创建采购单

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_19

2.合并请求接口

这里有两种情况如下:

  • 如果没有选中采购单,那么会自动创建采购单进行合并
  • 有的话,就用采购单id

controller

/**
* 合并采购单
*/
@PostMapping("/merge")
public R merge(@RequestBody MergeVo mergeVo) {
boolean flag = purchaseService.mergePurchase(mergeVo);
if(flag){
return R.ok();
}else {
return R.error().put("msg","请选择新建或已分配的采购需求");
}
}

VO如下:

@Data
public class MergeVo {

private Long purchaseId;

private List<Long> items;
}

impl

实际上就是创建完采购需求对象和采购单对象后,点击合并,这两个对象信息会发生变化,整体就是做这些操作

具体的看注释,这里还用到了一些枚举类的写法,通过枚举类获得状态信息,了解即可,这里就不写了,可以去看老师的源码

@Transactional
@Override
public boolean mergePurchase(MergeVo mergeVo) {
//一、获取Vo中的信息
//如果指定了采购单,那就获取采购单的id
Long purchaseId = mergeVo.getPurchaseId();
//获得采购需求的id
List<Long> items = mergeVo.getItems();

//二、过滤采购需求
//对采购需求id进行过滤,如果采购需求处于新建或者已分配的收集成新的集合
//这样做的目的是为了进行筛选,如果你选中正在采购的是不会被合并的
List<Long> collect = items.stream()
.filter(i -> {
//通过采购需求的id获取采购需求实体类
PurchaseDetailEntity purchaseDetailEntity = purchaseDetailService.getById(i);
if (purchaseDetailEntity.getStatus() == WareConstant.PurchaseDetailStatusEnum.CREATED.getCode()
|| purchaseDetailEntity.getStatus() == WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode()) {
return true;
} else {
return false;
}
}
).collect(Collectors.toList());

//三、没有指定采购单逻辑和指定了的逻辑
if (collect != null && collect.size() > 0) {
//3.1如果没有指定采购单,那就自动创建一个
if (purchaseId == null) {
PurchaseEntity purchaseEntity = new PurchaseEntity();
//如果是新创建的采购单,创建时间更新时间,状态都是没有默认值的所以这默认值我们自己来赋值
purchaseEntity.setCreateTime(new Date());
purchaseEntity.setUpdateTime(new Date());
//这里设置采购单的状态采用的是枚举类的形式获取
purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATED.getCode());
this.save(purchaseEntity);
//获得自动创建的采购单id
purchaseId = purchaseEntity.getId();
}

/** 3.2指定采购单了,逻辑如下
* 1.采购单id为Vo中获取的指定id
* 2.设置所有的采购需求对象并收集成对象
*/
Long finalPurchaseId = purchaseId;
List<PurchaseDetailEntity> collect1 = collect.stream().map(i -> {
//获取所有的采购需求对象
//更新采购需求的状态,一共需要该两个点,一个是采购状态,一个是采购单id。设置采购需求的id是为了区分是哪一个进行了更改
PurchaseDetailEntity purchaseDetailEntity = purchaseDetailService.getById(i);
purchaseDetailEntity.setPurchaseId(finalPurchaseId);
purchaseDetailEntity.setId(i);
purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());
return purchaseDetailEntity;
}).collect(Collectors.toList());

//批量更改采购需求,这里是MP里的接口,可直接传入对象,MP会自动读取里面的ID
purchaseDetailService.updateBatchById(collect1);

//四、优化时间更新,为了显示的时间符合我们的样式
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setId(purchaseId);
purchaseEntity.setUpdateTime(new Date());

//五、更新采购单
return this.updateById(purchaseEntity);
} else {
return false;
}
}

领取采购单

这里我们只用写好接口的功能,这个请求一般是由app来进行发送

controller

/**
* 领取采购单
*/
@PostMapping("/received")
public R received(@RequestBody List<Long> ids){
purchaseService.received(ids);
return R.ok();
}

impl

  1. 领取采购单,通过接口测试工具完成请求
  2. 领取玩采购单后,更改采购单状态和对应采购需求状态
  1. 采购单状态改为已领取
  2. 采购需求状态改为正在采购
@Override
public void received(List<Long> ids) {
//1.确认当前采购单状态
List<PurchaseEntity> collect = ids.stream().map(item -> {
//通过采购单id获取采购单对象
PurchaseEntity purchaseEntity = this.getById(item);
return purchaseEntity;
}).filter(id -> {
//对采购单对象进行过滤,如果状态为新建或者已分配的留下
if (id.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() ||
id.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {
return true;
} else {
return false;
}
}).map(item -> {
//对上面收集好的在进行过滤,改变采购单状态为已领取(RECEIVE)
item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
//对上面收集好的在进行过滤,改变采购单更新时间
item.setUpdateTime(new Date());
return item;
}).collect(Collectors.toList());

//2.批量修改改变采购单状态
this.updateBatchById(collect);

//3.改变采购需求中的状态
if (collect != null && collect.size() > 0) {
collect.forEach(item -> {
List<PurchaseDetailEntity> entities = purchaseDetailService.listDetailByPurchaseId(item.getId());
List<PurchaseDetailEntity> detailEntities = entities.stream().map(entity -> {
PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();
purchaseDetailEntity.setId(entity.getId());
//将采购需求中的状态改为正在采购
purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());
return purchaseDetailEntity;
}).collect(Collectors.toList());
purchaseDetailService.updateBatchById(detailEntities);
});
}
}

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_json_20

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_21

完成采购

controller

这里我们只用写好接口的功能,这个请求一般是由app来进行发送

/**
* 完成采购单
*/
@PostMapping("/done")
public R finished(@RequestBody PurchaseDoneVo doneVo){
purchaseService.done(doneVo);
return R.ok();
}

VO如下:
@Data
public class PurchaseDoneVo {
@NonNull
private Long id;

private List<PurchaseItemDoneVo> items;

public PurchaseDoneVo(){}
}

@Data
public class PurchaseItemDoneVo {

private Long itemId;

private Integer status;

private String reason;
}

impl

完成采购主要注意有几个地方发生了变化,做好逻辑的判断即可

代码如下:

/**
* 采购完成一共三地方会发生变化
* 1.采购单状态
* 2.库存增加
* 3.采购需求状态发生变化
* @param doneVo
*/
@Override
public void done(PurchaseDoneVo doneVo) {
//获取完成的是哪一个采购单
Long id = doneVo.getId();
//一、初始化
Boolean flag = true;
//获取采购单id集合
List<PurchaseItemDoneVo> items = doneVo.getItems();
//收集结果
List<PurchaseDetailEntity> updates = new ArrayList<>();

for (PurchaseItemDoneVo item : items) {
PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();
if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()) {
flag = false;
purchaseDetailEntity.setStatus(item.getStatus());
} else {
//二、采购需求状态发生变化
purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());
//由采购单的id获取采购需求对象,有什么用呢?是用来给增加库存时赋值用的
PurchaseDetailEntity entity = purchaseDetailService.getById(item.getItemId());
//三、库存增加
wareSkuService.addStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());
}
//采购完成,采购需求中的状态也会发生变化,给实体类对象指明id,从而修改对象的状态
purchaseDetailEntity.setId(item.getItemId());
//把要修改的采购需求对象放到集合里
updates.add(purchaseDetailEntity);
}
//因为一个采购单里有多个采购需求合并的,所以批量修改采购需求对象
purchaseDetailService.updateBatchById(updates);

//四.改变采购单状态
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setId(id);
purchaseEntity.setStatus(flag ? WareConstant.PurchaseStatusEnum.FINISH.getCode() :
WareConstant.PurchaseStatusEnum.HASERROR.getCode());
purchaseEntity.setUpdateTime(new Date());
this.updateById(purchaseEntity);
}

测试数据如下:

这里id = 6是对6号采购单发起操作,里面的item9和10是采购单对应的采购需求

{
"id":16,"items":[
{
"itemId":17,"status":3,"reason":""
},
{
"itemId":18,"status":4,"reason":"无货"
}
]
}

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_List_22

采购单状态如下:

有异常是因为我们有一个采购单没有采购完成

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_List_23

采购需求如下:

没有完成的采购需求会显示采购失败

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_封装_24

库存如下:

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_封装_25

显示商品库存中的sku_name

谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)_开发语言_26

怎么显示呢?锁定库存就是本表库存表相关的可以直接设置,而sku_name是mall-product微服务里才能查询的到的

那就写Feign接口呗,这里介绍两种feign接口的写法:

1.给远程调用的微服务发请求

@FeignClient("mall-product") 指定微服务
/product/skuinfo/info/{skuId}

2.给网关发请求

  • @FeignClient(“mall-gateway”)
  • /api/product/skuinfo/info/{skuId}
@FeignClient("mall-gateway")
public interface ProductFeignService {
@RequestMapping("/api/product/skuinfo/info/{skuId}")
public R info(@PathVariable("skuId") Long skuId);

}

增加库存的时候注入FeignService接口即可实现远程调用

这里采取了trycatch的形式来捕获异常,可以防止远程调用失败时,事务回滚

@Transactional
@Override
public void addStock(Long skuId, Long wareId, Integer skuNum) {
//判断如果没有此库存记录,则为新增操作;如果有则为更改操作
List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
WareSkuEntity wareSkuEntity = new WareSkuEntity();
wareSkuEntity.setSkuId(skuId);
wareSkuEntity.setStock(skuNum);
wareSkuEntity.setWareId(wareId);
wareSkuEntity.setStockLocked(0);
//TODO 远程查询sku的名字
//如果查询名字查询失败了,事务回滚有点不值得,所以用trycatch来捕捉一下
try {
R info = productFeignService.info(skuId);
Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
if (info.getCode() == 0){
wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));
}
} catch (Exception e) {
e.printStackTrace();
}

wareSkuDao.insert(wareSkuEntity);
} else {
wareSkuDao.addStock(skuId, wareId, skuNum);
}
}


标签:SKU,return,String,--,List,SPU,new,采购,id
From: https://blog.51cto.com/u_14519396/6023992

相关文章

  • 谷粒商城--整合Elasticsearch和商品的上架
    整合Elasticsearch和商品的上架​​一、整合ES​​​​ES常用概念​​​​索引,类型,文档是什么?​​​​倒排索引​​​​相关度分数score的计算​​​​安装ES和Kibana​​​......
  • Javase多态(对多态的理解、多态的体现)
    前言刚开始学Java的时候对多态这个概念一直没弄太明白,随着后面的学习无意中都在用着多态,又看了一些文章决定整理一下!举例创建一个Person类,Student类继承Person类,并分别创建......
  • 统一观测丨如何使用 Prometheus 监控 MySQL
     MySQL作为最流行的关系型数据库管理系统之一,非常多系统的后端存储都有着MySQL的身影,可谓是广泛应用于各行各业。与此同时,数据库作为应用服务的核心组件,直接影响着应......
  • 多项式模板
    多项式模板\(\text{导数运算法则}\)$(x\pmy)'=x'\pmy'$$(ax)'=ax'$(\(a\)为常数)\((xy)'=x'y+xy'\)$(\displaystyle\frac{x}{y})=\displaystyle......
  • 统一观测丨如何使用 Prometheus 监控 MySQL
     MySQL作为最流行的关系型数据库管理系统之一,非常多系统的后端存储都有着MySQL的身影,可谓是广泛应用于各行各业。与此同时,数据库作为应用服务的核心组件,直接影响着应......
  • 第五十七章 历史监视器 - 汇总
    第五十七章历史监视器-汇总汇总ApplicationMonitor执行的%Monitor.System.HistoryPerf和%Monitor.System.HistorySys类也在每天结束时创建每小时和每天的摘要。......
  • 【学习笔记】组合数学学习笔记
    参考资料:《组合数学》,OI-Wiki排列组合四个计数原理加法原理:并列的方案数加和。乘法原理:叠加的方案数相乘。减法原理:正难则反,补集转换。除法原理:目测用处不大......
  • ximo脱壳-手脱tElock 0.98b1壳、exe32pack壳、WinUpack加的壳
    一、手脱tElock0.98b1壳用最后一次异常法首先使用OD载入程序  然后不停的用shift+F9,直到程序运行起来,记录下程序运行起来的前一次的次数,然后执行到那一次 上图......
  • 以SQL SERVER和C#聊聊微软生态趋势中产品跨生态组合
    众所周知,老牌大厂微软intel都在裁人,国内短周期的互联网大厂(菊厂,狗厂,福报厂,开水团,鹅厂)更是如此。拿微软来说很多基础软件产品,SQLSERVER,C#开发语言,F#开发语言,Access,POWERBI......
  • IntelliJ中高效重构的 10 个快捷方式
    前言在日常的开发工作中,我们经常需要重构,重构可以让我们写出的代码更上一层楼。所以,我会借助IntelliJ提供的一些功能,帮助我高效进行重构。这里是我推荐10个快捷方式,也是我......