suntg 9 年 前
コミット
1f7bb33700
40 ファイル変更4471 行追加0 行削除
  1. 223 0
      pom.xml
  2. 16 0
      search-api/pom.xml
  3. 56 0
      search-api/src/main/java/com/uas/search/model/PageParams.java
  4. 94 0
      search-api/src/main/java/com/uas/search/service/SearchService.java
  5. 33 0
      search-console/src/main/java/com/uas/search/console/controller/BrandController.java
  6. 90 0
      search-console/src/main/java/com/uas/search/console/controller/SearchController.java
  7. 72 0
      search-console/src/main/java/com/uas/search/console/core/util/ContextUtils.java
  8. 93 0
      search-console/src/main/java/com/uas/search/console/core/util/FastjsonUtils.java
  9. 78 0
      search-console/src/main/java/com/uas/search/console/core/util/PathUtils.java
  10. 18 0
      search-console/src/main/java/com/uas/search/console/dao/BrandInfoDao.java
  11. 16 0
      search-console/src/main/java/com/uas/search/console/dao/ComponentInfoDao.java
  12. 16 0
      search-console/src/main/java/com/uas/search/console/dao/KindInfoDao.java
  13. 237 0
      search-console/src/main/java/com/uas/search/console/model/Brand.java
  14. 126 0
      search-console/src/main/java/com/uas/search/console/model/BrandInfo.java
  15. 480 0
      search-console/src/main/java/com/uas/search/console/model/Component.java
  16. 358 0
      search-console/src/main/java/com/uas/search/console/model/ComponentInfo.java
  17. 235 0
      search-console/src/main/java/com/uas/search/console/model/Kind.java
  18. 106 0
      search-console/src/main/java/com/uas/search/console/model/KindInfo.java
  19. 138 0
      search-console/src/main/java/com/uas/search/console/model/KindProperty.java
  20. 104 0
      search-console/src/main/java/com/uas/search/console/model/KindPropertyValues.java
  21. 68 0
      search-console/src/main/java/com/uas/search/console/model/Property.java
  22. 114 0
      search-console/src/main/java/com/uas/search/console/model/PropertyValue.java
  23. 15 0
      search-console/src/main/java/com/uas/search/console/service/IndexerService.java
  24. 275 0
      search-console/src/main/java/com/uas/search/console/service/impl/IndexerServiceImpl.java
  25. 472 0
      search-console/src/main/java/com/uas/search/console/service/impl/SearchServiceImpl.java
  26. 19 0
      search-console/src/main/java/com/uas/search/console/support/ApplicationContextRegister.java
  27. 160 0
      search-console/src/main/java/com/uas/search/console/support/IndexSearcherManager.java
  28. 72 0
      search-console/src/main/java/com/uas/search/console/support/IndexWriterManager.java
  29. 60 0
      search-console/src/main/java/com/uas/search/console/util/SearchConstants.java
  30. 10 0
      search-console/src/main/java/com/uas/search/console/util/Test.java
  31. 127 0
      search-console/src/main/resources/applicationContext.xml
  32. 8 0
      search-console/src/main/resources/ehcache.xml
  33. 270 0
      search-console/src/main/resources/ehcache.xsd
  34. 24 0
      search-console/src/main/resources/jdbc.properties
  35. 11 0
      search-console/src/main/resources/jpa.xml
  36. 17 0
      search-console/src/main/resources/provider.xml
  37. 23 0
      search-console/src/main/webapp/WEB-INF/log4j.properties
  38. 10 0
      search-console/src/main/webapp/WEB-INF/views/index.html
  39. 86 0
      search-console/src/main/webapp/WEB-INF/web.xml
  40. 41 0
      search-console/src/main/webapp/WEB-INF/webmvc.xml

+ 223 - 0
pom.xml

@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>com.uas.search</groupId>
+	<artifactId>search-parent</artifactId>
+	<version>0.0.1</version>
+	<packaging>pom</packaging>
+
+	<name>search-parent</name>
+	<url>http://maven.apache.org</url>
+
+	<properties>
+		<project.version>0.0.1</project.version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<java.source.version>1.7</java.source.version>
+		<java.target.version>1.7</java.target.version>
+		<maven.compiler.plugin.version>2.3.2</maven.compiler.plugin.version>
+		<junit.version>4.12</junit.version>
+		<dubbo.version>2.8.4</dubbo.version>
+		<zookeeper.version>3.4.6</zookeeper.version>
+		<zclient.version>0.1</zclient.version>
+		<servlet.version>3.0-alpha-1</servlet.version>
+		<spring.version>4.1.6.RELEASE</spring.version>
+		<hessian.version>4.0.7</hessian.version>
+		<fastjson.version>1.1.39</fastjson.version>
+		<zkclient.version>0.1</zkclient.version>
+		<log4j.version>1.2.16</log4j.version>
+		<commons.logging.version>1.1.1</commons.logging.version>
+		<spring.data.jpa.version>1.8.0.RELEASE</spring.data.jpa.version>
+		<commons.dbcp.version>1.4</commons.dbcp.version>
+		<oracle.jdbc.version>11.2.0</oracle.jdbc.version>
+	</properties>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>javax.servlet</groupId>
+				<artifactId>servlet-api</artifactId>
+				<version>${servlet.version}</version>
+				<scope>provided</scope>
+			</dependency>
+			<dependency>
+				<groupId>com.uas.search</groupId>
+				<artifactId>search-api</artifactId>
+				<version>${project.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>junit</groupId>
+				<artifactId>junit</artifactId>
+				<version>${junit.version}</version>
+				<scope>test</scope>
+			</dependency>
+			<!-- jdbc -->
+			<dependency>
+				<groupId>com.oracle</groupId>
+				<artifactId>ojdbc6</artifactId>
+				<version>${oracle.jdbc.version}</version>
+			</dependency>
+			<!-- spring -->
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-core</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-context</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-webmvc</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-aop</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-beans</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-expression</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-web</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-jdbc</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-tx</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework</groupId>
+				<artifactId>spring-context-support</artifactId>
+				<version>${spring.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.springframework.data</groupId>
+				<artifactId>spring-data-jpa</artifactId>
+				<version>${spring.data.jpa.version}</version>
+			</dependency>
+			<!-- dubbo -->
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>dubbo</artifactId>
+				<version>${dubbo.version}</version>
+				<exclusions>
+					<exclusion>
+						<groupId>javax.servlet</groupId>
+						<artifactId>javax.servlet-api</artifactId>
+					</exclusion>
+					<exclusion>
+						<groupId>commons-logging</groupId>
+						<artifactId>commons-logging</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+			<!-- alibaba -->
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>fastjson</artifactId>
+				<version>${fastjson.version}</version>
+			</dependency>
+			<!-- hessian -->
+			<dependency>
+				<groupId>com.caucho</groupId>
+				<artifactId>hessian</artifactId>
+				<version>${hession.version}</version>
+			</dependency>
+			<!-- zookeeper -->
+			<dependency>
+				<groupId>org.apache.zookeeper</groupId>
+				<artifactId>zookeeper</artifactId>
+				<version>${zookeeper.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.sgroschupf</groupId>
+				<artifactId>zkclient</artifactId>
+				<version>${zkclient.version}</version>
+			</dependency>
+			<!-- log4j -->
+			<dependency>
+				<groupId>log4j</groupId>
+				<artifactId>log4j</artifactId>
+				<version>${log4j.version}</version>
+			</dependency>
+			<!-- commons-logging -->
+			<dependency>
+				<groupId>commons-logging</groupId>
+				<artifactId>commons-logging</artifactId>
+				<version>${commons.logging.version}</version>
+				<exclusions>
+					<exclusion>
+						<groupId>javax.servlet</groupId>
+						<artifactId>servlet-api</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+			<!-- dbcp -->
+			<dependency>
+				<groupId>commons-dbcp</groupId>
+				<artifactId>commons-dbcp</artifactId>
+				<version>${commons.dbcp.version}</version>
+			</dependency>
+			<!-- hibernate -->
+			<dependency>
+				<groupId>org.hibernate</groupId>
+				<artifactId>hibernate-core</artifactId>
+				<version>4.3.7.Final</version>
+			</dependency>
+			<dependency>
+				<groupId>org.hibernate</groupId>
+				<artifactId>hibernate-entitymanager</artifactId>
+				<version>4.3.7.Final</version>
+				<exclusions>
+					<exclusion>
+						<groupId>cglib</groupId>
+						<artifactId>cglib</artifactId>
+					</exclusion>
+					<exclusion>
+						<groupId>dom4j</groupId>
+						<artifactId>dom4j</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+			<dependency>
+				<groupId>org.hibernate.javax.persistence</groupId>
+				<artifactId>hibernate-jpa-2.1-api</artifactId>
+				<version>1.0.0.Final</version>
+			</dependency>
+			<dependency>
+				<groupId>org.hibernate</groupId>
+				<artifactId>hibernate-ehcache</artifactId>
+				<version>4.3.7.Final</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-collections</groupId>
+				<artifactId>commons-collections</artifactId>
+				<version>3.2.1</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<modules>
+		<module>search-api</module>
+		<module>search-console</module>
+	</modules>
+</project>

+ 16 - 0
search-api/pom.xml

@@ -0,0 +1,16 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.uas.search</groupId>
+		<artifactId>search-parent</artifactId>
+		<version>0.0.1</version>
+	</parent>
+	<artifactId>search-api</artifactId>
+	<name>search-api</name>
+	<url>http://maven.apache.org</url>
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+</project>

+ 56 - 0
search-api/src/main/java/com/uas/search/model/PageParams.java

@@ -0,0 +1,56 @@
+package com.uas.search.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * 分页参数
+ * 
+ * @author suntg
+ * @since 2016年8月3日下午9:10:47
+ */
+public class PageParams implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+	private int page;
+	private int size;
+	private Map<String, Object> filters;
+
+	public PageParams() {
+
+	}
+
+	public PageParams(int page, int size, Map<String, Object> filters) {
+		this.page = page;
+		this.size = size;
+		this.filters = filters;
+	}
+
+	public int getPage() {
+		return page;
+	}
+
+	public void setPage(int page) {
+		this.page = page;
+	}
+
+	public int getSize() {
+		return size;
+	}
+
+	public void setSize(int size) {
+		this.size = size;
+	}
+
+	public Map<String, Object> getFilters() {
+		return filters;
+	}
+
+	public void setFilters(Map<String, Object> filters) {
+		this.filters = filters;
+	}
+
+}

+ 94 - 0
search-api/src/main/java/com/uas/search/service/SearchService.java

@@ -0,0 +1,94 @@
+package com.uas.search.service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.uas.search.model.PageParams;
+
+/**
+ * 搜索服務的接口
+ * 
+ * @author suntg
+ * @since 2016年7月29日下午4:58:45
+ */
+public interface SearchService {
+
+	/**
+	 * 根据关键字搜索产品类目id
+	 * 
+	 * @param keyword
+	 *            关键词
+	 * @return 符合条件的类目id
+	 */
+	public List<Long> getKindIds(String keyword);
+
+	/**
+	 * 根据关键词搜索产品类目
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	public List<Map<String, Object>> getKinds(String keyword);
+
+	/**
+	 * 根据关键词搜索产品品牌id
+	 * 
+	 * @param keyword
+	 *            关键词
+	 * @return 符合条件的品牌id
+	 */
+	public List<Long> getBrandIds(String keyword);
+
+	/**
+	 * 根据关键词搜索产品品牌
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	public List<Map<String, Object>> getBrands(String keyword);
+
+	/**
+	 * 根据关键词搜索产品
+	 * 
+	 * @param keyword
+	 *            关键词
+	 * @return 符合条件的产品id
+	 */
+	public Map<String, Object> getComponentIds(String keyword, PageParams params);
+
+	/**
+	 * 根据产品搜索获取产品类目id的统计
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	public Set<Long> getKindIdsBySearchComponent(String keyword, String brandId);
+
+	/**
+	 * 根据产品搜索获取产品类目的统计
+	 * 
+	 * @param keyword
+	 * @param brandId
+	 * @return
+	 */
+	public List<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId);
+
+	/**
+	 * 根据产品搜索获取产品品牌id的统计
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId);
+
+	/**
+	 * 根据产品搜索获取产品品牌的统计
+	 * 
+	 * @param keyword
+	 * @param kindId
+	 * @return
+	 */
+	public List<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId);
+
+}

+ 33 - 0
search-console/src/main/java/com/uas/search/console/controller/BrandController.java

@@ -0,0 +1,33 @@
+package com.uas.search.console.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.uas.search.console.dao.BrandInfoDao;
+import com.uas.search.console.model.BrandInfo;
+
+/**
+ * 品牌数据请求
+ * 
+ * @author suntg
+ * @since 2016年8月3日上午11:08:36
+ */
+@Controller
+@RequestMapping("/brands")
+public class BrandController {
+
+	@Autowired
+	private BrandInfoDao brandInfoDao;
+
+	@RequestMapping(method = RequestMethod.GET)
+	@ResponseBody
+	public List<BrandInfo> getAllBrandInfos() {
+		return brandInfoDao.findAll();
+	}
+
+}

