PUT product
{
"mappings":{
"properties": {
"skuId":{ "type": "long" },
"spuId":{ "type": "keyword" },
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": { "type": "keyword" },
"skuImg" : { "type": "keyword" },
"saleCount":{ "type":"long" },
"hasStock": { "type": "boolean" },
"hotScore": { "type": "long" },
"brandId": { "type": "long" },
"catalogId": { "type": "long" },
"brandName": {"type": "keyword"},
"brandImg":{
"type": "keyword",
"index": false,
"doc_values": false
},
"catalogName": {"type": "keyword" },
"attrs": {
"type": "nested",
"properties": {
"attrId": {"type": "long" },
"attrName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrValue": {"type": "keyword" }
}
}
}
}
}
es默认会对数组对象扁平化处理,导致结果出现意外情况,这是我们需要指定为 nested 嵌入式类型
DELETE my_index
PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
GET my_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"user.first": "Alice"
}
},
{
"match": {
"user.last": "Smith"
}
}
]
}
}
}
# 可以查得到数据,应该是无法查询到的
GET my_index/_mapping
# 删除后重新导入数据
DELETE my_index
PUT my_index
{
"mappings": {
"properties": {
"user": {
"type": "nested"
}
}
}
}
PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
GET my_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"user.first": "Alice"
}
},
{
"match": {
"user.last": "Smith"
}
}
]
}
}
}
# 再次查询就查不到了
这节课主要是构造 up 上架商品的接口的大体框架
controller
// SpuInfoController
/// product/spuinfo/{spuinfo}/up
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId){
spuInfoService.up(spuId);
return R.ok();
}
放在 common 的 to 中,这里基本是根据 es 构造的索引创建的 bean
@Data
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;
}
}
service
@Override
public void up(Long spuId) {
// 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);
// TODO 4.查询当前 sku 的所有可以被检索的规格属性
// 2. 封装每个 sku 的信息
skus.stream().map(sku -> {
// 组装需要的数据
SkuEsModel esModel = new SkuEsModel();
BeanUtils.copyProperties(sku, esModel);
esModel.setSkuPrice(sku.getPrice());
esModel.setSkuImg(sku.getSkuDefaultImg());
// TODO 1.远程调用库存系统 是否有库存
// TODO 2.热度评分.0.
// TODO 3.品牌和分类的名字信息
BrandEntity brand = brandService.getById(esModel.getBrandId());
esModel.setBrandName(brand.getName());
esModel.setBrandImg(brand.getLogo());
CategoryEntity category = categoryService.getById(esModel.getCatalogId());
esModel.setCatalogName(category.getName());
return esModel;
}).collect(Collectors.toList());
// TODO 5. 将数据发送给 es 进行保存
}
其实不怎么复杂,基本的增删改查加上一些集合操作,体会下这里的 stream map 即可
@Override
public void up(Long spuId) {
// 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);
// TODO 4.查询当前 sku 的所有可以被检索的规格属性
List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListforspu(spuId);
List<Long> attrIds = baseAttrs.stream().map(attr -> {
return attr.getAttrId();
}).collect(Collectors.toList());
List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);
Set<Long> idSet = new HashSet<>(searchAttrIds);
List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {
return idSet.contains(item.getAttrId());
}).map(item -> {
SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(item, attrs);
return attrs;
}).collect(Collectors.toList());
// 2. 封装每个 sku 的信息
skus.stream().map(sku -> {
// 组装需要的数据
SkuEsModel esModel = new SkuEsModel();
BeanUtils.copyProperties(sku, esModel);
esModel.setSkuPrice(sku.getPrice());
esModel.setSkuImg(sku.getSkuDefaultImg());
// TODO 1.远程调用库存系统 是否有库存
// TODO 2.热度评分.0
esModel.setHotScore(0L);
// TODO 3.品牌和分类的名字信息
BrandEntity brand = brandService.getById(esModel.getBrandId());
esModel.setBrandName(brand.getName());
esModel.setBrandImg(brand.getLogo());
CategoryEntity category = categoryService.getById(esModel.getCatalogId());
esModel.setCatalogName(category.getName());
// 设置检索属性
esModel.setAttrs(attrsList);
return esModel;
}).collect(Collectors.toList());
// TODO 5. 将数据发送给 es 进行保存
}
本节也比较简单,也是老套路,注意点有两个
@Override
public void up(Long spuId) {
// 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);
List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
// TODO 1.远程调用库存系统 是否有库存
Map<Long, Boolean> stockMap = null;
try {
R<List<SkuHasStockVo>> skuHasStock = wareFeignService.getSkusHasStock(skuIdList);
stockMap = skuHasStock.getData().stream()
.collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
} catch (Exception e) {
log.error("库存服务异常:原因{}", e);
}
// TODO 4.查询当前 sku 的所有可以被检索的规格属性
List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListforspu(spuId);
List<Long> attrIds = baseAttrs.stream().map(attr -> {
return attr.getAttrId();
}).collect(Collectors.toList());
List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);
Set<Long> idSet = new HashSet<>(searchAttrIds);
List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {
return idSet.contains(item.getAttrId());
}).map(item -> {
SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(item, attrs);
return attrs;
}).collect(Collectors.toList());
// 2. 封装每个 sku 的信息
Map<Long, Boolean> finalStockMap = stockMap;
skus.stream().map(sku -> {
// 组装需要的数据
SkuEsModel esModel = new SkuEsModel();
BeanUtils.copyProperties(sku, esModel);
esModel.setSkuPrice(sku.getPrice());
esModel.setSkuImg(sku.getSkuDefaultImg());
// 设置库存信息
if (finalStockMap == null) {
esModel.setHasStock(true);
} else {
esModel.setHasStock(finalStockMap.get(sku.getSkuId()));
}
// TODO 2.热度评分.0
esModel.setHotScore(0L);
// TODO 3.品牌和分类的名字信息
BrandEntity brand = brandService.getById(esModel.getBrandId());
esModel.setBrandName(brand.getName());
esModel.setBrandImg(brand.getLogo());
CategoryEntity category = categoryService.getById(esModel.getCatalogId());
esModel.setCatalogName(category.getName());
// 设置检索属性
esModel.setAttrs(attrsList);
return esModel;
}).collect(Collectors.toList());
// TODO 5. 将数据发送给 es 进行保存
}
给 R 添加泛型
/**
* 返回数据
*
* @author Mark [email protected]
*/
public class R<T> extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
feign
@FeignClient("gulimall-ware")
public interface WareFeignService {
/**
* 1.R 设计的时候加上泛型
*
* 2.直接返回我们需要的数据
*
* 3.自己封装解析结果
*
* @param skuIds
* @return
*/
@PostMapping("/ware/waresku/hasstock")
R<List<SkuHasStockVo>> getSkusHasStock(@RequestBody List<Long> skuIds);
}
WareSkuController
// 查询 sku 是否有库存
@PostMapping("/hasstock")
public R<List<SkuHasStockVo>> getSkusHasStock(@RequestBody List<Long> skuIds) {
// sku_id, stock
List<SkuHasStockVo> vos = wareSkuService.getSkusHasStock(skuIds);
R<List<SkuHasStockVo>> ok = R.ok();
ok.setData(vos);
return ok;
}
@Override
public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
SkuHasStockVo vo = new SkuHasStockVo();
// 查看当前 sku 的库存总量
long count = baseMapper.getSkuStock(skuId);
vo.setSkuId(skuId);
vo.setHasStock(count>0);
return vo;
}).collect(Collectors.toList());
return collect;
}
// mapper
long getSkuStock(Long skuId);
<select id="getSkuStock" resultType="java.lang.Long">
select SUM(stock - stock_locked)
from `wms_ware_sku`
where sku_id = #{skuId};
select>
这里的实现思路是调用远程服务 search ,在 search 模块里面把 商品的数据传到 es ,远程调用成功把spu状态改为已上架
但这里上架成功之后,没有修改上架状态
这里要提一嘴,这里雷神又debug了下,因为 R 是 hashmap ,序列化时会失效,改了另外一种方式,用了泛型
public R setData(Object data) {
put("data", data);
return this;
}
// 利用 fastjson 进行你装
public <T> T getData(TypeReference<T> typeReference) {
Object data = get("data"); // 默认是 map
String s = JSON.toJSONString(data);
T t = JSON.parseObject(s, typeReference);
return t;
}
R r = wareFeignService.getSkusHasStock(skuIdList);
TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>(){};
stockMap = r.getData(typeReference).stream()
.collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
其实这个功能我这边也常用,就是map转具体类的,不过用的形参不是TypeReference,直接传的 Class ,下面给出我这边的工具类供参考
public class MapObjectUtil {
// 改为 借助于 json 对象实现,兼容现在的 json 转换体系
//map转java对象
public static Object mapToObjectJson(Map<String, Object> map, Class<?> beanClass) throws Exception {
String jsonStr = JSONObject.toJSONString(map);
return JSONObject.parseObject(jsonStr, beanClass);
}
//java对象转map
public static Map<String, Object> objectToMapJson(Object obj) {
String jsonStr = JSONObject.toJSONString(obj);
return JSONObject.parseObject(jsonStr);
}
}
上架商品的功能就写到这里了,后续应该还会再完善简化下,其实很多都是重复的增删改查,主要还是看看 es 的应用场景,还有 远程调用的时候处理的一些细节,但可能这块得到的知识容量还是不怎么大