package com.product.lucene.service; import com.alibaba.fastjson.JSONObject; import com.product.common.lang.StringUtils; import com.product.core.config.CoreConst; import com.product.core.dao.BaseDao; import com.product.core.entity.DataTableEntity; import com.product.core.entity.FieldSetEntity; import com.product.core.permission.PermissionService; import com.product.core.spring.context.SpringMVCContextHolder; import com.product.lucene.config.CmnConst; import com.product.lucene.util.FileUtils; import com.product.util.BaseUtil; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.document.*; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.*; import org.apache.lucene.search.highlight.Fragmenter; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.QueryScorer; import org.apache.lucene.search.highlight.SimpleFragmenter; import org.apache.lucene.search.highlight.SimpleHTMLFormatter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 文件检索 */ @Service("luceneService") public class LuceneService { @Autowired BaseDao baseDao; @Autowired private IndexWriter indexWriter; @Autowired PermissionService permissionService; @Autowired private SearcherManager searcherManager; /** * 创建索引 * * @param fse * @throws IOException */ public void createdIndex(FieldSetEntity fse) { String function_uuid = fse.getString(CoreConst.FUNCTION_UUID); // 获取功能 File file = (File) fse.getObject("file"); // 获取附件 // 获取全文检索配置 FieldSetEntity fseConfig = baseDao.getFieldSetEntityByFilter(CmnConst.PRODUCT_SYS_DOCUMENT_SEARCH, "function_uuid=?", new Object[] { function_uuid }, false); if (fseConfig == null) return; String table_uuid = fseConfig.getString(CmnConst.TABLE_NAME); // 获取缓存表配置 FieldSetEntity fseTable = baseDao.getFieldSetEntity(CoreConst.PRODUCT_SYS_DATAMODEL_TABLE, table_uuid, false); if (fseTable == null) return; // 获取附件uuids String attachments = fse.getString(CmnConst.ATTACHMENT_UUID); if (StringUtils.isEmpty(attachments)) return; // 遍历获取附件信息 String[] attachment = attachments.split(","); List docs = new ArrayList<>(); for (int i = 0; i < attachment.length; i++) { // 获取附件信息 FieldSetEntity fseAttachment = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, attachment[i], false); if (fseAttachment == null || file == null) return; // 生成索引信息 Document doc = new Document(); doc.add(new StringField(CoreConst.UUID, attachment[i], Field.Store.YES)); // 附件UUID doc.add(new StoredField(CoreConst.FUNCTION_UUID, function_uuid)); // 功能UUID doc.add(new StoredField(CmnConst.UPLOAD_USER, fseAttachment.getString(CoreConst.CREATED_BY))); // 上传人 doc.add(new LongPoint(CmnConst.UPLOAD_DATE, fseAttachment.getDate(CoreConst.CREATED_UTC_DATETIME).getTime())); // 上传时间 doc.add(new TextField(CmnConst.FILE_NAME, fseAttachment.getString(CmnConst.FILE_NAME), Field.Store.YES)); // 文件名 doc.add(new TextField(CmnConst.FILE_CONTENT, FileUtils.FileToString(file), Field.Store.YES)); // 文件内容 docs.add(doc); } // 写入索引 try { indexWriter.addDocuments(docs); indexWriter.commit(); } catch (IOException e) { e.printStackTrace(); } finally { file.delete(); } } /** * 文档检索 * * @param fse * @return * @throws Exception * @throws ParseException */ public JSONObject searchProduct(FieldSetEntity fse) throws Exception { // 刷新IndexSearcher实例 searcherManager.maybeRefresh(); // 获取安全的IndexSearcher实例 IndexSearcher indexSearcher = null; // 文档信息汇总 List jsonTotal = new ArrayList<>(); Long totalCount = 0L; try { indexSearcher = searcherManager.acquire(); // 获取条件查询结果 Query finalQuery = searchFilterParse(fse); // 获取高亮匹配器 Highlighter highlighter = highlighterParse(finalQuery); // 分页查询文档 TopDocs topDocs = searchByPage(fse.getInteger(CoreConst.CPAGE), fse.getInteger(CoreConst.PAGESIZE), indexSearcher, finalQuery); // 获取检索命中项,总命中数 ScoreDoc[] hits = topDocs.scoreDocs; totalCount = topDocs.totalHits.value; for (int i = 0; i < hits.length; i++) { // 获取检索命中的文档信息 Document doc = indexSearcher.doc(hits[i].doc); String fileUUID = doc.get(CoreConst.UUID); String functionUUID = doc.get(CoreConst.FUNCTION_UUID); String fileName = doc.get(CmnConst.FILE_NAME); String fileContent = doc.get(CmnConst.FILE_CONTENT); JSONObject json = new JSONObject(); if (!StringUtils.isEmpty(functionUUID)) { getConfig(json, functionUUID, fileUUID); } json.put(CoreConst.FUNCTION_UUID, doc.get(CoreConst.FUNCTION_UUID)); // 文件名增加高亮显示 String file_name = highlighter.getBestFragment(new SmartChineseAnalyzer(), CmnConst.FILE_NAME, CmnConst.FILE_NAME); if (StringUtils.isEmpty(file_name)) { file_name = fileName; } // 文件内容增加高亮显示 String file_content = highlighter.getBestFragment(new SmartChineseAnalyzer(), CmnConst.FILE_CONTENT, fileContent); if (StringUtils.isEmpty(file_content)) { file_content = fileContent; } json.put("file_uuid", fileUUID); json.put("function_uuid", functionUUID); json.put(CmnConst.FILE_NAME, file_name); json.put(CmnConst.FILE_CONTENT, file_content); jsonTotal.add(json); } } finally { // 关闭实例资源 searcherManager.release(indexSearcher); } JSONObject jsonReturn = new JSONObject(); getFileInfo(jsonTotal); jsonReturn.put(CmnConst.TOTALCOUNT, totalCount); jsonReturn.put(CmnConst.DATA, jsonTotal); return jsonReturn; } /** * 解析查询条件 * @param fse * @return * @throws ParseException */ public Query searchFilterParse(FieldSetEntity fse) throws ParseException { // 上传人 String uploadUser = fse.getString("upload_user"); String startDate = fse.getString("start_date"); String endDate = fse.getString("end_date"); String searchKey = fse.getString(CmnConst.SEARCH_KEY); // 创建查询条件 BooleanQuery.Builder boolQuery = new BooleanQuery.Builder(); // 上传人过滤 if (!BaseUtil.strIsNull(uploadUser)) { Query uploaderQuery = new TermQuery(new Term("upload_user", uploadUser)); boolQuery.add(uploaderQuery, BooleanClause.Occur.MUST); } // 上传时间过滤 if (!BaseUtil.strIsNull(startDate) || !BaseUtil.strIsNull(endDate)) { Long longStartDate = Long.MIN_VALUE; Long longEndData = Long.MAX_VALUE; if (!BaseUtil.strIsNull(startDate)) { longStartDate = fse.getDate("start_date").getTime(); } if (!BaseUtil.strIsNull(endDate)) { longEndData = fse.getDate("end_date").getTime(); } Query timeRangeQuery = LongPoint.newRangeQuery("upload_date", longStartDate, longEndData); boolQuery.add(timeRangeQuery, BooleanClause.Occur.MUST); } // 关键词过滤 if (!BaseUtil.strIsNull(searchKey)) { QueryParser queryParser = new MultiFieldQueryParser( new String[] { CmnConst.FILE_NAME, CmnConst.FILE_CONTENT }, new SmartChineseAnalyzer()); Query query = queryParser.parse(searchKey); boolQuery.add(query, BooleanClause.Occur.MUST); } return boolQuery.build(); } /** * 查询文件高亮匹配器 * @param finalQuery * @return */ public Highlighter highlighterParse(Query finalQuery) { // 高亮格式 SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter(CmnConst.HIGHLIGHT_BEFORE, CmnConst.HIGHLIGHT_AFTER); // 高亮匹配器 Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(finalQuery)); Fragmenter fragmenter = new SimpleFragmenter(100); highlighter.setTextFragmenter(fragmenter); return highlighter; } public void getFileInfo(List listJsonFile) { StringBuilder sql = new StringBuilder(); for (int i = 0; i < listJsonFile.size(); i++) { JSONObject jsonFile = listJsonFile.get(i); if (i > 0) { sql.append("UNION ALL \n"); } sql.append("SELECT \n"); sql.append(" A.uuid,A.attachment_data_table,A.attachment_data_field,D.user_name AS upload_user,A.created_utc_datetime AS upload_date, \n"); sql.append(" B.org_fields,B.user_fields,B.title_field,B.time_field \n"); sql.append("FROM product_sys_attachments A \n"); sql.append("LEFT JOIN product_sys_document_search B ON B.table_name = A.attachment_data_table \n"); sql.append("LEFT JOIN product_sys_functions C ON C.uuid = B.function_uuid \n"); sql.append("LEFT JOIN product_sys_users D ON D.user_id = A.created_by \n"); sql.append("WHERE A.uuid = '").append(jsonFile.get("file_uuid")).append("' \n"); } DataTableEntity dtConfig = baseDao.listTable(sql.toString(), new Object[] {}); if (!BaseUtil.dataTableIsEmpty(dtConfig)) { StringBuilder sqlService = new StringBuilder(); for (int i = 0; i < dtConfig.getRows(); i++) { FieldSetEntity fseConfig = dtConfig.getFieldSetEntity(i); if (i > 0) { sqlService.append("UNION ALL \n"); } sqlService.append("SELECT \n"); sqlService.append(" A.uuid, \n"); sqlService.append(" B.uuid AS service_uuid, \n"); sqlService.append(" A.upload_user, A.upload_date, \n"); sqlService.append(" ").append(fseConfig.getString(CmnConst.TITLE_FIELD)).append(" AS service_title, \n"); sqlService.append(" ").append(fseConfig.getString(CmnConst.TIME_FIELD)).append(" AS service_time \n"); sqlService.append("FROM ( \n"); sqlService.append(" SELECT \n"); sqlService.append(" '").append(fseConfig.getString(CoreConst.UUID)).append("' AS uuid, \n"); sqlService.append(" '").append(fseConfig.getString(CmnConst.UPLOAD_USER)).append("' AS upload_user, \n"); sqlService.append(" '").append(fseConfig.getString(CmnConst.UPLOAD_DATE)).append("' AS upload_date \n"); sqlService.append(") A \n"); sqlService.append("LEFT JOIN ").append(fseConfig.getString("attachment_data_table")).append(" B \n"); sqlService.append("ON B.").append(fseConfig.getString(CmnConst.ATTACHMENT_DATA_FIELD)).append(" LIKE '%").append(fseConfig.getUUID()).append("%' \n"); } DataTableEntity dtService = baseDao.listTable(sqlService.toString(), new Object[] {}); } } /** * 获取文档检索配置信息(权限,功能名称,跳转按钮) * * @param json 检索文件对应信息 * @param function_uuid 所属功能UUID * @param service_uuid 附件UUID */ public void getConfig(JSONObject json, String function_uuid, String uuid) { StringBuilder searchConfigSql = new StringBuilder(); searchConfigSql.append("SELECT \n"); searchConfigSql.append(" A.uuid,A.function_uuid,A.function_button_uuid,A.table_name, \n"); searchConfigSql.append(" A.attachment_file,A.org_fields,A.user_fields,title_field,time_field, \n"); searchConfigSql.append(" B.function_name,C.button_name \n"); searchConfigSql.append("FROM product_sys_document_search A \n"); searchConfigSql.append("LEFT JOIN product_sys_functions B ON A.function_uuid = B.uuid \n"); searchConfigSql.append("LEFT JOIN product_sys_function_buttons C ON A.function_button_uuid = C.uuid \n"); searchConfigSql.append("WHERE A.function_uuid=?"); // 获取文档检索配置(功能,跳转按钮) FieldSetEntity fseConfig = baseDao.getFieldSetEntityBySQL(searchConfigSql.toString(), new Object[] { function_uuid }, false); if (fseConfig != null) { // 获取跳转按钮 并 判断跳转权限 String buttonName = fseConfig.getString(CoreConst.BUTTON_NAME); if (BaseUtil.strIsNull(buttonName)) { json.put(CmnConst.IS_PERSSION, 2); } else { json.put(CmnConst.SKIPBUTTON, fseConfig.getString(CoreConst.BUTTON_NAME)); } // 获取权限过滤字段 String org_fields = fseConfig.getString(CmnConst.ORG_FIELDS); String user_fields = fseConfig.getString(CmnConst.USER_FIELDS); // 获取附件信息,结合数据权限字段,生成原数据的过滤SQL FieldSetEntity fseAttachment = baseDao.getFieldSetEntity(CmnConst.PRODUCT_SYS_ATTACHMENTS, uuid, false); baseDao.loadPromptData(fseAttachment); if (fseAttachment != null) { StringBuilder filter = new StringBuilder(); filter.append(fseAttachment.getString(CmnConst.ATTACHMENT_DATA_FIELD)); filter.append(" =? "); if (StringUtils.isEmpty(org_fields)) { if (!StringUtils.isEmpty(user_fields)) { filter.append(" AND "); filter.append(permissionService .getDataFilter(fseAttachment.getString(CmnConst.ATTACHMENT_DATA_TABLE), user_fields)); } } else { if (StringUtils.isEmpty(user_fields)) { filter.append(" AND "); filter.append(permissionService.getDataFilter(org_fields)); } else { filter.append(" AND "); filter.append(permissionService.getDataFilter( fseAttachment.getString(CmnConst.ATTACHMENT_DATA_TABLE), user_fields, org_fields)); } } // 获取原数据 FieldSetEntity fseService = baseDao.getFieldSetEntityByFilter( fseAttachment.getString(CmnConst.ATTACHMENT_DATA_TABLE), filter.toString(), new Object[] { uuid }, false); if (fseService == null) { // 获取无权限过滤的原数据 FieldSetEntity fseService2 = baseDao.getFieldSetEntityByFilter( fseAttachment.getString(CmnConst.ATTACHMENT_DATA_TABLE), fseAttachment.getString(CmnConst.ATTACHMENT_DATA_FIELD) + "=?", new Object[] { uuid }, false); if (fseService2 == null) { json.put(CmnConst.TITLE, "业务数据不存在"); json.put(CmnConst.IS_PERSSION, 1); } else { json.put(CmnConst.SERVICE_UUID, fseService2.getString(CoreConst.UUID)); json.put(CmnConst.DATE_TIME, fseService2.getString(fseConfig.getString(CmnConst.TIME_FIELD))); json.put(CmnConst.TITLE, fseService2.getString(fseConfig.getString(CmnConst.TITLE_FIELD))); json.put(CmnConst.IS_PERSSION, 0); } } else { // 判断是否为文档管理功能,并处理单位文档和个人文档权限 if ("e4fa2c88-88a4-4ef4-9020-ebbe0440b4cf".equals(function_uuid)) { if (!SpringMVCContextHolder.getCurrentUserId() .equals(fseService.getString(CoreConst.CREATED_BY))) { json.put(CmnConst.IS_PERSSION, 0); } } else if ("01513ff6-c758-4384-b861-e58dfe146fdd".equals(function_uuid)) { // 获取单位文档所属目录权限 FieldSetEntity fseDirectRight = baseDao.getFieldSetByFilter( CmnConst.PRODUCT_OA_DIRECTORY_RIGHTS, "directory_uuid=?", new Object[] { fseService.getString(CmnConst.DIRECTORY_UUID) }, false); if (fseDirectRight == null) { json.put(CmnConst.IS_PERSSION, 0); } else { boolean succ = BaseUtil.multipleTypeConcat(fseDirectRight.getString(CmnConst.STORAGE_UUID), SpringMVCContextHolder.getCurrentUserId()); if (!succ) { json.put(CmnConst.IS_PERSSION, 0); } } } json.put(CmnConst.SERVICE_UUID, fseService.getString(CoreConst.UUID)); json.put(CmnConst.DATE_TIME, fseService.getString(fseConfig.getString(CmnConst.TIME_FIELD))); json.put(CmnConst.TITLE, fseService.getString(fseConfig.getString(CmnConst.TITLE_FIELD))); } json.put(CoreConst.FUNCTION_NAME, fseConfig.getString(CoreConst.FUNCTION_NAME)); json.put(CoreConst.CREATED_BY, fseAttachment.getString(CoreConst.CREATED_BY)); } } } /** * 文件检索查询分页 * * @param cpage 当前页 * @param pagesize 每页大小 * @param searcher 查询器 * @param query 查询对象 * @return * @throws IOException */ private TopDocs searchByPage(int cpage, int pagesize, IndexSearcher searcher, Query query) throws IOException { TopDocs result = null; if (query == null) { System.out.println(" Query is null return null "); return null; } ScoreDoc before = null; if (cpage != 1) { TopDocs docsBefore = searcher.search(query, (cpage - 1) * pagesize); ScoreDoc[] scoreDocs = docsBefore.scoreDocs; if (scoreDocs.length > 0) { before = scoreDocs[scoreDocs.length - 1]; } } result = searcher.searchAfter(before, query, pagesize); return result; } /** * 根据附件uuid删除索引 * * @param uuid * @throws IOException */ public void deleteIndexByUUID(String uuid) throws IOException { indexWriter.deleteDocuments(new Term("uuid", uuid)); indexWriter.commit(); } }