+ 90 - 0
search-console/src/main/java/com/uas/search/console/controller/SearchController.java

@@ -0,0 +1,90 @@
+package com.uas.search.console.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.uas.search.console.service.IndexerService;
+import com.uas.search.model.PageParams;
+import com.uas.search.service.SearchService;
+
+/**
+ * 搜索请求
+ * 
+ * @author suntg
+ * @since 2016年8月1日上午9:18:05
+ */
+@Controller
+@RequestMapping("/search")
+public class SearchController {
+
+	@Autowired
+	private SearchService searchService;
+
+	@Autowired
+	private IndexerService indexerService;
+
+	/**
+	 * 搜索产品类目
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	@RequestMapping("/kindIds")
+	@ResponseBody
+	public List<Long> seachKindIds(String keyword) {
+		return searchService.getKindIds(keyword);
+	}
+
+	/**
+	 * 搜索产品类目
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	@RequestMapping("/kinds")
+	@ResponseBody
+	public List<Map<String, Object>> seachKinds(String keyword) {
+		return searchService.getKinds(keyword);
+	}
+
+	/**
+	 * 搜索产品品牌
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	@RequestMapping("/brand")
+	@ResponseBody
+	public List<Long> searchBrand(String keyword) {
+		return searchService.getBrandIds(keyword);
+	}
+
+	/**
+	 * 搜索产品
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	@RequestMapping("/component")
+	@ResponseBody
+	public Map<String, Object> searchComponent(String keyword) {
+		return searchService.getComponentIds(keyword, new PageParams());
+	}
+
+	/**
+	 * 初始化创建索引文件
+	 * 
+	 * @return 所用时间 ms
+	 */
+	@RequestMapping("/init")
+	@ResponseBody
+	public String initIndexes() {
+		return "Indexes created success in " + indexerService.createIndexs() + " ms.";
+	}
+
+}

+ 72 - 0
search-console/src/main/java/com/uas/search/console/core/util/ContextUtils.java

@@ -0,0 +1,72 @@
+package com.uas.search.console.core.util;
+
+import org.apache.log4j.Logger;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * spring容器上下文对象
+ * 
+ * @author yingp
+ *
+ */
+public class ContextUtils {
+	private static ApplicationContext applicationContext;
+
+	private static Logger logger = Logger.getLogger(ContextUtils.class);
+
+	public static void setApplicationContext(ApplicationContext applicationContext) {
+		synchronized (ContextUtils.class) {
+			logger.debug("setApplicationContext, notifyAll");
+			ContextUtils.applicationContext = applicationContext;
+			ContextUtils.class.notifyAll();
+		}
+	}
+
+	public static ApplicationContext getApplicationContext() {
+		synchronized (ContextUtils.class) {
+			while (applicationContext == null) {
+				try {
+					logger.debug("getApplicationContext, wait...");
+					ContextUtils.class.wait(60000);
+					if (applicationContext == null) {
+						logger.warn("Have been waiting for ApplicationContext to be set for 1 minute", new Exception());
+					}
+				} catch (InterruptedException ex) {
+					logger.debug("getApplicationContext, wait interrupted");
+				}
+			}
+			return applicationContext;
+		}
+	}
+
+	/**
+	 * 获取bean
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public static Object getBean(String name) {
+		return getApplicationContext().getBean(name);
+	}
+
+	/**
+	 * 获取bean
+	 * 
+	 * @param cls
+	 * @return
+	 */
+	public static <T> T getBean(Class<T> cls) {
+		return getApplicationContext().getBean(cls);
+	}
+
+	/**
+	 * 触发事件
+	 * 
+	 * @param event
+	 */
+	public static void publishEvent(ApplicationEvent event) {
+		getApplicationContext().publishEvent(event);
+	}
+
+}

+ 93 - 0
search-console/src/main/java/com/uas/search/console/core/util/FastjsonUtils.java

@@ -0,0 +1,93 @@
+package com.uas.search.console.core.util;
+
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+
+/**
+ * @author yingp
+ * @see JSON
+ *
+ */
+public class FastjsonUtils {
+
+	public static Feature DEFAULT_PARSER_FEATURE = Feature.DisableCircularReferenceDetect;
+	public static SerializerFeature DEFAULT_SERIAL_FEATURE = SerializerFeature.DisableCircularReferenceDetect;
+
+	/**
+	 * 把JSON文本parse为JSONObject或者JSONArray
+	 * 
+	 * @param text
+	 * @return
+	 */
+	public static Object parse(String text) {
+		return JSON.parse(text, DEFAULT_PARSER_FEATURE);
+	}
+
+	/**
+	 * 把JSON文本parse成JSONObject
+	 * 
+	 * @param text
+	 * @return
+	 */
+	public static final JSONObject parseObject(String text) {
+		return JSON.parseObject(text, DEFAULT_PARSER_FEATURE);
+	}
+
+	/**
+	 * 把JSON文本parse为JavaBean
+	 * 
+	 * @param text
+	 * @param clazz
+	 * @return
+	 */
+	public static final <T> T fromJson(String text, Class<T> clazz) {
+		return JSON.parseObject(text, clazz, DEFAULT_PARSER_FEATURE);
+	}
+
+	/**
+	 * 把JSON文本parse成JSONArray
+	 * 
+	 * @param text
+	 * @return
+	 */
+	public static final JSONArray fromJsonArray(String text) {
+		return JSON.parseArray(text);
+	}
+
+	/**
+	 * 把JSON文本parse成JavaBean集合
+	 * 
+	 * @param text
+	 * @param clazz
+	 * @return
+	 */
+	public static final <T> List<T> fromJsonArray(String text, Class<T> clazz) {
+		return JSON.parseArray(text, clazz);
+	}
+
+	/**
+	 * 将JavaBean序列化为JSON文本
+	 * 
+	 * @param object
+	 * @return
+	 */
+	public static final String toJson(Object object) {
+		return JSON.toJSONString(object, DEFAULT_SERIAL_FEATURE);
+	}
+
+	/**
+	 * 将JavaBean转换为JSONObject或者JSONArray。
+	 * 
+	 * @param javaObject
+	 * @return
+	 */
+	public static final Object toJSON(Object javaObject) {
+		return JSON.toJSON(javaObject);
+	}
+
+}

+ 78 - 0
search-console/src/main/java/com/uas/search/console/core/util/PathUtils.java

@@ -0,0 +1,78 @@
+package com.uas.search.console.core.util;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+/**
+ * 路径
+ * 
+ * @author yingp
+ *
+ */
+public class PathUtils {
+
+	private static String classPath;
+
+	private static String appPath;
+
+	private static String filePath;
+
+	/**
+	 * classes文件目录
+	 * 
+	 * @return
+	 */
+	public static String getClassPath() {
+		if (classPath == null)
+			setClassPath();
+		return classPath;
+	}
+
+	/**
+	 * 应用程序目录
+	 * 
+	 * @return
+	 */
+	public static String getAppPath() {
+		if (appPath == null)
+			setAppPath();
+		return appPath;
+	}
+
+	/**
+	 * 日志、附件文件等存放目录,与程序同级
+	 * 
+	 * @return
+	 */
+	public static String getFilePath() {
+		if (filePath == null)
+			setFilePath();
+		return filePath;
+	}
+
+	private static void setClassPath() {
+		Class<?> objClass = ContextUtils.getApplicationContext().getClass();
+		String strRealPath = objClass.getClassLoader().getResource("").getFile();
+		try {
+			strRealPath = URLDecoder.decode(strRealPath, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		File objFile = new File(strRealPath);
+		classPath = objFile.getParent() + File.separator;
+		if (classPath.contains("/")) {
+			classPath = "/" + classPath;
+		}
+	}
+
+	private static void setAppPath() {
+		File file = new File(getClassPath());
+		appPath = file.getParent() + File.separator;
+	}
+
+	private static void setFilePath() {
+		File file = new File(getAppPath());
+		filePath = file.getParent() + File.separator;
+	}
+}

+ 18 - 0
search-console/src/main/java/com/uas/search/console/dao/BrandInfoDao.java

@@ -0,0 +1,18 @@
+package com.uas.search.console.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import com.uas.search.console.model.BrandInfo;
+
+/**
+ * 品牌简要信息Dao
+ * 
+ * @author suntg
+ * @since 2016年8月3日上午11:07:33
+ */
+@Repository
+public interface BrandInfoDao extends JpaSpecificationExecutor<BrandInfo>, JpaRepository<BrandInfo, Long> {
+
+}

+ 16 - 0
search-console/src/main/java/com/uas/search/console/dao/ComponentInfoDao.java

@@ -0,0 +1,16 @@
+package com.uas.search.console.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import com.uas.search.console.model.ComponentInfo;
+
+/**
+ * 产品简要信息Dao
+ * 
+ * @author suntg
+ * @since 2016年8月3日上午11:29:18
+ */
+public interface ComponentInfoDao extends JpaSpecificationExecutor<ComponentInfo>, JpaRepository<ComponentInfo, Long> {
+
+}

+ 16 - 0
search-console/src/main/java/com/uas/search/console/dao/KindInfoDao.java

@@ -0,0 +1,16 @@
+package com.uas.search.console.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import com.uas.search.console.model.KindInfo;
+
+/**
+ * 产品类目简要信息Dao
+ * 
+ * @author suntg
+ * @since 2016年8月3日上午11:30:16
+ */
+public interface KindInfoDao extends JpaSpecificationExecutor<KindInfo>, JpaRepository<KindInfo, Long> {
+
+}

+ 237 - 0
search-console/src/main/java/com/uas/search/console/model/Brand.java

@@ -0,0 +1,237 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 品牌
+ * 
+ * @author suntg
+ * @since 2016年3月11日上午9:30:00
+ */
+@Entity
+@Table(name = "product$brand")
+public class Brand implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "br_id")
+	private Long id;
+
+	/**
+	 * uuid
+	 */
+	@Column(name = "br_uuid", unique = true)
+	private String uuid;
+
+	/**
+	 * 品牌中文名称
+	 */
+	@Column(name = "br_name_cn")
+	private String nameCn;
+
+	/**
+	 * 品牌英文名称
+	 */
+	@Column(name = "br_name_en")
+	private String nameEn;
+
+	/**
+	 * logo路径
+	 */
+	@Column(name = "br_logourl")
+	private String logoUrl;
+
+	/**
+	 * 品牌所属厂商
+	 */
+	@Column(name = "br_vendor")
+	private String vendor;
+
+	/**
+	 * 品牌简介
+	 */
+	@Column(name = "br_brief", length = 4000)
+	private String brief;
+
+	/**
+	 * 产品系列
+	 */
+	@Column(name = "br_series", length = 4000)
+	private String series;
+
+	/**
+	 * 主要成就
+	 */
+	@Column(name = "br_achievement", length = 4000)
+	private String achievement;
+
+	/**
+	 * 所属地区
+	 */
+	@Column(name = "br_area")
+	private String area;
+
+	/**
+	 * 应用领域
+	 */
+	@Column(name = "br_application")
+	private String application;
+
+	/**
+	 * 创建时间
+	 */
+	@Column(name = "br_createtime")
+	private Date createTime;
+
+	/**
+	 * 最后修改时间
+	 */
+	@Column(name = "br_modifytime")
+	private Date modifyTime;
+
+	/**
+	 * 版本号
+	 */
+	@Column(name = "br_version")
+	private Short version;
+
+	/**
+	 * 权重,可用于排序
+	 */
+	@Column(name = "br_weight")
+	private Double weight;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	public String getNameEn() {
+		return nameEn;
+	}
+
+	public void setNameEn(String nameEn) {
+		this.nameEn = nameEn;
+	}
+
+	public String getLogoUrl() {
+		return logoUrl;
+	}
+
+	public void setLogoUrl(String logoUrl) {
+		this.logoUrl = logoUrl;
+	}
+
+	public String getVendor() {
+		return vendor;
+	}
+
+	public void setVendor(String vendor) {
+		this.vendor = vendor;
+	}
+
+	public String getBrief() {
+		return brief;
+	}
+
+	public void setBrief(String brief) {
+		this.brief = brief;
+	}
+
+	public String getSeries() {
+		return series;
+	}
+
+	public void setSeries(String series) {
+		this.series = series;
+	}
+
+	public String getAchievement() {
+		return achievement;
+	}
+
+	public void setAchievement(String achievement) {
+		this.achievement = achievement;
+	}
+
+	public String getArea() {
+		return area;
+	}
+
+	public void setArea(String area) {
+		this.area = area;
+	}
+
+	public String getApplication() {
+		return application;
+	}
+
+	public void setApplication(String application) {
+		this.application = application;
+	}
+
+	public Date getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(Date createTime) {
+		this.createTime = createTime;
+	}
+
+	public Date getModifyTime() {
+		return modifyTime;
+	}
+
+	public void setModifyTime(Date modifyTime) {
+		this.modifyTime = modifyTime;
+	}
+
+	public Double getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Double weight) {
+		this.weight = weight;
+	}
+
+	public Short getVersion() {
+		return version;
+	}
+
+	public void setVersion(Short version) {
+		this.version = version;
+	}
+
+}

