杜洪波
2024-08-30 e27c43f254793a834f4713637494b088bdb34680
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
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();
    }
}