库存管理
1. 仓库列表维护
1.1 注册中心配置
首先我们需要把库存服务注册到注册中心中。
然后在nacos中发现注册的服务
1.2 网关路由配置
客户端首先访问的都是网关服务,所以需要配置对应的路由规则
就可以完成对仓库列表的处理了
1.3 关键字查询
然后实现仓库列表的关键字查询
2.商品库存管理
添加对应的检索条件
效果展示
3.采购流程
完整的采购流程
3.1 采购需求维护
3.2 采购需求合并
3.2.1 查询分配的采购单
合并采购需求时我们需要把这些采购需求合并到一个采购单中,所以首先需要创建采购单
然后在整合时我们需要查询出状态为 新建 或者 已分配 的采购单
对应的需要创建接口
然后就可以实现整合处理了。
3.2.2 采购需求合并
接收传递的信息创建对应的VO对象
代码语言:javascript复制@Data
public class MergeVO {
//{ purchaseId: this.purchaseId, items: items }
private Long purchaseId;
private List<Long> items;
}
创建对应的枚举类型的常量
代码语言:javascript复制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;
}
}
}
添加对应的接口
然后在service中完成对应的业务处理
代码语言:javascript复制 /**
* 完成采购需求的合单操作
* @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<Long> items = mergeVO.getItems();
final long finalPurchaseId = purchaseId;
List<PurchaseDetailEntity> 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;
}
3.3 领取采购单
采购单合单完成后,采购人员需要对应的领取采购单来执行后续的流程
先创建对应的领取采购单的接口
代码语言:javascript复制 /**
* 领取采购单
* [2,3,4]
* @return
*/
@PostMapping("/receive")
public R receive(@RequestBody List<Long> ids){
purchaseService.received(ids);
return R.ok();
}
在service中实现领取采购单的业务
代码语言:javascript复制 /**
* 领取采购单
* @param ids
*/
@Transactional
@Override
public void received(List<Long> ids) {
// 1.领取的采购单的状态只能是新建或者已分配的采购单 其他的是不能领取的
List<PurchaseEntity> 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<PurchaseDetailEntity> detailEntities = detailService.listDetailByPurchaseId(id);
List<PurchaseDetailEntity> 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发送请求测试
数据状态更新
搞定
3.4 完成采购操作
3.4.1 VO对象
创建对应的VO对象来接收传递的数据
代码语言:javascript复制/**
* 采购项的VO数据
*/
@Data
public class PurchaseItemDoneVO {
private Long itemId;
private Integer status;
private String reason;
}
代码语言:javascript复制/**
* 采购单的VO数据
*/
@Data
public class PurchaseDoneVO {
private Long id;
private List<PurchaseItemDoneVO> items;
}
3.4.2 OpenFeign配置
OpenFeign的配置-在商品入库的时候我们需要通过OpenFegin来调用商品服务的接口来查询sku的名称
代码语言:javascript复制@FeignClient("mall-product")
public interface ProductFeignService {
/**
* 当然我们也可以通过网关来调用商品服务
* @param skuId
* @return
*/
@RequestMapping("/product/skuinfo/info/{skuId}")
public R info(@PathVariable("skuId") Long skuId);
}
在启动类中添加Enable注解
不要忘了添加对应的依赖
3.4.3 业务代码实现
首先是Controller接口的创建
然后对应的service逻辑的处理
代码语言:javascript复制 @Transactional
@Override
public void done(PurchaseDoneVO vo) {
// 获取采购单编号
Long id = vo.getId();
// 2.改变采购项的状态
Boolean flag = true; // 记录采购的状态 默认为 完成
// 获取所有的采购项
List<PurchaseItemDoneVO> items = vo.getItems();
List<PurchaseDetailEntity> 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);
}
然后就是入库的逻辑操作
代码语言:javascript复制 /**
* 入库操作
* @param skuId 商品编号
* @param wareId 仓库编号
* @param skuNum 采购商品的数量
*/
@Override
public void addStock(Long skuId, Long wareId, Integer skuNum) {
// 判断是否有改商品和仓库的入库记录
List<WareSkuEntity> list = skuDao.selectList(new QueryWrapper<WareSkuEntity>().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<String,Object> data = (Map<String, Object>) 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代码
代码语言:javascript复制@Mapper
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {
void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);
}
代码语言:javascript复制 <insert id="addStock">
UPDATE wms_ware_sku SET stock=stock #{skuNum} WHERE sku_id=#{skuId} AND ware_id=#{wareId}
</insert>
3.4.4 PostMan测试
最后通过PostMan来测试完成操作
代码语言:javascript复制{
"id":3,
"items":[
{"itemId":4,"status":3,"reason":""}
,{"itemId":5,"status":3,"reason":""}
]
}
商品库存
采购单
采购项
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