+ 126 - 0
search-console/src/main/java/com/uas/search/console/model/BrandInfo.java

@@ -0,0 +1,126 @@
+package com.uas.search.console.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 简要品牌信息
+ * 
+ * @author suntg
+ * @since 2016年3月11日上午10:02:20
+ */
+@Entity
+@Table(name = "product$brand")
+public class BrandInfo {
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "br_id")
+	private Long id;
+
+	/**
+	 * uuid
+	 */
+	@Column(name = "br_uuid", unique = true)
+	private String uuid;
+
+	/**
+	 * 品牌中文名称
+	 */
+	@Column(name = "br_name_cn")
+	private String nameCn;
+
+	/**
+	 * 品牌英文名称
+	 */
+	@Column(name = "br_name_en")
+	private String nameEn;
+
+	/**
+	 * logo路径
+	 */
+	@Column(name = "br_logourl")
+	private String logoUrl;
+
+	/**
+	 * 权重,可用于排序
+	 */
+	@Column(name = "br_weight")
+	private Double weight;
+
+	@Column(name = "br_vendor")
+	private String venodr;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	public String getNameEn() {
+		return nameEn;
+	}
+
+	public void setNameEn(String nameEn) {
+		this.nameEn = nameEn;
+	}
+
+	public String getLogoUrl() {
+		return logoUrl;
+	}
+
+	public void setLogoUrl(String logoUrl) {
+		this.logoUrl = logoUrl;
+	}
+
+	public Double getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Double weight) {
+		this.weight = weight;
+	}
+
+	public String getVenodr() {
+		return venodr;
+	}
+
+	public void setVenodr(String venodr) {
+		this.venodr = venodr;
+	}
+
+	public BrandInfo() {
+
+	}
+
+	public BrandInfo(Brand brand) {
+		this.id = brand.getId();
+		this.uuid = brand.getUuid();
+		this.nameCn = brand.getNameCn();
+		this.nameEn = brand.getNameEn();
+		this.logoUrl = brand.getLogoUrl();
+		this.weight = brand.getWeight();
+	}
+}

+ 480 - 0
search-console/src/main/java/com/uas/search/console/model/Component.java

@@ -0,0 +1,480 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.OrderBy;
+import javax.persistence.Table;
+
+/**
+ * 标准器件
+ * 
+ * @author suntg
+ * @since 2016年3月11日上午10:24:29
+ */
+@Entity
+@Table(name = "product$component")
+public class Component implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "cmp_id")
+	private Long id;
+
+	/**
+	 * 器件的uuid
+	 */
+	@Column(name = "cmp_uuid", unique = true)
+	private String uuid;
+
+	/**
+	 * 原厂型号
+	 */
+	@Column(name = "cmp_code")
+	private String code;
+
+	/**
+	 * 类目id
+	 */
+	@Column(name = "cmp_kiid")
+	private Long kindid;
+
+	/**
+	 * 器件的类目
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH })
+	@JoinColumn(name = "cmp_kiid", insertable = false, updatable = false)
+	private KindInfo kind;
+
+	/**
+	 * 品牌id
+	 */
+	@Column(name = "cmp_brid")
+	private Long brandid;
+
+	/**
+	 * 器件的品牌
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH })
+	@JoinColumn(name = "cmp_brid", insertable = false, updatable = false)
+	private BrandInfo brand;
+
+	/**
+	 * 器件封装规格 这个封装规格和下面的封装规格值应该是一样的。在componentInfo中有packaging 赋值给spec,
+	 * 现在已packaging 为准(spec 有值的只有一个器件), by yujia
+	 */
+
+	@Column(name = "cmp_packaging")
+	private String packaging;
+
+	/**
+	 * 器件的标准单位
+	 */
+	@Column(name = "cmp_unit")
+	private String unit;
+
+	/**
+	 * 单重(g)
+	 */
+	@Column(name = "cmp_weight")
+	private Float weight;
+
+	/**
+	 * 器件描述
+	 */
+	@Column(name = "cmp_description", length = 4000)
+	private String description;
+
+	/**
+	 * 所属公司
+	 */
+	@Column(name = "cmp_company")
+	private String company;
+
+	/**
+	 * 所属公司url
+	 */
+	@Column(name = "cmp_companyUrl")
+	private String companyUrl;
+
+	/**
+	 * 附件id
+	 * 
+	 * @TODO 建立对应的附件表
+	 */
+	@Column(name = "cmp_attach")
+	private String attach;
+
+	/**
+	 * 图片path
+	 */
+	@Column(name = "cmp_img")
+	private String img;
+
+	/**
+	 * 版本
+	 */
+	@Column(name = "cmp_version")
+	private Short version;
+
+	/**
+	 * 器件创建时间
+	 */
+	@Column(name = "cmp_createtime")
+	private Date createTime;
+
+	/**
+	 * 器件最后修改时间
+	 */
+	@Column(name = "cmp_modifytime")
+	private Date modifyTime;
+
+	/**
+	 * 带的属性及属性的值
+	 */
+	@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
+	@JoinColumn(name = "pv_componentid")
+	@OrderBy("detno")
+	private Set<PropertyValue> properties;
+
+	/**
+	 * 以下为器件的库存交易属性,由器件对应的上架商品发生变化时,更新反应到器件
+	 */
+
+	/**
+	 * 器件的库存
+	 */
+	@Column(name = "cmp_reserve")
+	private Double reserve;
+
+	/**
+	 * 器件的库存类型
+	 */
+	@Column(name = "cmp_reservetype")
+	private Short reserveType;
+
+	/**
+	 * 器件的最低单价
+	 */
+	@Column(name = "cmp_minprice")
+	private Double minPrice;
+
+	/**
+	 * 器件的最小起订量
+	 */
+	@Column(name = "cmp_minbuyqty")
+	private Double minBuyQty;
+
+	/**
+	 * 器件最小送货周期
+	 */
+	@Column(name = "cmp_mindelivery")
+	private Short minDelivery;
+
+	/**
+	 * 器件最大送货周期
+	 */
+	@Column(name = "cmp_maxdelivery")
+	private Short maxDelivery;
+
+	/**
+	 * 交易订单数 - 来自订单表中统计订单数
+	 */
+	@Column(name = "cmp_ordernumber")
+	private Double orderNumber;
+
+	/**
+	 * 交易数量 - 来自订单表中统计交易数量
+	 */
+	@Column(name = "cmp_orderqty")
+	private Double orderQty;
+
+	/**
+	 * 样品数量汇总
+	 */
+	@Column(name = "cmp_sampleqty")
+	private Double sampleQty;
+
+	/**
+	 * 申请样品数量
+	 */
+	@Column(name = "cmp_applysampleqty")
+	private Double applySampleQty;
+
+	/**
+	 * 原装正品数量
+	 */
+	@Column(name = "cmp_originalqty")
+	private Double originalQty;
+
+	/**
+	 * 工厂库存数量
+	 */
+	@Column(name = "cmp_excessqty")
+	private Double excessQty;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public KindInfo getKind() {
+		return kind;
+	}
+
+	public void setKind(KindInfo kind) {
+		this.kind = kind;
+	}
+
+	public BrandInfo getBrand() {
+		return brand;
+	}
+
+	public void setBrand(BrandInfo brand) {
+		this.brand = brand;
+	}
+
+	public String getPackaging() {
+		return packaging;
+	}
+
+	public void setPackaging(String packaging) {
+		this.packaging = packaging;
+	}
+
+	public String getUnit() {
+		return unit;
+	}
+
+	public void setUnit(String unit) {
+		this.unit = unit;
+	}
+
+	public Float getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Float weight) {
+		this.weight = weight;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public String getAttach() {
+		return attach;
+	}
+
+	public void setAttach(String attach) {
+		this.attach = attach;
+	}
+
+	public Short getVersion() {
+		return version;
+	}
+
+	public void setVersion(Short version) {
+		this.version = version;
+	}
+
+	public String getImg() {
+		return img;
+	}
+
+	public void setImg(String img) {
+		this.img = img;
+	}
+
+	public Date getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(Date createTime) {
+		this.createTime = createTime;
+	}
+
+	public Date getModifyTime() {
+		return modifyTime;
+	}
+
+	public void setModifyTime(Date modifyTime) {
+		this.modifyTime = modifyTime;
+	}
+
+	public Double getReserve() {
+		return reserve;
+	}
+
+	public void setReserve(Double reserve) {
+		this.reserve = reserve;
+	}
+
+	public Short getReserveType() {
+		return reserveType;
+	}
+
+	public void setReserveType(Short reserveType) {
+		this.reserveType = reserveType;
+	}
+
+	public Double getMinPrice() {
+		return minPrice;
+	}
+
+	public void setMinPrice(Double minPrice) {
+		this.minPrice = minPrice;
+	}
+
+	public Double getMinBuyQty() {
+		return minBuyQty;
+	}
+
+	public void setMinBuyQty(Double minBuyQty) {
+		this.minBuyQty = minBuyQty;
+	}
+
+	public Short getMinDelivery() {
+		return minDelivery;
+	}
+
+	public void setMinDelivery(Short minDelivery) {
+		this.minDelivery = minDelivery;
+	}
+
+	public Short getMaxDelivery() {
+		return maxDelivery;
+	}
+
+	public void setMaxDelivery(Short maxDelivery) {
+		this.maxDelivery = maxDelivery;
+	}
+
+	public Set<PropertyValue> getProperties() {
+		return properties;
+	}
+
+	public void setProperties(Set<PropertyValue> properties) {
+		this.properties = properties;
+	}
+
+	public String getCompany() {
+		return company;
+	}
+
+	public void setCompany(String company) {
+		this.company = company;
+	}
+
+	public String getCompanyUrl() {
+		return companyUrl;
+	}
+
+	public void setCompanyUrl(String companyUrl) {
+		this.companyUrl = companyUrl;
+	}
+
+	public Long getKindid() {
+		return kindid;
+	}
+
+	public void setKindid(Long kindid) {
+		this.kindid = kindid;
+	}
+
+	public Long getBrandid() {
+		return brandid;
+	}
+
+	public void setBrandid(Long brandid) {
+		this.brandid = brandid;
+	}
+
+	public Double getOrderNumber() {
+		return orderNumber;
+	}
+
+	public void setOrderNumber(Double orderNumber) {
+		this.orderNumber = orderNumber;
+	}
+
+	public Double getOrderQty() {
+		return orderQty;
+	}
+
+	public void setOrderQty(Double orderQty) {
+		this.orderQty = orderQty;
+	}
+
+	public Double getSampleQty() {
+		return sampleQty;
+	}
+
+	public void setSampleQty(Double sampleQty) {
+		this.sampleQty = sampleQty;
+	}
+
+	public Double getApplySampleQty() {
+		return applySampleQty;
+	}
+
+	public void setApplySampleQty(Double applySampleQty) {
+		this.applySampleQty = applySampleQty;
+	}
+
+	public Double getOriginalQty() {
+		return originalQty;
+	}
+
+	public void setOriginalQty(Double originalQty) {
+		this.originalQty = originalQty;
+	}
+
+	public Double getExcessQty() {
+		return excessQty;
+	}
+
+	public void setExcessQty(Double excessQty) {
+		this.excessQty = excessQty;
+	}
+
+}

+ 358 - 0
search-console/src/main/java/com/uas/search/console/model/ComponentInfo.java

