仓储服务
一、仓储服务-API
库存信息表:
wms_ware_info 包括仓库所在地区等仓库信息(与商品无关)
wms_ware_sku:具体商品的库存量和所在仓库
wms_purchase_detail:采购需求
wms_purchase:采购单
wms_ware_order_task:库存工作单
wms_ware_order_task_detail:库存工作单详情
1、整合ware服务
gulimall-ware:
- 创建项目后nacos注册、网关重写等
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
#ware服务路由
- id: ware_route
uri: lb://gulimall-ware
predicates:
- Path=/api/ware/**
filters:
# 把/api/* 去掉,剩下的留下来
- RewritePath=/api/(?<segment>.*),/$\{segment}
2、仓库维护
提供模糊查询的仓库信息:
@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)
.or().like("areacode", key);
}
IPage<WareInfoEntity> page = this.page(
new Query<WareInfoEntity>().getPage(params),
wrapper
);
return new PageUtils(page);
}
3、商品库存
功能:查询sku+库存id+库存数等信息
数据库表:wms_ware_sku
指明每个仓库有什么sku
手动新增库存:商品库存页面/新增,然后填写 sku_id+仓库+库存数 等信息
自动新增库存:需要通过采购完成
4、采购单维护
(1)采购需求
采购需求的生成方式可能有两种:
- 人工新增
- 系统检测到库存量低时自动创建
流程:
(2)合并采购需求
比如一个仓库的东西可以合并到一起,让采购人员一趟采购完
- 查询未领取的采购单:
/ware/purchase/unreceive/list
@Override
public PageUtils queryPageUnreceive(Map<String, Object> params) {
IPage<PurchaseEntity> page = this.page(
new Query<PurchaseEntity>().getPage(params),
new QueryWrapper<PurchaseEntity>().eq("status", 0).or().eq("status", 1)
);
return new PageUtils(page);
}
- 采购单
wms_purchase
采购单,里面有创建时间和分类人员等信息wms_purchase_detail
采购单详情表,指明采购单每项要采购的sku,可以自己新增采购单,然后让新的采购需求合并到已有采购单里。
采购单状态:只有新建、已分配的时候才能合并采购单
新建 0
已分配 1
已领取(正在采购) 2
已完成 3
有异常(采购失败) 4
- 合并采购需求:
/ware/purchase/merge
请求数据:
{
purchaseId: 1, # 采购单id,没有携带就新建采购单
items: [1, 2] # 采购商品
}
注:如果不选择整单直接点击确定,将弹出提示。
流程:
- 如果没有带过来采购单id,先新建采购单
- 然后修改【采购需求】里对应的【采购单id、采购需求状态】,即
purchase_detail
表 - 采购需求是
purchase_detail
表、采购单是purchase
表。采购单由多个采购需求组成 - 采购单页面分配采购需求成功后应该刷新页面,或者说不能重复分配采购需求给不同的采购单(或者说是一个更新操作)
PurchaseServiceImpl
/**
* 合并采购需求
*
* @param mergeVo
*/
@Transactional
@Override
public void mergePurchase(MergeVo mergeVo) {
Long purchaseId = mergeVo.getPurchaseId();
// 一、判断是否有采购单,没有的话新建一个
if (purchaseId == null) {
//1、新建一个
PurchaseEntity purchaseEntity = new PurchaseEntity();
//2、修改对应转态
purchaseEntity.setStatus(WareConstant.PurchaseStatusEnum.CREATED.getCode());
purchaseEntity.setCreateTime(new Date());
purchaseEntity.setUpdateTime(new Date());
this.save(purchaseEntity);
purchaseId = purchaseEntity.getId();
}
//TODO 确认采购单状态是0,1才可以合并
// 二、有采购单id,进行合并
List<Long> items = mergeVo.getItems();
Long finalPurchaseId = purchaseId;
List<PurchaseDetailEntity> collect = items.stream().map(i -> {
// 采购需求
PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
detailEntity.setId(i);
// 合并到那个采购单
detailEntity.setPurchaseId(finalPurchaseId);
// 设置最新状态码为已分配
detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());
return detailEntity;
}).collect(Collectors.toList());
// 保存修改
detailService.updateBatchById(collect);
// 三、分配完后更新时间
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setId(purchaseId);
purchaseEntity.setUpdateTime(new Date());
this.updateById(purchaseEntity);
}
采购需求状态枚举:
public class WareConstant {
/**
* 采购单状态枚举
*/
public enum PurchaseStatusEnum {
CREATED(0, "新建"),
ASSIGNED(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, "新建"),
ASSIGNED(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;
}
}
}
(3)领取采购单
请求地址:/ware/purchase/received
- 某个人领取了采购单后,先看采购单是否处于未分配状态,只有采购单是新建或以领取状态时,才更新采购单的状态
- 领取后确认采购单的状态、采购单人员等是否相符
- 更改采购单状态、更改采购需求状态
后台系统里没有领取采购单这个功能,我们暂时通过postman手动领取采购单
http://localhost:88/api/ware/purchase/received
/**
* 领取采购单
* ids:采购单id
* 过滤采购需求,并同步采购需求的状态
*/
@Override
public void received(List<Long> ids) {
//1、确认当前采购单是新建或者已分配状态
List<PurchaseEntity> collect = ids.stream().map(id -> {
// 1.1 查出采购单详细信息
PurchaseEntity byId = this.getById(id);
return byId;
}).filter(item -> {
// 1.2 过滤出新建过着已分配的采购单
if (item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() || item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {
return true;
}
return false;
}).map(item -> {
// 1.3 设置最新状态
item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
item.setUpdateTime(new Date());
return item;
}).collect(Collectors.toList());
//2、改变采购单的状态
this.updateBatchById(collect);
//3、改变采购需求的状态
collect.forEach((item) -> {
// 3.1 找到采购项(采购需求)
List<PurchaseDetailEntity> entities = detailService.listDetailByPurchaseId(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());
// 3.2 批量更新状态
detailService.updateBatchById(detailEntities);
});
}
(4)完成采购,添加到库存
可以多选采购单里哪些采购项(需求)完成了
- 采购项都完成的时候采购单为完成,否则为有异常
-
采购项完成时增加到库存
-
增加库存时要判断原来是否有库存以区分insert和update
API信息:同样相关页面也省略,通过POSTMAN发送,代表采购员采购回来了,提交信息
/**
* 完成采购
*
* @param doneVo
*/
@Transactional
@Override
public void done(PurchaseDoneVo doneVo) {
Long id = doneVo.getId();
//2、改变采购项(需求)的状态
Boolean flag = true;
List<PurchaseItemDoneVo> items = doneVo.getItems();
// 2.1 收集要更新的信息
List<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、将成功采购的进行入库
// 3.1 先查出采购项
PurchaseDetailEntity entity = detailService.getById(item.getItemId());
wareSkuService.addStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());
}
detailEntity.setId(item.getItemId());
// 更新信息
updates.add(detailEntity);
}
// 2.2 更新信息
detailService.updateBatchById(updates);
//1、改变采购单状态
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);
}
完成采购增加库存时,需要涉及到设置SKU的name信息到仓库中,这是通过feign远程调用“gulimall-product
”服务来实现根据sku_id查询得到sku_name的。
只要异常被捕获,事务是不会滚的(这里需要优化,是高级篇消息队列实现一致性事务的内容)
@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));
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 {
R info = productFeignService.info(skuId);
Map<String, Object> data = (Map<String, Object>) info.get("skuInfo");
if (info.getCode() == 0) {
skuEntity.setSkuName((String) data.get("skuName"));
}
} catch (Exception e) {
}
wareSkuDao.insert(skuEntity);
} else {
wareSkuDao.addStock(skuId, wareId, skuNum);
}
}