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;
|
import java.util.Map;
|
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<Document> 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<JSONObject> 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));
|
|
// 文件名增加高亮显示
|
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<JSONObject> 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,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("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, 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<String, FieldSetEntity> 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();
|
}
|
}
|