@@ -0,0 +1,358 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ * 标准器件
+ * 
+ * @author suntg
+ * @since 2016年3月11日上午10:24:29
+ */
+@Entity
+@Table(name = "product$component")
+public class ComponentInfo implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "cmp_id")
+	private Long id;
+
+	/**
+	 * 器件的uuid
+	 */
+	@Column(name = "cmp_uuid", unique = true)
+	private String uuid;
+
+	/**
+	 * 原厂型号
+	 */
+	@Column(name = "cmp_code")
+	private String code;
+
+	/**
+	 * 类目id
+	 */
+	@Column(name = "cmp_kiid")
+	private Long kindid;
+
+	/**
+	 * 器件的类目
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH })
+	@JoinColumn(name = "cmp_kiid", insertable = false, updatable = false)
+	private KindInfo kind;
+
+	/**
+	 * 品牌id
+	 */
+	@Column(name = "cmp_brid")
+	private Long brandid;
+
+	/**
+	 * 器件的品牌
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH })
+	@JoinColumn(name = "cmp_brid", insertable = false, updatable = false)
+	private BrandInfo brand;
+
+	/*
+	 * 器件规格
+	 */
+	@Column(name = "cmp_packaging")
+	private String packaging;
+
+	/**
+	 * 器件的标准单位
+	 */
+	@Column(name = "cmp_unit")
+	private String unit;
+
+	/**
+	 * 单重(g)
+	 */
+	@Column(name = "cmp_weight")
+	private Float weight;
+
+	/**
+	 * 附件id
+	 */
+	@Column(name = "cmp_attach")
+	private String attach;
+
+	/**
+	 * 图片path
+	 */
+	@Column(name = "cmp_img")
+	private String img;
+
+	/**
+	 * 以下为器件的库存交易属性,由器件对应的上架商品发生变化时,更新反应到器件
+	 */
+
+	/**
+	 * 器件的库存
+	 */
+	@Column(name = "cmp_reserve")
+	private Double reserve;
+
+	/**
+	 * 器件的库存类型
+	 */
+	@Column(name = "cmp_reservetype")
+	private Short reserveType;
+
+	/**
+	 * 器件的最低单价
+	 */
+	@Column(name = "cmp_minprice")
+	private Double minPrice;
+
+	/**
+	 * 器件的最小起订量
+	 */
+	@Column(name = "cmp_minbuyqty")
+	private Double minBuyQty;
+
+	/**
+	 * 器件最小送货周期
+	 */
+	@Column(name = "cmp_mindelivery")
+	private Short minDelivery;
+
+	/**
+	 * 器件最大送货周期
+	 */
+	@Column(name = "cmp_maxdelivery")
+	private Short maxDelivery;
+
+	/**
+	 * 交易订单数 - 来自订单表中统计订单数
+	 */
+	@Column(name = "cmp_ordernumber")
+	private Double orderNumber;
+
+	/**
+	 * 交易数量 - 来自订单表中统计交易数量
+	 */
+	@Column(name = "cmp_orderqty")
+	private Double orderQty;
+
+	/**
+	 * 样品数量汇总
+	 */
+	@Column(name = "cmp_sampleqty")
+	private Double sampleQty;
+
+	/**
+	 * 原装正品数量
+	 */
+	@Column(name = "cmp_originalqty")
+	private Double originalQty;
+
+	/**
+	 * 工厂库存数量
+	 */
+	@Column(name = "cmp_excessqty")
+	private Double excessQty;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public KindInfo getKind() {
+		return kind;
+	}
+
+	public void setKind(KindInfo kind) {
+		this.kind = kind;
+	}
+
+	public BrandInfo getBrand() {
+		return brand;
+	}
+
+	public void setBrand(BrandInfo brand) {
+		this.brand = brand;
+	}
+
+	public String getUnit() {
+		return unit;
+	}
+
+	public void setUnit(String unit) {
+		this.unit = unit;
+	}
+
+	public Float getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Float weight) {
+		this.weight = weight;
+	}
+
+	public String getAttach() {
+		return attach;
+	}
+
+	public void setAttach(String attach) {
+		this.attach = attach;
+	}
+
+	public String getImg() {
+		return img;
+	}
+
+	public void setImg(String img) {
+		this.img = img;
+	}
+
+	public Double getReserve() {
+		return reserve;
+	}
+
+	public void setReserve(Double reserve) {
+		this.reserve = reserve;
+	}
+
+	public Short getReserveType() {
+		return reserveType;
+	}
+
+	public void setReserveType(Short reserveType) {
+		this.reserveType = reserveType;
+	}
+
+	public Double getMinPrice() {
+		return minPrice;
+	}
+
+	public void setMinPrice(Double minPrice) {
+		this.minPrice = minPrice;
+	}
+
+	public Double getMinBuyQty() {
+		return minBuyQty;
+	}
+
+	public void setMinBuyQty(Double minBuyQty) {
+		this.minBuyQty = minBuyQty;
+	}
+
+	public Short getMinDelivery() {
+		return minDelivery;
+	}
+
+	public void setMinDelivery(Short minDelivery) {
+		this.minDelivery = minDelivery;
+	}
+
+	public Short getMaxDelivery() {
+		return maxDelivery;
+	}
+
+	public void setMaxDelivery(Short maxDelivery) {
+		this.maxDelivery = maxDelivery;
+	}
+
+	public Long getKindid() {
+		return kindid;
+	}
+
+	public void setKindid(Long kindid) {
+		this.kindid = kindid;
+	}
+
+	public Long getBrandid() {
+		return brandid;
+	}
+
+	public void setBrandid(Long brandid) {
+		this.brandid = brandid;
+	}
+
+	public String getPackaging() {
+		return packaging;
+	}
+
+	public void setPackaging(String packaging) {
+		this.packaging = packaging;
+	}
+
+	// 构造函数
+	public Double getOrderNumber() {
+		return orderNumber;
+	}
+
+	public void setOrderNumber(Double orderNumber) {
+		this.orderNumber = orderNumber;
+	}
+
+	public Double getOrderQty() {
+		return orderQty;
+	}
+
+	public void setOrderQty(Double orderQty) {
+		this.orderQty = orderQty;
+	}
+
+	public Double getSampleQty() {
+		return sampleQty;
+	}
+
+	public void setSampleQty(Double sampleQty) {
+		this.sampleQty = sampleQty;
+	}
+
+	public Double getOriginalQty() {
+		return originalQty;
+	}
+
+	public void setOriginalQty(Double originalQty) {
+		this.originalQty = originalQty;
+	}
+
+	public Double getExcessQty() {
+		return excessQty;
+	}
+
+	public void setExcessQty(Double excessQty) {
+		this.excessQty = excessQty;
+	}
+
+}

+ 235 - 0
search-console/src/main/java/com/uas/search/console/model/Kind.java

@@ -0,0 +1,235 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.OrderBy;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 产品类目
+ * 
+ * @author suntg
+ * @since 2016年3月10日下午3:08:40
+ */
+@Entity
+@Table(name = "product$kind")
+public class Kind implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * ID号
+	 */
+	@Id
+	@Column(name = "ki_id")
+	private Long id;
+
+	/**
+	 * 父级类目的id号
+	 */
+	@Column(name = "ki_parentid")
+	private Long parentid;
+
+	/**
+	 * 是否为叶子类目 1是 0否
+	 */
+	@Column(name = "ki_isleaf")
+	private Short isLeaf;
+
+	/**
+	 * 在父级类目中的排序序号
+	 */
+	@Column(name = "ki_detno")
+	private Short detno;
+
+	/**
+	 * 类目的名称
+	 */
+	@Column(name = "ki_name")
+	private String nameCn;
+
+	/**
+	 * 类目的英文名称
+	 */
+	@Column(name = "ki_name_en")
+	private String nameEn;
+
+	/**
+	 * 类目包含的器件总数,包括其所有子类目下的
+	 */
+	@Column(name = "ki_count")
+	private Long count;
+
+	/**
+	 * 类目的层级,从1开始,1、2、3、4
+	 */
+	@Column(name = "ki_level")
+	private Short level;
+
+	/**
+	 * 器件编号前缀
+	 */
+	@Column(name = "ki_cmpprefix")
+	private String componentPrefix;
+
+	/**
+	 * 当前器件编号后缀游标
+	 */
+	@Column(name = "ki_cmpsuffix")
+	private Long componentsuffix;
+
+	/**
+	 * 包含的属性
+	 */
+	@OneToMany(mappedBy = "kind", cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
+	@OrderBy("detno")
+	private Set<KindProperty> properties;
+
+	/**
+	 * 所含子类目
+	 */
+	@Transient
+	private List<Kind> children;
+
+	/**
+	 * 其兄弟类目
+	 */
+	@Transient
+	private List<Kind> bothers;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getParentid() {
+		return parentid;
+	}
+
+	public void setParentid(Long parentid) {
+		this.parentid = parentid;
+	}
+
+	public Short getIsLeaf() {
+		return isLeaf;
+	}
+
+	public void setIsLeaf(Short isLeaf) {
+		this.isLeaf = isLeaf;
+	}
+
+	public boolean isLeaf() {
+		return this.isLeaf == null ? false : getIsLeaf() == 1;
+	}
+
+	public Short getDetno() {
+		return detno;
+	}
+
+	public void setDetno(Short detno) {
+		this.detno = detno;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	public String getNameEn() {
+		return nameEn;
+	}
+
+	public void setNameEn(String nameEn) {
+		this.nameEn = nameEn;
+	}
+
+	public Long getCount() {
+		return count;
+	}
+
+	public void setCount(Long count) {
+		this.count = count;
+	}
+
+	public void addCount() {
+		this.count = this.count + 1;
+	}
+
+	public void addCount(Long count) {
+		this.count = this.count + count;
+	}
+
+	public void decreaseCount() {
+		this.count = this.count - 1;
+	}
+
+	public List<Kind> getChildren() {
+		return children;
+	}
+
+	public void setChildren(List<Kind> children) {
+		this.children = children;
+	}
+
+	public List<Kind> getBothers() {
+		return bothers;
+	}
+
+	public void setBothers(List<Kind> bothers) {
+		this.bothers = bothers;
+	}
+
+	@JSONField(serialize = false)
+	public Set<KindProperty> getProperties() {
+		return properties;
+	}
+
+	public void setProperties(Set<KindProperty> properties) {
+		this.properties = properties;
+	}
+
+	public Short getLevel() {
+		return level;
+	}
+
+	public void setLevel(Short level) {
+		this.level = level;
+	}
+
+	public String getComponentPrefix() {
+		return componentPrefix;
+	}
+
+	public void setComponentPrefix(String componentPrefix) {
+		this.componentPrefix = componentPrefix;
+	}
+
+	public Long getComponentsuffix() {
+		return componentsuffix;
+	}
+
+	public void setComponentsuffix(Long componentsuffix) {
+		this.componentsuffix = componentsuffix;
+	}
+
+}

+ 106 - 0
search-console/src/main/java/com/uas/search/console/model/KindInfo.java

@@ -0,0 +1,106 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 简要类目信息
+ * 
+ * @author suntg
+ * @since 2016年3月10日下午5:15:28
+ */
+@Entity
+@Table(name = "product$kind")
+public class KindInfo implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "ki_id")
+	private Long id;
+
+	/**
+	 * 类目名称
+	 */
+	@Column(name = "ki_name")
+	private String nameCn;
+
+	/**
+	 * 英文名
+	 */
+	@Column(name = "ki_name_en")
+	private String nameEn;
+
+	/**
+	 * 父节点id
+	 */
+	@Column(name = "ki_parentid")
+	private Long parentid;
+
+	/**
+	 * 类目的层级,从1开始,1、2、3、4
+	 */
+	@Column(name = "ki_level")
+	private Short level;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	public String getNameEn() {
+		return nameEn;
+	}
+
+	public void setNameEn(String nameEn) {
+		this.nameEn = nameEn;
+	}
+
+	public Long getParentid() {
+		return parentid;
+	}
+
+	public void setParentid(Long parentid) {
+		this.parentid = parentid;
+	}
+
+	public Short getLevel() {
+		return level;
+	}
+
+	public void setLevel(Short level) {
+		this.level = level;
+	}
+
+	public KindInfo() {
+
+	}
+
+	public KindInfo(Kind kind) {
+		this.id = kind.getId();
+		this.nameCn = kind.getNameCn();
+		this.nameEn = kind.getNameEn();
+		this.parentid = kind.getParentid();
+	}
+}

+ 138 - 0
search-console/src/main/java/com/uas/search/console/model/KindProperty.java

@@ -0,0 +1,138 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.OrderBy;
+import javax.persistence.Table;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 类目对应的属性
+ * 
+ * @author suntg
+ * @since 2016年3月11日下午3:10:34
+ */
+@Entity
+@Table(name = "product$kindproperty")
+public class KindProperty implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "kp_id")
+	private Long id;
+
+	/**
+	 * 类目id
+	 */
+	@Column(name = "kp_kindid")
+	private Long kindId;
+
+	/**
+	 * 类目
+	 */
+	@ManyToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY)
+	@JoinColumn(name = "kp_kindid", insertable = false, updatable = false)
+	private Kind kind;
+
+	/**
+	 * 序号
+	 */
+	@Column(name = "kp_detno")
+	private Short detno;
+
+	/**
+	 * 属性id
+	 */
+	@Column(name = "kp_propertyid")
+	private Long propertyId;
+
+	/**
+	 * 属性
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY)
+	@JoinColumn(name = "kp_propertyid", insertable = false, updatable = false)
+	private Property property;
+
+	/**
+	 * 存在的各种值
+	 */
+	@OneToMany(mappedBy = "kindProperty", cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
+	@OrderBy("detno")
+	private Set<KindPropertyValues> values;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	@JSONField(serialize = false)
+	public Kind getKind() {
+		return kind;
+	}
+
+	public void setKind(Kind kind) {
+		this.kind = kind;
+	}
+
+	public Property getProperty() {
+		return property;
+	}
+
+	public void setProperty(Property property) {
+		this.property = property;
+	}
+
+	public Set<KindPropertyValues> getValues() {
+		return values;
+	}
+
+	public void setValues(Set<KindPropertyValues> values) {
+		this.values = values;
+	}
+
+	public Short getDetno() {
+		return detno;
+	}
+
+	public void setDetno(Short detno) {
+		this.detno = detno;
+	}
+
+	public Long getKindId() {
+		return kindId;
+	}
+
+	public void setKindId(Long kindId) {
+		this.kindId = kindId;
+	}
+
+	public Long getPropertyId() {
+		return propertyId;
+	}
+
+	public void setPropertyId(Long propertyId) {
+		this.propertyId = propertyId;
+	}
+
+}

