diff --git a/search/pom.xml b/search/pom.xml
index 865c280c..410ab070 100644
--- a/search/pom.xml
+++ b/search/pom.xml
@@ -13,8 +13,13 @@
pom
search-application
- search-service-api
- search-service-impl
+ search-biz
+ search-biz-api
+
+
+ search-rpc
+ search-rest
+ search-rpc-api
diff --git a/search/search-application/pom.xml b/search/search-application/pom.xml
index 1f0fbc32..0e839c13 100644
--- a/search/search-application/pom.xml
+++ b/search/search-application/pom.xml
@@ -15,92 +15,15 @@
cn.iocoder.mall
- common-framework
+ search-rest
1.0-SNAPSHOT
cn.iocoder.mall
- mall-spring-boot
+ search-rpc
1.0-SNAPSHOT
-
- cn.iocoder.mall
- user-sdk
- 1.0-SNAPSHOT
-
-
- cn.iocoder.mall
- search-service-api
- 1.0-SNAPSHOT
-
-
- cn.iocoder.mall
- search-service-impl
- 1.0-SNAPSHOT
-
-
- cn.iocoder.mall
- system-service-api
- 1.0-SNAPSHOT
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
- io.springfox
- springfox-swagger2
-
-
- com.github.xiaoymin
- swagger-bootstrap-ui
-
-
-
-
- com.alibaba.cloud
- spring-cloud-starter-alibaba-sentinel
-
-
-
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
-
- io.micrometer
- micrometer-registry-prometheus
-
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
-
-
diff --git a/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java b/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java
index 228823e2..1f125797 100644
--- a/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java
+++ b/search/search-application/src/main/java/cn/iocoder/mall/search/application/SearchApplication.java
@@ -2,13 +2,22 @@ package cn.iocoder.mall.search.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.search"})
@EnableAsync(proxyTargetClass = true)
public class SearchApplication {
-
+ /**
+ * 设置需要读取的配置文件的名字。
+ * 基于 {@link org.springframework.boot.context.config.ConfigFileApplicationListener#CONFIG_NAME_PROPERTY} 实现。
+ */
+ private static final String CONFIG_NAME_VALUE = "biz,rest,rpc,application";
public static void main(String[] args) {
+
+ // 设置环境变量
+ System.setProperty(ConfigFileApplicationListener.CONFIG_NAME_PROPERTY, CONFIG_NAME_VALUE);
+
// 解决 ES java.lang.IllegalStateException: availableProcessors is already
System.setProperty("es.set.netty.runtime.available.processors", "false");
SpringApplication.run(SearchApplication.class, args);
diff --git a/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java b/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java
deleted file mode 100644
index 50835667..00000000
--- a/search/search-application/src/main/java/cn/iocoder/mall/search/application/controller/users/UsersProductSearchController.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package cn.iocoder.mall.search.application.controller.users;
-
-import cn.iocoder.common.framework.util.StringUtil;
-import cn.iocoder.common.framework.vo.CommonResult;
-import cn.iocoder.common.framework.vo.SortingField;
-import cn.iocoder.mall.search.api.ProductSearchService;
-import cn.iocoder.mall.search.api.bo.ProductConditionBO;
-import cn.iocoder.mall.search.api.bo.ProductPageBO;
-import cn.iocoder.mall.search.api.dto.ProductConditionDTO;
-import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
-import org.apache.dubbo.config.annotation.Reference;
-import io.swagger.annotations.Api;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Collections;
-
-import static cn.iocoder.common.framework.vo.CommonResult.success;
-
-// TODO 芋艿,搜索关键字的配置
-// TODO 芋艿,搜索日志
-
-@RestController
-@RequestMapping("users/product")
-@Api("商品搜索")
-public class UsersProductSearchController {
-
- @Reference(validation = "true", version = "${dubbo.provider.ProductSearchService.version}")
- private ProductSearchService productSearchService;
-
- @GetMapping("/page") // TODO 芋艿,后面把 BO 改成 VO
- public CommonResult page(@RequestParam(value = "cid", required = false) Integer cid,
- @RequestParam(value = "keyword", required = false) String keyword,
- @RequestParam(value = "pageNo", required = false) Integer pageNo,
- @RequestParam(value = "pageSize", required = false) Integer pageSize,
- @RequestParam(value = "sortField", required = false) String sortField,
- @RequestParam(value = "sortOrder", required = false) String sortOrder) {
- // 创建 ProductSearchPageDTO 对象
- ProductSearchPageDTO productSearchPageDTO = new ProductSearchPageDTO().setCid(cid).setKeyword(keyword)
- .setPageNo(pageNo).setPageSize(pageSize);
- if (StringUtil.hasText(sortField) && StringUtil.hasText(sortOrder)) {
- productSearchPageDTO.setSorts(Collections.singletonList(new SortingField(sortField, sortOrder)));
- }
- // 执行搜索
- return success(productSearchService.getSearchPage(productSearchPageDTO));
- }
-
- @GetMapping("/condition") // TODO 芋艿,后面把 BO 改成 VO
- public CommonResult condition(@RequestParam(value = "keyword", required = false) String keyword) {
- // 创建 ProductConditionDTO 对象
- ProductConditionDTO productConditionDTO = new ProductConditionDTO().setKeyword(keyword)
- .setFields(Collections.singleton(ProductConditionDTO.FIELD_CATEGORY));
- // 执行搜索
- return success(productSearchService.getSearchCondition(productConditionDTO));
- }
-
-}
diff --git a/search/search-application/src/main/resources/application.yaml b/search/search-application/src/main/resources/application.yaml
index c531b15d..4ddc8d28 100644
--- a/search/search-application/src/main/resources/application.yaml
+++ b/search/search-application/src/main/resources/application.yaml
@@ -1,29 +1,6 @@
spring:
application:
name: search-application
-
- # Spring Cloud 配置项
- cloud:
- # Spring Cloud Sentinel 配置项
- sentinel:
- transport:
- dashboard: s1.iocoder.cn:12088 # Sentinel Dashboard 服务地址
- eager: true # 项目启动时,直接连接到 Sentinel
-
-# server
-server:
- port: 18086
- servlet:
- context-path: /search-api/
-
-swagger:
- enable: false
-
-
-management:
- endpoints:
- web:
- exposure:
- include: health,info,env,metrics,prometheus
- metrics:
- enabled: true
+ # Profile 的配置项
+ profiles:
+ active: local
diff --git a/search/search-biz-api/pom.xml b/search/search-biz-api/pom.xml
new file mode 100644
index 00000000..b8d93c0d
--- /dev/null
+++ b/search/search-biz-api/pom.xml
@@ -0,0 +1,23 @@
+
+
+
+ search
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ search-biz-api
+
+
+
+
+ cn.iocoder.mall
+ common-framework
+ 1.0-SNAPSHOT
+
+
+
+
diff --git a/search/search-biz/pom.xml b/search/search-biz/pom.xml
new file mode 100644
index 00000000..7a6c27c5
--- /dev/null
+++ b/search/search-biz/pom.xml
@@ -0,0 +1,110 @@
+
+
+
+ search
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ search-biz
+
+
+
+
+ cn.iocoder.mall
+ mall-spring-boot
+ 1.0-SNAPSHOT
+
+
+
+ search-biz-api
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+
+
+ cn.iocoder.mall
+ product-rpc-api
+ 1.0-SNAPSHOT
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-elasticsearch
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-dubbo
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-stream-rocketmq
+
+
+
+
+ com.google.guava
+ guava
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+ org.mapstruct
+ mapstruct-jdk8
+
+
+ org.projectlombok
+ lombok
+
+
+
+ com.alibaba
+ fastjson
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.github.vanroy
+ spring-boot-starter-data-jest
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+
+
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductBO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductBO.java
new file mode 100644
index 00000000..7ba30104
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductBO.java
@@ -0,0 +1,86 @@
+package cn.iocoder.mall.search.biz.bo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 商品 ES BO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductBO implements Serializable {
+
+ private Integer id;
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ private String name;
+ /**
+ * 卖点
+ */
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ private String description;
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 分类名
+ */
+ private String categoryName;
+ /**
+ * 商品主图地数组
+ */
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ private Boolean visible;
+ /**
+ * 排序字段
+ */
+ private Integer sort;
+
+ // ========== Sku 相关字段 =========
+ /**
+ * 原价格,单位:分
+ */
+ private Integer originalPrice;
+ /**
+ * 购买价格,单位:分。
+ */
+ private Integer buyPrice;
+ /**
+ * 库存数量
+ */
+ private Integer quantity;
+
+ // ========== 促销活动相关字段 =========
+ // 目前只促销单体商品促销,目前仅限制折扣。
+ /**
+ * 促销活动编号
+ */
+ private Integer promotionActivityId;
+ /**
+ * 促销活动标题
+ */
+ private String promotionActivityTitle;
+ /**
+ * 促销活动类型
+ */
+ private Integer promotionActivityType;
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductConditionBO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductConditionBO.java
new file mode 100644
index 00000000..b8eebbfe
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductConditionBO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.search.biz.bo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 商品搜索条件返回 BO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductConditionBO {
+
+ /**
+ * 商品分类数组
+ */
+ private List categories;
+
+ @Data
+ @Accessors(chain = true)
+ public static class Category {
+
+ /**
+ * 分类编号
+ */
+ private Integer id;
+ /**
+ * 分类名称
+ */
+ private String name;
+
+ }
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductPageBO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductPageBO.java
new file mode 100644
index 00000000..827b7996
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/bo/ProductPageBO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.mall.search.biz.bo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class ProductPageBO implements Serializable {
+
+ /**
+ * 管理员数组
+ */
+ private List list;
+ /**
+ * 总量
+ */
+ private Integer total;
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/config/JPAConfiguration.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/config/JPAConfiguration.java
new file mode 100644
index 00000000..acb35fd2
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/config/JPAConfiguration.java
@@ -0,0 +1,9 @@
+package cn.iocoder.mall.search.biz.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
+
+@Configuration
+@EnableElasticsearchRepositories(basePackages = "cn.iocoder.mall.search.biz.dao")
+public class JPAConfiguration {
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java
new file mode 100644
index 00000000..5b531576
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/constant/FieldAnalyzer.java
@@ -0,0 +1,26 @@
+package cn.iocoder.mall.search.biz.constant;
+
+/**
+ * ES 字段分析器的枚举类
+ *
+ * 关于 IK 分词,文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。
+ * 目前项目使用的 ES 版本是 6.7.1 ,可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。
+ * 如果不知道怎么安装 ES ,可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。
+ */
+public class FieldAnalyzer {
+
+ /**
+ * IK 最大化分词
+ *
+ * 会将文本做最细粒度的拆分
+ */
+ public static final String IK_MAX_WORD = "ik_max_word";
+
+ /**
+ * IK 智能分词
+ *
+ * 会做最粗粒度的拆分
+ */
+ public static final String IK_SMART = "ik_smart";
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
new file mode 100644
index 00000000..9227f361
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
@@ -0,0 +1,17 @@
+package cn.iocoder.mall.search.biz.convert;
+
+import cn.iocoder.mall.search.biz.bo.ProductBO;
+import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ProductSearchConvert {
+
+ ProductSearchConvert INSTANCE = Mappers.getMapper(ProductSearchConvert.class);
+
+ List convert(List list);
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java
new file mode 100644
index 00000000..1b4b869d
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dao/ProductRepository.java
@@ -0,0 +1,69 @@
+package cn.iocoder.mall.search.biz.dao;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.util.StringUtil;
+import cn.iocoder.common.framework.vo.SortingField;
+import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
+import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
+import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
+
+@Repository
+public interface ProductRepository extends ElasticsearchRepository {
+
+ @Deprecated
+ ESProductDO findByName(String name);
+
+ default Page search(Integer cid, String keyword, Integer pageNo, Integer pageSize,
+ List sortFields) {
+ NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
+ .withPageable(PageRequest.of(pageNo - 1, pageSize));
+ // 筛选条件 cid
+ if (cid != null) {
+ nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery("cid", cid));
+ }
+ // 筛选
+ if (StringUtil.hasText(keyword)) {
+ FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { // TODO 芋艿,分值随便打的
+ new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("name", keyword),
+ ScoreFunctionBuilders.weightFactorFunction(10)),
+ new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("sellPoint", keyword),
+ ScoreFunctionBuilders.weightFactorFunction(2)),
+ new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("categoryName", keyword),
+ ScoreFunctionBuilders.weightFactorFunction(3)),
+// new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery("description", keyword),
+// ScoreFunctionBuilders.weightFactorFunction(2)), // TODO 芋艿,目前这么做,如果商品描述很长,在按照价格降序,会命中超级多的关键字。
+ };
+ FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions)
+ .scoreMode(FunctionScoreQuery.ScoreMode.SUM)
+ .setMinScore(2F); // TODO 芋艿,需要考虑下 score
+ nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);
+ } else {
+ nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
+ }
+ // 排序
+ if (!CollectionUtil.isEmpty(sortFields)) {
+ sortFields.forEach(sortField -> nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getField())
+ .order(SortOrder.fromString(sortField.getOrder()))));
+ } else if (StringUtil.hasText(keyword)) {
+ nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));
+ } else {
+ nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("sort").order(SortOrder.DESC));
+ }
+ // 执行查询
+ return search(nativeSearchQueryBuilder.build());
+ }
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java
new file mode 100644
index 00000000..bd06b0ab
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dataobject/ESProductDO.java
@@ -0,0 +1,96 @@
+package cn.iocoder.mall.search.biz.dataobject;
+
+import cn.iocoder.mall.search.biz.constant.FieldAnalyzer;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.elasticsearch.annotations.Document;
+import org.springframework.data.elasticsearch.annotations.Field;
+import org.springframework.data.elasticsearch.annotations.FieldType;
+
+import java.util.List;
+
+/**
+ * 商品 ES DO
+ */
+@Document(indexName = "product", type = "spu", shards = 1, replicas = 0)
+@Data
+@Accessors(chain = true)
+public class ESProductDO {
+
+ @Id
+ private Integer id;
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
+ private String name;
+ /**
+ * 卖点
+ */
+ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
+ private String description;
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 分类名
+ */
+ @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)
+ private String categoryName;
+ /**
+ * 商品主图地数组
+ */
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ private Boolean visible;
+ /**
+ * 排序字段
+ */
+ private Integer sort;
+
+ // ========== Sku 相关字段 =========
+ /**
+ * 原价格,单位:分
+ */
+ private Integer originalPrice;
+ /**
+ * 购买价格,单位:分。
+ */
+ private Integer buyPrice;
+ /**
+ * 库存数量
+ */
+ private Integer quantity;
+
+ // ========== 促销活动相关字段 =========
+ // 目前只促销单体商品促销,目前仅限制折扣。
+ /**
+ * 促销活动编号
+ */
+ private Integer promotionActivityId;
+ /**
+ * 促销活动标题
+ */
+ private String promotionActivityTitle;
+ /**
+ * 促销活动类型
+ */
+ private Integer promotionActivityType;
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductConditionDTO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductConditionDTO.java
new file mode 100644
index 00000000..4bfe0fea
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductConditionDTO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.mall.search.biz.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Collection;
+
+/**
+ * 获得商品检索条件 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductConditionDTO {
+
+ /**
+ * Field - 商品分类
+ */
+ public static final String FIELD_CATEGORY = "category";
+
+ /**
+ * 关键字
+ */
+ private String keyword;
+ /**
+ * 需要返回的搜索条件的 fields 名
+ */
+ private Collection fields;
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductSearchPageDTO.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductSearchPageDTO.java
new file mode 100644
index 00000000..430a32ed
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/dto/ProductSearchPageDTO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.mall.search.biz.dto;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.vo.SortingField;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 商品检索分页 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductSearchPageDTO {
+
+ public static final Set SORT_FIELDS = CollectionUtil.asSet("buyPrice");
+
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 关键字
+ */
+ private String keyword;
+
+ /**
+ * 页码
+ */
+ private Integer pageNo;
+ /**
+ * 分页大小
+ */
+ private Integer pageSize;
+
+ /**
+ * 排序字段数组
+ */
+ private List sorts;
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchService.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchService.java
new file mode 100644
index 00000000..50101d74
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchService.java
@@ -0,0 +1,20 @@
+package cn.iocoder.mall.search.biz.service;
+
+
+public interface ProductSearchService {
+
+ Integer rebuild();
+
+ /**
+ * 构建商品的搜索索引
+ *
+ * @param id 商品编号
+ * @return 构建结果
+ */
+ Boolean save(Integer id);
+//
+// ProductPageBO getSearchPage(ProductSearchPageDTO searchPageDTO);
+//
+// ProductConditionBO getSearchCondition(ProductConditionDTO conditionDTO);
+
+}
diff --git a/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java
new file mode 100644
index 00000000..af4b0880
--- /dev/null
+++ b/search/search-biz/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java
@@ -0,0 +1,147 @@
+package cn.iocoder.mall.search.biz.service;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.vo.SortingField;
+import cn.iocoder.mall.search.biz.dao.ProductRepository;
+import cn.iocoder.mall.search.biz.dto.ProductSearchPageDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import java.util.List;
+
+@Service
+@org.apache.dubbo.config.annotation.Service(validation = "true", version = "${dubbo.provider.ProductSearchService.version}")
+public class ProductSearchServiceImpl implements ProductSearchService {
+
+ private static final Integer REBUILD_FETCH_PER_SIZE = 100;
+
+ @Autowired
+ private ProductRepository productRepository;
+ @Autowired
+ private ElasticsearchTemplate elasticsearchTemplate; // 因为需要使用到聚合操作,只好引入 ElasticsearchTemplate 。
+
+// @Reference(validation = "true", version = "${dubbo.consumer.ProductSpuService.version}")
+// private ProductSpuService productSpuService;
+// @Reference(validation = "true", version = "${dubbo.consumer.ProductCategoryService.version}")
+// private ProductCategoryService productCategoryService;
+// @Reference(validation = "true", version = "${dubbo.consumer.CartService.version}")
+// private CartService cartService;
+
+// @Override
+// public Integer rebuild() {
+// // TODO 芋艿,因为目前商品比较少,所以写的很粗暴。等未来重构
+// Integer lastId = null;
+// int rebuildCounts = 0;
+// while (true) {
+// List spus = productSpuService.getProductSpuDetailListForSync(lastId, REBUILD_FETCH_PER_SIZE);
+// rebuildCounts += spus.size();
+// // 存储到 ES 中
+// List products = spus.stream().map(this::convert).collect(Collectors.toList());
+// productRepository.saveAll(products);
+// // 设置新的 lastId ,或者结束
+// if (spus.size() < REBUILD_FETCH_PER_SIZE) {
+// break;
+// } else {
+// lastId = spus.get(spus.size() - 1).getId();
+// }
+// }
+// // 返回成功
+// return rebuildCounts;
+// }
+//
+// @Override
+// public Boolean save(Integer id) {
+// // 获得商品性情
+// ProductSpuDetailBO result = productSpuService.getProductSpuDetail(id);
+// // 存储到 ES 中
+// ESProductDO product = convert(result);
+// productRepository.save(product);
+// // 返回成功
+// return true;
+// }
+//
+// @SuppressWarnings("OptionalGetWithoutIsPresent")
+// private ESProductDO convert(ProductSpuDetailBO spu) {
+// // 获得最小价格的 SKU ,用于下面的价格计算
+// ProductSpuDetailBO.Sku sku = spu.getSkus().stream().min(Comparator.comparing(ProductSpuDetailBO.Sku::getPrice)).get();
+// // 价格计算
+// CalcSkuPriceBO calSkuPriceResult = cartService.calcSkuPrice(sku.getId());
+// // 拼装结果
+// return ProductSearchConvert.INSTANCE.convert(spu, calSkuPriceResult);
+// }
+//
+// @Override
+// public ProductPageBO getSearchPage(ProductSearchPageDTO searchPageDTO) {
+// checkSortFieldInvalid(searchPageDTO.getSorts());
+// // 执行查询
+// Page searchPage = productRepository.search(searchPageDTO.getCid(), searchPageDTO.getKeyword(),
+// searchPageDTO.getPageNo(), searchPageDTO.getPageSize(), searchPageDTO.getSorts());
+// // 转换结果
+// return new ProductPageBO()
+// .setList(ProductSearchConvert.INSTANCE.convert(searchPage.getContent()))
+// .setTotal((int) searchPage.getTotalElements());
+// }
+
+ private void checkSortFieldInvalid(List sorts) {
+ if (CollectionUtil.isEmpty(sorts)) {
+ return;
+ }
+ sorts.forEach(sortingField -> Assert.isTrue(ProductSearchPageDTO.SORT_FIELDS.contains(sortingField.getField()),
+ String.format("排序字段(%s) 不在允许范围内", sortingField.getField())));
+ }
+
+ @Override
+ public Integer rebuild() {
+ return null;
+ }
+
+ @Override
+ public Boolean save(Integer id) {
+ return null;
+ }
+
+// @Override
+// public ProductConditionBO getSearchCondition(ProductConditionDTO conditionDTO) {
+// // 创建 ES 搜索条件
+// NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
+// // 筛选
+// if (StringUtil.hasText(conditionDTO.getKeyword())) { // 如果有 keyword ,就去匹配
+// nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(conditionDTO.getKeyword(),
+// "name", "sellPoint", "categoryName"));
+// } else {
+// nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
+// }
+// // 聚合
+// if (conditionDTO.getFields().contains(ProductConditionDTO.FIELD_CATEGORY)) { // 商品分类
+// nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("cids").field("cid"));
+// }
+// // 执行查询
+// ProductConditionBO condition = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), response -> {
+// ProductConditionBO result = new ProductConditionBO();
+// // categoryIds 聚合
+// Aggregation categoryIdsAggregation = response.getAggregations().get("cids");
+// if (categoryIdsAggregation != null) {
+// result.setCategories(new ArrayList<>());
+// for (LongTerms.Bucket bucket : (((LongTerms) categoryIdsAggregation).getBuckets())) {
+// result.getCategories().add(new ProductConditionBO.Category().setId(bucket.getKeyAsNumber().intValue()));
+// }
+// }
+// // 返回结果
+// return result;
+// });
+// // 聚合其它数据源
+// if (!CollectionUtil.isEmpty(condition.getCategories())) {
+// // 查询指定的 ProductCategoryBO 数组,并转换成 ProductCategoryBO Map
+// Map categoryMap = productCategoryService.getListByIds(
+// condition.getCategories().stream().map(ProductConditionBO.Category::getId).collect(Collectors.toList()))
+// .stream().collect(Collectors.toMap(ProductCategoryBO::getId, category -> category));
+// // 设置分类名
+// condition.getCategories().forEach(category -> category.setName(categoryMap.get(category.getId()).getName()));
+// }
+// // 返回结果
+// return condition;
+// }
+
+}
diff --git a/search/search-biz/src/main/resources/biz.properties b/search/search-biz/src/main/resources/biz.properties
new file mode 100644
index 00000000..197907af
--- /dev/null
+++ b/search/search-biz/src/main/resources/biz.properties
@@ -0,0 +1,8 @@
+##################### 业务模块 #####################
+## OAuth2CodeService
+modules.oauth2-code-service.access-token-expire-time-millis = 2880000
+modules.oauth2-code-service.refresh-token-expire-time-millis = 43200000
+## OAuth2MobileCodeService
+modules.oauth2-mobile-code-service.code-expire-time-millis = 600000
+modules.oauth2-mobile-code-service.send-maximum-quantity-per-day = 10
+modules.oauth2-mobile-code-service.send-frequency = 60000
diff --git a/search/search-biz/src/main/resources/biz.yaml b/search/search-biz/src/main/resources/biz.yaml
new file mode 100644
index 00000000..fc41b8a9
--- /dev/null
+++ b/search/search-biz/src/main/resources/biz.yaml
@@ -0,0 +1,7 @@
+spring:
+ data:
+ # Jest 配置项
+ jest:
+ uri: http://127.0.0.1:9200
+
+
diff --git a/search/search-rest/pom.xml b/search/search-rest/pom.xml
new file mode 100644
index 00000000..fa0bbe14
--- /dev/null
+++ b/search/search-rest/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ search
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ search-rest
+ 提供 商品搜索服务的 Rest 接口的实现,提供对外调用
+
+
+
+
+ cn.iocoder.mall
+ search-biz
+ 1.0-SNAPSHOT
+
+
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-web
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-security
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-swagger
+ 1.0-SNAPSHOT
+
+
+
+
+
\ No newline at end of file
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/controller/user/UsersProductSearchController.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/controller/user/UsersProductSearchController.java
new file mode 100644
index 00000000..9a44420b
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/controller/user/UsersProductSearchController.java
@@ -0,0 +1,72 @@
+package cn.iocoder.mall.search.rest.controller.user;
+
+import cn.iocoder.common.framework.constant.MallConstants;
+import cn.iocoder.common.framework.util.StringUtil;
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.common.framework.vo.SortingField;
+import cn.iocoder.mall.search.biz.service.ProductSearchService;
+import cn.iocoder.mall.search.rest.response.user.ProductPageResponse;
+import io.swagger.annotations.Api;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import cn.iocoder.mall.search.rest.request.user.ProductConditionRequest;
+import cn.iocoder.mall.search.rest.request.user.ProductSearchPageRequest;
+import cn.iocoder.mall.search.rest.response.user.ProductConditionResponse;
+
+import java.util.Collections;
+
+import static cn.iocoder.common.framework.vo.CommonResult.success;
+
+/**
+ * Created with IDEA
+ *
+ * @author : lhl
+ * @version : 1.0
+ * @Time : 19:26
+ * @date : 2020/5/14
+ */
+@RestController
+@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "users/product")
+@Api(tags = "商品查询 API")
+@Slf4j
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
+public class UsersProductSearchController {
+
+
+ private final ProductSearchService productSearchService;
+
+ @GetMapping("/page") // TODO 芋艿,后面把 BO 改成 VO
+ public CommonResult page(@RequestParam(value = "cid", required = false) Integer cid,
+ @RequestParam(value = "keyword", required = false) String keyword,
+ @RequestParam(value = "pageNo", required = false) Integer pageNo,
+ @RequestParam(value = "pageSize", required = false) Integer pageSize,
+ @RequestParam(value = "sortField", required = false) String sortField,
+ @RequestParam(value = "sortOrder", required = false) String sortOrder) {
+ // 创建 ProductSearchPageDTO 对象
+ ProductSearchPageRequest productSearchPageDTO = new ProductSearchPageRequest().setCid(cid).setKeyword(keyword)
+ .setPageNo(pageNo).setPageSize(pageSize);
+ if (StringUtil.hasText(sortField) && StringUtil.hasText(sortOrder)) {
+ productSearchPageDTO.setSorts(Collections.singletonList(new SortingField(sortField, sortOrder)));
+ }
+ // 执行搜索
+// return success(productSearchService.getSearchPage(productSearchPageDTO));
+ return success(null);
+ }
+
+ @GetMapping("/condition") // TODO 芋艿,后面把 BO 改成 VO
+ public CommonResult condition(@RequestParam(value = "keyword", required = false) String keyword) {
+ // 创建 ProductConditionDTO 对象
+ ProductConditionRequest productConditionDTO = new ProductConditionRequest().setKeyword(keyword)
+ .setFields(Collections.singleton(ProductConditionRequest.FIELD_CATEGORY));
+ // 执行搜索
+// return success(productSearchService.getSearchCondition(productConditionDTO));
+ return success(null);
+ }
+
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/convert/user/UsersProductSearchConvert.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/convert/user/UsersProductSearchConvert.java
new file mode 100644
index 00000000..4fcdac3b
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/convert/user/UsersProductSearchConvert.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.search.rest.convert.user;
+
+import cn.iocoder.mall.search.biz.bo.ProductBO;
+import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface UsersProductSearchConvert {
+
+ cn.iocoder.mall.search.biz.convert.ProductSearchConvert INSTANCE = Mappers.getMapper(cn.iocoder.mall.search.biz.convert.ProductSearchConvert.class);
+
+// @Mappings({})
+// ESProductDO convert(ProductSpuDetailBO spu);
+
+// @Mappings({})
+// default ESProductDO convert(ProductSpuDetailBO spu, CalcSkuPriceBO calcSkuPrice) {
+// // Spu 的基础数据
+// ESProductDO product = this.convert(spu);
+// product.setOriginalPrice(calcSkuPrice.getOriginalPrice()).setBuyPrice(calcSkuPrice.getBuyPrice());
+// // 设置促销活动相关字段
+// if (calcSkuPrice.getTimeLimitedDiscount() != null) {
+// PromotionActivityBO activity = calcSkuPrice.getTimeLimitedDiscount();
+// product.setPromotionActivityId(activity.getId()).setPromotionActivityTitle(activity.getTitle())
+// .setPromotionActivityType(activity.getActivityType());
+// }
+// // 返回
+// return product;
+// }
+
+ List convert(List list);
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductConditionRequest.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductConditionRequest.java
new file mode 100644
index 00000000..c90b0b95
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductConditionRequest.java
@@ -0,0 +1,29 @@
+package cn.iocoder.mall.search.rest.request.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Collection;
+
+/**
+ * 获得商品检索条件 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class UsersProductConditionRequest{
+
+ /**
+ * Field - 商品分类
+ */
+ public static final String FIELD_CATEGORY = "category";
+
+ /**
+ * 关键字
+ */
+ private String keyword;
+ /**
+ * 需要返回的搜索条件的 fields 名
+ */
+ private Collection fields;
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductSearchPageRequest.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductSearchPageRequest.java
new file mode 100644
index 00000000..bdb2c52f
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/request/user/UsersProductSearchPageRequest.java
@@ -0,0 +1,48 @@
+package cn.iocoder.mall.search.rest.request.user;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.vo.SortingField;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created with IDEA
+ *
+ * @author : lhl
+ * @version : 1.0
+ * @Time : 19:09
+ * @date : 2020/5/14
+ */
+@Data
+@Accessors(chain = true)
+public class UsersProductSearchPageRequest {
+
+ public static final Set SORT_FIELDS = CollectionUtil.asSet("buyPrice");
+
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 关键字
+ */
+ private String keyword;
+
+ /**
+ * 页码
+ */
+ private Integer pageNo;
+ /**
+ * 分页大小
+ */
+ private Integer pageSize;
+
+ /**
+ * 排序字段数组
+ */
+ private List sorts;
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductConditionResponse.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductConditionResponse.java
new file mode 100644
index 00000000..4b008a8f
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductConditionResponse.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.search.rest.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 商品搜索条件返回 BO
+ */
+@Data
+@Accessors(chain = true)
+public class UsersProductConditionResponse {
+
+ /**
+ * 商品分类数组
+ */
+ private List categories;
+
+ @Data
+ @Accessors(chain = true)
+ public static class Category {
+
+ /**
+ * 分类编号
+ */
+ private Integer id;
+ /**
+ * 分类名称
+ */
+ private String name;
+
+ }
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductPageResponse.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductPageResponse.java
new file mode 100644
index 00000000..56ece146
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductPageResponse.java
@@ -0,0 +1,22 @@
+package cn.iocoder.mall.search.rest.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class UsersProductPageResponse implements Serializable {
+
+ /**
+ * 管理员数组
+ */
+ private List list;
+ /**
+ * 总量
+ */
+ private Integer total;
+
+}
diff --git a/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductResponse.java b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductResponse.java
new file mode 100644
index 00000000..5d5127fc
--- /dev/null
+++ b/search/search-rest/src/main/java/cn/iocoder/mall/search/rest/response/user/UsersProductResponse.java
@@ -0,0 +1,86 @@
+package cn.iocoder.mall.search.rest.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 商品 ES BO
+ */
+@Data
+@Accessors(chain = true)
+public class UsersProductResponse implements Serializable {
+
+ private Integer id;
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ private String name;
+ /**
+ * 卖点
+ */
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ private String description;
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 分类名
+ */
+ private String categoryName;
+ /**
+ * 商品主图地数组
+ */
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ private Boolean visible;
+ /**
+ * 排序字段
+ */
+ private Integer sort;
+
+ // ========== Sku 相关字段 =========
+ /**
+ * 原价格,单位:分
+ */
+ private Integer originalPrice;
+ /**
+ * 购买价格,单位:分。
+ */
+ private Integer buyPrice;
+ /**
+ * 库存数量
+ */
+ private Integer quantity;
+
+ // ========== 促销活动相关字段 =========
+ // 目前只促销单体商品促销,目前仅限制折扣。
+ /**
+ * 促销活动编号
+ */
+ private Integer promotionActivityId;
+ /**
+ * 促销活动标题
+ */
+ private String promotionActivityTitle;
+ /**
+ * 促销活动类型
+ */
+ private Integer promotionActivityType;
+
+}
diff --git a/search/search-rest/src/main/resources/rest.yaml b/search/search-rest/src/main/resources/rest.yaml
new file mode 100644
index 00000000..778308fc
--- /dev/null
+++ b/search/search-rest/src/main/resources/rest.yaml
@@ -0,0 +1,12 @@
+# 服务器的配置项
+server:
+ port: 18099
+ servlet:
+ context-path: /search-api/
+
+# Swagger 配置项
+swagger:
+ title: 商品查询子系统
+ description: 商品查询子系统
+ version: 1.0.0
+ base-package: cn.iocoder.mall.search.rest.controller
diff --git a/search/search-rpc-api/pom.xml b/search/search-rpc-api/pom.xml
new file mode 100644
index 00000000..2dd3c6c5
--- /dev/null
+++ b/search/search-rpc-api/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ search
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ search-rpc-api
+
+
+
+
+
+ search-biz-api
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+
+
+
+ javax.validation
+ validation-api
+
+
+ org.projectlombok
+ lombok
+
+
+
\ No newline at end of file
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/api/user/ProductSearchRPC.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/api/user/ProductSearchRPC.java
new file mode 100644
index 00000000..57a7e83a
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/api/user/ProductSearchRPC.java
@@ -0,0 +1,21 @@
+package cn.iocoder.mall.search.biz.api.user;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+
+public interface ProductSearchRPC {
+
+ CommonResult rebuild();
+
+ /**
+ * 构建商品的搜索索引
+ *
+ * @param id 商品编号
+ * @return 构建结果
+ */
+ CommonResult save(Integer id);
+
+// ProductPageBO getSearchPage(ProductSearchPageDTO searchPageDTO);
+//
+// ProductConditionBO getSearchCondition(ProductConditionDTO conditionDTO);
+
+}
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductConditionRequest.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductConditionRequest.java
new file mode 100644
index 00000000..eb9fd74e
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductConditionRequest.java
@@ -0,0 +1,29 @@
+package cn.iocoder.mall.search.biz.request.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Collection;
+
+/**
+ * 获得商品检索条件 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductConditionRequest {
+
+ /**
+ * Field - 商品分类
+ */
+ public static final String FIELD_CATEGORY = "category";
+
+ /**
+ * 关键字
+ */
+ private String keyword;
+ /**
+ * 需要返回的搜索条件的 fields 名
+ */
+ private Collection fields;
+
+}
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductSearchPageRequest.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductSearchPageRequest.java
new file mode 100644
index 00000000..a722eea4
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/request/user/ProductSearchPageRequest.java
@@ -0,0 +1,48 @@
+package cn.iocoder.mall.search.biz.request.user;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.vo.SortingField;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Created with IDEA
+ *
+ * @author : lhl
+ * @version : 1.0
+ * @Time : 19:09
+ * @date : 2020/5/14
+ */
+@Data
+@Accessors(chain = true)
+public class ProductSearchPageRequest {
+
+ public static final Set SORT_FIELDS = CollectionUtil.asSet("buyPrice");
+
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 关键字
+ */
+ private String keyword;
+
+ /**
+ * 页码
+ */
+ private Integer pageNo;
+ /**
+ * 分页大小
+ */
+ private Integer pageSize;
+
+ /**
+ * 排序字段数组
+ */
+ private List sorts;
+
+}
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductConditionResponse.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductConditionResponse.java
new file mode 100644
index 00000000..2b48816f
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductConditionResponse.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.search.biz.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 商品搜索条件返回 BO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductConditionResponse {
+
+ /**
+ * 商品分类数组
+ */
+ private List categories;
+
+ @Data
+ @Accessors(chain = true)
+ public static class Category {
+
+ /**
+ * 分类编号
+ */
+ private Integer id;
+ /**
+ * 分类名称
+ */
+ private String name;
+
+ }
+
+}
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductPageResponse.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductPageResponse.java
new file mode 100644
index 00000000..56db936a
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductPageResponse.java
@@ -0,0 +1,22 @@
+package cn.iocoder.mall.search.biz.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class ProductPageResponse implements Serializable {
+
+ /**
+ * 管理员数组
+ */
+ private List list;
+ /**
+ * 总量
+ */
+ private Integer total;
+
+}
diff --git a/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductResponse.java b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductResponse.java
new file mode 100644
index 00000000..31952582
--- /dev/null
+++ b/search/search-rpc-api/src/main/java/cn/iocoder/mall/search/biz/response/user/ProductResponse.java
@@ -0,0 +1,86 @@
+package cn.iocoder.mall.search.biz.response.user;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 商品 ES BO
+ */
+@Data
+@Accessors(chain = true)
+public class ProductResponse implements Serializable {
+
+ private Integer id;
+
+ // ========== 基本信息 =========
+ /**
+ * SPU 名字
+ */
+ private String name;
+ /**
+ * 卖点
+ */
+ private String sellPoint;
+ /**
+ * 描述
+ */
+ private String description;
+ /**
+ * 分类编号
+ */
+ private Integer cid;
+ /**
+ * 分类名
+ */
+ private String categoryName;
+ /**
+ * 商品主图地数组
+ */
+ private List picUrls;
+
+ // ========== 其他信息 =========
+ /**
+ * 是否上架商品(是否可见)。
+ *
+ * true 为已上架
+ * false 为已下架
+ */
+ private Boolean visible;
+ /**
+ * 排序字段
+ */
+ private Integer sort;
+
+ // ========== Sku 相关字段 =========
+ /**
+ * 原价格,单位:分
+ */
+ private Integer originalPrice;
+ /**
+ * 购买价格,单位:分。
+ */
+ private Integer buyPrice;
+ /**
+ * 库存数量
+ */
+ private Integer quantity;
+
+ // ========== 促销活动相关字段 =========
+ // 目前只促销单体商品促销,目前仅限制折扣。
+ /**
+ * 促销活动编号
+ */
+ private Integer promotionActivityId;
+ /**
+ * 促销活动标题
+ */
+ private String promotionActivityTitle;
+ /**
+ * 促销活动类型
+ */
+ private Integer promotionActivityType;
+
+}
diff --git a/search/search-rpc/pom.xml b/search/search-rpc/pom.xml
new file mode 100644
index 00000000..d46e9e2f
--- /dev/null
+++ b/search/search-rpc/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+ search
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ search-rpc
+
+
+
+
+
+ cn.iocoder.mall
+ search-rpc-api
+ 1.0-SNAPSHOT
+
+
+
+ cn.iocoder.mall
+ search-biz
+ 1.0-SNAPSHOT
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-dubbo
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.github.vanroy
+ spring-boot-starter-data-jest
+
+
+
+
\ No newline at end of file
diff --git a/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java b/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
new file mode 100644
index 00000000..b8314290
--- /dev/null
+++ b/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.search.biz.convert;
+
+import cn.iocoder.mall.search.biz.bo.ProductBO;
+import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+@Mapper
+public interface ProductSearchConvert {
+
+ ProductSearchConvert INSTANCE = Mappers.getMapper(ProductSearchConvert.class);
+
+// @Mappings({})
+// ESProductDO convert(ProductSpuDetailBO spu);
+
+// @Mappings({})
+// default ESProductDO convert(ProductSpuDetailBO spu, CalcSkuPriceBO calcSkuPrice) {
+// // Spu 的基础数据
+// ESProductDO product = this.convert(spu);
+// product.setOriginalPrice(calcSkuPrice.getOriginalPrice()).setBuyPrice(calcSkuPrice.getBuyPrice());
+// // 设置促销活动相关字段
+// if (calcSkuPrice.getTimeLimitedDiscount() != null) {
+// PromotionActivityBO activity = calcSkuPrice.getTimeLimitedDiscount();
+// product.setPromotionActivityId(activity.getId()).setPromotionActivityTitle(activity.getTitle())
+// .setPromotionActivityType(activity.getActivityType());
+// }
+// // 返回
+// return product;
+// }
+
+ List convert(List list);
+
+}
diff --git a/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/rpc/user/ProductSearchRPCImpl.java b/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/rpc/user/ProductSearchRPCImpl.java
new file mode 100644
index 00000000..129068bd
--- /dev/null
+++ b/search/search-rpc/src/main/java/cn/iocoder/mall/search/biz/rpc/user/ProductSearchRPCImpl.java
@@ -0,0 +1,29 @@
+package cn.iocoder.mall.search.biz.rpc.user;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.mall.search.biz.api.user.ProductSearchRPC;
+import cn.iocoder.mall.search.biz.service.ProductSearchService;
+import org.apache.dubbo.config.annotation.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+
+
+@Service(validation = "true", version = "${dubbo.provider.ProductSearchRpc.version}")
+public class ProductSearchRPCImpl implements ProductSearchRPC {
+
+ @Autowired
+ private ProductSearchService productSearchService;
+
+ @Override
+ public CommonResult rebuild() {
+ return null;
+ }
+
+ @Override
+ public CommonResult save(Integer id){
+// ProductSpuDetailBO productSpuDetail = productSpuService.getProductSpuDetail(spuId);
+// return ProductSpuConvert.INSTANCE.convertDetail(productSpuDetail);
+ return CommonResult.success(true);
+ }
+
+
+}
diff --git a/search/search-rpc/src/main/resources/rpc-local.yaml b/search/search-rpc/src/main/resources/rpc-local.yaml
new file mode 100644
index 00000000..e056170a
--- /dev/null
+++ b/search/search-rpc/src/main/resources/rpc-local.yaml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: s1.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: local # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://s1.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/search/search-rpc/src/main/resources/rpc-test.yaml b/search/search-rpc/src/main/resources/rpc-test.yaml
new file mode 100644
index 00000000..d3d0e9e6
--- /dev/null
+++ b/search/search-rpc/src/main/resources/rpc-test.yaml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: s1.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: test # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://s1.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/search/search-rpc/src/main/resources/rpc.yaml b/search/search-rpc/src/main/resources/rpc.yaml
new file mode 100644
index 00000000..398fbf43
--- /dev/null
+++ b/search/search-rpc/src/main/resources/rpc.yaml
@@ -0,0 +1,22 @@
+# Dubbo 配置项
+dubbo:
+ # Spring Cloud Alibaba Dubbo 专属配置
+ cloud:
+ subscribed-services: 'search-application' # 设置订阅的应用列表,默认为 * 订阅所有应用
+ # Dubbo 提供者的协议
+ protocol:
+ name: dubbo
+ port: -1
+ # Dubbo 提供服务的扫描基础包
+ scan:
+ base-packages: cn.iocoder.mall.search.rpc.rpc
+ # Dubbo 服务提供者的配置
+ provider:
+# filter: -exception
+# ProductSpuService:
+# version: 1.0.0
+
+ # Dubbo 服务消费者的配置
+ consumer:
+# ProductSpuService:
+# version: 1.0.0
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java
index ca4f714a..6d4bbf1d 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/ProductSearchService.java
@@ -1,9 +1,9 @@
-package cn.iocoder.mall.search.api;
+package cn.iocoder.mall.search.biz;
-import cn.iocoder.mall.search.api.bo.ProductConditionBO;
-import cn.iocoder.mall.search.api.bo.ProductPageBO;
-import cn.iocoder.mall.search.api.dto.ProductConditionDTO;
-import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
+import cn.iocoder.mall.search.biz.bo.ProductConditionBO;
+import cn.iocoder.mall.search.biz.bo.ProductPageBO;
+import cn.iocoder.mall.search.biz.dto.ProductConditionDTO;
+import cn.iocoder.mall.search.biz.dto.ProductSearchPageDTO;
public interface ProductSearchService {
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java
index acaa15fc..7ba30104 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductBO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.api.bo;
+package cn.iocoder.mall.search.biz.bo;
import lombok.Data;
import lombok.experimental.Accessors;
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java
index 238b0dbb..b8eebbfe 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductConditionBO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.api.bo;
+package cn.iocoder.mall.search.biz.bo;
import lombok.Data;
import lombok.experimental.Accessors;
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java
index b18871a5..827b7996 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/bo/ProductPageBO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.api.bo;
+package cn.iocoder.mall.search.biz.bo;
import lombok.Data;
import lombok.experimental.Accessors;
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java
index ff1d54c0..4bfe0fea 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductConditionDTO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.api.dto;
+package cn.iocoder.mall.search.biz.dto;
import lombok.Data;
import lombok.experimental.Accessors;
diff --git a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java
index bfc465bd..430a32ed 100644
--- a/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java
+++ b/search/search-service-api/src/main/java/cn/iocoder/mall/search/api/dto/ProductSearchPageDTO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.api.dto;
+package cn.iocoder.mall.search.biz.dto;
import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.vo.SortingField;
diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
index ed239635..6a42e33c 100644
--- a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
+++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/convert/ProductSearchConvert.java
@@ -3,7 +3,7 @@ package cn.iocoder.mall.search.biz.convert;
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
-import cn.iocoder.mall.search.api.bo.ProductBO;
+import cn.iocoder.mall.search.biz.bo.ProductBO;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java
index 81d4fa6e..d2b3156e 100644
--- a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java
+++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/mq/PayTransactionPaySuccessConsumer.java
@@ -1,7 +1,7 @@
package cn.iocoder.mall.search.biz.mq;
import cn.iocoder.mall.product.api.message.ProductUpdateMessage;
-import cn.iocoder.mall.search.api.ProductSearchService;
+import cn.iocoder.mall.search.biz.ProductSearchService;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java
index 1a787a8a..1adda27c 100644
--- a/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java
+++ b/search/search-service-impl/src/main/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImpl.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.biz.service;
+package cn.iocoder.mall.search.biz.api;
import cn.iocoder.common.framework.util.CollectionUtil;
import cn.iocoder.common.framework.util.StringUtil;
@@ -9,11 +9,11 @@ import cn.iocoder.mall.product.api.ProductCategoryService;
import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductCategoryBO;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
-import cn.iocoder.mall.search.api.ProductSearchService;
-import cn.iocoder.mall.search.api.bo.ProductConditionBO;
-import cn.iocoder.mall.search.api.bo.ProductPageBO;
-import cn.iocoder.mall.search.api.dto.ProductConditionDTO;
-import cn.iocoder.mall.search.api.dto.ProductSearchPageDTO;
+import cn.iocoder.mall.search.biz.ProductSearchService;
+import cn.iocoder.mall.search.biz.bo.ProductConditionBO;
+import cn.iocoder.mall.search.biz.bo.ProductPageBO;
+import cn.iocoder.mall.search.biz.dto.ProductConditionDTO;
+import cn.iocoder.mall.search.biz.dto.ProductSearchPageDTO;
import cn.iocoder.mall.search.biz.convert.ProductSearchConvert;
import cn.iocoder.mall.search.biz.dao.ProductRepository;
import cn.iocoder.mall.search.biz.dataobject.ESProductDO;
diff --git a/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImplTest.java b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImplTest.java
index 44047855..06ffc3b2 100644
--- a/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImplTest.java
+++ b/search/search-service-impl/src/test/java/cn/iocoder/mall/search/biz/service/ProductSearchServiceImplTest.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.search.biz.service;
+package cn.iocoder.mall.search.biz.api;
import cn.iocoder.mall.search.biz.dao.ProductRepository;
import org.junit.Test;