package jnpf.bizcommon.onlyoffice.entity; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * OnlyOffice 集成参数,绑定 {@code config/shared/jnpf-biz-common.yaml} 的 {@code onlyoffice.*}。 * *

三个 base url 之所以分开而不是共用一个「网关地址」,是因为它们的**视角不同**: * {@code dsPublicUrl} 是浏览器视角(容器名浏览器解析不了),另两个是 DS 容器视角。 * 宿主 java -jar 栈下后两者要覆盖成 host.docker.internal(DS 在容器、网关在宿主)。 */ @Data @Component @ConfigurationProperties(prefix = "onlyoffice") public class OnlyOfficeProperties { /** 浏览器加载编辑器 api.js 的地址,如 http://localhost:20000 */ private String dsPublicUrl; /** DS 回源拉原始文档的网关地址,如 http://cx-gateway:30000 */ private String fileFetchBaseUrl; /** DS 回调后端的网关地址,如 http://cx-gateway:30000 */ private String callbackBaseUrl; /** * 后端反向拉取 DS 产物的地址(后端视角),如 http://cx-onlyoffice。 * *

保存回调里 DS 给的下载 url 用的是**它自己视角**的主机名,后端不一定能直连; * 配了本项就只保留其 path+query 换到这个主机上。留空则原样使用 DS 给的 url。 */ private String dsInternalUrl; /** * 与 DS 容器 JWT_SECRET 同源。后端用它签发编辑器 config,并校验 DS 回调的 * Authorization 头。回调端点在网关白名单内(平台补丁 #9),此密钥是其唯一安全边界。 */ private String jwtSecret; /** 编辑锁心跳超时(分钟)。锁正常由 status 2/4 回调释放,此项是浏览器崩溃等异常场景的兜底 */ private Integer lockTimeoutMinutes = 30; /** * 是否允许在编辑器里下载文档(对应「文件 → 下载为」与顶栏右侧的下载图标)。 * *

默认关闭。⚠️ 它只收界面入口,不是安全边界——{@code /api/file/Image/**} 在网关白名单内、 * 只靠 securityKey 票据鉴权,知道 URL 的人照样能取到原文件。 */ private boolean allowDownload = false; /** 是否允许在编辑器里打印(对应「文件 → 打印」与顶栏右侧的打印图标)。默认关闭,同上注意事项 */ private boolean allowPrint = false; /** 编辑权限判定,见 {@link EditPermission} */ private EditPermission editPermission = new EditPermission(); /** ONLYOFFICE 插件注册表与场景绑定 */ private Plugins plugins = new Plugins(); @Data public static class Plugins { /** 插件编码到部署定义的映射 */ private Map definitions = new LinkedHashMap<>(); /** 允许启用插件的业务场景 */ private List scenes = new ArrayList<>(); } @Data public static class PluginDefinition { /** 与插件 config.json 完全一致的 GUID */ private String guid; /** 插件契约版本 */ private String version; /** 浏览器可访问的插件资源基础地址,不含 release */ private String publicBaseUrl; /** 不可变发布目录;正式版本为 0.1.0 */ private String release = "0.1.0"; /** 浏览器可访问的插件业务 API 基础地址 */ private String apiBaseUrl; } @Data public static class PluginSceneMapping { private String bizScene; private String bizModule; private List pluginCodes = new ArrayList<>(); /** 打开编辑器后自动启动的插件编码,必须属于 pluginCodes */ private String autostartPluginCode; /** 当前场景需要禁用的 Document Server 内置插件 GUID */ private List disabledPluginGuids = new ArrayList<>(); } /** * 「谁能编辑」的判定规则。 * *

粒度是**菜单/按钮级**,与平台在线表单的单条更新端点一致 * ({@code VisualdevModelDataController#update} 只做 {@code StpUtil.checkPermission})。 * 判定所需的权限码在登录时就已灌进 Sa-Token 会话,纯本地判断、零 Feign 调用。 * *

为什么不做「记录级」(这一条检验单该不该给这个人改):那属于业务规则, * 依赖业务表字段(班组、负责人、流程状态),而 biz-common 的准入规则禁止它含单一 * 业务模块的专属逻辑。需要记录级时应由业务模块自行判定后再打开编辑器。 */ @Data public static class EditPermission { /** * 未登记的 bizModule(或请求根本没带 bizModule)时的取向。 * *

{@code false}(默认)= 放行编辑,只打一条 warn 日志。这是**兼容默认值**: * 里程碑 3 的最简调用 {@code openOfficeDocument({ file })} 并不传 bizModule, * 一上来就严格会让所有既有调用方突然变只读。 * *

{@code true} = 一律只读。业务侧把该登记的都登记完之后应当打开它—— * 否则「配置漏登记」这种失误会表现为「悄悄放行编辑」,而不是被人察觉的报错。 */ private boolean strict = false; /** * bizModule → 在线表单 moduleId 的登记表。 * *

刻意用 List 而不是 Map:Spring Boot Binder 对 Map 是**合并**语义、对 List 是 * **整体替换**,用 Map 的话删掉一条登记根本不生效(audit 的 layer0 注册表踩过这个坑, * 见 {@code AuditFormRegistry})。 */ private List modules = new ArrayList<>(); } @Data public static class ModuleMapping { /** 调用方传来的业务模块标识,与 {@code OnlyOfficeConfigParam.bizModule} 对应 */ private String bizModule; /** 对应的在线表单功能主键(base_module.f_id),权限码就是拿它拼的 */ private String moduleId; /** * 是否要求 {@code moduleId::btn_edit} 按钮权限,默认 true。 * *

⚠️ 这条是个陷阱位:在线表单只有勾了「启用按钮权限」时,用户的权限表里才会有 * {@code moduleId::btn_edit}。没启用却把本项留成 true,会导致**所有人都被降级只读**。 * 该模块没启用按钮权限时填 false,此时只校验菜单级权限。 */ private boolean requireBtnEdit = true; } }