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 com.product.util.UnifySQLSymbol; 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; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * 文件检索 */ @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 listJsonFile = new ArrayList<>(); Long totalCount = 0L; try { // 获取安全的IndexSearcher实例 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); JSONObject jsonFile = new JSONObject(); jsonFile.put(CmnConst.FILE_UUID, doc.get(CoreConst.UUID)); jsonFile.put(CoreConst.FUNCTION_UUID, doc.get(CoreConst.FUNCTION_UUID)); if (!StringUtils.isEmpty(doc.get(CoreConst.FUNCTION_UUID))) { FieldSetEntity fs = BaseUtil.getSingleInfoByCache("所有功能", new String[]{jsonFile.getString(CoreConst.FUNCTION_UUID)}); if (FieldSetEntity.isEmpty(fs)) { //找不到功能重新根据表名查找 //清空 jsonFile.remove(CoreConst.FUNCTION_UUID); } } if (StringUtils.isEmpty(jsonFile.get(CoreConst.FUNCTION_UUID))) { //查询功能信息 String sql = "SELECT * FROM product_sys_functions where table_uuid =(select uuid from product_sys_datamodel_table where table_name=?) limit 1"; FieldSetEntity fseFunction = baseDao.getFieldSetBySQL(sql, new String[]{doc.get(CmnConst.TABLE_NAME)}, false); if (fseFunction != null) { jsonFile.put(CoreConst.FUNCTION_UUID, fseFunction.getString(CoreConst.UUID)); //回写功能信息到文档信息中 doc.add(new StoredField(CoreConst.FUNCTION_UUID, fseFunction.getString(CoreConst.UUID))); indexWriter.updateDocument(new Term(CoreConst.UUID, doc.get(CoreConst.UUID)), doc); } } // 文件名增加高亮显示 String fileName = doc.get(CmnConst.FILE_NAME); String file_name = highlighter.getBestFragment(new SmartChineseAnalyzer(), CmnConst.FILE_NAME, fileName); if (StringUtils.isEmpty(file_name)) { file_name = fileName; } // 文件内容增加高亮显示 String fileContent = doc.get(CmnConst.FILE_CONTENT); String file_content = highlighter.getBestFragment(new SmartChineseAnalyzer(), CmnConst.FILE_CONTENT, fileContent); if (StringUtils.isEmpty(file_content)) { file_content = fileContent; } jsonFile.put(CmnConst.FILE_NAME, file_name); jsonFile.put(CmnConst.FILE_CONTENT, file_content); listJsonFile.add(jsonFile); } } finally { // 关闭实例资源 searcherManager.release(indexSearcher); } // 创建返回信息对象 JSONObject jsonReturn = new JSONObject(); jsonReturn.put(CmnConst.TOTALCOUNT, totalCount); if (listJsonFile.size() > 0) { // 获取文档展示信息 getFileInfo(listJsonFile); jsonReturn.put(CmnConst.DATA, listJsonFile); } 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; } /** * 获取文档展示信息 * @param listJsonFile */ public void getFileInfo(List listJsonFile) { StringBuilder sql = new StringBuilder(); Set fileUuids = listJsonFile.stream().map(jsonFile -> jsonFile.getString(CmnConst.FILE_UUID)).collect(Collectors.toSet()); sql.append("SELECT \n"); sql.append(" A.uuid,A.attachment_data_table,A.attachment_data_field,DATE_FORMAT(A.created_utc_datetime,'%Y-%m-%d') AS upload_date, \n"); sql.append(" B.org_fields,B.user_fields,B.title_field,B.time_field, \n"); sql.append(" C.uuid AS function_uuid,C.function_name,D.user_name AS upload_user "); sql.append("FROM product_sys_attachments A \n"); sql.append(" JOIN product_sys_datamodel_table as " + UnifySQLSymbol.getSymbol("table") + " on A.attachment_data_table=" + UnifySQLSymbol.getSymbol("table") + ".table_name \n"); sql.append("LEFT JOIN product_sys_document_search B ON B.table_name = " + UnifySQLSymbol.getSymbol("table") + ".uuid \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 ").append(BaseUtil.buildQuestionMarkFilter("A.uuid", fileUuids.size(), true)); // 获取文档附件、配置信息 DataTableEntity dtConfig = baseDao.listTable(sql.toString(), fileUuids.toArray()); 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, A.function_uuid, A.function_name, \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(" '").append(fseConfig.getString(CoreConst.FUNCTION_UUID)).append("' AS function_uuid, \n"); sqlService.append(" '").append(fseConfig.getString(CoreConst.FUNCTION_NAME)).append("' AS function_name \n"); sqlService.append(") A \n"); sqlService.append("LEFT JOIN ").append(fseConfig.getString(CmnConst.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[]{}); if (!BaseUtil.dataTableIsEmpty(dtService)) { Map map = dtService.getData().stream() .collect(Collectors.toMap( fseService -> fseService.getUUID(), fseService -> fseService )); for (int i = 0; i < listJsonFile.size(); i++) { JSONObject jsonFile = listJsonFile.get(i); FieldSetEntity fseService = map.get(jsonFile.get(CmnConst.FILE_UUID)); if (fseService != null) { jsonFile.put(CoreConst.FUNCTION_NAME, fseService.getString(CoreConst.FUNCTION_NAME)); jsonFile.put(CoreConst.FUNCTION_UUID, fseService.getString(CoreConst.FUNCTION_UUID)); jsonFile.put(CmnConst.UPLOAD_USER, fseService.getString(CmnConst.UPLOAD_USER)); jsonFile.put(CmnConst.UPLOAD_DATE, fseService.getString(CmnConst.UPLOAD_DATE)); jsonFile.put(CmnConst.SERVICE_UUID, fseService.getString(CmnConst.SERVICE_UUID)); jsonFile.put(CmnConst.SERVICE_TITLE, fseService.getString(CmnConst.SERVICE_TITLE)); jsonFile.put(CmnConst.SERVICE_TIME, fseService.getString(CmnConst.SERVICE_TIME)); } } } } } public JSONObject permission(String uuid, String function_uuid) { JSONObject json = new JSONObject(); 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, 1); json.put("permission_msg", "数据配置-该文档配置不支持跳转"); return json; } else { json.put(CmnConst.IS_PERSSION, 0); 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); 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("permission_msg", "数据异常-业务数据不存在"); json.put(CmnConst.IS_PERSSION, 2); } else { json.put("permission_msg", "数据策略-此文档您没有查看权限"); json.put(CmnConst.IS_PERSSION, 3); } } else { // 判断是否为文档管理功能,并处理单位文档和个人文档权限 if ("e4fa2c88-88a4-4ef4-9020-ebbe0440b4cf".equals(function_uuid)) { if (!SpringMVCContextHolder.getCurrentUserId() .equals(fseService.getString(CoreConst.CREATED_BY))) { json.put("permission_msg", "文档权限策略-此文档您没有查看权限"); json.put(CmnConst.IS_PERSSION, 4); } } 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("permission_msg", "文档权限策略-此文档您没有查看权限"); json.put(CmnConst.IS_PERSSION, 4); } else { boolean succ = BaseUtil.multipleTypeConcat(fseDirectRight.getString(CmnConst.STORAGE_UUID), SpringMVCContextHolder.getCurrentUserId()); if (!succ) { json.put("permission_msg", "文档权限策略-此文档您没有查看权限"); json.put(CmnConst.IS_PERSSION, 4); } } } } } } return json; } /** * 文件检索查询分页 * * @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(); } }