进行上架操作
一. 业务思路
- 根据es mapping创建java entity
- 根据spuId 查询到sku列表 根据查询到的sku列表 将sku值映射到 第一步创建的java entity中
- 设置商品热度 热度服务hotScore 先初始化为0 之后再拓展
- 第三步查询到的sku中有保存brandId 根据brandId 查询到brandName 将brandId和brandName映射到java entity中
- 第三步查询中的sku也有保存categoryId 根据categoryId 查询到categoryName 将categoryId和categoryName映射到java entity中
- 根据spuId查询出attrs 然后过滤出可以查询的attr属性 并设置java entity
- 远程查询库存 若库存大于0 啧表示有库存
- 2 - 7 步已经构造出整个es enttiy了 远程调用search服务进行上架操作
- 根据远程调用的结果判断是否上架成功
二. 具体实现
2.1 构造java entity
根据json构造java entity
{
"mappings": {
"properties": {
"skuId": {
"type": "long"
},
"spuId": {
"type": "keyword"
},
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "keyword"
},
"skuImg": {
"type": "keyword",
"index": false,
"doc_values": false
},
"saleCount": {
"type": "long"
},
"hasStock": {
"type": "boolean"
},
"notScore": {
"type": "long"
},
"brandId": {
"type": "long"
},
"catelogI": {
"type": "long"
},
"brandName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"brandImg": {
"type": "keyword",
"index": false,
"doc_values": false
},
"catelogName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrValue": {
"type": "keyword"
}
}
}
}
}
}
这里存储属性使用了静态内部类
静态内部类和普通内部类的区别是 静态内部类可以独立与外部类存在的 而普通内部类必须是以外部类为前提存在
public class SkuESEntity {
private Long skuId;
private Long spuId;
private String skuTitle;
private BigDecimal skuPrice;
private String skuImg;
private Long saleCount;
private Boolean hasStock;
private Long hotScore;
private Long brandId;
private Long catalogId;
private String brandName;
private String brandImg;
private String catalogName;
public Long getSkuId() {
return skuId;
}
public void setSkuId(Long skuId) {
this.skuId = skuId;
}
public Long getSpuId() {
return spuId;
}
public void setSpuId(Long spuId) {
this.spuId = spuId;
}
public String getSkuTitle() {
return skuTitle;
}
public void setSkuTitle(String skuTitle) {
this.skuTitle = skuTitle;
}
public BigDecimal getSkuPrice() {
return skuPrice;
}
public void setSkuPrice(BigDecimal skuPrice) {
this.skuPrice = skuPrice;
}
public String getSkuImg() {
return skuImg;
}
public void setSkuImg(String skuImg) {
this.skuImg = skuImg;
}
public Long getSaleCount() {
return saleCount;
}
public void setSaleCount(Long saleCount) {
this.saleCount = saleCount;
}
public Boolean getHasStock() {
return hasStock;
}
public void setHasStock(Boolean hasStock) {
this.hasStock = hasStock;
}
public Long getHotScore() {
return hotScore;
}
public void setHotScore(Long hotScore) {
this.hotScore = hotScore;
}
public Long getBrandId() {
return brandId;
}
public void setBrandId(Long brandId) {
this.brandId = brandId;
}
public Long getCatalogId() {
return catalogId;
}
public void setCatalogId(Long catalogId) {
this.catalogId = catalogId;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getBrandImg() {
return brandImg;
}
public void setBrandImg(String brandImg) {
this.brandImg = brandImg;
}
public String getCatalogName() {
return catalogName;
}
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
public List<Attr> getAttrs() {
return attrs;
}
public void setAttrs(List<Attr> attrs) {
this.attrs = attrs;
}
private List<Attr> attrs;
public static class Attr {
private Long attrId;
private String attrName;
private String attrValue;
public Long getAttrId() {
return attrId;
}
public void setAttrId(Long attrId) {
this.attrId = attrId;
}
public String getAttrName() {
return attrName;
}
public void setAttrName(String attrName) {
this.attrName = attrName;
}
public String getAttrValue() {
return attrValue;
}
public void setAttrValue(String attrValue) {
this.attrValue = attrValue;
}
}
}
2.2 根据spuId查询到基本信息并映射到java entity中
这没什么好说的调用mapper查询sku列表 然后編列sku列表可以 直接使用copyProperties复制bean的直接复制 不可以使用copyProperties手动复制即可
// 查询sku列表
List<PmsSkuInfo> skuInfoList = this.getSkuInfoById(spuId);
List<SkuESEntity> skuESEntityList = skuInfoList.stream().map((skuInfo) -> {
SkuESEntity skuESEntity = new SkuESEntity();
BeanUtils.copyProperties(skuInfo, skuESEntity);
skuESEntity.setSkuPrice(skuInfo.getPrice());
skuESEntity.setSkuImg(skuInfo.getSkuDefaultImg());
return skuESEntity;
}).collect(Collectors.toList());
2.3 设置热度操作
直接初始化为0即可
skuESEntity.setHotScore(0L);
2.4 设置brand信息
这个sku中都有保存 直接查询设置即可
PmsBrand brand = null;
if (brandId != null) {
brand = brandService.getById(brandId);
}
if (brand != null) {
skuESEntity.setBrandImg(brand.getLogo());
skuESEntity.setBrandName(brand.getName());
}
2.5 设置category信息
这一步和上一部基本相同 sku中也有保存
PmsCategory category = categoryService.getById(skuInfo.getCatalogId());
skuESEntity.setCatalogName(category.getName());
2.6 查询并设置商品属性
表关系如下
2.6.1首先根据spuid查询到spu中关联的所有属性
// 查询attrs
List<PmsProductAttrValue> attrValues = pmsProductAttrValueService.attrValueList(spuId);
2.6.2 拿到属性id查询attrs表中 searcType为1的的id返回 属性id列表
// 过滤出可以被检索出的属性id
List<Long> attrsIdList = attrValues.stream().map(PmsProductAttrValue::getAttrId).collect(Collectors.toList());
List<Long> searchableAttrIds = attrService.getSearchableAttrIds(attrsIdList);
过滤attr的mapper
使用in语法为查出in列表范围中的值
2.6.3 可以list.contains()可以查询id是否存在在list中 然后根据这个条件过滤attr即可 最后组装成es java 内部类 返回
List<SkuESEntity.Attr> skuEsEntityAttrs = attrValues.stream().filter((attr) -> {
return searchableAttrIds.contains(attr.getAttrId());
}).map((attr) -> {
SkuESEntity.Attr skuEsEntityAttr = new SkuESEntity.Attr();
BeanUtils.copyProperties(attr, skuEsEntityAttr);
return skuEsEntityAttr;
}).collect(Collectors.toList());
2.6.4 最后设置到es entity中
// 设置检索属性
skuESEntity.setAttrs(skuEsEntityAttrs);
2.7 远程调用库存服务
创建一个to to中保存skuId和是否有库存
判断是否有库存可以根据库存数来判断 若大于0表示有库存 否则无 因为有不同的仓库存放不同的sku 将所有仓库的库存数 - 减去所有仓库的被锁定的库存即可算出库存数
将传入的skuid列表遍历查询返回即可
public List<WareSkuHasStockTO> skuIdsHasStock(List<Long> skuIds) {
return skuIds.stream().map((skuId) -> {
WareSkuHasStockTO wareSkuHasStockTO = new WareSkuHasStockTO();
Long skuCount = wmsWareSkuMapper.getSkuStockCount(skuId);
wareSkuHasStockTO.setSkuId(skuId);
// 如果搜不到代表没库存
if (skuCount == null) {
wareSkuHasStockTO.setHasStock(false);
} else {
wareSkuHasStockTO.setHasStock(skuCount > 0);
}
return wareSkuHasStockTO;
}).collect(Collectors.toList());
}
创建fign接口
@FeignClient(value = "ware-service")
@RequestMapping("/wms-ware-sku")
public interface WareFeignService {
@PostMapping("/hasStock")
public Result hasStock(@RequestBody List<Long> skuIds);
}
调用feign接口 将data转换为map 根据map 设置库存
Result result = wareFeignService.hasStock(skuIdList);
List<Map<String, Object>> wareSkuHasStockTOMaps = (List<Map<String, Object>>) result.getData();
wareSkuHasStockMap = new HashMap<>();
Map<Long, Boolean> finalWareSkuHasStockMap1 = wareSkuHasStockMap;
wareSkuHasStockTOMaps.forEach((map) -> {
finalWareSkuHasStockMap1.put(Long.valueOf(String.valueOf(map.get("skuId"))), (Boolean) map.get("hasStock"));
});
若map = 0 则表示无库存 然后根据id获取是否有库存即可
if (finalWareSkuHasStockMap == null && finalWareSkuHasStockMap.size() <= 0) {
skuESEntity.setHasStock(false);
} else {
skuESEntity.setHasStock(finalWareSkuHasStockMap.get(skuInfo.getSkuId()));
}
2.8 原创调用es
创建一个constract用于保存常量 这里先保存个index名称
public class ElasticSearchContract {
public static final String PRODUCT_INDEX = "product";
}
然后拼装批量上传请求 这个很简单 根据文档设置index id document即可
List<BulkOperation> bulkOperations = new ArrayList<>();
skuESEntities.forEach((sku) -> {
BulkOperation bulkOperation = new BulkOperation.Builder().create((doc) -> doc.index(ElasticSearchContract.PRODUCT_INDEX)
.document(sku)
.id(sku.getSkuId().toString())).build();
bulkOperations.add(bulkOperation);
});
进行批量操作
BulkRequest bulkRequest = new BulkRequest.Builder()
.index(ElasticSearchContract.PRODUCT_INDEX).operations(bulkOperations).build();
BulkResponse bulkResponse = elasticsearchClient.bulk(bulkRequest);
如果 item.error() 中不为空 则表示该商品上架失败
bulkResponse.errors(); 可以获取是否有上架失败的列
可以记录上架失败的日志进行输出
List<BulkResponseItem> items = bulkResponse.items();
List<BulkResponseItem> upErrorSkuList = items.stream().filter((item) -> {
return item.error() != null;
}).collect(Collectors.toList());
if (upErrorSkuList.size() > 0) {
logger.error("上架失败{}", upErrorSkuList);
}
return bulkResponse.errors();
若有异常或若有上架失败的 则直接返回
@PostMapping("/product")
public Result saveProduct(@RequestBody List<SkuESEntity> skuESEntities) {
Boolean result = true;
try {
result = elasticSearchProductSaveService.productUpToEs(skuESEntities);
} catch (IOException e) {
logger.error("上架失败{}", e.getMessage());
return new Result(ResponseStatusEnum.PRODUCT_UP_ERROR.getCode(), ResponseStatusEnum.PRODUCT_UP_ERROR.getMessage(), false);
}
if (result) {
return new Result(ResponseStatusEnum.PRODUCT_UP_ERROR.getCode(), ResponseStatusEnum.PRODUCT_UP_ERROR.getMessage(), false);
} else {
return Result.ok();
}
}
2.9 进行上架操作
Result result = elasticSearchSaveFeignService.saveProduct(skuESEntityList);
if (result.getSuccess()) {
// 如果上架成功 啧修改spu 状态为上架
spuInfoMapper.changeSpuStatusUp(spuId, ProductConstruct.SpuStatus.UP_STATUS.getCode());
} else {
// todo 接口反复调用幂等性问题 上架失败重试操作
}
虽然道路是曲折的,但前途是光明的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南