Browse Source

B2C新增物料搜索

dongbw 7 years ago
parent
commit
bcf8956e42

+ 56 - 1
mall-search/init.sql

@@ -307,7 +307,6 @@ CREATE DEFINER = `root`@`%` TRIGGER `lucene_pcb_u` AFTER UPDATE ON `product$pcb`
 end
 ;;
 delimiter ;
-
 -- ----------------------------
 -- Triggers structure for table product$pcb
 -- ----------------------------
@@ -880,3 +879,59 @@ CREATE DEFINER = `root`@`%` TRIGGER `lucene_purchase_dt_d` AFTER DELETE ON `trad
 end
 ;;
 delimiter ;
+
+
+-- 2018年5月22日 20:01:19 dongbw 
+-- ----------------------------
+-- Triggers structure for table v$product$private
+-- ----------------------------
+DROP TRIGGER IF EXISTS `lucene_product_i`;
+delimiter ;;
+CREATE DEFINER = `root`@`%` TRIGGER `lucene_product_i` AFTER INSERT ON `products` FOR EACH ROW begin
+	declare v_table_name varchar(64) default 'v$product$private';
+    declare v_method_type varchar(6) default 'insert';
+    declare v_data_id int;
+    declare v_data text default null;
+    declare v_priority int default 1;
+
+    set v_data_id=new.pr_id;
+    call enqueue_lucene_message(v_table_name, v_method_type, v_data_id, v_data, v_priority);
+end
+;;
+delimiter ;
+
+-- ----------------------------
+-- Triggers structure for table v$product$private
+-- ----------------------------
+DROP TRIGGER IF EXISTS `lucene_kind_u`;
+delimiter ;;
+CREATE DEFINER = `root`@`%` TRIGGER `lucene_kind_u` AFTER UPDATE ON `products` FOR EACH ROW begin
+	declare v_table_name varchar(64) default 'v$product$private';
+    declare v_method_type varchar(6) default 'update';
+    declare v_data_id int;
+    declare v_data text default null;
+    declare v_priority int default 1;
+
+    set v_data_id=old.pr_id;
+    call enqueue_lucene_message(v_table_name, v_method_type, v_data_id, v_data, v_priority);
+end
+;;
+delimiter ;
+
+-- ----------------------------
+-- Triggers structure for table v$product$private
+-- ----------------------------
+DROP TRIGGER IF EXISTS `lucene_kind_d`;
+delimiter ;;
+CREATE DEFINER = `root`@`%` TRIGGER `lucene_kind_d` AFTER DELETE ON `products` FOR EACH ROW begin
+	declare v_table_name varchar(64) default 'v$product$private';
+    declare v_method_type varchar(6) default 'delete';
+    declare v_data_id int;
+    declare v_data text default null;
+    declare v_priority int default 1;
+
+    set v_data_id=old.pr_id;
+    call enqueue_lucene_message(v_table_name, v_method_type, v_data_id, v_data, v_priority);
+end
+;;
+delimiter ;

+ 35 - 0
mall-search/src/main/java/com/uas/search/constant/SearchConstants.java

@@ -63,6 +63,12 @@ public class SearchConstants {
 	 */
 	public static final String PURCHASE_INVOICE_TABLE_NAME = "trade$invoice_fmpu";
 
+	/**
+	 * 物料表(标准)
+	 */
+	public static final String PRODUCTS_PRIVATE_TABLE_NAME = "v$product$private";
+
+
 	// 类目索引字段的key
 	public static final String KIND_ID_FIELD = "ki_id";
 	public static final String KIND_NAMECN_FIELD = "ki_name_cn";
@@ -247,4 +253,33 @@ public class SearchConstants {
 	public static final String PURCHASE_INVOICE_DETAILS_FIELD = "pi_details";
 
 	public static final String CREATETIME_FIELD = "createtime";
+
+	/**
+	 * 物料索引字段
+	 */
+	public static final String PRODUCT_PRIVATE_ID_FIELD = "pr_id";
+	// 1标准  0非标准
+	public static final String PRODUCT_PRIVATE_STANDARD_FIELD = "pr_standard";
+	// 1可用  0 不可用
+	public static final String PRODUCT_PRIVATE_B2CENABLED_FIELD = "pr_b2cenabled";
+    /**
+     * 非标字段
+     */
+    // 名称
+    public static final String PRODUCT_PRIVATE_TITLE_FIELD = "pr_title";
+    // 型号
+	public static final String PRODUCT_PRIVATE_CMPCODE_FIELD = "pr_cmpcode";
+	// 品牌
+	public static final String PRODUCT_PRIVATE_BRAND_FIELD = "pr_brand";
+    /**
+     * 标准字段
+     */
+    // 类目
+    public static final String PRODUCT_PRIVATE_KIND_FIELD = "pr_kind";
+    // 标准型号
+    public static final String PRODUCT_PRIVATE_PCMPCODE_FIELD = "pr_pcmpcode";
+    // 标准英文品牌
+    public static final String PRODUCT_PRIVATE_PBRANDEN_FIELD = "pr_pbranden";
+
+
 }

+ 19 - 0
mall-search/src/main/java/com/uas/search/controller/IndexController.java

@@ -76,6 +76,25 @@ public class IndexController {
         return message;
     }
 
+    /**
+     *  下载标准物料数据
+     * @param threads        线程数量,默认为 1
+     * @param startFileIndex 开始的文件,默认为 1
+     * @param endFileIndex   结束的文件,默认为 1024 * 1024 * 1024
+     * @param validateResult 下载完成后,是否对结果进行校验
+     * @param request
+     * @return
+     */
+    @RequestMapping("/multiDownloadProduct")
+    @ResponseBody
+    public String multiDownloadStandardProduct(Integer threads, Integer startFileIndex, Integer endFileIndex, String validateResult, HttpServletRequest request) {
+        long start = System.currentTimeMillis();
+        String message= "Downloaded: "+ indexService.multiDownloadProduct(threads, startFileIndex, endFileIndex,
+                StringUtils.isEmpty(validateResult) ? DownloadHelper.ValidateResult.CURRENT : DownloadHelper.ValidateResult.valueOf(validateResult.toUpperCase()));
+        message += String.format(", Time: %.2fs", (System.currentTimeMillis()-start)/1000.0);
+        return message;
+    }
+
     @RequestMapping("/multiDownloadGoods")
     @ResponseBody
     public String multiDownloadGoods(Integer threads, Integer startFileIndex, Integer endFileIndex, String validateResult, HttpServletRequest request) {

+ 45 - 0
mall-search/src/main/java/com/uas/search/controller/SearchController.java

@@ -44,6 +44,51 @@ public class SearchController {
 
 	private Logger logger = LoggerFactory.getLogger(getClass());
 
+	/**
+	 * 查询标准物料
+	 * @param keyword 关键词
+	 * @param page  页码
+	 * @param size  尺寸
+	 * @param request request
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+	@RequestMapping("/standardProductIds")
+	@ResponseBody
+	public SPage<Long> searchStandardProductIds(@RequestParam String keyword, Integer page, Integer size,
+									HttpServletRequest request) throws IOException {
+		return searchService.getStandardProductIds(keyword, page, size);
+	}
+
+	/**
+	 * 查询非标准物料
+	 * @param keyword 关键词
+	 * @param page  页码
+	 * @param size  尺寸
+	 * @param request request
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+	@RequestMapping("/nonStandardProductIds")
+	@ResponseBody
+	public SPage<Long> searchNonStandardProductIds(@RequestParam String keyword, Integer page, Integer size,
+										  HttpServletRequest request) throws IOException {
+		return searchService.getNonStandardProductIds(keyword, page, size);
+	}
+
+	/**
+	 * 根据id获取索引详情
+	 * @param id
+	 * @param request
+	 * @return
+	 * @throws IOException
+	 */
+	@RequestMapping("/product/{id}")
+	@ResponseBody
+	public V_Products getProduct(@PathVariable Long id, HttpServletRequest request) throws IOException {
+		return searchService.getProduct(id);
+	}
+
 	@RequestMapping("/kindIds")
 	@ResponseBody
 	public SPage<Long> seachKindIds(@RequestParam String keyword, Integer page, Integer size,

+ 14 - 0
mall-search/src/main/java/com/uas/search/dao/V_ProductsDao.java

@@ -0,0 +1,14 @@
+package com.uas.search.dao;
+
+import com.uas.search.model.V_Products;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author dongbw
+ * @since 2018年5月22日 19:01:25
+ */
+@Repository
+public interface V_ProductsDao extends JpaSpecificationExecutor<V_Products>, JpaRepository<V_Products, Long> {
+}

+ 26 - 0
mall-search/src/main/java/com/uas/search/jms/QueueMessageParser.java

@@ -38,6 +38,9 @@ public class QueueMessageParser {
 	@Autowired
 	private PurchaseInvoiceDao purchaseInvoiceDao;
 
+	@Autowired
+	private V_ProductsDao v_productsDao;
+
 	/**
 	 * 对得到的json消息进行解析
 	 * 
@@ -82,6 +85,8 @@ public class QueueMessageParser {
 				object = parsePurchase(dataId, methodType);
 			} else if (tableName.equals(SearchConstants.PURCHASE_INVOICE_TABLE_NAME)) {
 				object = parsePurchaseInvoice(dataId, methodType);
+			} else if (tableName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
+				object = parseProduct(dataId, methodType);
 			} else {
 				throw new IllegalStateException("unsupported table name: " + tableName);
 			}
@@ -115,6 +120,27 @@ public class QueueMessageParser {
 		return kind;
 	}
 
+	/**
+	 * 对product物料进行解析
+	 *
+	 * @param id id
+	 * @param methodType 改动类型
+	 * @return product物料对象
+	 * @throws JSONException
+	 */
+	private V_Products parseProduct(@NotEmpty("id") Long id, @NotEmpty("methodType") String methodType) throws JSONException {
+		V_Products product;
+		// delete操作
+		// 删除后数据库中可能已经没有相应数据了,无法通过dao获取,需要手动创建对象
+		if (methodType.equalsIgnoreCase("delete")) {
+			product = new V_Products();
+			product.setId(id);
+		} else {
+			product = v_productsDao.findOne(id);
+		}
+		return product;
+	}
+
 	/**
 	 * 对brand品牌进行解析
 	 *

+ 163 - 0
mall-search/src/main/java/com/uas/search/model/V_Products.java

@@ -0,0 +1,163 @@
+package com.uas.search.model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * 物料视图
+ *
+ * @author dongbw
+ * @since 2018年5月22日 18:21:57
+ */
+@Entity
+@Table(name = "v$product$private")
+public class V_Products implements Serializable {
+
+    /**
+     * 序列号
+     */
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @Column(name = "pr_id")
+    private Long id;
+
+    /**
+     * 名称
+     */
+    @Column(name = "pr_title")
+    private String title;
+
+    /**
+     * 型号
+     */
+    @Column(name = "pr_cmpcode")
+    private String cmpCode;
+
+    /**
+     * 品牌
+     */
+    @Column(name = "pr_brand")
+    private String brand;
+
+    /**
+     * 类目
+     */
+    @Column(name = "pr_kind")
+    private String kind;
+
+    /**
+     * 标准型号
+     */
+    @Column(name = "pr_pcmpcode")
+    private String pCmpCode;
+
+    /**
+     * 标准英文品牌
+     */
+    @Column(name = "pr_pbranden")
+    private String pBrandEn;
+
+    /**
+     * 是否标准(1标准 ,非1 非标)
+     */
+    @Column(name = "pr_standard")
+    private Short standard;
+
+    /**
+     * 是否可用(0不可用,非0可用)
+     */
+    @Column(name = "pr_b2cenabled")
+    private Short b2cEnabled;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getCmpCode() {
+        return cmpCode;
+    }
+
+    public void setCmpCode(String cmpCode) {
+        this.cmpCode = cmpCode;
+    }
+
+    public String getBrand() {
+        return brand;
+    }
+
+    public void setBrand(String brand) {
+        this.brand = brand;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getpCmpCode() {
+        return pCmpCode;
+    }
+
+    public void setpCmpCode(String pCmpCode) {
+        this.pCmpCode = pCmpCode;
+    }
+
+    public String getpBrandEn() {
+        return pBrandEn;
+    }
+
+    public void setpBrandEn(String pBrandEn) {
+        this.pBrandEn = pBrandEn;
+    }
+
+    public Short getStandard() {
+        return standard;
+    }
+
+    public void setStandard(Short standard) {
+        this.standard = standard;
+    }
+
+    public Short getB2cEnabled() {
+        return b2cEnabled;
+    }
+
+    public void setB2cEnabled(Short b2cEnabled) {
+        this.b2cEnabled = b2cEnabled;
+    }
+
+
+    @Override
+    public String toString() {
+        return "V_Products{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", cmpCode='" + cmpCode + '\'' +
+                ", brand='" + brand + '\'' +
+                ", kind='" + kind + '\'' +
+                ", pCmpCode='" + pCmpCode + '\'' +
+                ", pBrandEn='" + pBrandEn + '\'' +
+                ", standard=" + standard +
+                ", b2cEnabled=" + b2cEnabled +
+                '}';
+    }
+}

+ 11 - 0
mall-search/src/main/java/com/uas/search/service/IndexService.java

@@ -36,6 +36,17 @@ public interface IndexService {
      */
     public long multiDownloadComponent(Integer threads, Integer startFileIndex, Integer endFileIndex, DownloadHelper.ValidateResult validateResult);
 
+    /**
+     * 多线程下载器件的数据至本地文件中,以供建索引用
+     *
+     * @param threads        线程数量,默认为 1
+     * @param startFileIndex 开始的文件,默认为 1
+     * @param endFileIndex   结束的文件,默认为 1024 * 1024 * 1024
+     * @param validateResult 下载完成后,是否对结果进行校验
+     * @return 下载的数据条数
+     */
+    long multiDownloadProduct(Integer threads, Integer startFileIndex, Integer endFileIndex, DownloadHelper.ValidateResult validateResult);
+
     /**
      * 多线程下载批次的数据至本地文件中,以供建索引用
      *

+ 27 - 0
mall-search/src/main/java/com/uas/search/service/SearchService.java

@@ -522,4 +522,31 @@ public interface SearchService {
 	 */
 	public SPage<Object> getObjects(String tableName, String keyword, String field, Boolean tokenized, @NotEmpty("page") Integer page, @NotEmpty("size") Integer size) throws IOException;
 
+	/**
+	 * 查询标准物料
+	 * @param keyword 关键词
+	 * @param page  页码
+	 * @param size  尺寸
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+    SPage<Long> getStandardProductIds(String keyword, Integer page, Integer size) throws IOException;
+
+	/**
+	 * 查询非标准物料
+	 * @param keyword 关键词
+	 * @param page  页码
+	 * @param size  尺寸
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+	SPage<Long> getNonStandardProductIds(String keyword, Integer page, Integer size) throws IOException;
+
+	/**
+	 * 根据id获取物料
+	 *
+	 * @param id
+	 * @return
+	 */
+    V_Products getProduct(Long id) throws IOException;
 }

+ 64 - 0
mall-search/src/main/java/com/uas/search/service/impl/IndexServiceImpl.java

@@ -90,6 +90,8 @@ public class IndexServiceImpl implements IndexService {
     private IndexSearcherManager indexSearcherManager = IndexSearcherManager.getInstance();
     @Autowired
     private SearchService searchService;
+    @Autowired
+    private V_ProductsDao v_productsDao;
     /**
      * 是否正在创建索引
      */
@@ -142,6 +144,8 @@ public class IndexServiceImpl implements IndexService {
                         size = createPurchaseIndexes();
                     } else if (tableName.equals(SearchConstants.PURCHASE_INVOICE_TABLE_NAME)) {
                         size = createPurchaseInvoiceIndexes();
+                    } else if (tableName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
+                        size = createProductIndexesFromFiles();
                     } else {
                         logger.error("不支持创建该表索引:" + tableName);
                         continue;
@@ -536,6 +540,52 @@ public class IndexServiceImpl implements IndexService {
         return size;
     }
 
+    private Long createProductIndexesFromFiles() throws IOException {
+        logger.info("正在创建物料索引...");
+        Long size = 0L;
+        BufferedReader bufferedReader = null;
+        try {
+            // 从本地路径读取器件数据
+            String productDataPath = SearchUtils.getDataPath(PRODUCTS_PRIVATE_TABLE_NAME);
+            File[] files = new File(productDataPath).listFiles();
+            if (files == null || files.length == 0) {
+                logger.info("创建器件索引失败,原因:物料数据文件不存在!");
+                return 0L;
+            }
+            for (File file : files) {
+                logger.info("读取文件: " + file.getName());
+                bufferedReader = new BufferedReader(new FileReader(file));
+                String line;
+                while (!StringUtils.isEmpty(line = bufferedReader.readLine())) {
+                    V_Products product;
+                    try {
+                        product = JSONObject.parseObject(line, V_Products.class);
+                    } catch (JSONException e) {
+                        throw new IllegalArgumentException(line, e);
+                    }
+                    Document document = ObjectToDocumentUtils.toDocument(product);
+                    if (document != null) {
+                        size++;
+                        // 每创建10000条,打印一次进度
+                        if (size % 10000 == 0) {
+                            logger.info("Product indexed..................." + size);
+                        }
+                        indexWriter.addDocument(document);
+                    }
+                }
+                indexWriter.commit();
+            }
+        } catch (FileNotFoundException e) {
+            logger.error("创建物料索引失败,原因:物料数据文件不存在!");
+            return 0L;
+        } finally {
+            if (bufferedReader != null) {
+                bufferedReader.close();
+            }
+        }
+        return size;
+    }
+
     /**
      * 根据本地 PCB 的数据文件,转为 PCB 批次数据
      *
@@ -662,6 +712,11 @@ public class IndexServiceImpl implements IndexService {
         return multiDownloadData(COMPONENT_TABLE_NAME, threads, startFileIndex, endFileIndex, validateResult);
     }
 
+    @Override
+    public long multiDownloadProduct(Integer threads, Integer startFileIndex, Integer endFileIndex, DownloadHelper.ValidateResult validateResult) {
+        return multiDownloadData(PRODUCTS_PRIVATE_TABLE_NAME, threads, startFileIndex, endFileIndex, validateResult);
+    }
+
     @Override
     public long multiDownloadGoods(Integer threads, Integer startFileIndex, Integer endFileIndex, DownloadHelper.ValidateResult validateResult) {
         return multiDownloadData(GOODS_TABLE_NAME, threads, startFileIndex, endFileIndex, validateResult);
@@ -719,6 +774,9 @@ public class IndexServiceImpl implements IndexService {
                 }
             }, validateResult);
             return downloadHelper.getResult();
+        } else if (tableName.equals(PRODUCTS_PRIVATE_TABLE_NAME)) {
+            DownloadHelper<V_Products> downloadHelper = new DownloadHelper<>(threads, startFileIndex, endFileIndex, tableName, "id", v_productsDao, new DownloadService<V_Products>() {}, validateResult);
+            return downloadHelper.getResult();
         } else {
             throw new IllegalArgumentException("多线程下载不支持该表:" + tableName);
         }
@@ -780,6 +838,9 @@ public class IndexServiceImpl implements IndexService {
                     } else if (obj instanceof PurchaseInvoice) {
                         indexWriter.updateDocument(new Term(SearchConstants.PURCHASE_INVOICE_ID_FIELD,
                                 String.valueOf(((PurchaseInvoice) obj).getId())), document);
+                    } else if (obj instanceof  V_Products) {
+                        indexWriter.updateDocument(new Term(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME,
+                                String.valueOf(((V_Products) obj).getId())), document);
                     } else {
                         throw new IllegalStateException("Message parsing failed!");
                     }
@@ -880,6 +941,9 @@ public class IndexServiceImpl implements IndexService {
                 } else if (obj instanceof PurchaseInvoice) {
                     indexWriter.deleteDocuments(new Term(SearchConstants.PURCHASE_INVOICE_ID_FIELD,
                             String.valueOf(((PurchaseInvoice) obj).getId())));
+                } else if (obj instanceof V_Products) {
+                    indexWriter.deleteDocuments(new Term(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME,
+                            String.valueOf(((V_Products) obj).getId())));
                 } else {
                     throw new IllegalStateException("Message parsing failed!");
                 }

+ 98 - 0
mall-search/src/main/java/com/uas/search/service/impl/SearchServiceImpl.java

@@ -45,6 +45,98 @@ public class SearchServiceImpl implements SearchService {
 
 	private static Logger logger = LoggerFactory.getLogger(SearchServiceImpl.class);
 
+	/**
+	 * 查询标准物料
+	 *
+	 * @param keyword 关键词
+	 * @param page    页码
+	 * @param size    尺寸
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+	@Override
+	public SPage<Long> getStandardProductIds(String keyword, Integer page, Integer size) throws IOException {
+		List<Long> ids = new ArrayList<>();
+		SPage<Document> documents = getStandardProductDocuments(keyword, page, size);
+		SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
+				documents.getSize(), documents.isFirst(), documents.isLast());
+		for (Document document : documents.getContent()) {
+			ids.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
+		}
+		sPage.setContent(ids);
+		return sPage;
+	}
+
+	/**
+	 * 查询非标准物料
+	 *
+	 * @param keyword 关键词
+	 * @param page    页码
+	 * @param size    尺寸
+	 * @return idPage
+	 * @throws IOException 输入异常
+	 */
+	@Override
+	public SPage<Long> getNonStandardProductIds(String keyword, Integer page, Integer size) throws IOException {
+		List<Long> ids = new ArrayList<>();
+		SPage<Document> documents = getNonStandardProductDocuments(keyword, page, size);
+		SPage<Long> sPage = new SPage<>(documents.getTotalPage(), documents.getTotalElement(), documents.getPage(),
+				documents.getSize(), documents.isFirst(), documents.isLast());
+		for (Document document : documents.getContent()) {
+			ids.add(Long.parseLong(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
+		}
+		sPage.setContent(ids);
+		return sPage;
+	}
+
+	private SPage<Document> getStandardProductDocuments(String keyword, Integer page, Integer size) throws IOException {
+//		if (SearchUtils.isKeywordInvalid(keyword)) {
+//			throw new IllegalArgumentException("搜索关键词无效:" + keyword);
+//		}
+		BooleanQuery q1 = new BooleanQuery();
+		q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
+		q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
+		BooleanQuery booleanQuery = new BooleanQuery();
+		booleanQuery.add(q1, BooleanClause.Occur.MUST);
+		if (!StringUtils.isEmpty(keyword)) {
+			BooleanQuery q2 = new BooleanQuery();
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PBRANDEN_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			booleanQuery.add(q2, BooleanClause.Occur.MUST);
+		}
+		logger.info(booleanQuery.toString());
+		return SearchUtils.getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct(keyword)), page, size);
+	}
+
+	private SPage<Document> getNonStandardProductDocuments(String keyword, Integer page, Integer size) throws IOException {
+//		if (SearchUtils.isKeywordInvalid(keyword)) {
+//			throw new IllegalArgumentException("搜索关键词无效:" + keyword);
+//		}
+		BooleanQuery q1 = new BooleanQuery();
+		q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(0))), BooleanClause.Occur.MUST);
+		q1.add(new TermQuery(new Term(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1))), BooleanClause.Occur.MUST);
+		BooleanQuery booleanQuery = new BooleanQuery();
+		booleanQuery.add(q1, BooleanClause.Occur.MUST);
+		if (!StringUtils.isEmpty(keyword)) {
+			BooleanQuery q2 = new BooleanQuery();
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_TITLE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_BRAND_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			q2.add(createQuery(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD, keyword, true,1), BooleanClause.Occur.SHOULD);
+			booleanQuery.add(q2, BooleanClause.Occur.MUST);
+		}
+		logger.info(booleanQuery.toString());
+		return SearchUtils.getDocuments(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, booleanQuery, new Sort(sortProduct(keyword)), page, size);
+	}
+
+	/**
+	 * @return 物料排序规则
+	 */
+	private SortField sortProduct(String keyword) {
+		// id
+		return new SortField(SearchConstants.PRODUCT_PRIVATE_ID_FIELD, new StringFieldComparatorSource(keyword));
+	}
+
 	@Override
 	public SPage<Long> getKindIds(String keyword, Integer page, Integer size) throws IOException {
 		List<Long> ids = new ArrayList<>();
@@ -1549,6 +1641,12 @@ public class SearchServiceImpl implements SearchService {
 		booleanQuery.add(booleanQuery2, BooleanClause.Occur.FILTER);
 	}
 
+    @Override
+    public V_Products getProduct(Long id) throws IOException {
+        return DocumentToObjectUtils.toProduct(
+                SearchUtils.getDocumentById(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME, SearchConstants.PRODUCT_PRIVATE_ID_FIELD, id));
+    }
+
 	@Override
 	public Kind getKind(Long id) throws IOException {
 		return DocumentToObjectUtils.toKind(

+ 39 - 1
mall-search/src/main/java/com/uas/search/util/DocumentToObjectUtils.java

@@ -41,10 +41,48 @@ public class DocumentToObjectUtils {
 			return toPurchase(document);
 		} else if (tableName.equals(SearchConstants.PURCHASE_INVOICE_TABLE_NAME)) {
 			return toPurchaseInvoice(document);
-		}
+		} else if (tableName.equals(SearchConstants.PRODUCTS_PRIVATE_TABLE_NAME)) {
+		    return toProduct(document);
+        }
 		throw new IllegalArgumentException("不支持的表:" + tableName);
 	}
 
+    /**
+     * 将Document转换为标准物料对象
+     *
+     * @param document
+     * @return
+     */
+    public static V_Products toProduct(Document document) {
+        if (document == null) {
+            return null;
+        }
+        V_Products products = new V_Products();
+        products.setId(Long.valueOf(document.get(SearchConstants.PRODUCT_PRIVATE_ID_FIELD)));
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_TITLE_FIELD))) {
+            products.setTitle(document.get(SearchConstants.PRODUCT_PRIVATE_TITLE_FIELD));
+        }
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_BRAND_FIELD))) {
+            products.setBrand(document.get(SearchConstants.PRODUCT_PRIVATE_BRAND_FIELD));
+        }
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD))) {
+            products.setCmpCode(document.get(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD));
+        }
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD))) {
+            products.setKind(document.get(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD));
+        }
+        // 标准型号和英文标准品牌为空时不建立索引
+        products.setpBrandEn(document.get(SearchConstants.PRODUCT_PRIVATE_PBRANDEN_FIELD));
+        products.setpCmpCode(document.get(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD));
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD))) {
+            products.setStandard(Short.valueOf(document.get(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD)));
+        }
+        if (!StringUtils.isEmpty(document.get(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD))) {
+            products.setB2cEnabled(Short.valueOf(document.get(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD)));
+        }
+        return products;
+    }
+
 	/**
 	 * 将Document转换为类目对象
 	 * 

+ 48 - 1
mall-search/src/main/java/com/uas/search/util/ObjectToDocumentUtils.java

@@ -54,7 +54,9 @@ public class ObjectToDocumentUtils {
 			return toDocument((Purchase) object);
 		} else if (object instanceof PurchaseInvoice) {
 			return toDocument((PurchaseInvoice) object);
-        } else {
+        } else if (object instanceof V_Products) {
+			return toDocument((V_Products) object);
+		} else {
 			throw new IllegalArgumentException("不支持将以下类型转换为Document:" + object.getClass().getName());
 		}
 	}
@@ -90,6 +92,51 @@ public class ObjectToDocumentUtils {
 		return document;
 	}
 
+	/**
+	 * Product对象转为Document
+	 *
+	 * @param product
+	 * @return
+	 */
+	public static Document toDocument(V_Products product) {
+		if (product == null || product.getId() == null || StringUtils.isEmpty(product.getpBrandEn())
+				|| StringUtils.isEmpty(product.getpCmpCode())) {
+			return null;
+		}
+		Document document = new Document();
+		// 不能用LongField,否则后续实时更新索引时,方法updateDocument(new Term("", ""),
+		// doc)无法根据id进行更新
+		document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_ID_FIELD, String.valueOf(product.getId()), Store.YES));
+		document.add(new DoubleDocValuesField(SearchConstants.PRODUCT_PRIVATE_ID_FIELD, product.getId()));
+		if (!StringUtils.isEmpty(product.getTitle())) {
+            document.add(new TextField(SearchConstants.PRODUCT_PRIVATE_TITLE_FIELD, product.getTitle(), Store.YES));
+        }
+        if (!StringUtils.isEmpty(product.getBrand())) {
+            document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_BRAND_FIELD, product.getBrand(), Store.YES));
+        }
+        if (!StringUtils.isEmpty(product.getCmpCode())) {
+            document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_CMPCODE_FIELD, product.getCmpCode(), Store.YES));
+        }
+        if (!StringUtils.isEmpty(product.getKind())) {
+            document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_KIND_FIELD, product.getKind(), Store.YES));
+        }
+        document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_PBRANDEN_FIELD, product.getpBrandEn(), Store.YES));
+		document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_PCMPCODE_FIELD, product.getpCmpCode(), Store.YES));
+		if (product.getStandard() != null) {
+			document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(product.getStandard()), Store.YES));
+		} else {
+			// 为空默认非标
+			document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_STANDARD_FIELD, String.valueOf(0), Store.YES));
+		}
+		if (product.getB2cEnabled() != null) {
+			document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(product.getB2cEnabled()), Store.YES));
+		} else {
+			// 为空默认可用
+			document.add(new StringField(SearchConstants.PRODUCT_PRIVATE_B2CENABLED_FIELD, String.valueOf(1), Store.YES));
+		}
+		return document;
+	}
+
 	/**
 	 * Brand对象转为Document
 	 *

+ 153 - 0
search-console-b2b/src/main/java/com/uas/search/console/b2b/jms/LuceneMessage.java

@@ -0,0 +1,153 @@
+package com.uas.search.console.b2b.jms;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 用于实时更新的消息
+ *
+ * @author sunyj
+ * @since 2017/10/11 16:12
+ */
+@Entity
+@Table(name = "lucene$message")
+public class LuceneMessage implements Serializable {
+
+    /**
+     * 序列号
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @Id
+    @Column(name = "me_id")
+    private Long id;
+
+    /**
+     * 表名
+     */
+    @Column(name = "me_table_name")
+    private String tableName;
+
+    /**
+     * 更改类型
+     */
+    @Column(name = "me_method_type")
+    private String methodType;
+
+    /**
+     * 数据 id
+     */
+    @Column(name = "me_data_id")
+    private Long dataId;
+
+    /**
+     * 数据,可为空
+     */
+    @Column(name = "me_data")
+    private String data;
+
+    /**
+     * 优先级
+     */
+    @Column(name = "me_priority")
+    private Long priority;
+
+    /**
+     * 尝试次数
+     */
+    @Column(name = "me_retry_count")
+    private Long retryCount;
+
+    /**
+     * 时间
+     */
+    @Column(name = "me_create_time")
+    private Date createTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public String getMethodType() {
+        return methodType;
+    }
+
+    public void setMethodType(String methodType) {
+        this.methodType = methodType;
+    }
+
+    public Long getDataId() {
+        return dataId;
+    }
+
+    public void setDataId(Long dataId) {
+        this.dataId = dataId;
+    }
+
+    public String getData() {
+        return data;
+    }
+
+    public void setData(String data) {
+        this.data = data;
+    }
+
+    public Long getPriority() {
+        return priority;
+    }
+
+    public void setPriority(Long priority) {
+        this.priority = priority;
+    }
+
+    public Long getRetryCount() {
+        return retryCount;
+    }
+
+    public void setRetryCount(Long retryCount) {
+        this.retryCount = retryCount;
+    }
+
+    public String getCreateTime() {
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTime);
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    @Override
+    public String toString() {
+        return "LuceneMessage{" +
+                "id=" + id +
+                ", tableName='" + tableName + '\'' +
+                ", methodType='" + methodType + '\'' +
+                ", dataId=" + dataId +
+                ", data='" + data + '\'' +
+                ", priority=" + priority +
+                ", retryCount=" + retryCount +
+                ", createTime=" + createTime +
+                '}';
+    }
+}
+

+ 45 - 0
search-console-b2b/src/main/java/com/uas/search/console/b2b/jms/LuceneMessageDao.java

@@ -0,0 +1,45 @@
+package com.uas.search.console.b2b.jms;
+
+import com.uas.search.annotation.NotEmpty;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.query.Procedure;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author sunyj
+ * @since 2016年7月7日 下午5:52:52
+ */
+@Repository
+public interface LuceneMessageDao
+        extends JpaSpecificationExecutor<LuceneMessage>, JpaRepository<LuceneMessage, Long> {
+
+    /**
+     * 获取指定数目的数据
+     *
+     * @param start 开始的记录
+     * @param size 指定数目
+     * @return 数据
+     */
+    @Query(value = "select * from lucene$message where me_retry_count < 5 order by me_priority desc, me_id limit ?1, ?2", nativeQuery = true)
+    List<LuceneMessage> findList(@NotEmpty("start") Integer start, @NotEmpty("size") Integer size);
+
+    /**
+     * 统计行数
+     * @return 行数
+     */
+    @Query(value = "select count(1) from lucene$message where me_retry_count < 5", nativeQuery = true)
+    long count();
+
+    /**
+     * 出队消息
+     * @param id 消息 id
+     */
+    @Transactional
+    @Procedure(procedureName = "dequeue_lucene_message")
+    void dequeueLuceneMessage(@NotEmpty("id") Long id);
+}