From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master

---
 jnpf-biz-common/jnpf-biz-common-onlyoffice/src/main/java/jnpf/bizcommon/onlyoffice/service/impl/OnlyOfficePluginResolver.java |  141 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 141 insertions(+), 0 deletions(-)

diff --git a/jnpf-biz-common/jnpf-biz-common-onlyoffice/src/main/java/jnpf/bizcommon/onlyoffice/service/impl/OnlyOfficePluginResolver.java b/jnpf-biz-common/jnpf-biz-common-onlyoffice/src/main/java/jnpf/bizcommon/onlyoffice/service/impl/OnlyOfficePluginResolver.java
new file mode 100644
index 0000000..a86bc28
--- /dev/null
+++ b/jnpf-biz-common/jnpf-biz-common-onlyoffice/src/main/java/jnpf/bizcommon/onlyoffice/service/impl/OnlyOfficePluginResolver.java
@@ -0,0 +1,141 @@
+package jnpf.bizcommon.onlyoffice.service.impl;
+
+import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeConfigParam;
+import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeProperties;
+import jnpf.exception.DataException;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedHashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+@Component
+@RequiredArgsConstructor
+public class OnlyOfficePluginResolver {
+
+    private static final Pattern PATH_COMPONENT_PATTERN = Pattern.compile("[A-Za-z0-9._-]+");
+
+    private final OnlyOfficeProperties properties;
+
+    public List<ResolvedPlugin> resolve(OnlyOfficeConfigParam param) {
+        if (ObjectUtils.isEmpty(param.getBizScene())) {
+            return java.util.Collections.emptyList();
+        }
+
+        OnlyOfficeProperties.PluginSceneMapping mapping = properties.getPlugins().getScenes().stream()
+                .filter(item -> param.getBizScene().equals(item.getBizScene()))
+                .findFirst()
+                .orElseThrow(() -> new DataException("鏈厤缃殑 OnlyOffice 鎻掍欢鍦烘櫙锛�" + param.getBizScene()));
+        if (!mapping.getBizModule().equals(param.getBizModule())) {
+            throw new DataException("OnlyOffice 鎻掍欢鍦烘櫙涓庝笟鍔℃ā鍧椾笉鍖归厤");
+        }
+        if (ObjectUtils.isEmpty(param.getBizDataId())) {
+            throw new DataException("鍚敤 OnlyOffice 鎻掍欢鏃� bizDataId 涓嶈兘涓虹┖");
+        }
+
+        if (ObjectUtils.isEmpty(mapping.getPluginCodes())) {
+            throw new DataException("OnlyOffice 鎻掍欢鍦烘櫙鏈厤缃彃浠�");
+        }
+        String autostartPluginCode = mapping.getAutostartPluginCode();
+        if (!ObjectUtils.isEmpty(autostartPluginCode)
+                && !mapping.getPluginCodes().contains(autostartPluginCode)) {
+            throw new DataException("OnlyOffice 鑷姩鍚姩鎻掍欢鏈寘鍚湪褰撳墠鍦烘櫙涓�");
+        }
+        List<ResolvedPlugin> resolved = new ArrayList<>();
+        Set<String> guids = new HashSet<>();
+        for (String pluginCode : mapping.getPluginCodes()) {
+            if (ObjectUtils.isEmpty(pluginCode) || !PATH_COMPONENT_PATTERN.matcher(pluginCode).matches()) {
+                throw new DataException("OnlyOffice 鎻掍欢缂栫爜閰嶇疆鏃犳晥");
+            }
+            OnlyOfficeProperties.PluginDefinition definition = properties.getPlugins().getDefinitions().get(pluginCode);
+            if (definition == null) {
+                throw new DataException("OnlyOffice 鎻掍欢瀹氫箟涓嶅瓨鍦細" + pluginCode);
+            }
+            String guid = definition.getGuid();
+            if (ObjectUtils.isEmpty(guid) || !guids.add(guid)) {
+                throw new DataException("OnlyOffice 鎻掍欢 GUID 閰嶇疆鏃犳晥鎴栭噸澶�");
+            }
+            String version = definition.getVersion();
+            String release = definition.getRelease();
+            if (!validPathComponent(version) || !validPathComponent(release)) {
+                throw new DataException("OnlyOffice 鎻掍欢鐗堟湰鎴� release 閰嶇疆鏃犳晥");
+            }
+            String baseUrl = normalizedUrl(definition.getPublicBaseUrl(), "public-base-url");
+            String apiBaseUrl = normalizedUrl(definition.getApiBaseUrl(), "api-base-url");
+            resolved.add(new ResolvedPlugin(pluginCode, guid, version,
+                    baseUrl + "/" + release + "/" + pluginCode + "/config.json",
+                    apiBaseUrl, mapping.getBizScene(), mapping.getBizModule(),
+                    pluginCode.equals(autostartPluginCode), mapping.getDisabledPluginGuids()));
+        }
+        return resolved;
+    }
+
+    private boolean validPathComponent(String value) {
+        return !ObjectUtils.isEmpty(value) && PATH_COMPONENT_PATTERN.matcher(value).matches();
+    }
+
+    private String normalizedUrl(String value, String name) {
+        if (ObjectUtils.isEmpty(value)) {
+            throw new DataException("OnlyOffice 鎻掍欢 " + name + " 鏈厤缃�");
+        }
+        String normalized = value.replaceAll("/+$", "");
+        try {
+            URI uri = new URI(normalized);
+            if (!("http".equals(uri.getScheme()) || "https".equals(uri.getScheme()))
+                    || ObjectUtils.isEmpty(uri.getHost()) || uri.getQuery() != null || uri.getFragment() != null) {
+                throw new DataException("OnlyOffice 鎻掍欢 " + name + " 閰嶇疆鏃犳晥");
+            }
+        } catch (URISyntaxException e) {
+            throw new DataException("OnlyOffice 鎻掍欢 " + name + " 閰嶇疆鏃犳晥");
+        }
+        return normalized;
+    }
+
+    @Getter
+    public static class ResolvedPlugin {
+
+        private final String manifestUrl;
+        private final String pluginCode;
+        private final String guid;
+        private final String version;
+        private final String apiBaseUrl;
+        private final String bizScene;
+        private final String bizModule;
+        private final boolean autostart;
+        private final List<String> disabledPluginGuids;
+
+        private ResolvedPlugin(String pluginCode, String guid, String version, String manifestUrl, String apiBaseUrl,
+                               String bizScene, String bizModule, boolean autostart,
+                               List<String> disabledPluginGuids) {
+            this.pluginCode = pluginCode;
+            this.guid = guid;
+            this.version = version;
+            this.manifestUrl = manifestUrl;
+            this.apiBaseUrl = apiBaseUrl;
+            this.bizScene = bizScene;
+            this.bizModule = bizModule;
+            this.autostart = autostart;
+            this.disabledPluginGuids = new ArrayList<>(disabledPluginGuids);
+        }
+
+        public Map<String, Object> runtimeContext(String documentId, String bizDataId) {
+            Map<String, Object> context = new LinkedHashMap<>();
+            context.put("apiBaseUrl", apiBaseUrl);
+            context.put("documentId", documentId);
+            context.put("bizDataId", bizDataId);
+            context.put("bizModule", bizModule);
+            context.put("bizScene", bizScene);
+            context.put("pluginVersion", version);
+            return context;
+        }
+    }
+}

--
Gitblit v1.8.0