+ 104 - 0
search-console/src/main/java/com/uas/search/console/model/KindPropertyValues.java

@@ -0,0 +1,104 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+/**
+ * 类目属性存在的各种可能的值
+ * 
+ * @author suntg
+ * @since 2016年3月11日下午2:47:21
+ */
+@Entity
+@Table(name = "product$kindpropertyvalue")
+public class KindPropertyValues implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "kpv_id")
+	private Long id;
+
+	/**
+	 * 类目属性关联主键
+	 */
+	@Column(name = "kpv_kindpropertyid")
+	private Long kindPropertyId;
+
+	/**
+	 * 类目属性
+	 */
+	@ManyToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY)
+	@JoinColumn(name = "kpv_kindpropertyid", insertable = false, updatable = false)
+	private KindProperty kindProperty;
+
+	/**
+	 * 在类目中的排序
+	 */
+	@Column(name = "kpv_number")
+	private Short detno;
+
+	/**
+	 * 存在的值
+	 */
+	@Column(name = "kpv_value")
+	private String value;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	@JSONField(serialize = false)
+	public KindProperty getKindProperty() {
+		return kindProperty;
+	}
+
+	public void setKindProperty(KindProperty kindProperty) {
+		this.kindProperty = kindProperty;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public Short getDetno() {
+		return detno;
+	}
+
+	public void setDetno(Short detno) {
+		this.detno = detno;
+	}
+
+	public Long getKindPropertyId() {
+		return kindPropertyId;
+	}
+
+	public void setKindPropertyId(Long kindPropertyId) {
+		this.kindPropertyId = kindPropertyId;
+	}
+
+}

+ 68 - 0
search-console/src/main/java/com/uas/search/console/model/Property.java

@@ -0,0 +1,68 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 标准属性
+ * 
+ * @author suntg
+ * @since 2016年3月11日下午5:02:18
+ */
+@Entity
+@Table(name = "product$property")
+public class Property implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "pt_id")
+	private Long id;
+
+	/**
+	 * 属性中文名
+	 */
+	@Column(name = "pt_label")
+	private String labelCn;
+
+	/**
+	 * 属性名 英文
+	 */
+	@Column(name = "pt_label_en")
+	private String labelEn;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getLabelCn() {
+		return labelCn;
+	}
+
+	public void setLabelCn(String labelCn) {
+		this.labelCn = labelCn;
+	}
+
+	public String getLabelEn() {
+		return labelEn;
+	}
+
+	public void setLabelEn(String labelEn) {
+		this.labelEn = labelEn;
+	}
+
+}

+ 114 - 0
search-console/src/main/java/com/uas/search/console/model/PropertyValue.java

@@ -0,0 +1,114 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ * 器件对应的属性的值
+ * 
+ * @author suntg
+ * @since 2016年3月11日下午2:24:29
+ */
+@Entity
+@Table(name = "product$propertyvalue")
+public class PropertyValue implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "pv_id")
+	private Long id;
+
+	/**
+	 * 器件Id
+	 */
+	@Column(name = "pv_componentid")
+	private Long componentid;
+
+	/**
+	 * 属性Id
+	 */
+	@Column(name = "pv_propertyid")
+	private Long propertyid;
+
+	/**
+	 * 属性
+	 */
+	@OneToOne(cascade = { CascadeType.REFRESH })
+	@JoinColumn(name = "pv_propertyid", insertable = false, updatable = false)
+	private Property property;
+
+	/**
+	 * 排序
+	 */
+	@Column(name = "pv_detno")
+	private Short detno;
+
+	/**
+	 * 属性的值
+	 */
+	@Column(name = "pv_value")
+	private String value;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getComponentid() {
+		return componentid;
+	}
+
+	public void setComponentid(Long componentid) {
+		this.componentid = componentid;
+	}
+
+	public Property getProperty() {
+		return property;
+	}
+
+	public void setProperty(Property property) {
+		this.property = property;
+	}
+
+	public Short getDetno() {
+		return detno;
+	}
+
+	public void setDetno(Short detno) {
+		this.detno = detno;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public Long getPropertyid() {
+		return propertyid;
+	}
+
+	public void setPropertyid(Long propertyid) {
+		this.propertyid = propertyid;
+	}
+
+}

+ 15 - 0
search-console/src/main/java/com/uas/search/console/service/IndexerService.java

@@ -0,0 +1,15 @@
+package com.uas.search.console.service;
+
+/**
+ * 索引处理
+ */
+public interface IndexerService {
+
+	/**
+	 * 初始化时,从数据库中得到全部类目、品牌和器件对象,写进索引中
+	 * 
+	 * @return 创建的索引花费总时间 ms
+	 */
+	public Long createIndexs();
+
+}

+ 275 - 0
search-console/src/main/java/com/uas/search/console/service/impl/IndexerServiceImpl.java

