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

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

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

分组与属性关联

显示属性

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

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

image-20220808231237643

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) {//分布查询,第一步去关联表中查出所有的组和属性idList<AttrAttrgroupRelationEntity> entities = relationService.list(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id",attrgroupId));//第二收集属性idList<Long> attrIds = entities.stream().map((attr) -> {return attr.getAttrId();}).collect(Collectors.toList());List<AttrEntity> list = this.listByIds(attrIds);return list;
}

测试

属性显示成功

image-20220811124827549

移除属性

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

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>

查询分组未关联的属性

逻辑分析

image-20220811172114595

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:

注意非空判断

image-20220811173223505

测试

image-20220811183248352

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

image-20220811183459861

查询分组未关联的属性

image-20220811183601207

image-20220811183612731

添加属性关联

常规的调用,注意点是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);
}

发布商品

调试会员等级接口

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

添加如下会员:

image-20220812111110133

获取分类关联的品牌

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

image-20220812115152211

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

接口功能如下

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

image-20220812180531794

vo

@Data
public class AttrGroupWithAttrsVo {/*** 分组id*/@TableIdprivate 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是最好的选择,不需要对实体类字段进行修改

image-20220812181322056

/*** 获取分类下的所有分组及属性* @param catelogId* @return*/@Overridepublic 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;}

测试

image-20220812180358798

商品新增vo抽取

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

image-20220812210242531

[在线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没有的那就可以自己赋值

表结构如下:

image-20220813192926303

这里的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_valueList<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检索

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. 加入网关

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

image-20220906101248116

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

多条件分页查询

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

合并采购需求

合并逻辑:

image-20220906105205032

1.创建采购单

image-20220906105356726

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中的信息//如果指定了采购单,那就获取采购单的idLong purchaseId = mergeVo.getPurchaseId();//获得采购需求的idList<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);//获得自动创建的采购单idpurchaseId = 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会自动读取里面的IDpurchaseDetailService.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. 采购需求状态改为正在采购
@Overridepublic 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);});}}

image-20220814004750176

image-20220814004807416

完成采购

controller

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

/*** 完成采购单*/
@PostMapping("/done")
public R finished(@RequestBody PurchaseDoneVo doneVo){purchaseService.done(doneVo);return R.ok();
}VO如下:
@Data
public class PurchaseDoneVo {@NonNullprivate 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*/@Overridepublic 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":"无货"}]
}

image-20220907215915451

采购单状态如下:

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

image-20220907215937701

采购需求如下:

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

image-20220907220051000

库存如下:

image-20220907220103041

显示商品库存中的sku_name

image-20220907222230675

怎么显示呢?锁定库存就是本表库存表相关的可以直接设置,而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);}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部