# 库存管理 ## 1. 仓库列表维护 ### 1.1 注册中心配置 首先我们需要把库存服务注册到注册中心中。 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/ffe007ee3cb045a8a769602b4daf110d.png) 然后在nacos中发现注册的服务 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/2cd50f4b472a4c27bb9d714f72dcf732.png) ### 1.2 网关路由配置 客户端首先访问的都是网关服务,所以需要配置对应的路由规则 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/55fff046a02945ef8f6598ab3b19c5c4.png) 就可以完成对仓库列表的处理了 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/0f376760065442218178efb218e08e36.png) ### 1.3 关键字查询 然后实现仓库列表的关键字查询 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/b058f7fd116048ba9ebafb0e0bb1f2a1.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/de17c98be222486e9124ecfa8e02dd3b.png) ## 2.商品库存管理 添加对应的检索条件 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/5e0ca40e7da341d28a7959dda2829b76.png) 效果展示 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/85978e672fc94cb68e2956d54ee35d71.png) ## 3.采购流程 完整的采购流程 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/e177da945b4f4e9db0be884e190985c5.png) ### 3.1 采购需求维护 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/7a739681053843b38290d073c6505942.png) ### 3.2 采购需求合并 #### 3.2.1 查询分配的采购单 合并采购需求时我们需要把这些采购需求合并到一个采购单中,所以首先需要创建采购单 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/42fbfc10f4da4b779738745120811c54.png) 然后在整合时我们需要查询出状态为 新建 或者 已分配 的采购单 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/461f06b520cb43b48e9862efe48340a4.png) 对应的需要创建接口 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/e9fb9e00b1c3432e9896a5a53b90793a.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/3e5e3d3e21844cd081557012a15112b9.png) 然后就可以实现整合处理了。 #### 3.2.2 采购需求合并 接收传递的信息创建对应的VO对象 ```java @Data public class MergeVO { //{ purchaseId: this.purchaseId, items: items } private Long purchaseId; private List items; } ``` 创建对应的枚举类型的常量 ```java package com.msb.common.constant; /** * 库存模块的常量 * */ public class WareConstant { /** * 采购单状态 */ public enum PurchaseStatusEnum{ CREATED(0,"新建") ,ASSIGED(1,"已分配") ,RECEIVE(2,"已领取") ,FINISH(3,"已完成") ,HASERROR(4,"有异常"); private int code; private String msg; PurchaseStatusEnum(int code,String msg){ this.code = code; this.msg = msg; } public int getCode(){ return code; } public String getMsg() { return msg; } } /** * 采购需求状态 */ public enum PurchaseDetailStatusEnum{ CREATED(0,"新建") ,ASSIGED(1,"已分配") ,BUYING(2,"正在采购") ,FINISH(3,"已完成") ,HASERROR(4,"采购失败"); private int code; private String msg; PurchaseDetailStatusEnum(int code,String msg){ this.code = code; this.msg = msg; } public int getCode(){ return code; } public String getMsg() { return msg; } } } ``` 添加对应的接口 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/7a88de1c4a684f5a822d5d500721d6c3.png) 然后在service中完成对应的业务处理 ```java /** * 完成采购需求的合单操作 * @param mergeVO * @return */ @Transactional @Override public Integer merge(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(); } // 整合菜单需求单 List items = mergeVO.getItems(); final long finalPurchaseId = purchaseId; List list = items.stream().map(i -> { PurchaseDetailEntity detailEntity = new PurchaseDetailEntity(); // 更新每一条 需求单的 采购单编号 detailEntity.setId(i); detailEntity.setPurchaseId(finalPurchaseId); detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGED.getCode()); return detailEntity; }).collect(Collectors.toList()); detailService.updateBatchById(list); // 更新对应的采购单的更新时间 PurchaseEntity entity = new PurchaseEntity(); entity.setId(purchaseId); entity.setUpdateTime(new Date()); this.updateById(entity); return null; } ``` ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/5d2b3aa124ba45ed8d0af94e8b4f92a7.png) ### 3.3 领取采购单 采购单合单完成后,采购人员需要对应的领取采购单来执行后续的流程 先创建对应的领取采购单的接口 ```java /** * 领取采购单 * [2,3,4] * @return */ @PostMapping("/receive") public R receive(@RequestBody List ids){ purchaseService.received(ids); return R.ok(); } ``` 在service中实现领取采购单的业务 ```java /** * 领取采购单 * @param ids */ @Transactional @Override public void received(List ids) { // 1.领取的采购单的状态只能是新建或者已分配的采购单 其他的是不能领取的 List list = ids.stream().map(id -> { return this.getById(id); }).filter(item -> { if (item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() || item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGED.getCode()) { return true; } return false; }).map(item->{ item.setUpdateTime(new Date()); // 设置更新时间 // 更新采购单的状态为 已领取 item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode()); return item; }).collect(Collectors.toList()); // 2.更新采购单的状态为 已领取 this.updateBatchById(list); // 3.更新采购项的状态为 正在采购 for (Long id : ids) { // 根据采购单id 找到对应的采购项对象 List detailEntities = detailService.listDetailByPurchaseId(id); List collect = detailEntities.stream().map(item -> { PurchaseDetailEntity entity = new PurchaseDetailEntity(); entity.setId(item.getId()); entity.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode()); return entity; }).collect(Collectors.toList()); // 批量更新采购项 detailService.updateBatchById(collect); } } ``` postman发送请求测试 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/4039948b6741492ab0c210dc7d32543e.png) 数据状态更新 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/6e07b121c0a74c0f9829bcf395284807.png) ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/f7a2d4f1d8b14368995265be7c614c23.png) 搞定 ### 3.4 完成采购操作 #### 3.4.1 VO对象 创建对应的VO对象来接收传递的数据 ```java /** * 采购项的VO数据 */ @Data public class PurchaseItemDoneVO { private Long itemId; private Integer status; private String reason; } ``` ```java /** * 采购单的VO数据 */ @Data public class PurchaseDoneVO { private Long id; private List items; } ``` #### 3.4.2 OpenFeign配置 OpenFeign的配置-在商品入库的时候我们需要通过OpenFegin来调用商品服务的接口来查询sku的名称 ```java @FeignClient("mall-product") public interface ProductFeignService { /** * 当然我们也可以通过网关来调用商品服务 * @param skuId * @return */ @RequestMapping("/product/skuinfo/info/{skuId}") public R info(@PathVariable("skuId") Long skuId); } ``` 在启动类中添加Enable注解 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/da6abdc337384f469fdad88cca9a2d54.png) 不要忘了添加对应的依赖 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/d95804e5d5044713bb8ab356c94bc4f3.png) #### 3.4.3 业务代码实现 首先是Controller接口的创建 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/4e96f2f0e3b34368a9b15850fa594488.png) 然后对应的service逻辑的处理 ```java @Transactional @Override public void done(PurchaseDoneVO vo) { // 获取采购单编号 Long id = vo.getId(); // 2.改变采购项的状态 Boolean flag = true; // 记录采购的状态 默认为 完成 // 获取所有的采购项 List items = vo.getItems(); List list = 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 detailEntity1 = detailService.getById(item.getItemId()); wareSkuService.addStock(detailEntity1.getSkuId(),detailEntity1.getWareId(),detailEntity1.getSkuNum()); } detailEntity.setId(item.getItemId()); //detailService.updateById(detailEntity); list.add(detailEntity); } detailService.updateBatchById(list); // 批量更新 采购项 // 1.改变采购单的状态 PurchaseEntity purchaseEntity = new PurchaseEntity(); purchaseEntity.setId(id); purchaseEntity.setStatus(flag?WareConstant.PurchaseStatusEnum.FINISH.getCode() : WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()); purchaseEntity.setUpdateTime(new Date()); this.updateById(purchaseEntity); } ``` 然后就是入库的逻辑操作 ```java /** * 入库操作 * @param skuId 商品编号 * @param wareId 仓库编号 * @param skuNum 采购商品的数量 */ @Override public void addStock(Long skuId, Long wareId, Integer skuNum) { // 判断是否有改商品和仓库的入库记录 List list = skuDao.selectList(new QueryWrapper().eq("sku_id", skuId).eq("ware_id", wareId)); if(list == null || list.size() == 0){ // 如果没有就新增商品库存记录 WareSkuEntity entity = new WareSkuEntity(); entity.setSkuId(skuId); entity.setWareId(wareId); entity.setStock(skuNum); entity.setStockLocked(0); try { // 动态的设置商品的名称 R info = productFeignService.info(skuId); // 通过Feign远程调用商品服务的接口 Map data = (Map) info.get("skuInfo"); if(info.getCode() == 0){ entity.setSkuName((String) data.get("skuName")); } }catch (Exception e){ } skuDao.insert(entity); // 插入商品库存记录 }else{ // 如果有就更新库存 skuDao.addStock(skuId,wareId,skuNum); } } ``` 然后对应的Dao接口和Mapper的SQL代码 ```java @Mapper public interface WareSkuDao extends BaseMapper { void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum); } ``` ```xml UPDATE wms_ware_sku SET stock=stock+#{skuNum} WHERE sku_id=#{skuId} AND ware_id=#{wareId} ``` #### 3.4.4 PostMan测试 最后通过PostMan来测试完成操作 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/6dd6674b5cc74b9c9fe5c03c6688021d.png) ```json { "id":3, "items":[ {"itemId":4,"status":3,"reason":""} ,{"itemId":5,"status":3,"reason":""} ] } ``` 商品库存 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/9e718fdb019b4d7b811e14350b9b180e.png) 采购单 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/2482e8344bd945a9b0b1ae1cc7dd1365.png) 采购项 ![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/1462/1644300085000/6b83c397623a4e388cfc98dc527c9032.png) ## 4.阶段性总结 ### 4.1 分布式的概念 微服务(SpringCloudAlibaba) 注册中心,配置中心,远程调用,网关,负载均衡,链路追踪... ### 4.2 技术栈 SpringBoot2.4.12 SpringCloud MyBatis-Plus MySQL Vue ElementUI 人人fast,阿里云对象存储 ### 4.3 环境 Docker Linux Vagrant MySQL Redis 人人开源 ### 4.4 开发规范 数据校验JSR303 全局跨域 R全局统一返回,全局异常处理 枚举状态 业务状态,VO、DTO、PO划分,逻辑删除,Lombok