@@ -0,0 +1,275 @@
+package com.uas.search.console.service.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.store.FSDirectory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.uas.platform.core.model.PageInfo;
+import com.uas.platform.core.model.PageParams;
+import com.uas.search.console.dao.BrandInfoDao;
+import com.uas.search.console.dao.ComponentInfoDao;
+import com.uas.search.console.dao.KindInfoDao;
+import com.uas.search.console.model.BrandInfo;
+import com.uas.search.console.model.ComponentInfo;
+import com.uas.search.console.model.KindInfo;
+import com.uas.search.console.service.IndexerService;
+import com.uas.search.console.support.IndexWriterManager;
+import com.uas.search.console.util.SearchConstants;
+
+/**
+ * 创建索引
+ */
+@Service
+public class IndexerServiceImpl implements IndexerService {
+
+	@Autowired
+	private KindInfoDao kindDao;
+
+	@Autowired
+	private BrandInfoDao brandDao;
+
+	@Autowired
+	private ComponentInfoDao componentDao;
+
+	private IndexWriter indexWriter;
+
+	private static IndexWriterManager indexWriterManager;
+
+	private static final int PAGE_SIZE = 1000;
+
+	public IndexerServiceImpl() {
+		try {
+			FSDirectory fsDirectory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
+			indexWriterManager = new IndexWriterManager(fsDirectory);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public Long createIndexs() {
+		// 清除旧索引
+		File file = new File(SearchConstants.INDEX_DIR);
+		if (file.isDirectory()) {
+			deleteOldIndex(file);
+		}
+
+		try {
+			indexWriter = indexWriterManager.get();
+			Long startTime = new Date().getTime();
+
+			Long kindSize = createKindIndexs();
+			Long kindTime = new Date().getTime();
+			System.out.println("创建类目索引: " + kindSize + "条,耗时 " + (kindTime - startTime) + " ms");
+
+			Long brandSize = createBrandIndexs();
+			Long brandTime = new Date().getTime();
+			System.out.println("创建品牌索引: " + brandSize + "条,耗时 " + (brandTime - kindTime) + " ms");
+
+			Long componentSize = createComponentIndexs();
+			Long componentTime = new Date().getTime();
+			System.out.println("创建器件索引: " + componentSize + "条,耗时 " + (componentTime - brandTime) + " ms");
+
+			System.out.println("索引创建成功, 共用时间 " + (componentTime - startTime) + " ms");
+
+			return componentTime - startTime;
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} finally {
+			indexWriterManager.release();
+		}
+
+		return null;
+	}
+
+	/**
+	 * 创建类目索引
+	 * 
+	 * @return 写入的类目索引数
+	 * @throws IOException
+	 */
+	private Long createKindIndexs() throws IOException {
+		List<KindInfo> kinds = kindDao.findAll();
+
+		if (CollectionUtils.isEmpty(kinds))
+			return 0L;
+
+		for (KindInfo kind : kinds) {
+			Document document = toDocument(kind);
+			if (document != null)
+				indexWriter.addDocument(document);
+		}
+		indexWriter.commit();
+		return (long) kinds.size();
+	}
+
+	/**
+	 * 创建品牌索引
+	 * 
+	 * @return 写入的品牌索引数
+	 * @throws IOException
+	 */
+	private Long createBrandIndexs() throws IOException {
+		List<BrandInfo> brands = brandDao.findAll();
+		if (CollectionUtils.isEmpty(brands))
+			return 0L;
+
+		for (BrandInfo brand : brands) {
+			Document document = toDocument(brand);
+			if (document != null) {
+				indexWriter.addDocument(document);
+			}
+		}
+		indexWriter.commit();
+		return (long) brands.size();
+	}
+
+	/**
+	 * 创建器件索引
+	 * 
+	 * @return 写入的器件索引数
+	 * @throws IOException
+	 */
+	public Long createComponentIndexs() throws IOException {
+		Long size = 0L;
+		PageParams params = new PageParams();
+		int page = 1;
+		params.setCount(PAGE_SIZE);
+		params.setPage(page);
+
+		PageInfo info = new PageInfo(params);
+		Page<ComponentInfo> pageResult = componentDao.findAll(info);
+		while (pageResult.getTotalElements() > size) {
+			List<ComponentInfo> components = pageResult.getContent();
+			for (ComponentInfo component : components) {
+				Document document = toDocument(component);
+				if (document != null) {
+					indexWriter.addDocument(document);
+				}
+			}
+			size += components.size();
+
+			page++;
+			params.setPage(page);
+			info = new PageInfo(params);
+			pageResult = componentDao.findAll(info);
+		}
+		indexWriter.commit();
+		return size;
+	}
+
+	/**
+	 * KindSimpleInfo对象转为Document
+	 * 
+	 * @param kind
+	 * @return
+	 */
+	private Document toDocument(KindInfo kind) {
+		Document document = new Document();
+		// 不能用LongField,否则后续实时更新索引时,方法updateDocument(new Term("", ""),
+		// doc)无法根据id进行更新
+		if (kind == null || kind.getId() == null || StringUtils.isEmpty(kind.getNameCn())) {
+			return null;
+		}
+		document.add(new StringField(SearchConstants.KIND_ID_FIELD, String.valueOf(kind.getId()), Store.YES));
+		document.add(new TextField(SearchConstants.KIND_NAMECN_FIELD, kind.getNameCn(), Store.YES));
+		return document;
+	}
+
+	/**
+	 * BrandSimpleInfo对象转为Document
+	 * 
+	 * @param brand
+	 * @return
+	 */
+	private Document toDocument(BrandInfo brand) {
+		Document document = new Document();
+		if (brand == null || brand.getId() == null || StringUtils.isEmpty(brand.getNameCn())) {
+			return null;
+		}
+		// 不用LongField,否则后续实时更新索引时无法根据id进行更新
+		document.add(new StringField(SearchConstants.BRAND_ID_FIELD, String.valueOf(brand.getId()), Store.YES));
+		document.add(new TextField(SearchConstants.BRAND_NAMECN_FIELD, brand.getNameCn(), Store.YES));
+		document.add(new StringField(SearchConstants.BRAND_UUID_FIELD, brand.getUuid(), Store.YES));
+		return document;
+	}
+
+	/**
+	 * ComponentSimpleInfo对象转为Document
+	 * 
+	 * @param component
+	 * @return
+	 */
+	private Document toDocument(ComponentInfo component) {
+		if (component == null || component.getId() == null || StringUtils.isEmpty(component.getCode())) {
+			return null;
+		}
+		Document document = new Document();
+		// 不用LongField
+		document.add(new StringField(SearchConstants.COMPONENT_ID_FIELD, String.valueOf(component.getId()), Store.YES));
+		// 转小写
+		String code = component.getCode().toLowerCase();
+		document.add(new StringField(SearchConstants.COMPONENT_CODE_FIELD, code, Store.YES));
+
+		if (component.getKind() != null) {
+			// 不用LongField
+			document.add(new StringField(SearchConstants.COMPONENT_KINDID_FIELD, String.valueOf(component.getKind()
+					.getId()), Store.YES));
+			document.add(new StringField(SearchConstants.COMPONENT_KINDNAME_FIELD, component.getKind().getNameCn(),
+					Store.YES));
+		}
+
+		if (component.getBrand() != null) {
+			// 不用LongField
+			document.add(new StringField(SearchConstants.COMPONENT_BRANDID_FIELD, String.valueOf(component.getBrand()
+					.getId()), Store.YES));
+			document.add(new StringField(SearchConstants.COMPONENT_BRANDUUID_FIELD, String.valueOf(component.getBrand()
+					.getUuid()), Store.YES));
+			document.add(new StringField(SearchConstants.COMPONENT_BRANDNAME_FIELD, String.valueOf(component.getBrand()
+					.getNameCn()), Store.YES));
+		}
+
+		// TODO 属性值加入索引
+		return document;
+	}
+
+	/**
+	 * 删除原有索引文件
+	 * 
+	 * @param file
+	 */
+	private void deleteOldIndex(File file) {
+		if (file == null) {
+			return;
+		}
+		if (file.isDirectory()) {
+			File[] files = file.listFiles();
+			for (File f : files) {
+				deleteOldIndex(f);
+			}
+		}
+
+		file.delete();
+		try {
+			System.out.println("deleted " + file.getCanonicalPath());
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 472 - 0
search-console/src/main/java/com/uas/search/console/service/impl/SearchServiceImpl.java

@@ -0,0 +1,472 @@
+package com.uas.search.console.service.impl;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.wltea.analyzer.lucene.IKAnalyzer;
+
+import com.uas.search.console.support.IndexSearcherManager;
+import com.uas.search.console.util.SearchConstants;
+import com.uas.search.model.PageParams;
+import com.uas.search.service.SearchService;
+
+@Service
+public class SearchServiceImpl implements SearchService {
+
+	/**
+	 * 默认最大搜索的记录条数
+	 */
+	private static final int TOP_NUM = 1024 * 1024 * 1024;
+
+	private static IndexSearcherManager searcherManager = new IndexSearcherManager();
+
+	public List<Long> getKindIds(String keyword) {
+		List<Long> ids = new ArrayList<Long>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.KIND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.KIND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				ids.add(Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return ids;
+	}
+
+	public List<Long> getBrandIds(String keyword) {
+		List<Long> ids = new ArrayList<Long>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.BRAND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.BRAND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				ids.add(Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+
+		return ids;
+	}
+
+	public Map<String, Object> getComponentIds(String keyword, PageParams page) {
+		Map<String, Object> map = new HashMap<String, Object>();
+		List<Long> ids = new ArrayList<Long>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		if (page.getPage() == 0)
+			page.setPage(1);
+		if (page.getSize() == 0)
+			page.setSize(20);
+
+		try {
+			BooleanQuery booleanQuery = new BooleanQuery();
+			// 因SearcherController里调用URLEncoder.encode,搜索词中的空格(若有)会被替换为"+",因此需要进行恢复
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
+			if (!StringUtils.isEmpty(page.getFilters())) {
+				if (!StringUtils.isEmpty(page.getFilters().get("kindId"))) {// 筛选类目
+					String kindId = String.valueOf(page.getFilters().get("kindId"));
+					TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
+					booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
+				}
+				if (!StringUtils.isEmpty(page.getFilters().get("brandId"))) {// 筛选品牌
+					String brandId = String.valueOf(page.getFilters().get("brandId"));
+					TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, brandId));
+					booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
+				}
+			}
+
+			TopDocs hits;
+			if (page.getPage() > 1) {// 不是第一页
+				TopDocs previousHits = searcher.search(booleanQuery, (page.getPage() - 1) * page.getSize());
+				ScoreDoc[] previousScoreDocs = previousHits.scoreDocs;
+				ScoreDoc after = previousScoreDocs[previousScoreDocs.length - 1];
+				hits = searcher.searchAfter(after, booleanQuery, page.getSize());
+			} else {
+				hits = searcher.search(booleanQuery, page.getSize());
+			}
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			if (hits.totalHits > 0) {
+				for (ScoreDoc scoreDoc : scoreDocs) {
+					Document document = searcher.doc(scoreDoc.doc);
+					String componentId = document.get(SearchConstants.COMPONENT_ID_FIELD);
+					ids.add(Long.parseLong(componentId));
+				}
+			}
+			map.put("components", ids);
+			map.put("page", page.getPage());
+			map.put("size", page.getSize());
+			map.put("total", hits.totalHits);
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return map;
+	}
+
+	public Set<Long> getKindIdsBySearchComponent(String keyword, String brandId) {
+		Set<Long> kindIds = new HashSet<Long>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		try {
+			BooleanQuery booleanQuery = new BooleanQuery();
+
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
+
+			// 筛选品牌
+			if (!StringUtils.isEmpty(brandId)) {
+				TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, brandId));
+				booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			if (hits.totalHits > 0) {
+				ScoreDoc[] scoreDocs = hits.scoreDocs;
+				for (ScoreDoc scoreDoc : scoreDocs) {
+					Document document = searcher.doc(scoreDoc.doc);
+					String cmp_kind_id = document.get(SearchConstants.COMPONENT_KINDID_FIELD);
+					if (!StringUtils.isEmpty(cmp_kind_id)) {
+						kindIds.add(Long.parseLong(cmp_kind_id));
+					}
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return kindIds;
+	}
+
+	public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
+		Set<Long> brandIds = new HashSet<Long>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword) || searcher == null) {
+			return null;
+		}
+
+		try {
+			BooleanQuery booleanQuery = new BooleanQuery();
+
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
+
+			// 筛选类目
+			if (!StringUtils.isEmpty(kindId)) {
+				TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
+				booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			if (hits.totalHits > 0) {
+				ScoreDoc[] scoreDocs = hits.scoreDocs;
+				for (ScoreDoc scoreDoc : scoreDocs) {
+					Document document = searcher.doc(scoreDoc.doc);
+					String cmp_brand_id = document.get(SearchConstants.COMPONENT_BRANDID_FIELD);
+					if (!StringUtils.isEmpty(cmp_brand_id)) {
+						brandIds.add(Long.parseLong(cmp_brand_id));
+					}
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+
+		return brandIds;
+	}
+
+	/**
+	 * 判断搜索词是否为无效的(比如只包含特殊字符,不含有任何字母、数字、汉字等有意义的字符)
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	private boolean isKeywordInvalid(String keyword) {
+		if (StringUtils.isEmpty(keyword)) {
+			return true;
+		}
+		// 将特殊字符剔除
+		keyword = keyword.replaceAll("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]+", "");
+		if (StringUtils.isEmpty(keyword)) {
+			return true;
+		}
+		return false;
+	}
+
+	public List<Map<String, Object>> getKinds(String keyword) {
+		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.KIND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.KIND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				Map<String, Object> kind = new HashMap<String, Object>();
+				kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
+				kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
+				kinds.add(kind);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return kinds;
+	}
+
+	public List<Map<String, Object>> getBrands(String keyword) {
+		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.BRAND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.BRAND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				Map<String, Object> brand = new HashMap<String, Object>();
+				brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
+				brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
+				brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
+				brands.add(brand);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return brands;
+	}
+
+	public List<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
+		Set<Long> kindIds = new HashSet<Long>();
+		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		try {
+			BooleanQuery booleanQuery = new BooleanQuery();
+
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
+
+			// 筛选品牌
+			if (!StringUtils.isEmpty(brandId)) {
+				TermQuery brandQuery = new TermQuery(new Term(SearchConstants.COMPONENT_BRANDID_FIELD, brandId));
+				booleanQuery.add(brandQuery, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			if (hits.totalHits > 0) {
+				ScoreDoc[] scoreDocs = hits.scoreDocs;
+				for (ScoreDoc scoreDoc : scoreDocs) {
+					Document document = searcher.doc(scoreDoc.doc);
+					String cmp_kind_id = document.get(SearchConstants.COMPONENT_KINDID_FIELD);
+					if (!StringUtils.isEmpty(cmp_kind_id)) {
+						kindIds.add(Long.parseLong(cmp_kind_id));
+						Map<String, Object> kind = new HashMap<String, Object>();
+						kind.put("id", Long.parseLong(cmp_kind_id));
+						kind.put("nameCn", document.get(SearchConstants.COMPONENT_KINDNAME_FIELD));
+						kinds.add(kind);
+					}
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return kinds;
+	}
+
+	public List<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId) {
+		Set<Long> brandIds = new HashSet<Long>();
+		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (isKeywordInvalid(keyword) || searcher == null) {
+			return null;
+		}
+
+		try {
+			BooleanQuery booleanQuery = new BooleanQuery();
+
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
+
+			// 筛选类目
+			if (!StringUtils.isEmpty(kindId)) {
+				TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
+				booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			if (hits.totalHits > 0) {
+				ScoreDoc[] scoreDocs = hits.scoreDocs;
+				for (ScoreDoc scoreDoc : scoreDocs) {
+					Document document = searcher.doc(scoreDoc.doc);
+					String cmp_brand_id = document.get(SearchConstants.COMPONENT_BRANDID_FIELD);
+					if (!StringUtils.isEmpty(cmp_brand_id)) {
+						brandIds.add(Long.parseLong(cmp_brand_id));
+						Map<String, Object> brand = new HashMap<String, Object>();
+						brand.put("id", Long.parseLong(cmp_brand_id));
+						brand.put("uuid", document.get(SearchConstants.COMPONENT_BRANDUUID_FIELD));
+						brand.put("nameCn", document.get(SearchConstants.COMPONENT_BRANDNAME_FIELD));
+						brands.add(brand);
+					}
+				}
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return brands;
+	}
+
+}

+ 19 - 0
search-console/src/main/java/com/uas/search/console/support/ApplicationContextRegister.java

@@ -0,0 +1,19 @@
+package com.uas.search.console.support;
+
+import org.apache.log4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import com.uas.search.console.core.util.ContextUtils;
+
+public class ApplicationContextRegister implements ApplicationContextAware {
+
+	private static Logger logger = Logger.getLogger(ApplicationContextRegister.class);
+
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		ContextUtils.setApplicationContext(applicationContext);
+		logger.debug("ApplicationContext registed");
+	}
+
+}

+ 160 - 0
search-console/src/main/java/com/uas/search/console/support/IndexSearcherManager.java

@@ -0,0 +1,160 @@
+package com.uas.search.console.support;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.NIOFSDirectory;
+
+import com.uas.search.console.util.SearchConstants;
+
+/**
+ * 将索引加入缓存,对IndexSearcher进行管理
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午8:50:55
+ */
+public class IndexSearcherManager {
+
+	private IndexSearcher currentSearcher;
+
+	private FSDirectory directory;
+
+	public IndexSearcherManager() {
+		try {
+			String os = System.getProperty("os.name");
+			// 本来NIOFSDirectory速度更快,但因为jre的bug,在Windows平台上,其性能很差
+			if (os.toLowerCase().startsWith("win")) {
+				directory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
+			} else {
+				directory = NIOFSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
+			}
+			warm();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public IndexSearcherManager(FSDirectory dir) {
+		directory = dir;
+		warm();
+	}
+
+	/**
+	 * 对currentSearcher进行初始化操作
+	 * 
+	 * @throws IOException
+	 */
+	public synchronized void warm() {
+		File[] files = directory.getDirectory().toFile().listFiles();
+		// 不为空的话说明索引已成功加载
+		if (currentSearcher == null) {
+			// 路径不为空文件夹
+			if (files.length != 0) {
+				try {
+					currentSearcher = new IndexSearcher(DirectoryReader.open(directory));
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			} else {
+				System.out.println("获取搜索错误,原因:索引文件不存在");
+			}
+		}
+	}
+
+	/**
+	 * 内存中IndexReader(可能)更新,根据版本号确定是否更新
+	 * 
+	 * @throws InterruptedException
+	 * @throws IOException
+	 */
+	public void maybeReopen() {
+		startReopen();
+
+		try {
+			// 每次都要进行初始化处理,是为了防止加载索引时,索引为空,而后来索引不为空时,却不能够正确加载
+			warm();
+			if (currentSearcher == null) {
+				return;
+			}
+			IndexSearcher searcher = get();
+			Long currentVersion = ((DirectoryReader) searcher.getIndexReader()).getVersion();
+			// 版本号不一致(本地索引有更改),更新IndexReader
+			try {
+				if (DirectoryReader.open(directory).getVersion() != currentVersion) {
+					IndexReader newReader = DirectoryReader.open(directory);
+					IndexSearcher newSearcher = new IndexSearcher(newReader);
+					swapIndexSearcher(newSearcher);
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			release(searcher);
+		} finally {
+			doneReopen();
+		}
+
+	}
+
+	private boolean reopening = false;
+
+	private synchronized void startReopen() {
+		while (reopening) {
+			try {
+				wait();
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+		reopening = true;
+	}
+
+	private synchronized void doneReopen() {
+		reopening = false;
+		notifyAll();
+	}
+
+	/**
+	 * 得到IndexSearcher
+	 * 
+	 * @return
+	 */
+	public synchronized IndexSearcher get() {
+		if (currentSearcher != null) {
+			currentSearcher.getIndexReader().incRef();
+		}
+		return currentSearcher;
+	}
+
+	/**
+	 * 释放indexSearcher,将对indexReader的引用减1,为0时成为垃圾
+	 * 
+	 * @param indexSearcher
+	 * @throws IOException
+	 */
+	public synchronized void release(IndexSearcher indexSearcher) {
+		if (indexSearcher != null) {
+			try {
+				indexSearcher.getIndexReader().decRef();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	/**
+	 * 更新IndexSearcher
+	 * 
+	 * @param indexSearcher
+	 *            新IndexSearcher
+	 * @throws IOException
+	 */
+	private synchronized void swapIndexSearcher(IndexSearcher indexSearcher) {
+		release(currentSearcher);
+		currentSearcher = indexSearcher;
+	}
+}

+ 72 - 0
search-console/src/main/java/com/uas/search/console/support/IndexWriterManager.java

@@ -0,0 +1,72 @@
+package com.uas.search.console.support;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.store.FSDirectory;
+
+import com.uas.search.console.util.SearchConstants;
+
+/**
+ * 对IndexWriter进行管理,防止同时有多个方法对索引进行修改,抛出LockObtainFailedException异常
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午8:50:04
+ */
+public class IndexWriterManager {
+
+	private IndexWriter indexWriter;
+
+	private FSDirectory directory;
+
+	public IndexWriterManager(FSDirectory dir) {
+		directory = dir;
+	}
+
+	/**
+	 * 得到IndexWriter,用完后需调用release释放IndexWriter
+	 * 
+	 * @return
+	 * @throws IOException
+	 * @throws InterruptedException
+	 */
+	public synchronized IndexWriter get() throws IOException, InterruptedException {
+		startUsing();
+		if (indexWriter == null) {
+			IndexWriterConfig config = new IndexWriterConfig(SearchConstants.IK_ANALYZER);// 此处用IK
+			indexWriter = new IndexWriter(directory, config);
+		}
+		return indexWriter;
+	}
+
+	/**
+	 * 对索引的更改一般不会很频繁,每次更改后关闭IndexWriter可能比一直打开开销更小
+	 * 不关闭的话,多次创建索引会抛出NoSuchFileException异常(IndexWriter保留的是旧信息?未更新?)
+	 */
+	public synchronized void release() {
+		if (indexWriter != null) {
+			try {
+				indexWriter.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+			indexWriter = null;
+		}
+		doneUsing();
+	}
+
+	private boolean using = false;
+
+	private synchronized void startUsing() throws InterruptedException {
+		while (using) {
+			wait();
+		}
+		using = true;
+	}
+
+	private synchronized void doneUsing() {
+		using = false;
+		notifyAll();
+	}
+}

+ 60 - 0
search-console/src/main/java/com/uas/search/console/util/SearchConstants.java

@@ -0,0 +1,60 @@
+package com.uas.search.console.util;
+
+import org.apache.log4j.Logger;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.wltea.analyzer.lucene.IKAnalyzer;
+
+import com.uas.search.console.core.util.PathUtils;
+
+/**
+ * 索引相关的常量
+ * 
+ * @author suntg
+ * @since 2016年8月3日下午1:54:14
+ */
+public class SearchConstants {
+
+	public static Logger logger = Logger.getLogger("search");
+
+	/**
+	 * IK分词解析器
+	 */
+	public static final Analyzer IK_ANALYZER = new IKAnalyzer();
+	/**
+	 * 标准分词解析器
+	 */
+	public static final Analyzer STANDARD_ANALYZER = new StandardAnalyzer();
+
+	/**
+	 * 关键词最大允许长度
+	 */
+	public static final int KEYWORD_MAX_LENGTH = 30;
+
+	/**
+	 * 索引文件存储路径
+	 */
+	public static final String INDEX_DIR = PathUtils.getFilePath() + "indexes";
+
+	/**
+	 * 各索引字段的字符标示
+	 */
+	public static final String KIND_ID_FIELD = "ki_id";
+	public static final String KIND_NAMECN_FIELD = "ki_name_cn";
+
+	public static final String BRAND_ID_FIELD = "br_id";
+	public static final String BRAND_NAMECN_FIELD = "br_name_cn";
+	public static final String BRAND_UUID_FIELD = "br_uuid";
+	public static final String BRAND_WEIGHT_FIELD = "br_weight";
+
+	public static final String COMPONENT_ID_FIELD = "cmp_id";
+	public static final String COMPONENT_UUID_FIELD = "cmp_uuid";
+	public static final String COMPONENT_CODE_FIELD = "cmp_code";
+	public static final String COMPONENT_RESERVE_FIELD = "cmp_reserve";
+	public static final String COMPONENT_KINDID_FIELD = "cmp_kind_id";
+	public static final String COMPONENT_KINDNAME_FIELD = "cmp_kind_name_cn";
+	public static final String COMPONENT_BRANDID_FIELD = "cmp_brand_id";
+	public static final String COMPONENT_BRANDUUID_FIELD = "cmp_brand_uuid";
+	public static final String COMPONENT_BRANDNAME_FIELD = "cmp_brand_name_cn";
+
+}

+ 10 - 0
search-console/src/main/java/com/uas/search/console/util/Test.java

@@ -0,0 +1,10 @@
+package com.uas.search.console.util;
+
+import com.uas.search.console.core.util.ContextUtils;
+
+public class Test {
+
+	public static void main(String[] args) {
+		System.out.println(ContextUtils.getApplicationContext());
+	}
+}

+ 127 - 0
search-console/src/main/resources/applicationContext.xml

@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
+	xmlns:cache="http://www.springframework.org/schema/cache" xmlns:util="http://www.springframework.org/schema/util"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd   
+	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
+	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
+	http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
+	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
+	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
+	http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd">
+
+	<context:property-placeholder location="classpath*:*.properties" />
+	<!-- 注册spring上下文对象 -->
+	<bean class="com.uas.search.console.support.ApplicationContextRegister" />
+
+	<!-- 扫描注解 -->
+	<context:annotation-config />
+	<!-- 扫描的包 -->
+	<context:component-scan base-package="com.uas.search.console" />
+	
+	<!-- dataSource -->
+	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
+		destroy-method="close">
+		<property name="driverClassName" value="${jdbc.driverClassName}" />
+		<property name="url" value="${jdbc.url}" />
+		<property name="username" value="${jdbc.username}" />
+		<property name="password" value="${jdbc.password}" />
+		<!-- 连接初始值,连接池启动时创建的连接数量的初始值 -->
+		<property name="initialSize" value="${jdbc.initialSize}" />
+		<!-- 连接池的最大值,同一时间可以从池分配的最多连接数量,0时无限制 -->
+		<property name="maxActive" value="${jdbc.maxActive}" />
+		<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 ,0时无限制 -->
+		<property name="maxIdle" value="${jdbc.maxIdle}" />
+		<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
+		<property name="minIdle" value="${jdbc.minIdle}" />
+		<!-- 是否对已备语句进行池管理(布尔值),是否对PreparedStatement进行缓存 -->
+		<property name="poolPreparedStatements" value="true" />
+		<!-- 是否对sql进行自动提交 -->
+		<property name="defaultAutoCommit" value="true" />
+		<!-- 回收超时连接 -->
+		<property name="removeAbandoned" value="true" />
+		<!-- 连接空闲时校验连接有效性 -->
+		<property name="testWhileIdle" value="true" />
+		<!-- 校验连接有效性的sql -->
+		<property name="validationQuery" value="select 1 from dual" />
+		<!-- 每过timeBetweenEvictionRunsMillis 时间,就会启动一个线程,校验连接池中闲置时间超过minEvictableIdleTimeMillis的连接对象 -->
+		<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
+		<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
+	</bean>
+	<!-- hibernate jpa -->
+	<bean class="org.springframework.orm.jpa.JpaTransactionManager"
+		id="transactionManager">
+		<property name="entityManagerFactory" ref="entityManagerFactory" />
+	</bean>
+	<tx:annotation-driven transaction-manager="transactionManager" />
+
+	<bean id="transactionInterceptor"
+		class="org.springframework.transaction.interceptor.TransactionInterceptor">
+		<property name="transactionManager" ref="transactionManager" />
+		<property name="transactionAttributeSource">
+			<bean
+				class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"></bean>
+		</property>
+	</bean>
+
+	<bean id="transactionAttributeSourceAdvisor"
+		class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
+		<property name="transactionInterceptor" ref="transactionInterceptor" />
+	</bean>
+
+	<bean id="entityManagerFactory"
+		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+		<property name="persistenceUnitName" value="persistenceUnit" />
+		<!-- <property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml" 
+			/> -->
+		<property name="packagesToScan" value="com.uas.search.console" />
+		<property name="dataSource" ref="dataSource" />
+		<property name="jpaVendorAdapter">
+			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
+				<property name="generateDdl" value="false" />
+				<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
+				<property name="showSql" value="false" />
+			</bean>
+		</property>
+		<property name="jpaDialect">
+			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
+		</property>
+		<property name="jpaProperties">
+			<props>
+				<prop key="hibernate.cache.provider_configuration_file_resource_path">classpath:spring/ehcache.xml</prop>
+				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
+				</prop>
+				<prop key="hibernate.cache.use_query_cache">true</prop>
+				<prop key="hibernate.cache.use_second_level_cache">true</prop>
+				<prop key="hibernate.generate_statistics">true</prop>
+				<prop key="hibernate.use_sql_comments">true</prop>
+				<prop key="hibernate.format_sql">true</prop>
+				<prop key="hibernate.generate_statistics">true</prop>
+				<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
+			</props>
+		</property>
+	</bean>
+	<!-- jdbctemplate -->
+	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
+		<property name="dataSource" ref="dataSource" />
+	</bean>
+	<!-- 使用ehcache对象缓存 -->
+	<cache:annotation-driven />
+	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
+		<property name="cacheManager" ref="ehcache"></property>
+	</bean>
+	<bean id="ehcache"
+		class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
+		<property name="configLocation" value="classpath:ehcache.xml"></property>
+		<property name="shared" value="true"></property>
+	</bean>
+	
+	<!-- jpa -->
+	<import resource="jpa.xml"/>
+	
+	<!-- dubbo 服务 -->
+	<import resource="provider.xml"/>
+	
+</beans>

+ 8 - 0
search-console/src/main/resources/ehcache.xml

@@ -0,0 +1,8 @@
+<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:noNamespaceSchemaLocation="classpath:ehcache.xsd">
+	<diskStore path="java.io.tmpdir" />
+	<defaultCache maxElementsInMemory="10000" eternal="false"
+		timeToIdleSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000"
+		diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
+		timeToLiveSeconds="8" memoryStoreEvictionPolicy="LRU" />
+</ehcache>

+ 270 - 0
search-console/src/main/resources/ehcache.xsd

@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="1.7">
+
+    <xs:element name="ehcache">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="1" minOccurs="0" ref="diskStore"/>
+                <xs:element maxOccurs="1" minOccurs="0" ref="transactionManagerLookup"/>
+                <xs:element maxOccurs="1" minOccurs="0" ref="cacheManagerEventListenerFactory"/>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cacheManagerPeerProviderFactory"/>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cacheManagerPeerListenerFactory"/>
+                <xs:element maxOccurs="1" minOccurs="0" ref="terracottaConfig"/>
+                <xs:element ref="defaultCache"/>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="cache"/>
+            </xs:sequence>
+            <xs:attribute name="name" use="optional"/>
+            <xs:attribute default="true" name="updateCheck" type="xs:boolean" use="optional"/>
+            <xs:attribute default="autodetect" name="monitoring" type="monitoringType" use="optional"/>
+            <xs:attribute default="true" name="dynamicConfig" type="xs:boolean" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="diskStore">
+        <xs:complexType>
+            <xs:attribute name="path" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+     <xs:element name="transactionManagerLookup">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheManagerEventListenerFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheManagerPeerProviderFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheManagerPeerListenerFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="terracottaConfig">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="1" minOccurs="0" name="tc-config">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:any maxOccurs="unbounded" minOccurs="0" processContents="skip"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+            <xs:attribute default="localhost:9510" name="url" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <!-- add clone support for addition of cacheExceptionHandler. Important! -->
+    <xs:element name="defaultCache">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheEventListenerFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheExtensionFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheLoaderFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheDecoratorFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="bootstrapCacheLoaderFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="cacheExceptionHandlerFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="terracotta"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriter"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="copyStrategy"/>
+            </xs:sequence>
+            <xs:attribute name="diskExpiryThreadIntervalSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="diskSpoolBufferSizeMB" type="xs:integer" use="optional"/>
+            <xs:attribute name="diskPersistent" type="xs:boolean" use="optional"/>
+            <xs:attribute name="diskAccessStripes" type="xs:integer" use="optional" default="1"/>
+            <xs:attribute name="eternal" type="xs:boolean" use="required"/>
+            <xs:attribute name="maxElementsInMemory" type="xs:integer" use="required"/>
+            <xs:attribute name="clearOnFlush" type="xs:boolean" use="optional"/>
+            <xs:attribute name="memoryStoreEvictionPolicy" type="xs:string" use="optional"/>
+            <xs:attribute name="overflowToDisk" type="xs:boolean" use="required"/>
+            <xs:attribute name="timeToIdleSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="timeToLiveSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="maxElementsOnDisk" type="xs:integer" use="optional"/>
+            <xs:attribute name="transactionalMode" type="transactionalMode" use="optional" default="off"/>
+            <xs:attribute name="statistics" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="copyOnRead" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="copyOnWrite" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="cacheLoaderTimeoutMillis" type="xs:integer" use="optional" default="0"/>
+            <xs:attribute name="overflowToOffHeap" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="maxMemoryOffHeap" type="xs:string" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cache">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheEventListenerFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheExtensionFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheLoaderFactory"/>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="cacheDecoratorFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="bootstrapCacheLoaderFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="cacheExceptionHandlerFactory"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="terracotta"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriter"/>
+                <xs:element minOccurs="0" maxOccurs="1" ref="copyStrategy"/>
+            </xs:sequence>
+            <xs:attribute name="diskExpiryThreadIntervalSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="diskSpoolBufferSizeMB" type="xs:integer" use="optional"/>
+            <xs:attribute name="diskPersistent" type="xs:boolean" use="optional"/>
+            <xs:attribute name="diskAccessStripes" type="xs:integer" use="optional" default="1"/>
+            <xs:attribute name="eternal" type="xs:boolean" use="required"/>
+            <xs:attribute name="maxElementsInMemory" type="xs:integer" use="required"/>
+            <xs:attribute name="memoryStoreEvictionPolicy" type="xs:string" use="optional"/>
+            <xs:attribute name="clearOnFlush" type="xs:boolean" use="optional"/>
+            <xs:attribute name="name" type="xs:string" use="required"/>
+            <xs:attribute name="overflowToDisk" type="xs:boolean" use="required"/>
+            <xs:attribute name="timeToIdleSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="timeToLiveSeconds" type="xs:integer" use="optional"/>
+            <xs:attribute name="maxElementsOnDisk" type="xs:integer" use="optional"/>
+            <xs:attribute name="transactionalMode" type="transactionalMode" use="optional" default="off" />
+            <xs:attribute name="statistics" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="copyOnRead" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="copyOnWrite" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="logging" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="cacheLoaderTimeoutMillis" type="xs:integer" use="optional" default="0"/>
+            <xs:attribute name="overflowToOffHeap" type="xs:boolean" use="optional" default="false"/>
+            <xs:attribute name="maxMemoryOffHeap" type="xs:string" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheEventListenerFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+            <xs:attribute name="listenFor" use="optional" type="notificationScope" default="all"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="bootstrapCacheLoaderFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheExtensionFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheExceptionHandlerFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheLoaderFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="cacheDecoratorFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="terracotta">
+        <xs:complexType>
+            <xs:attribute name="clustered" use="optional" type="xs:boolean" default="true"/>
+            <xs:attribute name="valueMode" use="optional" type="terracottaCacheValueType" default="serialization"/>
+            <xs:attribute name="coherentReads" use="optional" type="xs:boolean" default="true"/>
+            <xs:attribute name="localKeyCache" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="localKeyCacheSize" use="optional" type="xs:positiveInteger" default="300000"/>
+            <xs:attribute name="orphanEviction" use="optional" type="xs:boolean" default="true"/>
+            <xs:attribute name="orphanEvictionPeriod" use="optional" type="xs:positiveInteger" default="4"/>
+            <xs:attribute name="copyOnRead" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="coherent" use="optional" type="xs:boolean" default="true"/>
+            <xs:attribute name="synchronousWrites" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="storageStrategy" use="optional" type="storageStrategyType" default="classic"/>
+            <xs:attribute name="concurrency" use="optional" type="xs:nonNegativeInteger" default="0"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:simpleType name="monitoringType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="autodetect"/>
+            <xs:enumeration value="on"/>
+            <xs:enumeration value="off"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="terracottaCacheValueType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="serialization" />
+            <xs:enumeration value="identity" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="storageStrategyType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="classic" />
+            <xs:enumeration value="DCV2" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="transactionalMode">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="off"/>
+            <xs:enumeration value="xa"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:element name="cacheWriter">
+        <xs:complexType>
+            <xs:sequence >
+                <xs:element minOccurs="0" maxOccurs="1" ref="cacheWriterFactory"/>
+            </xs:sequence>
+            <xs:attribute name="writeMode" use="optional" type="writeModeType" default="write-through"/>
+            <xs:attribute name="notifyListenersOnException" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="minWriteDelay" use="optional" type="xs:nonNegativeInteger" default="1"/>
+            <xs:attribute name="maxWriteDelay" use="optional" type="xs:nonNegativeInteger" default="1"/>
+            <xs:attribute name="rateLimitPerSecond" use="optional" type="xs:nonNegativeInteger" default="0"/>
+            <xs:attribute name="writeCoalescing" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="writeBatching" use="optional" type="xs:boolean" default="false"/>
+            <xs:attribute name="writeBatchSize" use="optional" type="xs:positiveInteger" default="1"/>
+            <xs:attribute name="retryAttempts" use="optional" type="xs:nonNegativeInteger" default="0"/>
+            <xs:attribute name="retryAttemptDelaySeconds" use="optional" type="xs:nonNegativeInteger" default="1"/>
+        </xs:complexType>
+    </xs:element>
+    <xs:simpleType name="writeModeType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="write-through" />
+            <xs:enumeration value="write-behind" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:element name="cacheWriterFactory">
+        <xs:complexType>
+            <xs:attribute name="class" use="required"/>
+            <xs:attribute name="properties" use="optional"/>
+            <xs:attribute name="propertySeparator" use="optional"/>
+        </xs:complexType>
+    </xs:element>
+
+    <xs:element name="copyStrategy">
+        <xs:complexType>
+            <xs:attribute name="class" use="required" type="xs:string" />
+        </xs:complexType>
+    </xs:element>
+
+    <xs:simpleType name="notificationScope">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="local"/>
+            <xs:enumeration value="remote"/>
+            <xs:enumeration value="all"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>

+ 24 - 0
search-console/src/main/resources/jdbc.properties

@@ -0,0 +1,24 @@
+#jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
+#jdbc.url=jdbc:oracle:thin:@192.168.253.6:1521:orcl
+#jdbc.username=uuplatformdemo
+#jdbc.password=selectuuplatform
+#jdbc.initialSize=10
+#jdbc.maxActive=100
+#jdbc.maxIdle=50
+#jdbc.minIdle=50
+#jdbc.suspectTimeout=60
+#jdbc.timeBetweenEvictionRunsMillis=30000
+#jdbc.minEvictableIdleTimeMillis=60000
+
+#product mode
+jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
+jdbc.url=jdbc:oracle:thin:@10.10.100.200:1521:orcl
+jdbc.username=platform$b2b
+jdbc.password=select*fromuu
+jdbc.initialSize=10
+jdbc.maxActive=100
+jdbc.maxIdle=50
+jdbc.minIdle=50
+jdbc.suspectTimeout=60
+jdbc.timeBetweenEvictionRunsMillis=30000
+jdbc.minEvictableIdleTimeMillis=60000

+ 11 - 0
search-console/src/main/resources/jpa.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans 
+	http://www.springframework.org/schema/beans/spring-beans.xsd 
+	http://www.springframework.org/schema/data/jpa 
+	http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
+
+	<repositories base-package="com.uas.search.console" />
+
+</beans:beans>

+ 17 - 0
search-console/src/main/resources/provider.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
+	
+	<dubbo:application name="search_provider" />
+	
+	<dubbo:registry address="zookeeper://10.10.100.11:2181" />
+	
+	<dubbo:protocol name="dubbo" port="20880" />
+	
+	<bean id="searchService" class="com.uas.search.console.service.impl.SearchServiceImpl" />
+	
+	<dubbo:service interface="com.uas.search.service.SearchService" ref="searchService" />
+	
+</beans>

+ 23 - 0
search-console/src/main/webapp/WEB-INF/log4j.properties

@@ -0,0 +1,23 @@
+# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
+# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
+log4j.rootLogger=ERROR, stdout, logfile
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c]:%m%n
+
+log4j.appender.logfile=org.apache.log4j.RollingFileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.File=../log4j.log
+# Keep three backup files.
+log4j.appender.logfile.MaxBackupIndex=3
+log4j.appender.logfile.MaxFileSize=5120KB
+# Pattern to output: date priority [category] - message
+log4j.appender.logfile.layout.ConversionPattern=%d %p [%c]:%m%n
+#log4j.logger.org.springframework.samples.petclinic.aspects=DEBUG
+
+log4j.logger.com.uas = error
+#=========================================
+#=   spring framewokr log configuration  =
+#=========================================
+log4j.logger.org.springframework = 

+ 10 - 0
search-console/src/main/webapp/WEB-INF/views/index.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Insert title here</title>
+</head>
+<body>
+This is the search console index.html.
+</body>
+</html>

+ 86 - 0
search-console/src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,86 @@
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	version="3.0"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/web-app_3_0.xsd">
+
+	<display-name>search-console</display-name>
+	<description>The implementation of Search Service.</description>
+
+	<context-param>
+		<param-name>webAppRootKey</param-name>
+		<param-value>spring.webapp.dfs</param-value>
+	</context-param>
+	<context-param>
+		<param-name>spring.profiles.active</param-name>
+		<param-value>production</param-value>
+	</context-param>
+	<context-param>
+		<param-name>log4jConfigLocation</param-name>
+		<param-value>/WEB-INF/log4j.properties</param-value>
+	</context-param>
+	<context-param>
+		<param-name>log4jRefreshInterval</param-name>
+		<param-value>60000</param-value>
+	</context-param>
+	<context-param>
+		<param-name>contextConfigLocation</param-name>
+		<param-value>classpath*:applicationContext.xml</param-value>
+	</context-param>
+	
+	<!-- filters -->
+	<filter>
+		<filter-name>CharacterEncodingFilter</filter-name>
+		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
+		<async-supported>true</async-supported>
+		<init-param>
+			<param-name>encoding</param-name>
+			<param-value>UTF-8</param-value>
+		</init-param>
+		<init-param>
+			<param-name>forceEncoding</param-name>
+			<param-value>true</param-value>
+		</init-param>
+	</filter>
+	<filter-mapping>
+		<filter-name>CharacterEncodingFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+
+	<filter>
+		<filter-name>HttpMethodFilter</filter-name>
+		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
+		<async-supported>true</async-supported>
+	</filter>
+	<filter-mapping>
+		<filter-name>HttpMethodFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+	</filter-mapping>
+	
+	<!-- Handles Spring requests -->
+	<servlet>
+		<servlet-name>spring-mvc-dispatcher</servlet-name>
+		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+		<init-param>
+			<param-name>contextConfigLocation</param-name>
+			<param-value>/WEB-INF/webmvc.xml</param-value>
+		</init-param>
+		<load-on-startup>1</load-on-startup>
+		<async-supported>true</async-supported>
+	</servlet>
+	<servlet-mapping>
+		<servlet-name>spring-mvc-dispatcher</servlet-name>
+		<url-pattern>/</url-pattern>
+	</servlet-mapping>
+	
+	<!-- Creates the Spring Container shared by all Servlets and Filters -->
+	<listener>
+		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+	</listener>
+	<listener>
+		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
+	</listener>
+	<!-- Spring localization -->
+	<listener>
+		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
+	</listener>
+
+</web-app>

+ 41 - 0
search-console/src/main/webapp/WEB-INF/webmvc.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
+	xmlns:context="http://www.springframework.org/schema/context"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+
+	<mvc:annotation-driven>
+		<mvc:message-converters register-defaults="true">
+			<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
+			<bean id="fastJsonHttpMessageConverter"
+				class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
+				<property name="supportedMediaTypes">
+					<list>
+						<value>application/json;charset=UTF-8</value>
+					</list>
+				</property>
+				<property name="features">
+					<list>
+						<value>DisableCircularReferenceDetect</value>
+					</list>
+				</property>
+			</bean>
+		</mvc:message-converters>
+	</mvc:annotation-driven>
+
+	<mvc:default-servlet-handler />
+
+	<mvc:resources mapping="/static/**" location="/resources/" />
+
+	<context:component-scan base-package="com.uas.search.console" />
+
+	<bean
+		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+		<property name="prefix" value="/WEB-INF/views/" />
+		<property name="suffix" value=".html" />
+		<property name="contentType" value="text/html;charset=UTF-8" />
+	</bean>
+
+	<mvc:view-controller path="/" view-name="index" />
+
+</beans>