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 org.springframework.util.StringUtils;
|
|
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 (UploadEditSceneConfigBuilder.SCENE.equals(param.getBizScene())
|
&& UploadEditSceneConfigBuilder.MODULE.equals(param.getBizModule())) {
|
return java.util.Collections.emptyList();
|
}
|
// 流程受控预览无需插件;查看权限仍由专属场景 Builder 和回源票据校验。
|
if (WorkflowFilePreviewConfigBuilder.SCENE.equals(param.getBizScene())
|
&& WorkflowFilePreviewConfigBuilder.MODULE.equals(param.getBizModule())) {
|
return java.util.Collections.emptyList();
|
}
|
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()
|
.orElse(null);
|
if (mapping == null) {
|
if (properties.getPlugins().getNonPluginScenes().contains(param.getBizScene())) {
|
return java.util.Collections.emptyList();
|
}
|
throw new DataException("未配置的 OnlyOffice 插件场景:" + param.getBizScene());
|
}
|
if (!mapping.getBizModule().equals(param.getBizModule())) {
|
throw new DataException("OnlyOffice 插件场景与业务模块不匹配");
|
}
|
if (ObjectUtils.isEmpty(param.getBizDataId()) && !isDmsQianpiye(param)) {
|
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;
|
}
|
|
public List<ResolvedPlugin> resolveUploadQianpiyeAnnotator() {
|
OnlyOfficeConfigParam param = new OnlyOfficeConfigParam();
|
param.setBizScene("dms.qianpiye");
|
param.setBizModule("dms");
|
ResolvedPlugin plugin = resolve(param).stream()
|
.filter(item -> "dms-qianpiye-annotator".equals(item.getPluginCode()))
|
.findFirst().orElseThrow(() -> new DataException("签批页标注插件未配置"));
|
return java.util.Collections.singletonList(new ResolvedPlugin(plugin.getPluginCode(), plugin.getGuid(),
|
plugin.getVersion(), plugin.getManifestUrl(), plugin.getApiBaseUrl(),
|
plugin.getBizScene(), plugin.getBizModule(), true, plugin.getDisabledPluginGuids()));
|
}
|
|
private boolean validPathComponent(String value) {
|
return !ObjectUtils.isEmpty(value) && PATH_COMPONENT_PATTERN.matcher(value).matches();
|
}
|
|
private boolean isDmsQianpiye(OnlyOfficeConfigParam param) {
|
return "dms.qianpiye".equals(param.getBizScene()) && "dms".equals(param.getBizModule());
|
}
|
|
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);
|
if (StringUtils.hasText(bizDataId)) {
|
context.put("bizDataId", bizDataId);
|
}
|
context.put("bizModule", bizModule);
|
context.put("bizScene", bizScene);
|
context.put("pluginVersion", version);
|
return context;
|
}
|
}
|
}
|