写这个文章主要是为了梳理业务,理清思路。
1.定义我们上传产品的映射
这是我们对上传产品的映射信息,主要是sku表的信息和attr属性表的信息。
attrs的类型是嵌套。属性表查得信息必须是可以检索的,比如我们可以在京东的搜索页面,通过选择cpu的型号进行搜索匹配。
然后热点值hitscore以后完善。然后需要查询我们是否具有库存hasStock,通过各种数据的处理来进行封装。
2 定义映射对象
@Data
/**
* 本质上传输对象 实际上在product和搜索微服务都应该有该对象
*/
public class SkuEsModel {
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;
private List<Attrs> attrs;
//检索属性
@Data
public static class Attrs {
private Long attrId;
private String attrName;
private String attrValue;
}
}
3业务实现
3.1编写上架接口,传入spuId
/**
* 实现商品上架
*/
@PostMapping("{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId) {
spuInfoService.spuUp(spuId);
return R.ok();
}
3.2对service层数据进行处理封装
3.2.1 第一步根据spuId查出所有的sku信息
List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);
3.2 .2查询sku可以被检索的规格属性
controller层
根据spuId查出在产品属性表的属性id的集合,封装为set
List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrList(spuId);
List<Long> attrIds = baseAttrs.stream().map(productAttrValueEntity -> {
return productAttrValueEntity.getAttrId();
}).collect(Collectors.toList());
根据返回的属性ld的集合,查找属性,而且是可以被检索的索性,返回值存入set集合中,方便以后进行校验
<select id="selectSearchAttrs" resultType="java.lang.Long">
select * from pms_attr
where attr_id in
<foreach collection="attrIds" item="id" separator="," open="(" close=")">#{id}</foreach>
and search_type=1
</select>
然后把数据封装到attrs中,对于任何一个spu来讲,他的attrs都是相同的。
//把数据封装attrs
List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(productAttrValueEntity -> {
return set.contains(productAttrValueEntity.getAttrId());
}).map(productAttrValueEntity -> {
SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(productAttrValueEntity, attrs);
return attrs;
}).collect(Collectors.toList());
3.2.3 对库存数据进行查询,由于使用stream流每次根据spu查询会比较繁琐,消耗性能,我们把库存统一查询。首先或许所有的skuId的集合,最后封装为kv map形式的数据。方便数据传输,专门做了vo类。
public class SkuHasStockVo {
private Long skuId;
private boolean hasStock; //是否有库存
}
在fegin调用查询数据是,注意返回的类型包装,R类型继承hashmap,需要我们根据类 TypeReference进行处理(阿里提供),利用json进行复杂类型的转换,仅仅依靠泛型是无法转换的,我们的数据类型是List
/**
* @param key 获取指定key的名字
*/
public <T> T getData(String key, TypeReference<T> typeReference){
// get("data") 默认是map类型 所以再由map转成string再转json
Object data = get(key);
return JSON.parseObject(JSON.toJSONString(data), typeReference);
}
/**
* 复杂类型转换 TypeReference
* @param typeReference
*/
public <T> T getData(TypeReference<T> typeReference){
// get("data") 默认是map类型 所以再由map转成string再转json
Object data = get("data");
String s = JSON.toJSONString(data);
T t = JSON.parseObject(s, typeReference);
return t;
}
fegin调用service时的封装
最后就是我们取到数据,转换传入类型转换为map格式,我们通过key(skuId)就可以查到值是否有库存
List<Long> skuIds = skus.stream().map(sku -> {
return sku.getSkuId();
}).collect(Collectors.toList());
Map<Long, Boolean> stockMap=null;
try {
R r = wareFeginService.getSkusHasStocks(skuIds);
TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {
};
stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, skuHasStockVo -> skuHasStockVo.isHasStock()));
log.warn("服务调用成功" + typeReference);
} catch (Exception e) {
log.error("网络抖动查不到库存数据{}"+e);
}
4数据封装,传递给es上架,实现数据迁移
List<SkuEsModel> upProducts = skus.stream().map(sku -> {
//组装需要的数据
SkuEsModel skuEsModel = new SkuEsModel();
BeanUtils.copyProperties(sku, skuEsModel);
//skuPrice,skuImg,hotscore
skuEsModel.setSkuPrice(sku.getPrice());
skuEsModel.setSkuImg(sku.getSkuDefaultImg());
//查询是否还有库存
if(finalStockMap ==null){
skuEsModel.setHasStock(true);
}else{
skuEsModel.setHasStock(finalStockMap.get(sku.getSkuId()));
}
//TODO 2 热度评分
skuEsModel.setHotScore( 0l);
//品牌名字 分类名字
BrandEntity brandEntity = brandService.getById(skuEsModel.getBrandId());
skuEsModel.setBrandName(brandEntity.getName());
skuEsModel.setBrandImg(brandEntity.getLogo());
categoryService.getById(skuEsModel.getCatalogId());
//设置检索属性
skuEsModel.setAttrs(attrsList);
return skuEsModel;
}).collect(Collectors.toList());
装好数据调用搜索服务上架,上架成功就要修改spu的状态,新建转为上架状态。
这是搜索的服务层
R r = searchFeginService.startProduct(upProducts);
if(r.getCode()==0){
//商品上架成功
//修改spu的状态
baseMapper.updateSpuStatus(spuId,ProductConstant.StatusEnum.UP_SPU.getCode());
}else{
`` //2 给es保存这些数据
BulkRequest bulkRequest = new BulkRequest();
for (SkuEsModel skuEsModel : skuEsModels) {
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
indexRequest.id(skuEsModel.getSkuId().toString());
String s = JSON.toJSONString(skuEsModel);
indexRequest.source(s,XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimailESConfig.COMMON_OPTIONS);
//如果出现上传错误 TODO
boolean hasFailures = bulk.hasFailures();
List<String> ids = Arrays.stream(bulk.getItems()).map(item -> {
return item.getId();
}
).collect(Collectors.toList());
log.error("商品上传{} 状态"+ids+bulk.toString());
return hasFailures;
}
controller层
@PostMapping("/product")
public R startProduct(@RequestBody List<SkuEsModel> skuEsModels) {
boolean b=false;
try {
b = productSaveService.startProduct(skuEsModels);
} catch (IOException e) {
log.error("ElasticSaveController商品上传出现异常",e);
return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMessage());
}
if(!b){return R.ok();}else { return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMessage());}
}
}