diff --git a/ruoyi-ui/src/plugins/tab.js b/ruoyi-ui/src/plugins/tab.js index 95a3848b..f5d7a3e0 100644 --- a/ruoyi-ui/src/plugins/tab.js +++ b/ruoyi-ui/src/plugins/tab.js @@ -1,66 +1,67 @@ -import store from '@/store' -import router from '@/router'; - -export default { - // 刷新当前tab页签 - refreshPage(obj) { - const { path, matched } = router.currentRoute; - if (obj === undefined) { - matched.forEach((m) => { - if (m.components && m.components.default && m.components.default.name) { - if (!['Layout', 'ParentView'].includes(m.components.default.name)) { - obj = { name: m.components.default.name, path: path }; - } - } - }); - } - return store.dispatch('tagsView/delCachedView', obj).then(() => { - const { path } = obj - router.replace({ - path: '/redirect' + path - }) - }) - }, - // 关闭当前tab页签,打开新页签 - closeOpenPage(obj) { - store.dispatch("tagsView/delView", router.currentRoute); - if (obj !== undefined) { - return router.push(obj); - } - }, - // 关闭指定tab页签 - closePage(obj) { - if (obj === undefined) { - return store.dispatch('tagsView/delView', router.currentRoute).then(({ lastPath }) => { - return router.push(lastPath || '/'); - }); - } - return store.dispatch('tagsView/delView', obj); - }, - // 关闭所有tab页签 - closeAllPage() { - return store.dispatch('tagsView/delAllViews'); - }, - // 关闭左侧tab页签 - closeLeftPage(obj) { - return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute); - }, - // 关闭右侧tab页签 - closeRightPage(obj) { - return store.dispatch('tagsView/delRightTags', obj || router.currentRoute); - }, - // 关闭其他tab页签 - closeOtherPage(obj) { - return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute); - }, - // 添加tab页签 - openPage(title, url) { - var obj = { path: url, meta: { title: title } } - store.dispatch('tagsView/addView', obj); - return router.push(url); - }, - // 修改tab页签 - updatePage(obj) { - return store.dispatch('tagsView/updateVisitedView', obj); - } -} +import store from '@/store' +import router from '@/router'; + +export default { + // 刷新当前tab页签 + refreshPage(obj) { + const { path, query, matched } = router.currentRoute; + if (obj === undefined) { + matched.forEach((m) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { name: m.components.default.name, path: path, query: query }; + } + } + }); + } + return store.dispatch('tagsView/delCachedView', obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj) { + store.dispatch("tagsView/delView", router.currentRoute); + if (obj !== undefined) { + return router.push(obj); + } + }, + // 关闭指定tab页签 + closePage(obj) { + if (obj === undefined) { + return store.dispatch('tagsView/delView', router.currentRoute).then(({ lastPath }) => { + return router.push(lastPath || '/'); + }); + } + return store.dispatch('tagsView/delView', obj); + }, + // 关闭所有tab页签 + closeAllPage() { + return store.dispatch('tagsView/delAllViews'); + }, + // 关闭左侧tab页签 + closeLeftPage(obj) { + return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute); + }, + // 关闭右侧tab页签 + closeRightPage(obj) { + return store.dispatch('tagsView/delRightTags', obj || router.currentRoute); + }, + // 关闭其他tab页签 + closeOtherPage(obj) { + return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute); + }, + // 添加tab页签 + openPage(title, url) { + var obj = { path: url, meta: { title: title } } + store.dispatch('tagsView/addView', obj); + return router.push(url); + }, + // 修改tab页签 + updatePage(obj) { + return store.dispatch('tagsView/updateVisitedView', obj); + } +} diff --git a/xjs-business/xjs-business-common/src/main/java/com/xjs/consts/EsConst.java b/xjs-business/xjs-business-common/src/main/java/com/xjs/consts/EsConst.java index 98d53326..58638f29 100644 --- a/xjs-business/xjs-business-common/src/main/java/com/xjs/consts/EsConst.java +++ b/xjs-business/xjs-business-common/src/main/java/com/xjs/consts/EsConst.java @@ -2,6 +2,7 @@ package com.xjs.consts; /** * ES常量 + * * @author xiejs * @since 2022-04-06 */ @@ -9,5 +10,10 @@ public class EsConst { /** * sku数据在ES中的索引 */ - public static final String PRODUCT_INDEX = "product"; + public static final String PRODUCT_INDEX = "xjs_product"; + + + public static final int PRODUCT_PAGESIZE = 2; + + } diff --git a/xjs-business/xjs-project-mall/mall-product/src/main/resources/static/index/js/catalogLoader.js b/xjs-business/xjs-project-mall/mall-product/src/main/resources/static/index/js/catalogLoader.js index 60b51f01..3e5a1a3c 100644 --- a/xjs-business/xjs-project-mall/mall-product/src/main/resources/static/index/js/catalogLoader.js +++ b/xjs-business/xjs-project-mall/mall-product/src/main/resources/static/index/js/catalogLoader.js @@ -19,7 +19,7 @@ $(function(){ var ctg3List=ctg2["catalog3List"]; var len=0; $.each(ctg3List,function (i,ctg3) { - var cata3link = $("" + ctg3.name + ""); + var cata3link = $("" + ctg3.name + ""); li.append(cata3link); len=len+1+ctg3.name.length; }); @@ -40,4 +40,4 @@ $(function(){ } }); }); -}); \ No newline at end of file +}); diff --git a/xjs-business/xjs-project-mall/mall-product/src/main/resources/templates/index.html b/xjs-business/xjs-project-mall/mall-product/src/main/resources/templates/index.html index c17bb6e7..430e8925 100644 --- a/xjs-business/xjs-project-mall/mall-product/src/main/resources/templates/index.html +++ b/xjs-business/xjs-project-mall/mall-product/src/main/resources/templates/index.html @@ -115,7 +115,7 @@ - +
@@ -203,7 +203,7 @@
  • - +
@@ -611,7 +611,7 @@ diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/controller/SearchController.java b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/controller/SearchController.java new file mode 100644 index 00000000..06cae8e5 --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/controller/SearchController.java @@ -0,0 +1,37 @@ +package com.xjs.mall.search.controller; + +import com.xjs.mall.search.service.MallSearchService; +import com.xjs.mall.search.vo.SearchParam; +import com.xjs.mall.search.vo.SearchResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * @author xiejs + * @since 2022-05-11 + */ +@Controller +public class SearchController { + + @Autowired + MallSearchService mallSearchService; + + /** + * 自动将页面提交过来的所有请求查询参数封装成指定的对象 + * + * @param param 检索条件 + * @return url + */ + @GetMapping("/search.html") + public String listPage(SearchParam param, Model model) { + //1、根据传递来的页面的查询参数,去es检索商品 + SearchResult result = mallSearchService.search(param); + + model.addAttribute("result", result); + + return "list"; + } + +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/MallSearchService.java b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/MallSearchService.java new file mode 100644 index 00000000..f00925d7 --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/MallSearchService.java @@ -0,0 +1,19 @@ +package com.xjs.mall.search.service; + +import com.xjs.mall.search.vo.SearchParam; +import com.xjs.mall.search.vo.SearchResult; + +/** + * @author xiejs + * @since 2022-05-11 + */ +public interface MallSearchService { + + /** + * + * @param searchParam 检索的所有参数 + * @return obj + */ + SearchResult search(SearchParam searchParam); + +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/impl/MallSearchServiceImpl.java b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/impl/MallSearchServiceImpl.java new file mode 100644 index 00000000..178d1d87 --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/service/impl/MallSearchServiceImpl.java @@ -0,0 +1,370 @@ +package com.xjs.mall.search.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.utils.StringUtils; +import com.xjs.consts.EsConst; +import com.xjs.mall.search.config.ElasticsearchConfig; +import com.xjs.mall.search.service.MallSearchService; +import com.xjs.mall.search.vo.SearchParam; +import com.xjs.mall.search.vo.SearchResult; +import com.xjs.mall.to.es.SkuEsModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.NestedQueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.RangeQueryBuilder; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; +import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; +import org.elasticsearch.search.sort.SortOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 商城检索service + * + * @author xiejs + * @since 2022-05-11 + */ +@Service +@Slf4j +public class MallSearchServiceImpl implements MallSearchService { + + @Autowired + private RestHighLevelClient client; + + @Override + public SearchResult search(SearchParam searchParam) { + //动态构建查询需要的DSL语句 + + //1、准备检索请求 + SearchRequest searchRequest = this.buildSearchRequest(searchParam); + + SearchResult searchResult = null; + + try { + //2、执行检索请求 + SearchResponse response = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS); + + //3、分析响应数据封装成需要的数据 + searchResult = this.buildSearchResult(response,searchParam); + + + } catch (IOException e) { + e.printStackTrace(); + } + + return searchResult; + } + + /** + * 构建请求数据 + * + * @return 请求数据 + */ + private SearchRequest buildSearchRequest(SearchParam searchParam) { + //构建DSL语句 + SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); + + //模糊匹配,过滤(按照属性、分类、品牌、价格区间、库存) + + //1、构建bool-query + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + + //1.1、must模糊匹配 + if (!StringUtils.isEmpty(searchParam.getKeyword())) { + boolQuery.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword())); + } + + //1.2、bool-filter 按照三级分类id查询 + if (searchParam.getCatalog3Id() != null) { + boolQuery.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id())); + } + + //1.2、bool-filter 按照品牌id查询 + if (CollUtil.isNotEmpty(searchParam.getBrandId())) { + boolQuery.filter(QueryBuilders.termQuery("brandId", searchParam.getBrandId())); + } + + //1.2、bool-filter 按照是否有库存查询 + boolQuery.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1)); + + //1.2、bool-filter 按照价格区间查询 + if (StringUtils.isNotEmpty(searchParam.getSkuPrice())) { + RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice"); + String[] split = searchParam.getSkuPrice().split("_"); + if (split.length == 2) { + //区间 + rangeQuery.gte(split[0]).lte(split[1]); + } else if (split.length == 1) { + if (searchParam.getSkuPrice().startsWith("_")) { + rangeQuery.lte(split[0]); + } + if (searchParam.getSkuPrice().endsWith("_")) { + rangeQuery.gt(split[0]); + } + } + + + boolQuery.filter(rangeQuery); + } + + //1.2、bool-filter 按照所有指定的属性查询 + if (CollUtil.isNotEmpty(searchParam.getAttrs())) { + + for (String attrStr : searchParam.getAttrs()) { + BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery(); + + String[] split = attrStr.split("_"); + String attrId = split[0]; //检索的属性id + String[] attrValues = split[1].split(";"); //属性的检索用的值 + nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId)); + nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", attrValues)); + + //每一个必须都得生成一个nested查询 + NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None); + boolQuery.filter(nestedQuery); + } + } + + sourceBuilder.query(boolQuery); + + + //排序、分页、高亮 + + //2.1、排序 + String sort = searchParam.getSort(); + if (StringUtils.isNotEmpty(sort)) { + String[] split = sort.split("_"); + SortOrder order = split[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC; + sourceBuilder.sort(split[0], order); + } + + //2.2、分页 + sourceBuilder.from((searchParam.getPageNum() - 1) * EsConst.PRODUCT_PAGESIZE); + sourceBuilder.size(EsConst.PRODUCT_PAGESIZE); + + //2.3、高亮 + if (StringUtils.isNotEmpty(searchParam.getKeyword())) { + HighlightBuilder highlightBuilder = new HighlightBuilder(); + highlightBuilder.field("skuTitle"); + highlightBuilder.preTags(""); + highlightBuilder.postTags(""); + sourceBuilder.highlighter(highlightBuilder); + } + + + //聚合分析 + + //1. 按照品牌进行聚合 + TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg"); + brand_agg.field("brandId").size(50); + + //1.1 品牌的子聚合-品牌名聚合 + brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1)); + //1.2 品牌的子聚合-品牌图片聚合 + brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1)); + sourceBuilder.aggregation(brand_agg); + + //2. 按照分类信息进行聚合 + TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg"); + catalog_agg.field("catalogId").size(20); + catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1)); + sourceBuilder.aggregation(catalog_agg); + + // 3. 按照属性信息进行聚合 + NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs"); + //3.1 按照属性ID进行聚合 + TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId"); + attr_agg.subAggregation(attr_id_agg); + //3.1.1 在每个属性ID下,按照属性名进行聚合 + attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1)); + //3.1.2 在每个属性ID下,按照属性值进行聚合 + attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50)); + sourceBuilder.aggregation(attr_agg); + + log.info(sourceBuilder.toString()); + + return new SearchRequest(new String[]{EsConst.PRODUCT_INDEX}, sourceBuilder); + } + + /** + * 构建结果数据 + * + * @param response 返回数据 + * @return 返回数据 + */ + private SearchResult buildSearchResult(SearchResponse response, SearchParam param) { + SearchResult result = new SearchResult(); + + //1、返回的所有查询到的商品 + SearchHits hits = response.getHits(); + + List esModels = new ArrayList<>(); + //遍历所有商品信息 + if (hits.getHits() != null && hits.getHits().length > 0) { + for (SearchHit hit : hits.getHits()) { + String sourceAsString = hit.getSourceAsString(); + SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class); + + //判断是否按关键字检索,若是就显示高亮,否则不显示 + if (!StringUtils.isEmpty(param.getKeyword())) { + //拿到高亮信息显示标题 + HighlightField skuTitle = hit.getHighlightFields().get("skuTitle"); + String skuTitleValue = skuTitle.getFragments()[0].string(); + esModel.setSkuTitle(skuTitleValue); + } + esModels.add(esModel); + } + } + result.setProducts(esModels); + + //2、当前商品涉及到的所有属性信息 + List attrVos = new ArrayList<>(); + //获取属性信息的聚合 + ParsedNested attrsAgg = response.getAggregations().get("attr_agg"); + ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg"); + for (Terms.Bucket bucket : attrIdAgg.getBuckets()) { + SearchResult.AttrVo attrVo = new SearchResult.AttrVo(); + //1、得到属性的id + long attrId = bucket.getKeyAsNumber().longValue(); + attrVo.setAttrId(attrId); + + //2、得到属性的名字 + ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg"); + String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString(); + attrVo.setAttrName(attrName); + + //3、得到属性的所有值 + ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg"); + List attrValues = attrValueAgg.getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList()); + attrVo.setAttrValue(attrValues); + + attrVos.add(attrVo); + } + + result.setAttrs(attrVos); + + //3、当前商品涉及到的所有品牌信息 + List brandVos = new ArrayList<>(); + //获取到品牌的聚合 + ParsedLongTerms brandAgg = response.getAggregations().get("brand_agg"); + for (Terms.Bucket bucket : brandAgg.getBuckets()) { + SearchResult.BrandVo brandVo = new SearchResult.BrandVo(); + + //1、得到品牌的id + long brandId = bucket.getKeyAsNumber().longValue(); + brandVo.setBrandId(brandId); + + //2、得到品牌的名字 + ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg"); + String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString(); + brandVo.setBrandName(brandName); + + //3、得到品牌的图片 + ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg"); + String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString(); + brandVo.setBrandImg(brandImg); + + brandVos.add(brandVo); + } + result.setBrands(brandVos); + + //4、当前商品涉及到的所有分类信息 + //获取到分类的聚合 + List catalogVos = new ArrayList<>(); + ParsedLongTerms catalogAgg = response.getAggregations().get("catalog_agg"); + for (Terms.Bucket bucket : catalogAgg.getBuckets()) { + SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo(); + //得到分类id + String keyAsString = bucket.getKeyAsString(); + catalogVo.setCatalogId(Long.parseLong(keyAsString)); + + //得到分类名 + ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg"); + String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString(); + catalogVo.setCatalogName(catalogName); + catalogVos.add(catalogVo); + } + + result.setCatalogs(catalogVos); + //===============以上可以从聚合信息中获取====================// + + //5、分页信息-页码 + result.setPageNum(param.getPageNum()); + //5、1分页信息、总记录数 + long total = hits.getTotalHits().value; + result.setTotal(total); + + //5、2分页信息-总页码-计算 + int totalPages = (int) total % EsConst.PRODUCT_PAGESIZE == 0 ? + (int) total / EsConst.PRODUCT_PAGESIZE : ((int) total / EsConst.PRODUCT_PAGESIZE + 1); + result.setTotalPages(totalPages); + + List pageNavs = new ArrayList<>(); + for (int i = 1; i <= totalPages; i++) { + pageNavs.add(i); + } + result.setPageNavs(pageNavs); + + //6、构建面包屑导航 + /*if (param.getAttrs() != null && param.getAttrs().size() > 0) { + List collect = param.getAttrs().stream().map(attr -> { + //1、分析每一个attrs传过来的参数值 + SearchResult.NavVo navVo = new SearchResult.NavVo(); + String[] s = attr.split("_"); + navVo.setNavValue(s[1]); + R r = productFeignService.attrInfo(Long.parseLong(s[0])); + if (r.getCode() == 0) { + AttrResponseVo data = r.getData("attr", new TypeReference() { + }); + navVo.setNavName(data.getAttrName()); + } else { + navVo.setNavName(s[0]); + } + + //2、取消了这个面包屑以后,我们要跳转到哪个地方,将请求的地址url里面的当前置空 + //拿到所有的查询条件,去掉当前 + String encode = null; + try { + encode = URLEncoder.encode(attr, "UTF-8"); + encode.replace("+", "%20"); //浏览器对空格的编码和Java不一样,差异化处理 + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + String replace = param.get_queryString().replace("&attrs=" + attr, ""); + navVo.setLink("http://search.gulimall.com/list.html?" + replace); + + return navVo; + }).collect(Collectors.toList()); + + result.setNavs(collect); + }*/ + + log.info(result.toString()); + + return result; + } + + +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchParam.java b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchParam.java new file mode 100644 index 00000000..bacda75a --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchParam.java @@ -0,0 +1,64 @@ +package com.xjs.mall.search.vo; + +import lombok.Data; + +import java.util.List; + +/** + * es 检索条件实体 + * + * @author xiejs + * @since 2022-05-11 + */ +@Data +public class SearchParam { + + /** + * 页面传递的全文匹配关键字 + */ + private String keyword; + + /** + * 三级分类id + */ + private Long catalog3Id; + + /** + * 排序条件 saleCount_asc/desc skuPrice_asc/desc hotScore_asc/desc + */ + private String sort; + + + /** + * 过滤条件 + * hasStock(是否有货) 、skuPrice区间 、 brandId 、 catalog3Id 、 attrs + */ + + + /** + * 是否只显示有货(0:无库存 1:有库存) + */ + private Integer hasStock = 1; + + /** + * 价格区间查询 + */ + private String skuPrice; + + /** + * 品牌id + */ + private List brandId; + + /** + * 按照属性进行筛选 + */ + private List attrs; + + /** + * 页码 + */ + private Integer pageNum = 1; + + +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchResult.java b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchResult.java new file mode 100644 index 00000000..fa1c6b8d --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/java/com/xjs/mall/search/vo/SearchResult.java @@ -0,0 +1,120 @@ +package com.xjs.mall.search.vo; + +import com.xjs.mall.to.es.SkuEsModel; +import lombok.Data; + +import java.util.List; + +/** + * 检索响应信息实体 + * + * @author xiejs + * @since 2022-05-11 + */ +@Data +public class SearchResult { + + /** + * 查询到的所有商品信息 + */ + private List products; + + /** + * 当前页面 + */ + private Integer pageNum; + + /** + * 总数 + */ + private Long total; + + /** + * 总页码 + */ + private Integer totalPages; + + /** + * 当前结果查询的所有品牌 + */ + private List brands; + + /** + * 当前结果查询的所有属性 + */ + private List attrs; + + /** + * 当前结果查询的所有分类 + */ + private List catalogs; + + private List pageNavs; + + //================以上是返回给页面的所有信息========================= + + /* 面包屑导航数据 */ + private List navs; + + @Data + public static class NavVo { + private String navName; + private String navValue; + private String link; + } + + + @Data + public static class BrandVo { + /** + * 品牌id + */ + private Long brandId; + + /** + * 品牌名 + */ + private String brandName; + + /** + * 品牌logo + */ + private String brandImg; + } + + @Data + public static class AttrVo { + + /** + * 属性id + */ + private Long attrId; + + /** + * 属性名 + */ + private String attrName; + + /** + * 属性值 + */ + private List attrValue; + } + + @Data + public static class CatalogVo { + + /** + * 分类ID + */ + private Long catalogId; + + /** + * 分类名称 + */ + private String catalogName; + + } + + +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/resources/es/DSL.json b/xjs-business/xjs-project-mall/mall-search/src/main/resources/es/DSL.json new file mode 100644 index 00000000..7c96b65a --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/resources/es/DSL.json @@ -0,0 +1,99 @@ +{ + "query": { + "bool": { + "must": [ {"match": { "skuTitle": "华为" }} ], + "filter": [ + { "term": { "catalogId": "225" } }, + { "terms": {"brandId": [ "2"] } }, + { "term": { "hasStock": "false"} }, + { + "range": { + "skuPrice": { + "gte": 1000, + "lte": 7000 + } + } + }, + { + "nested": { + "path": "attrs", + "query": { + "bool": { + "must": [ + { + "term": { "attrs.attrId": { "value": "6"} } + } + ] + } + } + } + } + ] + } + }, + "sort": [ {"skuPrice": {"order": "desc" } } ], + "from": 0, + "size": 5, + "highlight": { + "fields": {"skuTitle": {}}, + "pre_tags": "", + "post_tags": "" + }, + "aggs": { + "brandAgg": { + "terms": { + "field": "brandId", + "size": 10 + }, + "aggs": { + "brandNameAgg": { + "terms": { + "field": "brandName", + "size": 10 + } + }, + + "brandImgAgg": { + "terms": { + "field": "brandImg", + "size": 10 + } + } + + } + }, + "catalogAgg":{ + "terms": { + "field": "catalogId", + "size": 10 + }, + "aggs": { + "catalogNameAgg": { + "terms": { + "field": "catalogName", + "size": 10 + } + } + } + }, + "attrs":{ + "nested": {"path": "attrs" }, + "aggs": { + "attrIdAgg": { + "terms": { + "field": "attrs.attrId", + "size": 10 + }, + "aggs": { + "attrNameAgg": { + "terms": { + "field": "attrs.attrName", + "size": 10 + } + } + } + } + } + } + } +} diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/resources/product-mapping.json b/xjs-business/xjs-project-mall/mall-search/src/main/resources/es/product-mapping.json similarity index 69% rename from xjs-business/xjs-project-mall/mall-search/src/main/resources/product-mapping.json rename to xjs-business/xjs-project-mall/mall-search/src/main/resources/es/product-mapping.json index 57e396f0..430062ec 100644 --- a/xjs-business/xjs-project-mall/mall-search/src/main/resources/product-mapping.json +++ b/xjs-business/xjs-project-mall/mall-search/src/main/resources/es/product-mapping.json @@ -15,14 +15,12 @@ "type": "keyword" }, "skuImg": { - "type": "keyword", - "index": false, - "doc_values": false + "type": "keyword" }, "saleCount": { "type": "long" }, - "hasStock": { + "hosStock": { "type": "boolean" }, "hotScore": { @@ -34,19 +32,14 @@ "catalogId": { "type": "long" }, - "catalogName": { - "type": "text", - "analyzer": "ik_smart" - }, "brandName": { - "type": "keyword", - "index": false, - "doc_values": false + "type": "keyword" }, "brandImg": { - "type": "keyword", - "index": false, - "doc_values": true + "type": "keyword" + }, + "catalogName": { + "type": "keyword" }, "attrs": { "type": "nested", @@ -55,9 +48,7 @@ "type": "long" }, "attrName": { - "type": "keyword", - "index": false, - "doc_values": false + "type": "keyword" }, "attrValue": { "type": "keyword" diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/index.html b/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/index.html deleted file mode 100644 index cba3084c..00000000 --- a/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/index.html +++ /dev/null @@ -1,3677 +0,0 @@ - - - - - - - - - - - Document - - - -
-
- - - - - -
-
- - -
- -
- - 搜索 -
-
-
- 我的购物车 - - 0 -
-
-

购物车中还没有商品,赶紧选购吧!

-
-
-
- -
- -
- - -
-
- -
- - - - - - - - -
-
- - -
- - - -
-
- -
- -
- - 1/169 - - - -
-
- - - -
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- -
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 - ¥1699.00 - ¥1699.00 - ¥1688.00 -

-

- - 荣耀8 4GB+64GB 全网通4G手机 魅海蓝 - - - 荣耀8 4GB+64GB 全网通4G手机 珠光白 - - - 荣耀8 4GB+64GB 全网通4G手机 樱语粉 - - - 荣耀8 4GB+64GB 全网通4G手机 流光金 - -

-

已有8万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥999.00 - ¥1099.00 - ¥1099.00 -

-

- - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 冰河银 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 铂光金 - - - 荣耀 畅玩6X 4GB 32GB 全网通4G手机 高配版 玫瑰金 - -

-

已有15万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
-
- - 关注 -
-

- - - -

- -

- ¥1699.00 -

-

- Apple iPhone X (A1865) 256GB 深空灰色 移动联通电信4G手机

-

已有52万+热门评价 - 二手有售 -

-

谷粒商城Apple产品... - - - -

-
-
-

自营 - 谷粒商城自营,品质保证 -

-

满赠 - 该商品参加满赠活动 -

-
-
-
-
- -
-
- - - < 上一页 - - 1 - 2 - 3 - ... - 169 - - 下一页 > - - - - 169页  到第 - - - 确定 - -
-
-
-
-
-
-
- - - - - - - - -
-
-
- 我的足迹 - - 更多浏览记录 - -
-
-
    -
  • -
    - - - -
    -
    - - ¥2998.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥88.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥199.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥799.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥599.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥699.00 - -
    -
  • -
  • -
    - - - -
    -
    - - ¥715.00 - -
    -
  • -
- - -
-
-
- -
- - - - -
-
- - -
-
- - - - diff --git a/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/list.html b/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/list.html new file mode 100644 index 00000000..ea211076 --- /dev/null +++ b/xjs-business/xjs-project-mall/mall-search/src/main/resources/templates/list.html @@ -0,0 +1,1957 @@ + + + + + + + + + + + Document + + + +
+
+ + + + + +
+
+ + +
+ +
+ + 搜索 +
+
+
+ 我的购物车 + + 0 +
+
+

购物车中还没有商品,赶紧选购吧!

+
+
+
+ +
+ +
+ + +
+
+ +
+ + + + + + + + +
+
+
+ +
+

手机商品筛选

+
共 10135个商品
+
+ + +
+ +
+ + + +
+
+ +
+ +
+ + 1/169 + + + +
+
+ + + +
+
+
+ + 关注 +
+

+ + + +

+ +

+ ¥5199.00 + +

+

+ + Apple iPhone 7 Plus (A1661) 32G 黑色 移动联通电信4G手机 + +

+

已有11万+热门评价 + 二手有售 +

+

谷粒商城Apple产品... + + + +

+
+
+

自营 + 谷粒商城自营,品质保证 +

+

满赠 + 该商品参加满赠活动 +

+
+
+
+ +
+ + +
+
+ + + < 上一页 + + 1 + 2 + 3 + ... + 169 + + 下一页 > + + + + 169页  到第 + + + 确定 + +
+
+
+
+
+
+
+ + + + + + + + +
+
+
+ 我的足迹 + + 更多浏览记录 + +
+
+
    +
  • +
    + + + +
    +
    + + ¥2998.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥88.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥199.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥799.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥599.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥699.00 + +
    +
  • +
  • +
    + + + +
    +
    + + ¥715.00 + +
    +
  • +
+ + +
+
+
+ +
+ + + + +
+
+ + +
+
+ + + +