仓储服务
一、整合仓储服务
1.1 注册中心和配置中心配置
#服务注册发现地址
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-ware
#配置中心地址
spring.application.name=gulimall-ware
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
@EnableTransactionManagement
@MapperScan("com.atguigu.gulimall.ware.dao")
//开启服务注册发现功能注解
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallWareApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallWareApplication.class, args);
}
}
#网关gateway路由规则配置
spring:
cloud:
gateway:
routes:
- id: ware_route
uri: lb://gulimall-ware
predicates:
- Path=/api/ware/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
重启 ware 服务,得利于逆向工程仓库维护基本的增删改查代码能够正常使用,如下图所示
1.2 仓库信息模糊检索功能
1.2.1 需求描述:点击仓库维护模块,输入要查询的参数名,后端能够根据参数名做模糊检索的功能。模糊检索描述:仓库id等于传入的参数值,或者仓库名称包含传入的参数值,或者仓库地址包含传入的参数值以及区域编码等于传入的参数值。 提示:视频中的区域编码是使用模糊匹配检索,个人理解这个检索不太恰当,应该使用 eq 精准匹配
。
@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)
.eq("areacode", key);
}
IPage<WareInfoEntity> page = this.page(
new Query<WareInfoEntity>().getPage(params),
wrapper
);
return new PageUtils(page);
}
实现效果如下图
二、查询库存与新建采购单
2.1 仓库管理查询库存功能
1.3.1 需求描述:点击库存管理,选择仓库编号以及skuId能够查询出库存信息(前端除了会带上基本的分页字段外,还带上skuId: 27,wareId: 1信息)。
@Override
public PageUtils queryPage(Map<String, Object> params) {
/**
* 前端发送商品库存查询请求会自动带上这两个字段
* skuId: 1
* wareId: 1
*/
QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<WareSkuEntity>();
String skuId = (String) params.get("skuId");
if (!StringUtils.isEmpty(skuId)){
wrapper.eq("sku_id ", skuId);
}
String wareId = (String) params.get("wareId");
if (!StringUtils.isEmpty(wareId)){
wrapper.eq("ware_id ", wareId);
}
IPage<WareSkuEntity> page = this.page(
new Query<WareSkuEntity>().getPage(params),
wrapper
);
return new PageUtils(page);
}
2.2 仓库管理查询采购单功能
2.1.1 采购简要流程图如下图所示
2.2.2 需求描述:点击采购单维护,选择采购需求我们可以根据仓库编号,采购需求状态以及关键字进行采购需求的搜索功能。
//采购需求PurchaseDetailServiceImpl
@Override
public PageUtils queryPage(Map<String, Object> params) {
/**
* key:
* status: 1
* wareId: 1
*/
QueryWrapper<PurchaseDetailEntity> wrapper = new QueryWrapper<PurchaseDetailEntity>();
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)){
//purchase_id采购单id sku_id skuId
wrapper.and(w->{
w.eq("purchase_id", key).or().eq("sku_id", key);
});
}
String status = (String) params.get("status");
if (!StringUtils.isEmpty(status)){
wrapper.eq("status", status);
}
String wareId = (String) params.get("wareId");
if (!StringUtils.isEmpty(wareId)){
wrapper.eq("ware_id", wareId);
}
IPage<PurchaseDetailEntity> page = this.page(
new Query<PurchaseDetailEntity>().getPage(params),
wrapper
);
return new PageUtils(page);
}
2.3 合并采购单需求
2.3.1 需求描述:选中采购需求id,点击批量操作的合并整单可以将我们的采购需求合并到新建或已分配的采购单上,如下图所示。
管理员列表新增两个采购人员,如下图所示。
新建采购单将采购单分配给采购人员,如下图所示。
点击确定后前端会发送采购需求id以及要合并的采购单id,如下图所示(若合并到整单不选中任何采购单id的话,系统会默认创建新的采购单)。
代码实现
@Data
public class MergeVO {
/**
* items:[1, 2] 采购项
* purchaseId:1 采购单id
*/
private List<Long> items;
private Long purchaseId;
}
/**
* 合并采购单
*/
@PostMapping("/merge")
public R mergePurchase(@RequestBody MergeVO mergeVO){
purchaseService.mergePurchase(mergeVO);
return R.ok();
}
@Transactional
@Override
public void mergePurchase(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();
}
//TODO 确认是采购单状态是0,1才可以合并
List<Long> items = mergeVO.getItems();
Long finalPurchaseId = purchaseId;
List<PurchaseDetailEntity> detailEntityList = items.stream().map(m -> {
PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
detailEntity.setId(m);
detailEntity.setPurchaseId(finalPurchaseId);
detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());
return detailEntity;
}).collect(Collectors.toList());
purchaseDetailService.updateBatchById(detailEntityList);
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setId(purchaseId);
purchaseEntity.setUpdateTime(new Date());
this.updateById(purchaseEntity);
}
2.4 领取采购单
2.4.1 需求描述:前面我们编写了将采购需求合并到某个采购单上,并且我们可以将采购单分配给具体的采购人员,采购人员可以领取采购单进行采购(此时采购单的状态变为已领取),进行商品的入库。就比如采购人员有自己的APP,当系统为他分配了采购单,APP上就会显示采购单的信息,他可以点击领取采购单进行采购。采购人员点击领取采购单后,采购需求就不能再合并到此采购单上了,而且采购单一旦领取,采购需求状态就应该变为正在采购。采购完成后APP也可以点击完成结束采购。
2.4.2 PurchaseController 层
/**
* 领取采购单
*/
@PostMapping("/received")
public R received(@RequestBody List<Long> ids){
purchaseService.received(ids);
return R.ok();
}
2.4.3 PurchaseServiceImpl层
/**
* 采购单的id
* @param ids
*/
@Override
public void received(List<Long> ids) {
//1、确认当前采购单是新建或者已分配状态
List<PurchaseEntity> collect = ids.stream().map(id -> {
PurchaseEntity byId = this.getById(id);
return byId;
}).filter(item -> {
//过滤状态:新建和已分配才能我们所要的
if (item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode() ||
item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode()) {
return true;
}
return false;
}).map(item->{
//改变状态:已领取
item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
item.setUpdateTime(new Date());
return item;
}).collect(Collectors.toList());
//2、改变采购单的状态
this.updateBatchById(collect);
//3、改变采购项的状态
collect.forEach((item)->{
List<PurchaseDetailEntity> entities = detailService.listDetailPurchaseId(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());
detailService.updateBatchById(detailEntities);
});
}
@Override
public List<PurchaseDetailEntity> listDetailPurchaseId(Long id) {
List<PurchaseDetailEntity> purchaseId = this.list(new QueryWrapper<PurchaseDetailEntity>().eq("purchase_id", id));
return purchaseId;
}
2.5 完成采购
2.5.1 前面我们编写了采购人员领取采购单的需求,采购人员通过APP领取采购单后,接下来可以进行采购。采购完成后商品的数量等信息就要进行入库操作。前端发送的请求如下:
{
#采购单id
"id": 2,
#采购项
"items": [
{"itemId": 1,
"status": 3,
"reason":""
},
{"itemId": 2,
"status": 4,
"reason":"无货"
}
]
}
2.5.2 VO 封装信息
@Data
public class PurchaseDoneVo {
//采购单id
@NotNull
private Long id;
private List<PurchaseItemDoneVo> items;
}
@Data
public class PurchaseItemDoneVo {
//{itemId:1,status:4,reason:""}
private Long itemId;
private Integer status;
private String reason;
}
2.5.3 PurchaseController 层
/**
* 完成采购单
*/
@PostMapping("/done")
public R finish(@RequestBody PurchaseDoneVo doneVo){
purchaseService.done(doneVo);
return R.ok();
}
2.5.4 PurchaseServiceImpl 层
@Transactional
@Override
public void done(PurchaseDoneVo doneVo) {
Long id = doneVo.getId();
//2.改变采购项的状态
//true是采购成功,false是采购失败
Boolean flag = true;
//items为{itemId:1,status:4,reason:""}这三值
List<PurchaseItemDoneVo> items = doneVo.getItems();
ArrayList<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.将成功采购的进行入库
PurchaseDetailEntity entity = detailService.getById(item.getItemId());
//addStock是新写方法,意思是采购成功添加库存
wareSkuService.addStock(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());
}
//采购需求设置id
detailEntity.setId(item.getItemId());
//采购需求添加遍历设置的每一条
updates.add(detailEntity);
}
//修改采购需求数据
detailService.updateBatchById(updates);
//1、改变采购单状态
PurchaseEntity purchaseEntity = new PurchaseEntity();
//设置采购id
purchaseEntity.setId(id);
//flag是true状态设置完成,flag是false状态设置失败
purchaseEntity.setStatus(flag ? WareConstant.PurchaseStatusEnum.FINISH.getCode() : WareConstant.PurchaseStatusEnum.HASERROR.getCode());
//设置时间
purchaseEntity.setUpdateTime(new Date());
//修改数据
this.updateById(purchaseEntity);
//3、将成功采购的进行入库
}
2.5.5 WareSkuServiceImpl 层
@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));
//如果还没有库存sku记录
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 {
//远程调用得到sku信息
R info = productFeignService.info(skuId);
//得到skuInfo的data是objct对象,我们把它强转为string 的map形式
Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");
//如果成功
if (info.getCode() == 0){
//也强转为string
skuEntity.setSkuName((String) data.get("skuName"));
}
}catch (Exception e){ }
wareSkuDao.insert(skuEntity);
}else {
//addStock新方法完善它
wareSkuDao.addStock(skuId,wareId,skuNum);
}
}
2.5.6 测试
原来这个采购单状态是已领取的,postman发送请求成功后,由于采购项id为2的无货,所以显示有异常。
采购项id为1的采购成功显示已完成,id为2的显示采购失败。
到采购项id为1的采购商品库存显示如下信息
为skuId为28,29,30的商品新增采购需求,如下图所示
选择5,6,7合并整单,点击确认默认新增采购单id为4的采购单
分配给采购人员admin,admin发送领取采购单请求,采购单状态变为已领取,如下图所示
这些商品状态变为正在采购中,如下图所示
完成4号采购单的采购,发送如下请求
采购单显示已完成,如下图所示
采购需求也显示已完成,如下图所示
商品库存信息如下图所示
2.5.7 feign远程查询sku信息
@FeignClient("gulimall-product")
public interface ProductFeignService {
/**
* /product/skuinfo/info/{skuId} 对应@FeignClient("gulimall-product")
* /api/product/skuinfo/info/{skuId} 对应@FeignClient("gulimall-gateway")
*
* 1)让所有请求过网关:
* 1.@FeignClient("gulimall-gateway"):给gulimall-gateway所在的机器发请求
* 2./api/product/skuinfo/info/{skuId}
* 2)直接让后台指定服务处理
* 1.@FeignClient("gulimall-product")
* 2./product/skuinfo/info/{skuId}
*/
@RequestMapping("/product/skuinfo/info/{skuId}")
public R info(@PathVariable("skuId") Long skuId);
}
2.6 spu规格维护
2.6.1 需求描述:选择 spu 管理,当商品信息第一次录入后后续许需要修改可在此界面进行修改及修改前的回显数据功能。
2.6.2 AttrController 层
@Autowired
private ProductAttrValueService productAttrValueService;
///product/attr/base/listforspu/{spuId}
@GetMapping("/base/listforspu/{spuId}")
public R baseAttrlistforspu(@PathVariable("spuId") Long spuId){
List<ProductAttrValueEntity> entities = productAttrValueService.baseAttrListForSpu(spuId);
return R.ok().put("data",entities);
}
@Override
public List<ProductAttrValueEntity> baseAttrListForSpu(Long spuId) {
List<ProductAttrValueEntity> entities =
this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
return entities;
}
2.6.3 能够会先商品的规格数据后,我们可以对这些数据进行修改。请求路径如下图所示
2.6.4 AttrController 层
///product/attr/update/{spuId} 更新spu属性
@PostMapping("/update/{spuId}")
public R updateSpuAttr(@PathVariable("spuId")Long spuId,
@RequestBody List<ProductAttrValueEntity> entities){
productAttrValueService.updateSpuAttr(spuId,entities);
return R.ok();
}
2.6.5 ProductAttrValueServiceImpl 层
@Transactional
@Override
public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
//1、删除这给spuId之前对应的所有属性
this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));
List<ProductAttrValueEntity> collect = entities.stream().map(item -> {
item.setSpuId(spuId);
return item;
}).collect(Collectors.toList());
this.saveBatch(collect);
}
重启项目,修改规格信息成功如下图所示