首先了解一下库存系统的数据库设计:
一、仓库管理
仓库管理的代码,我们得逆向工程已经全部自动完成了:
测试可用,新增2个仓库,以供后用!
完善一下,搜索时的模糊查询功能,略!
二、查询库存&创建采购需求
GET /ware/waresku/list
{ page: 1,//当前页码 limit: 10,//每页记录数 sidx: 'id',//排序字段 order: 'asc/desc',//排序方式 wareId: 123,//仓库id skuId: 123//商品id }
1、实现过程代码:略;效果如下:(从商品管理的具体Sku上点击 更多——库存管理,可以直接携带查询条件跳转到库存管理的页面)
直接跳转:
2、上面的过程很简单,但是需要注意的是:
实际使用过程中,新增库存的工作,我们并不会通过:此新增按钮直接新增,因为不严谨,可能实际并没有库存;
而是通过:采购单全流程 实现库存的增加;大概过程如下:
-
新建采购需求:来源:人工后台创建 + 系统自动发出低库存预警并创建采购需求;
-
创建采购单:采购单由一个或多个采购需求构成(一个大订单中其实包含多个商品的采购需求);人工合并或系统定时合并;
-
分配到采购人员
-
采购人员接单并进行采购工作;
-
采购完成后,采购单入库;
-
成功采购的商品自动添加库存;
3、新增采购需求:新增了两个:
完善一下条件搜索功能:略!
GET /ware/purchasedetail/list
{ page: 1,//当前页码 limit: 10,//每页记录数 sidx: 'id',//排序字段 order: 'asc/desc',//排序方式 key: '华为',//检索关键字 status: 0,//状态 wareId: 1,//仓库id }
三、合并采购需求
采购的简要流程大致如下:文字描述上节已经描述;
1、新增采购单:
2、合并采购需求到整单:注意:“已分配但是未领取”的采购单也是可以继续被合并的;
GET /ware/purchase/unreceive/list
无传参,略!
3、点击确认合并采购需求:
POST /ware/purchase/merge
{ purchaseId: 1, //整单id items:[1,2,3,4] //合并项集合 }
此处要满足一个设计:当提交的数据中没有purchaseId的时候,我们需要快速地创建一个采购单,并合并到它;
我们现在common中为库存模块定义一个常量枚举类:
public class WareConstant { public enum PurchaseStatusEnum { CREATED(0, "新建"), ASSIGNED(1, "已分配"), RECEIVED(2, "已领取"), FINISHED(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, "新建"), ASSIGNED(1, "已分配"), BUYING(2, "正在采购"), FINISHED(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; } } }
PurchaseServiceImpl.java:
@Transactional @Override public void mergePurchase(MergeVo mergeVo) { Long purchaseId = mergeVo.getPurchaseId(); //如果purchaseId存在,则合并到它,如果不存在则新建后再合并 if (purchaseId == null){ PurchaseEntity purchaseEntity = new PurchaseEntity(); purchaseEntity.setCreateTime(new Date()); purchaseEntity.setUpdateTime(new Date()); purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATED.getCode()); this.save(purchaseEntity); purchaseId = purchaseEntity.getId(); } Long finalPurchaseId = purchaseId; List<PurchaseDetailEntity> collect = mergeVo.getItems().stream().map(i -> { PurchaseDetailEntity detailEntity = new PurchaseDetailEntity(); detailEntity.setId(i); detailEntity.setPurchaseId(finalPurchaseId); detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode()); return detailEntity; }).collect(Collectors.toList()); //批量修改 purchaseDetailService.updateBatchById(collect); //更新采购单的最后更新时间 PurchaseEntity purchase = new PurchaseEntity(); purchase.setId(finalPurchaseId); purchase.setUpdateTime(new Date()); this.updateById(purchase); }
合并成功后的效果如下:
同时,对应的采购单的最后更新时间也会发生改变:
这样,合并采购需求的操作就完成了!
四、采购人员领取采购单
但是领取操作,不是由采购人员在我们后台进行操作的,正常情况下是采购人员有自己的App,在App上点击领取,调用我们得接口,完成领取操作;
所以,领取操作,我们使用 postman 模拟App操作;
注意,已经被领取的采购单,新的采购需求就不可以再被分配过去了,与上面合并环节相呼应了!
且采购单并领取后,除了采购单状态变为“已领取”,对应的采购需求状态将被置为“正在采购”!
POST /ware/purchase/received
[1,2,3,4]//采购单id
测试领取1号采购单:
成功后,检查采购单状态:
同时,对应的采购需求的状态也变为了“正在采购”:
五、完成采购
1、完成采购的功能也应该是由采购人员通过App点击完成,调用的接口为:
POST /ware/purchase/done
{ id: 123,//采购单id items: [{itemId:1,status:4,reason:""}]//完成/失败的需求详情 }
PurchaseServiceImpl.java:
@Transactional @Override public void done(PurchaseDoneVo vo) { //1、改变采购项状态; Boolean flag = true; //只有有任何一个采购项没有采购成功,都将被置为false List<PurchaseDetailEntity> updates = new ArrayList<>(); for (PurchaseDoneVo.ItemVo item: vo.getItems()){ PurchaseDetailEntity detailEntity = new PurchaseDetailEntity(); if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()){ flag = false; detailEntity.setStatus(item.getStatus()); }else { detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISHED.getCode()); //2、将成功采购的sku进行入库; PurchaseDetailEntity detail = purchaseDetailService.getById(item.getItemId()); wareSkuService.addStock(detail.getSkuId(), detail.getWareId(), detail.getSkuNum()); } detailEntity.setId(item.getItemId()); updates.add(detailEntity); } purchaseDetailService.updateBatchById(updates); //3、改变采购单状态; PurchaseEntity purchaseEntity = new PurchaseEntity(); purchaseEntity.setId(vo.getId()); purchaseEntity.setStatus(flag?WareConstant.PurchaseStatusEnum.FINISHED.getCode():WareConstant.PurchaseStatusEnum.HASERROR.getCode()); purchaseEntity.setUpdateTime(new Date()); this.updateById(purchaseEntity); }
WareSkuServiceImpl.java:
@Override public void addStock(Long skuId, Long wareId, Integer skuNum) { List<WareSkuEntity> list = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId)); if (CollectionUtils.isEmpty(list)){ WareSkuEntity entity = new WareSkuEntity(); entity.setSkuId(skuId); //远程调用zidanmall-product服务,查询skuName try { //如果失败,整个事务不需要回滚,因为只是name R info = productFeignService.info(skuId); if (info.getCode() == 0){ Map<String, Object> map = (Map<String, Object>) info.get("skuInfo"); entity.setSkuName((String) map.get("skuName")); } }catch (Exception e){ e.printStackTrace(); } entity.setWareId(wareId); entity.setStock(skuNum); entity.setStockLocked(0); this.save(entity); }else { this.baseMapper.addStock(skuId, wareId, skuNum); } }
2、测试——接口调用:
3、测试——对比:
采购完成前:
库存:
采购单信息:
采购需求:
采购完成后:
采购需求:
采购单:
商品库存: