• gulimall基础篇回顾Day-15


    一、整合仓储服务

    1.1 注册中心和配置中心配置

    #服务注册发现地址
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
      application:
        name: gulimall-ware
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    #配置中心地址
    spring.application.name=gulimall-ware
    spring.cloud.nacos.config.server-addr=127.0.0.1:8848
    
    • 1
    • 2
    • 3
    @EnableTransactionManagement
    @MapperScan("com.atguigu.gulimall.ware.dao")
    //开启服务注册发现功能注解
    @EnableDiscoveryClient
    @SpringBootApplication
    public class GulimallWareApplication {
        public static void main(String[] args) {
            SpringApplication.run(GulimallWareApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    #网关gateway路由规则配置
    spring:
      cloud:
        gateway:
          routes:
            - id: ware_route
              uri: lb://gulimall-ware
              predicates:
                - Path=/api/ware/**
              filters:
                - RewritePath=/api/(?>.*),/$\{segment}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    重启 ware 服务,得利于逆向工程仓库维护基本的增删改查代码能够正常使用,如下图所示
    在这里插入图片描述

    1.2 仓库信息模糊检索功能

    1.2.1 需求描述:点击仓库维护模块,输入要查询的参数名,后端能够根据参数名做模糊检索的功能。模糊检索描述:仓库id等于传入的参数值,或者仓库名称包含传入的参数值,或者仓库地址包含传入的参数值以及区域编码等于传入的参数值。 提示:视频中的区域编码是使用模糊匹配检索,个人理解这个检索不太恰当,应该使用 eq 精准匹配

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareInfoEntity> wrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        if (!StringUtils.isEmpty(key)){
            wrapper.eq("id", key)
                    .or().like("name", key)
                    .or().like("address", key)
                    .eq("areacode", key);
        }
        IPage<WareInfoEntity> page = this.page(
                new Query<WareInfoEntity>().getPage(params),
                wrapper
        );
    
        return new PageUtils(page);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    实现效果如下图
    在这里插入图片描述

    二、查询库存与新建采购单

    2.1 仓库管理查询库存功能

    1.3.1 需求描述:点击库存管理,选择仓库编号以及skuId能够查询出库存信息(前端除了会带上基本的分页字段外,还带上skuId: 27,wareId: 1信息)。
    在这里插入图片描述

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /**
         * 前端发送商品库存查询请求会自动带上这两个字段
         * skuId: 1
         * wareId: 1
         */
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<WareSkuEntity>();
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)){
            wrapper.eq("sku_id ", skuId);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)){
            wrapper.eq("ware_id ", wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );
    
        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

    2.2 仓库管理查询采购单功能

    2.1.1 采购简要流程图如下图所示
    在这里插入图片描述
    2.2.2 需求描述:点击采购单维护,选择采购需求我们可以根据仓库编号,采购需求状态以及关键字进行采购需求的搜索功能。
    在这里插入图片描述

    //采购需求PurchaseDetailServiceImpl
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /**
         * key:
         * status: 1
         * wareId: 1
         */
        QueryWrapper<PurchaseDetailEntity> wrapper = new QueryWrapper<PurchaseDetailEntity>();
        String key = (String) params.get("key");
        if (!StringUtils.isEmpty(key)){
            //purchase_id采购单id  sku_id skuId
            wrapper.and(w->{
                w.eq("purchase_id", key).or().eq("sku_id", key);
            });
        }
    
        String status = (String) params.get("status");
        if (!StringUtils.isEmpty(status)){
            wrapper.eq("status", status);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)){
            wrapper.eq("ware_id", wareId);
        }
        IPage<PurchaseDetailEntity> page = this.page(
                new Query<PurchaseDetailEntity>().getPage(params),
                wrapper
        );
    
        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

    2.3 合并采购单需求

    2.3.1 需求描述:选中采购需求id,点击批量操作的合并整单可以将我们的采购需求合并到新建或已分配的采购单上,如下图所示。

    在这里插入图片描述
    管理员列表新增两个采购人员,如下图所示。
    在这里插入图片描述
    新建采购单将采购单分配给采购人员,如下图所示。
    在这里插入图片描述
    点击确定后前端会发送采购需求id以及要合并的采购单id,如下图所示(若合并到整单不选中任何采购单id的话,系统会默认创建新的采购单)。
    在这里插入图片描述
    代码实现

    @Data
    public class MergeVO {
        /**
         * items:[1, 2] 采购项
         * purchaseId:1 采购单id
         */
        private List<Long> items;
        private Long purchaseId;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    /**
     * 合并采购单
     */
    @PostMapping("/merge")
    public R mergePurchase(@RequestBody MergeVO mergeVO){
        purchaseService.mergePurchase(mergeVO);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    @Transactional
    @Override
    public void mergePurchase(MergeVO mergeVO) {
        Long purchaseId = mergeVO.getPurchaseId();
        //如果采购单是空的就新建一个采购单,并设置默认的采购单状态,采购单创建及更新日期
        if (purchaseId == null){
            PurchaseEntity purchaseEntity = new PurchaseEntity();
            purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATED.getCode());
            purchaseEntity.setCreateTime(new Date());
            purchaseEntity.setUpdateTime(new Date());
            this.save(purchaseEntity);
            purchaseId = purchaseEntity.getId();
        }
        //TODO 确认是采购单状态是0,1才可以合并
        List<Long> items = mergeVO.getItems();
        Long finalPurchaseId = purchaseId;
        List<PurchaseDetailEntity> detailEntityList = items.stream().map(m -> {
            PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
            detailEntity.setId(m);
            detailEntity.setPurchaseId(finalPurchaseId);
            detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());
            return detailEntity;
        }).collect(Collectors.toList());
        purchaseDetailService.updateBatchById(detailEntityList);
    
        PurchaseEntity purchaseEntity = new PurchaseEntity();
        purchaseEntity.setId(purchaseId);
        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

    2.4 领取采购单

    2.4.1 需求描述:前面我们编写了将采购需求合并到某个采购单上,并且我们可以将采购单分配给具体的采购人员,采购人员可以领取采购单进行采购(此时采购单的状态变为已领取),进行商品的入库。就比如采购人员有自己的APP,当系统为他分配了采购单,APP上就会显示采购单的信息,他可以点击领取采购单进行采购。采购人员点击领取采购单后,采购需求就不能再合并到此采购单上了,而且采购单一旦领取,采购需求状态就应该变为正在采购。采购完成后APP也可以点击完成结束采购。

    2.4.2 PurchaseController 层

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

    2.4.3 PurchaseServiceImpl层

    /**
     * 采购单的id
     * @param ids
     */
    @Override
    public void received(List<Long> ids) {
        //1、确认当前采购单是新建或者已分配状态
        List<PurchaseEntity> collect = ids.stream().map(id -> {
            PurchaseEntity byId = this.getById(id);
            return byId;
        }).filter(item -> {
            //过滤状态:新建和已分配才能我们所要的
            if (item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() ||
                    item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {
                return true;
            }
            return false;
        }).map(item->{
            //改变状态:已领取
            item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
            item.setUpdateTime(new Date());
            return item;
        }).collect(Collectors.toList());
        //2、改变采购单的状态
        this.updateBatchById(collect);
        //3、改变采购项的状态
        collect.forEach((item)->{
            List<PurchaseDetailEntity> entities = detailService.listDetailPurchaseId(item.getId());
            List<PurchaseDetailEntity> detailEntities = entities.stream().map(entity -> {
                PurchaseDetailEntity entity1 = new PurchaseDetailEntity();
                entity1.setId(entity.getId());
                entity1.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());
                return entity1;
            }).collect(Collectors.toList());
            detailService.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
     @Override
     public List<PurchaseDetailEntity> listDetailPurchaseId(Long id) {
         List<PurchaseDetailEntity> purchaseId = this.list(new QueryWrapper<PurchaseDetailEntity>().eq("purchase_id", id));
         return purchaseId;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.5 完成采购

    2.5.1 前面我们编写了采购人员领取采购单的需求,采购人员通过APP领取采购单后,接下来可以进行采购。采购完成后商品的数量等信息就要进行入库操作。前端发送的请求如下:

    {  
       #采购单id
       "id": 2,
       #采购项
       "items": [
           {"itemId": 1,
           "status": 3,
           "reason":""
           },
           {"itemId": 2,
           "status": 4,
           "reason":"无货"
           }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.5.2 VO 封装信息

    @Data
    public class PurchaseDoneVo {
        //采购单id
        @NotNull
        private Long id;
    
        private List<PurchaseItemDoneVo> items;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    @Data
    public class PurchaseItemDoneVo {
        //{itemId:1,status:4,reason:""}
        private Long itemId;
        private Integer status;
        private String reason;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.5.3 PurchaseController 层

    /**
     * 完成采购单
     */
    @PostMapping("/done")
    public R finish(@RequestBody PurchaseDoneVo doneVo){
        purchaseService.done(doneVo);
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.5.4 PurchaseServiceImpl 层

    @Transactional
    @Override
    public void done(PurchaseDoneVo doneVo) {
    
        Long id = doneVo.getId();
    
        //2.改变采购项的状态
        //true是采购成功,false是采购失败
        Boolean flag = true;
        //items为{itemId:1,status:4,reason:""}这三值
        List<PurchaseItemDoneVo> items = doneVo.getItems();
    
        ArrayList<PurchaseDetailEntity> updates = new ArrayList<>();
        for (PurchaseItemDoneVo item : items) {
            PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
            if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()) {
                //采购失败
                flag = false;
                detailEntity.setStatus(item.getStatus());
            } else {
                //采购成功
                detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());
                //3.将成功采购的进行入库
                PurchaseDetailEntity entity = detailService.getById(item.getItemId());
                //addStock是新写方法,意思是采购成功添加库存
                wareSkuService.addStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());
            }
            //采购需求设置id
            detailEntity.setId(item.getItemId());
            //采购需求添加遍历设置的每一条
            updates.add(detailEntity);
        }
        //修改采购需求数据
        detailService.updateBatchById(updates);
    
        //1、改变采购单状态
        PurchaseEntity purchaseEntity = new PurchaseEntity();
        //设置采购id
        purchaseEntity.setId(id);
        //flag是true状态设置完成,flag是false状态设置失败
        purchaseEntity.setStatus(flag ? WareConstant.PurchaseStatusEnum.FINISH.getCode() : WareConstant.PurchaseStatusEnum.HASERROR.getCode());
        //设置时间
        purchaseEntity.setUpdateTime(new Date());
        //修改数据
        this.updateById(purchaseEntity);
        //3、将成功采购的进行入库
    }
    
    • 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

    2.5.5 WareSkuServiceImpl 层

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断如果还没有这个库存记录新增
        List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>()
                .eq("sku_id", skuId).eq("ware_id", wareId));
        //如果还没有库存sku记录
        if (entities == null || entities.size() == 0){
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setStock(skuNum);
            skuEntity.setWareId(wareId);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字
            //1、自己catch异常
            //TODO 还可以用什么办法让异常出现之后不回滚?高级
            try {
                //远程调用得到sku信息
                R info = productFeignService.info(skuId);
                //得到skuInfo的data是objct对象,我们把它强转为string 的map形式
                Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");
                //如果成功
                if (info.getCode() == 0){
                    //也强转为string
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e){ }
            wareSkuDao.insert(skuEntity);
        }else {
            //addStock新方法完善它
            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
    • 29
    • 30
    • 31
    • 32

    2.5.6 测试
    原来这个采购单状态是已领取的,postman发送请求成功后,由于采购项id为2的无货,所以显示有异常。
    在这里插入图片描述
    采购项id为1的采购成功显示已完成,id为2的显示采购失败。
    在这里插入图片描述
    到采购项id为1的采购商品库存显示如下信息
    在这里插入图片描述
    为skuId为28,29,30的商品新增采购需求,如下图所示
    在这里插入图片描述
    选择5,6,7合并整单,点击确认默认新增采购单id为4的采购单
    在这里插入图片描述
    分配给采购人员admin,admin发送领取采购单请求,采购单状态变为已领取,如下图所示
    在这里插入图片描述
    在这里插入图片描述
    这些商品状态变为正在采购中,如下图所示
    在这里插入图片描述

    完成4号采购单的采购,发送如下请求
    在这里插入图片描述
    采购单显示已完成,如下图所示
    在这里插入图片描述
    采购需求也显示已完成,如下图所示
    在这里插入图片描述
    商品库存信息如下图所示
    在这里插入图片描述
    2.5.7 feign远程查询sku信息

    @FeignClient("gulimall-product")
    public interface ProductFeignService {
        /**
         * /product/skuinfo/info/{skuId} 对应@FeignClient("gulimall-product")
         * /api/product/skuinfo/info/{skuId} 对应@FeignClient("gulimall-gateway")
         *
         * 1)让所有请求过网关:
         *  1.@FeignClient("gulimall-gateway"):给gulimall-gateway所在的机器发请求
         *  2./api/product/skuinfo/info/{skuId}
         * 2)直接让后台指定服务处理
         *  1.@FeignClient("gulimall-product")
         *  2./product/skuinfo/info/{skuId}
         */
        @RequestMapping("/product/skuinfo/info/{skuId}")
        public R info(@PathVariable("skuId") Long skuId);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.6 spu规格维护

    2.6.1 需求描述:选择 spu 管理,当商品信息第一次录入后后续许需要修改可在此界面进行修改及修改前的回显数据功能。
    在这里插入图片描述

    2.6.2 AttrController 层

    @Autowired
    private ProductAttrValueService productAttrValueService;
    ///product/attr/base/listforspu/{spuId}
    @GetMapping("/base/listforspu/{spuId}")
    public R baseAttrlistforspu(@PathVariable("spuId") Long spuId){
        List<ProductAttrValueEntity> entities = productAttrValueService.baseAttrListForSpu(spuId);
        return R.ok().put("data",entities);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    @Override
    public List<ProductAttrValueEntity> baseAttrListForSpu(Long spuId) {
        List<ProductAttrValueEntity> entities =
                this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
    
        return entities;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.6.3 能够会先商品的规格数据后,我们可以对这些数据进行修改。请求路径如下图所示
    在这里插入图片描述
    2.6.4 AttrController 层

    ///product/attr/update/{spuId} 更新spu属性
    @PostMapping("/update/{spuId}")
    public R updateSpuAttr(@PathVariable("spuId")Long spuId,
                           @RequestBody List<ProductAttrValueEntity> entities){
        productAttrValueService.updateSpuAttr(spuId,entities);
    
        return R.ok();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.6.5 ProductAttrValueServiceImpl 层

    @Transactional
    @Override
    public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
        //1、删除这给spuId之前对应的所有属性
        this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));
    
        List<ProductAttrValueEntity> collect = entities.stream().map(item -> {
            item.setSpuId(spuId);
            return item;
        }).collect(Collectors.toList());
        this.saveBatch(collect);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    重启项目,修改规格信息成功如下图所示
    在这里插入图片描述

  • 相关阅读:
    【Express】文件上传管理 multer 中间件
    文件I/O_03PageCache和Mmap
    【C++】动态内存 new和delete的简单运用和理解
    成都睿趣科技:抖音开店初期要注意什么
    chatGPT的耳朵!OpenAI的开源语音识别AI:Whisper !
    【数据集资源】大数据资源-数据集下载方法-汇总
    Bootstrap前端开发框架(简介、版本、优点、使用、布局容器、栅格系统(栅格选项参数,列嵌套,列偏移,列排序,响应式工具))
    ONNX OpenVino TensorRT MediaPipe NCNN Diffusers
    DeepFace【部署 03】轻量级人脸识别和面部属性分析框架deepface在Linux环境下服务部署(conda虚拟环境+docker)
    【大家的项目】可 Deferred 就绪的 Future 实现类
  • 原文地址:https://blog.csdn.net/m0_49692893/article/details/127830475