首先了解一下库存系统的数据库设计:

一、仓库管理
仓库管理的代码,我们得逆向工程已经全部自动完成了:

测试可用,新增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、测试——对比:
采购完成前:
库存:

采购单信息:

采购需求:

采购完成后:
采购需求:

采购单:

商品库存:




