diff --git a/src/main/java/neatlogic/framework/documentonline/crossover/IDocumentOnlineCrossoverMapper.java b/src/main/java/neatlogic/framework/documentonline/crossover/IDocumentOnlineCrossoverMapper.java index 3980fc82c5476d344fc1c6f045cd208a5bc4212f..1e4e18be2ea094d22b31fc5e6042111a11fe8cce 100644 --- a/src/main/java/neatlogic/framework/documentonline/crossover/IDocumentOnlineCrossoverMapper.java +++ b/src/main/java/neatlogic/framework/documentonline/crossover/IDocumentOnlineCrossoverMapper.java @@ -15,8 +15,12 @@ package neatlogic.framework.documentonline.crossover; import neatlogic.framework.crossover.ICrossoverService; import neatlogic.framework.documentonline.dto.DocumentOnlineConfigVo; +import java.util.List; + public interface IDocumentOnlineCrossoverMapper extends ICrossoverService { + List getAllDocumentOnlineConfigList(); + int insertDocumentOnlineConfig(DocumentOnlineConfigVo documentOnlineConfigVo); int deleteDocumentOnlineConfig(DocumentOnlineConfigVo documentOnlineConfigVo); diff --git a/src/main/java/neatlogic/framework/documentonline/exception/DocumentOnlineJarNameIllegalException.java b/src/main/java/neatlogic/framework/documentonline/exception/DocumentOnlineJarNameIllegalException.java new file mode 100644 index 0000000000000000000000000000000000000000..2020e4c36e7491089ba6b81f70c8d02b6816ba02 --- /dev/null +++ b/src/main/java/neatlogic/framework/documentonline/exception/DocumentOnlineJarNameIllegalException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +package neatlogic.framework.documentonline.exception; + +import neatlogic.framework.exception.core.ApiRuntimeException; + +public class DocumentOnlineJarNameIllegalException extends ApiRuntimeException { + + public DocumentOnlineJarNameIllegalException(String name) { + super("nfde.documentonlinejarnameillegalexception.documentonlinejarnameillegalexception", name); + } + +} diff --git a/src/main/java/neatlogic/framework/documentonline/util/DocumentOnlineManager.java b/src/main/java/neatlogic/framework/documentonline/util/DocumentOnlineManager.java new file mode 100644 index 0000000000000000000000000000000000000000..249b5b11855f20dad03b3afdf7acf2b9e75859e0 --- /dev/null +++ b/src/main/java/neatlogic/framework/documentonline/util/DocumentOnlineManager.java @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +package neatlogic.framework.documentonline.util; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import neatlogic.framework.common.config.Config; +import neatlogic.framework.crossover.CrossoverServiceFactory; +import neatlogic.framework.documentonline.crossover.IDocumentOnlineCrossoverMapper; +import neatlogic.framework.documentonline.dto.DocumentOnlineConfigVo; +import neatlogic.framework.documentonline.dto.DocumentOnlineDirectoryVo; +import neatlogic.framework.documentonline.dto.DocumentOnlineVo; +import neatlogic.framework.util.$; +import neatlogic.framework.util.HtmlUtil; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.MMapDirectory; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.wltea.analyzer.lucene.IKAnalyzer; + +import java.io.*; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.*; + +public class DocumentOnlineManager { + + private final static Logger logger = LoggerFactory.getLogger(DocumentOnlineManager.class); + /** + * War包外部在线帮助文档Jar包目录 + */ + public static final String OUTSIDE_WAR_DOCUMENTS_ONLINE_JARS = "documentOnlineJars"; + /** + * 在线帮助文档根目录 + */ + public static final String DIRECTORY_ROOT = "documentonline"; + /** + * 在线帮助文档索引库位置 + */ + public static final String INDEX_DIRECTORY = System.getProperty("java.io.tmpdir") + File.separator + DIRECTORY_ROOT; + + private static DocumentOnlineDirectoryVo DOCUMENT_ONLINE_DIRECTORY_ROOT = new DocumentOnlineDirectoryVo("root", false); + + public static DocumentOnlineDirectoryVo getDocumentOnlineDirectoryRoot() { + return DOCUMENT_ONLINE_DIRECTORY_ROOT; + } + + public static synchronized JSONObject initializeIndex( + List documentOnlineConfigList, + List mappingJsonLocationPatternList, + List mdFileLocationPatternList + ) { + JSONObject resultObj = new JSONObject(); + resultObj.put("mappingJsonLocationPatternList", mappingJsonLocationPatternList); + resultObj.put("mdFileLocationPatternList", mdFileLocationPatternList); + // 1.采集数据 + List mdResourceList = new ArrayList<>(); + /** + * 用于存储documentonline-mapping.json配置文件中的数据,不同模块jar包中都可能存在documentonline-mapping.json配置文件,可能有多个 + */ + List mappingConfigList = new ArrayList<>(); + try { + for (DocumentOnlineConfigVo documentOnlineConfigVo : documentOnlineConfigList) { + documentOnlineConfigVo.setSource("database"); + mappingConfigList.add(documentOnlineConfigVo); + } + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + // 在documentonline-mapping.json配置文件中已设置在线文档文件路径集合,用于防止同个在线文档重复配置 + List jsonResourceList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(mappingJsonLocationPatternList)) { + for (String locationPattern : mappingJsonLocationPatternList) { + Resource[] resources = resolver.getResources(locationPattern); + Collections.addAll(jsonResourceList, resources); + } + } + List mappingJsonResourceURLList = new ArrayList<>(); + for (Resource resource : jsonResourceList) { + String path = resource.getURL().toString(); + mappingJsonResourceURLList.add(path); + String content = null; + try (StringWriter writer = new StringWriter(); InputStream inputStream = resource.getInputStream()) { + // 读取文件内容 + IOUtils.copy(inputStream, writer, StandardCharsets.UTF_8); + content = writer.toString(); + } + if (StringUtils.isBlank(content)) { + continue; + } + try { + JSONArray mappingArray = JSON.parseArray(content); + for (int i = 0; i < mappingArray.size(); i++) { + JSONObject mappingObj = mappingArray.getJSONObject(i); + DocumentOnlineConfigVo documentOnlineConfigVo = mappingObj.toJavaObject(DocumentOnlineConfigVo.class); + if (mappingConfigList.contains(documentOnlineConfigVo)) { + continue; + } + String filePath = documentOnlineConfigVo.getFilePath(); + if (StringUtils.isBlank(filePath)) { + logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_a", path, i)); + continue; + } + String moduleGroup = documentOnlineConfigVo.getModuleGroup(); + if (StringUtils.isBlank(moduleGroup)) { + logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_b", path, i)); + continue; + } + documentOnlineConfigVo.setSource(path); + mappingConfigList.add(documentOnlineConfigVo); + // 初始化的时候以documentonline-mapping.json配置文件数据为主,根据删除主键数据库中数据 +// documentOnlineMapper.deleteDocumentOnlineConfig(documentOnlineConfigVo); + } + } catch (JSONException e) { + logger.error(e.getMessage(), e); + } + } + resultObj.put("mappingJsonResourceURLList", mappingJsonResourceURLList); + if (CollectionUtils.isNotEmpty(mdFileLocationPatternList)) { + for (String locationPattern : mdFileLocationPatternList) { + Resource[] resources = resolver.getResources(locationPattern); + Collections.addAll(mdResourceList, resources); + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + if (CollectionUtils.isNotEmpty(mdResourceList)) { + DocumentOnlineDirectoryVo documentOnlineDirectoryVo = new DocumentOnlineDirectoryVo("root", false); + // 1.创建分词器 + Analyzer analyzer = new IKAnalyzer(true); + // 2.创建dir目录对象,目录对象表示索引库的位置 + // 3.创建IndexWriterConfig对象,这个对象中指定切分词使用的分词器 + IndexWriterConfig config = new IndexWriterConfig(analyzer); + // 4.创建IndexWriter输出流对象,指定输出的位置和使用的config初始化对象 + try (Directory dir = MMapDirectory.open(Paths.get(INDEX_DIRECTORY)); + IndexWriter indexWriter = new IndexWriter(dir, config)) { + // 5.删除之前索引数据,然后在重新生成 + indexWriter.deleteAll(); + System.out.println($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.system_out_println", INDEX_DIRECTORY)); + // 存储所有在线文档的路径集合,用于判断不同jar包中有相同路径的文档 + List existingFilePathList = new ArrayList<>(); + List mdResourceURLList = new ArrayList<>(); + for (Resource resource : mdResourceList) { + String filename = resource.getFilename().substring(0, resource.getFilename().length() - 3); + int index = filename.indexOf("."); + if (index != -1) { + String sort = filename.substring(0, index); + if (StringUtils.isNumeric(sort)) { + filename = filename.substring(index + 1); + } + } + String path = resource.getURL().toString(); + mdResourceURLList.add(path); + String filePath = DocumentOnlineManager.getWithinJarAbsoluteFilePathByURL(resource.getURL()); + if (existingFilePathList.contains(filePath)) { + logger.error($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.error", filePath)); + System.exit(1); + } + existingFilePathList.add(filePath); + List configList = new ArrayList<>(); + // 根据文件路径找到配置映射信息,分析出文件所属的模块组、菜单,定位的锚点 + List mappingConfigs = getMappingConfigByFilePath(filePath, mappingConfigList); + if (CollectionUtils.isNotEmpty(mappingConfigs)) { + for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigs) { + if (StringUtils.isNotBlank(documentOnlineConfigVo.getModuleGroup())) { + DocumentOnlineConfigVo configVo = new DocumentOnlineConfigVo(documentOnlineConfigVo); + configVo.setFilePath(filePath); + configList.add(configVo); + } + } + } + List upwardNameList = new ArrayList<>(); + DocumentOnlineDirectoryVo directory = buildDirectory(documentOnlineDirectoryVo, filePath, path, configList); + if (directory != null) { + upwardNameList = directory.getUpwardNameList(); + } + String content = ""; + try (StringWriter writer = new StringWriter(); InputStream inputStream = resource.getInputStream()) { + // 读取文件内容 + IOUtils.copy(inputStream, writer, StandardCharsets.UTF_8); + content = writer.toString(); + } + Document document = new Document(); + // 创建域对象并且放入到文档中 + // 文件路径字段不分词 + document.add(new StringField("filePath", filePath, Field.Store.YES)); + // 上层名称列表字段不分词 + document.add(new StringField("upwardNameList", JSON.toJSONString(upwardNameList), Field.Store.YES)); + // 文件名称字段分词 + document.add(new TextField("fileName", filename, Field.Store.YES)); + // 文件内容字段分词 + document.add(new TextField("content", content, Field.Store.YES)); + indexWriter.addDocument(document); + } + resultObj.put("mdResourceURLList", mdResourceURLList); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } finally { + // 禁止添加子节点 + documentOnlineDirectoryVo.noAllowedAddChild(); + } + DOCUMENT_ONLINE_DIRECTORY_ROOT = documentOnlineDirectoryVo; + resultObj.put("DOCUMENT_ONLINE_DIRECTORY_ROOT", documentOnlineDirectoryVo); + } + for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigList) { + if (!documentOnlineConfigVo.isUsed()) { + logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_c") + JSON.toJSONString(documentOnlineConfigVo)); + } + } + resultObj.put("mappingConfigList", mappingConfigList); + return resultObj; + } + + /** + * 根据文件路径找到documentonline-mapping.json配置文件设置的映射信息 + * + * @param filePath 文件路径 + * @return 映射信息 + */ + private static List getMappingConfigByFilePath(String filePath, List mappingConfigList) { + List resultList = new ArrayList<>(); + for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigList) { + if (filePath.startsWith(documentOnlineConfigVo.getFilePath())) { + if (!resultList.contains(documentOnlineConfigVo)) { + resultList.add(documentOnlineConfigVo); + documentOnlineConfigVo.setUsed(true); + } + } + } + return resultList; + } + + /** + * 构建在线帮助文档目录 + * + * @param root + * @param filePath + */ + private static DocumentOnlineDirectoryVo buildDirectory(DocumentOnlineDirectoryVo root, String filePath, String path, List configList) { + int directoryRootIndex = filePath.indexOf(DIRECTORY_ROOT); + String substr = filePath.substring(directoryRootIndex + DIRECTORY_ROOT.length() + 1); + List nameList = new ArrayList<>(); + DocumentOnlineDirectoryVo parent = root; + String[] split = substr.split("/"); + for (String name : split) { + boolean isFile = false; + if (name.endsWith(".md")) { + isFile = true; + name = name.substring(0, name.length() - 3); + } + Integer prefix = null; + int index = name.indexOf("."); + if (index != -1) { + String sort = name.substring(0, index); + if (StringUtils.isNumeric(sort)) { + prefix = Integer.valueOf(sort); + name = name.substring(index + 1); + } + } + nameList.add(name); + if (isFile) { + List upwardNameList = new LinkedList<>(nameList); + upwardNameList.remove(0); + DocumentOnlineDirectoryVo child = new DocumentOnlineDirectoryVo(prefix, name, true, upwardNameList, path, configList); + parent.addChild(child); + return child; + } + DocumentOnlineDirectoryVo child = null; + List children = parent.getChildren(); + for (DocumentOnlineDirectoryVo childVo : children) { + if (Objects.equals(childVo.getName(), name)) { + child = childVo; + } + } + if (child == null) { + List upwardNameList = new LinkedList<>(nameList); + upwardNameList.remove(0); + child = new DocumentOnlineDirectoryVo(prefix, name, false, upwardNameList); + parent.addChild(child); + } + parent = child; + } + return null; + } + + + public static String interceptsSpecifiedNumberOfCharacters(InputStream inputStream, int skip, int number) throws IOException { + Parser parser = Parser.builder().build(); + HtmlRenderer renderer = HtmlRenderer.builder().build(); + StringBuilder stringBuilder = new StringBuilder(); + try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + bufferedReader.skip(skip); + while (stringBuilder.length() < number) { + String lineContent = bufferedReader.readLine(); + if (lineContent == null) { + break; + } + if (StringUtils.isBlank(lineContent)) { + continue; + } + // 1.先把这行内容中HTML标签去掉,因为第2步中将markdown语法转换成HTML标签时,会把原有的HTML标签中的尖括号转成实体字符 + // 例如: + // 转换成
    <img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" /></a>
+ lineContent = HtmlUtil.removeHtml(lineContent); + if (StringUtils.isBlank(lineContent)) { + continue; + } + // 2.把这行内容中markdown语法转换成HTML标签 + Node document = parser.parse(lineContent); + String html = renderer.render(document); + // 3.再次把这行内容中HTML标签去掉 + lineContent = HtmlUtil.removeHtml(html); + if (StringUtils.isBlank(lineContent)) { + continue; + } + stringBuilder.append(" "); + if (lineContent.length() > number - stringBuilder.length()) { + stringBuilder.append(lineContent.substring(0, number - stringBuilder.length())); + } else { + stringBuilder.append(lineContent); + } + } + } + return stringBuilder.toString(); + } + + + public static List getAllFileList(DocumentOnlineDirectoryVo directory, String moduleGroup, String menu) { + return getAllFileListAndSort(directory, moduleGroup, menu); + } + + /** + * 通过递归,获取某个目录下的指定模块、指定菜单下的文件,并且排序 + * @param directory 目录 + * @param moduleGroup 指定模块 + * @param menu 指定菜单 + * @return 返回文件列表 + */ + private static List getAllFileListAndSort(DocumentOnlineDirectoryVo directory, String moduleGroup, String menu) { + List list = getAllFileListUseRecurve(directory, moduleGroup, menu); + list.sort((o1, o2) -> { + if (o1.getPrefix() != null && o2.getPrefix() != null) { + return Integer.compare(o1.getPrefix(), o2.getPrefix()); + } else if (o1.getPrefix() != null && o2.getPrefix() == null) { + return -1; + } else if (o1.getPrefix() == null && o2.getPrefix() != null) { + return 1; + } else { + return String.CASE_INSENSITIVE_ORDER.compare(o1.getFileName(), o2.getFileName()); + } + }); + return list; + } + + /** + * 通过递归,获取某个目录下的指定模块、指定菜单下的文件 + * @param directory 目录 + * @param moduleGroup 指定模块 + * @param menu 指定菜单 + * @return 返回文件列表 + */ + private static List getAllFileListUseRecurve(DocumentOnlineDirectoryVo directory, String moduleGroup, String menu) { + List list = new ArrayList<>(); + for (DocumentOnlineDirectoryVo child : directory.getChildren()) { + if (child.getIsFile()) { + JSONObject returnObj = new JSONObject(); + if (StringUtils.isBlank(moduleGroup) || child.belongToOwner(moduleGroup, menu, returnObj)) { + DocumentOnlineVo documentOnlineVo = new DocumentOnlineVo(); + documentOnlineVo.setUpwardNameList(child.getUpwardNameList()); + documentOnlineVo.setPrefix(child.getPrefix()); + documentOnlineVo.setFileName(child.getName()); + documentOnlineVo.setFilePath(child.getFilePath()); + String anchorPoint = returnObj.getString("anchorPoint"); + documentOnlineVo.setAnchorPoint(anchorPoint); + documentOnlineVo.setConfigList(child.getConfigList()); + list.add(documentOnlineVo); + } + } else { + list.addAll(getAllFileListUseRecurve(child, moduleGroup, menu)); + } + } + return list; + } + + public static List getAllFileList(DocumentOnlineDirectoryVo directory) { + return getAllFileListAndSort(directory, null, null); + } + + public static DocumentOnlineDirectoryVo getDocumentOnlineDirectoryByFilePath(String filePath) { + if (StringUtils.isBlank(filePath)) { + return null; + } + int directoryRootIndex = filePath.indexOf(DocumentOnlineManager.DIRECTORY_ROOT); + filePath = filePath.substring(directoryRootIndex); + DocumentOnlineDirectoryVo directory = DocumentOnlineManager.getDocumentOnlineDirectoryRoot(); + String[] directoryNameList = filePath.split("/"); + for (int i = 1; i < directoryNameList.length; i++) { + String directoryName = directoryNameList[i]; + // 标记是否找到对应的目录 + boolean flag = false; + for (DocumentOnlineDirectoryVo child : directory.getChildren()) { + String childName = child.getName(); + if (child.getIsFile()) { + childName += ".md"; + } + if (child.getPrefix() != null) { + childName = child.getPrefix() + "." + childName; + } + if (Objects.equals(childName, directoryName)) { + directory = child; + flag = true; + break; + } + } + if (!flag) { + return null; + } + } + return directory; + } + + public static String getResourceLocationPatternByFilePath(String filePath) { + String locationPattern = null; + if (filePath.startsWith("jar:file:")) { + locationPattern = filePath; + } else { + locationPattern = "classpath:" + filePath; + } + return locationPattern; + } + + public static String getWithinJarAbsoluteFilePathByURL(URL url) { + String path = url.toString(); + int separatorIndex = path.lastIndexOf("/neatlogic/resources/"); + return path.substring(separatorIndex + 1); + } + + /** + * 从war包外部加载在线文档 + * @return + */ + public static JSONObject LoadDocumentsOutsideWar() { + List mappingJsonLocationPatternList = new ArrayList<>(); + List mdFileLocationPatternList = new ArrayList<>(); + String classpathRoot = Config.DATA_HOME() + OUTSIDE_WAR_DOCUMENTS_ONLINE_JARS; + File documentOnlineHome = new File(classpathRoot); + if (documentOnlineHome.exists()) { + File[] listFiles = documentOnlineHome.listFiles(); + if (listFiles != null) { + for (File file : listFiles) { + mappingJsonLocationPatternList.add("jar:file:" + classpathRoot + "/" + file.getName() + "!/neatlogic/**/documentonline-mapping.json"); + mdFileLocationPatternList.add("jar:file:" + classpathRoot + "/" + file.getName() + "!/neatlogic/**/*.md"); + } + // 先查询出数据库中数据 + IDocumentOnlineCrossoverMapper documentOnlineCrossoverMapper = CrossoverServiceFactory.getApi(IDocumentOnlineCrossoverMapper.class); + List documentOnlineConfigList = documentOnlineCrossoverMapper.getAllDocumentOnlineConfigList(); + return DocumentOnlineManager.initializeIndex(documentOnlineConfigList, mappingJsonLocationPatternList, mdFileLocationPatternList); + } + } + return new JSONObject(); + } + + /** + * 从war包内部加载在线文档 + * @return + */ + public static JSONObject LoadDocumentsWithinWar() { + String classpathRoot = "neatlogic/resources/**/" + DIRECTORY_ROOT + "/"; + String mappingJsonLocationPattern = "classpath*:" + classpathRoot + "**/documentonline-mapping.json"; + String mdFileLocationPattern = "classpath*:" + classpathRoot + "**/*.md"; + // 先查询出数据库中数据 + IDocumentOnlineCrossoverMapper documentOnlineCrossoverMapper = CrossoverServiceFactory.getApi(IDocumentOnlineCrossoverMapper.class); + List documentOnlineConfigList = documentOnlineCrossoverMapper.getAllDocumentOnlineConfigList(); + return DocumentOnlineManager.initializeIndex(documentOnlineConfigList, List.of(mappingJsonLocationPattern), List.of(mdFileLocationPattern)); + } +} diff --git a/src/main/java/neatlogic/module/framework/startup/DocumentOnlineInitializeIndexHandler.java b/src/main/java/neatlogic/module/framework/startup/DocumentOnlineInitializeIndexHandler.java index edb8cba5bc9c0085bd4f9014953e9d904abf36f6..adc233c131f078d2490987812c50a7c131467177 100644 --- a/src/main/java/neatlogic/module/framework/startup/DocumentOnlineInitializeIndexHandler.java +++ b/src/main/java/neatlogic/module/framework/startup/DocumentOnlineInitializeIndexHandler.java @@ -12,71 +12,15 @@ package neatlogic.module.framework.startup; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; -import neatlogic.framework.documentonline.dto.DocumentOnlineConfigVo; -import neatlogic.framework.documentonline.dto.DocumentOnlineDirectoryVo; +import neatlogic.framework.documentonline.util.DocumentOnlineManager; import neatlogic.framework.startup.StartupBase; -import neatlogic.framework.util.$; -import neatlogic.module.framework.dao.mapper.doumentonline.DocumentOnlineMapper; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.StringField; -import org.apache.lucene.document.TextField; -import org.apache.lucene.index.IndexWriter; -import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.MMapDirectory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; +import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Component; -import org.wltea.analyzer.lucene.IKAnalyzer; - -import java.io.File; -import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; @Component public class DocumentOnlineInitializeIndexHandler extends StartupBase { - private final Logger logger = LoggerFactory.getLogger(DocumentOnlineInitializeIndexHandler.class); - - @javax.annotation.Resource - private DocumentOnlineMapper documentOnlineMapper; - /** - * 在线帮助文档根目录 - */ - public static final String DIRECTORY_ROOT = "documentonline"; - /** - * 在线帮助文档索引库位置 - */ - public static final String INDEX_DIRECTORY = System.getProperty("java.io.tmpdir") + File.separator + DIRECTORY_ROOT; - /** - * 在线帮助文档类路径根目录 - */ - private static String classpathRoot = "neatlogic/resources/**/" + DIRECTORY_ROOT + "/"; - /** - * 用于存储documentonline-mapping.json配置文件中的数据,不同模块jar包中都可能存在documentonline-mapping.json配置文件,可能有多个 - */ - private static List mappingConfigList = new ArrayList(); - - public static final DocumentOnlineDirectoryVo DOCUMENT_ONLINE_DIRECTORY_ROOT = new DocumentOnlineDirectoryVo("root", false); - @Override public String getName() { return "nmfs.documentonlineinitializeindexhandler.getname"; @@ -84,136 +28,10 @@ public class DocumentOnlineInitializeIndexHandler extends StartupBase { @Override public int executeForAllTenant() { - IndexWriter indexWriter = null; - try { - // 先查询出数据库中数据 - List documentOnlineConfigList = documentOnlineMapper.getAllDocumentOnlineConfigList(); - for (DocumentOnlineConfigVo documentOnlineConfigVo : documentOnlineConfigList) { - documentOnlineConfigVo.setSource("database"); - mappingConfigList.add(documentOnlineConfigVo); - } - // 在documentonline-mapping.json配置文件中已设置在线文档文件路径集合,用于防止同个在线文档重复配置 - ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); - Resource[] jsonResources = resolver.getResources("classpath*:" + classpathRoot + "**/documentonline-mapping.json"); - for (Resource resource : jsonResources) { - String path = resource.getURL().getPath(); - StringWriter writer = new StringWriter(); - IOUtils.copy(resource.getInputStream(), writer, StandardCharsets.UTF_8); - String content = writer.toString(); - if (StringUtils.isBlank(content)) { - continue; - } - try { - JSONArray mappingArray = JSON.parseArray(content); - for (int i = 0; i < mappingArray.size(); i++) { - JSONObject mappingObj = mappingArray.getJSONObject(i); - DocumentOnlineConfigVo documentOnlineConfigVo = mappingObj.toJavaObject(DocumentOnlineConfigVo.class); - if (mappingConfigList.contains(documentOnlineConfigVo)) { - continue; - } - String filePath = documentOnlineConfigVo.getFilePath(); - if (StringUtils.isBlank(filePath)) { - logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_a", path, i)); - continue; - } - String moduleGroup = documentOnlineConfigVo.getModuleGroup(); - if (StringUtils.isBlank(moduleGroup)) { - logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_b", path, i)); - continue; - } - documentOnlineConfigVo.setSource(path); - mappingConfigList.add(documentOnlineConfigVo); - // 初始化的时候以documentonline-mapping.json配置文件数据为主,根据删除主键数据库中数据 -// documentOnlineMapper.deleteDocumentOnlineConfig(documentOnlineConfigVo); - } - } catch (JSONException e) { - logger.error(e.getMessage(), e); - } - } - - // 存储所有在线文档的路径集合,用于判断不同jar包中有相同路径的文档 - List existingFilePathList = new ArrayList<>(); - // 1.创建分词器 - Analyzer analyzer = new IKAnalyzer(true); - System.out.println($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.system_out_println", INDEX_DIRECTORY)); - // 2.创建dir目录对象,目录对象表示索引库的位置 - Directory dir = MMapDirectory.open(Paths.get(INDEX_DIRECTORY)); - // 3.创建IndexWriterConfig对象,这个对象中指定切分词使用的分词器 - IndexWriterConfig config = new IndexWriterConfig(analyzer); - // 4.创建IndexWriter输出流对象,指定输出的位置和使用的config初始化对象 - indexWriter = new IndexWriter(dir, config); - // 5.删除之前索引数据,然后在重新生成 - indexWriter.deleteAll(); - // 1.采集数据 - Resource[] mdResources = resolver.getResources("classpath*:" + classpathRoot + "**/*.md"); - for (Resource resource : mdResources) { - String filename = resource.getFilename().substring(0, resource.getFilename().length() - 3); - int index = filename.indexOf("."); - if (index != -1) { - String sort = filename.substring(0, index); - if (StringUtils.isNumeric(sort)) { - filename = filename.substring(index + 1); - } - } - String path = resource.getURL().getPath(); -// int separatorIndex = path.indexOf("!/"); -// String filePath = path.substring(separatorIndex + 2); - int separatorIndex = path.lastIndexOf("/neatlogic/resources/"); - String filePath = path.substring(separatorIndex + 1); - if (existingFilePathList.contains(filePath)) { - logger.error($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.error", filePath)); - System.exit(1); - } - existingFilePathList.add(filePath); - List configList = new ArrayList<>(); - // 根据文件路径找到配置映射信息,分析出文件所属的模块组、菜单,定位的锚点 - List mappingConfigs = getMappingConfigByFilePath(filePath); - if (CollectionUtils.isNotEmpty(mappingConfigs)) { - for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigs) { - if (StringUtils.isNotBlank(documentOnlineConfigVo.getModuleGroup())) { - DocumentOnlineConfigVo configVo = new DocumentOnlineConfigVo(documentOnlineConfigVo); - configVo.setFilePath(filePath); - configList.add(configVo); - } - } - } - DocumentOnlineDirectoryVo directory = buildDirectory(DOCUMENT_ONLINE_DIRECTORY_ROOT, filePath, configList); - // 读取文件内容 - StringWriter writer = new StringWriter(); - IOUtils.copy(resource.getInputStream(), writer, StandardCharsets.UTF_8); - String str = writer.toString(); - - Document document = new Document(); - // 创建域对象并且放入到文档中 - // 文件路径字段不分词 - document.add(new StringField("filePath", filePath, Field.Store.YES)); - // 上层名称列表字段不分词 - document.add(new StringField("upwardNameList", JSON.toJSONString(directory.getUpwardNameList()), Field.Store.YES)); - // 文件名称字段分词 - document.add(new TextField("fileName", filename, Field.Store.YES)); - // 文件内容字段分词 - document.add(new TextField("content", str, Field.Store.YES)); - indexWriter.addDocument(document); - } - } catch (Exception e) { - logger.error(e.getMessage(), e); - } finally { - // 8.释放资源 - if (indexWriter != null) { - try { - indexWriter.close(); - } catch (IOException e) { - logger.error(e.getMessage(), e); - } - } - // 禁止添加子节点 - DOCUMENT_ONLINE_DIRECTORY_ROOT.noAllowedAddChild(); - } - - for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigList) { - if (!documentOnlineConfigVo.isUsed()) { - logger.warn($.t("nmfs.documentonlineinitializeindexhandler.executeforalltenant.warn_c") + JSON.toJSONString(documentOnlineConfigVo)); - } + // 如果war外部路径(路径通过config.properties配置文件中documentonline.home变量设置)中有jar包,优先从外部加载说明文档,如果没有,再尝试从war内部加载说明文档 + JSONObject resultObj = DocumentOnlineManager.LoadDocumentsOutsideWar(); + if (MapUtils.isEmpty(resultObj)) { + DocumentOnlineManager.LoadDocumentsWithinWar(); } return 1; } @@ -222,92 +40,4 @@ public class DocumentOnlineInitializeIndexHandler extends StartupBase { public int sort() { return 10; } - - /** - * 根据文件路径找到documentonline-mapping.json配置文件设置的映射信息 - * - * @param filePath 文件路径 - * @return 映射信息 - */ - private List getMappingConfigByFilePath(String filePath) { - List resultList = new ArrayList<>(); -// List filePathList = new ArrayList<>(); -// List directoryList = new ArrayList<>(); -// String[] split = filePath.split("/"); -// for (String directory : split) { -// directoryList.add(directory); -// if (directoryList.size() > 2) { -// filePathList.add(String.join("/", directoryList)); -// } -// } -// if (CollectionUtils.isNotEmpty(filePathList)) { -// List documentOnlineConfigList = documentOnlineMapper.getDocumentOnlineConfigListByFilePathList(filePathList); -// for (DocumentOnlineConfigVo documentOnlineConfigVo : documentOnlineConfigList) { -// documentOnlineConfigVo.setSource("database"); -// resultList.add(documentOnlineConfigVo); -// } -// } - for (DocumentOnlineConfigVo documentOnlineConfigVo : mappingConfigList) { - if (filePath.startsWith(documentOnlineConfigVo.getFilePath())) { - if (!resultList.contains(documentOnlineConfigVo)) { - resultList.add(documentOnlineConfigVo); - documentOnlineConfigVo.setUsed(true); - } - } - } - return resultList; - } - - /** - * 构建在线帮助文档目录 - * - * @param root - * @param filePath - */ - private DocumentOnlineDirectoryVo buildDirectory(DocumentOnlineDirectoryVo root, String filePath, List configList) { - int directoryRootIndex = filePath.indexOf(DIRECTORY_ROOT); - String path = filePath.substring(directoryRootIndex + DIRECTORY_ROOT.length() + 1); - List nameList = new ArrayList<>(); - DocumentOnlineDirectoryVo parent = root; - String[] split = path.split("/"); - for (String name : split) { - boolean isFile = false; - if (name.endsWith(".md")) { - isFile = true; - name = name.substring(0, name.length() - 3); - } - Integer prefix = null; - int index = name.indexOf("."); - if (index != -1) { - String sort = name.substring(0, index); - if (StringUtils.isNumeric(sort)) { - prefix = Integer.valueOf(sort); - name = name.substring(index + 1); - } - } - nameList.add(name); - if (isFile) { - List upwardNameList = new LinkedList<>(nameList); - upwardNameList.remove(0); - DocumentOnlineDirectoryVo child = new DocumentOnlineDirectoryVo(prefix, name, true, upwardNameList, filePath, configList); - parent.addChild(child); - return child; - } - DocumentOnlineDirectoryVo child = null; - List children = parent.getChildren(); - for (DocumentOnlineDirectoryVo childVo : children) { - if (Objects.equals(childVo.getName(), name)) { - child = childVo; - } - } - if (child == null) { - List upwardNameList = new LinkedList<>(nameList); - upwardNameList.remove(0); - child = new DocumentOnlineDirectoryVo(prefix, name, false, upwardNameList); - parent.addChild(child); - } - parent = child; - } - return null; - } }