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


    分组与属性关联

    显示属性

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

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试

    属性显示成功

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    查询分组未关联的属性

    逻辑分析

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    发布商品

    调试会员等级接口

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

    添加如下会员:

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试

    开发规范

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

    image-20220812115152211

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

    接口功能如下

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

    image-20220812180531794

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    service

    vo的重要性:

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

    image-20220812181322056

    /**
         * 获取分类下的所有分组及属性
         * @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    测试

    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    商品新增业务流程分析

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

    因为这个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);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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积分信息异常");
                }
            }
    
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    测试

    检索功能

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

    spu检索

    controller

    /**
     * 列表
     */
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params){
        PageUtils page = spuInfoService.queryPageByCondition(params);
    
        return R.ok().put("page", page);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    sku检索

    controller

    /**
     * 列表
     */
    @RequestMapping("/list")
    public R list(@RequestParam Map<String, Object> params){
        PageUtils page = skuInfoService.queryPageByParams(params);
    
        return R.ok().put("page", page);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    仓库服务

    整合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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    多条件查询都是一样的套路,获得你搜索的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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    创建采购需求

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

    @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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    合并采购需求

    合并逻辑:

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    领取采购单

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

    controller

    /**
     * 领取采购单
     */
    @PostMapping("/received")
    public R received(@RequestBody List<Long> ids){
        purchaseService.received(ids);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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);
                });
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    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 {
        @NonNull
        private Long id;
    
        private List<PurchaseItemDoneVo> items;
    
        public PurchaseDoneVo(){}
    }
    
    @Data
    public class PurchaseItemDoneVo {
    
        private Long itemId;
    
        private Integer status;
    
        private String reason;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    测试数据如下:

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

    {
        "id":16,"items":[
            {
                "itemId":17,"status":3,"reason":""
            },
            {
                "itemId":18,"status":4,"reason":"无货"
            }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20220907215915451

    采购单状态如下:

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

    image-20220907215937701

    采购需求如下:

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

    image-20220907220051000

    库存如下:

    image-20220907220103041

    显示商品库存中的sku_name

    image-20220907222230675

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

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

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

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

    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);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    增加库存的时候注入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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    最新超高效的Docker学习笔记,实战总结一步到位,“啃完”不是问题
    Python算法题集_搜索旋转排序数组
    How to install a specific version of a package in R
    c# 获取windows 的 系统盘用户及用户名目录
    kettle从入门到精通 第五十三课 ETL之kettle MQTT/RabbitMQ consumer实战
    C++语言实现网络爬虫详细代码
    性能测试面试题总结(答案全)
    Spring Cloud面试题整理
    MySQL(10)视图
    实用新型专利的注意事项
  • 原文地址:https://blog.csdn.net/qq_45714272/article/details/126772686