package jnpf.bizcommon.onlyoffice.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jnpf.base.ActionResult; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeCallbackParam; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeConfigParam; import jnpf.bizcommon.onlyoffice.service.OnlyOfficeService; import jnpf.exception.DataException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Collections; import java.util.Map; /** * OnlyOffice 文档编辑。经网关 /api/biz/onlyoffice/**(StripPrefix=2)落到本类。 * *

⚠️ 本类下的端点鉴权形态不统一,新增端点前先想清楚属于哪一类: *

*/ @Slf4j @Tag(name = "OnlyOffice 文档编辑", description = "编辑器配置签发与保存回调") @RestController @RequestMapping("/onlyoffice") @RequiredArgsConstructor public class OnlyOfficeController { private final OnlyOfficeService onlyOfficeService; /** * 签发编辑器配置。前端拿返回值直接喂 {@code new DocsAPI.DocEditor(id, config)}。 * *

入参里的 mode 只是期望值:格式不可编辑、他人正持锁、上次保存失败三种情况下, * 后端会降级为只读,以实际返回的 editorConfig.mode 为准。 */ @Operation(summary = "签发编辑器配置") @GetMapping("/config") public ActionResult> config(OnlyOfficeConfigParam param) { try { return ActionResult.success(onlyOfficeService.buildEditorConfig(param)); } catch (DataException e) { log.warn("签发 OnlyOffice 配置失败:{}", e.getMessage()); return ActionResult.fail(e.getMessage()); } } @Operation(summary = "查询文档保存状态") @GetMapping("/save-status") public ActionResult> saveStatus(@RequestParam String fileId) { try { return ActionResult.success(onlyOfficeService.getSaveStatus(fileId)); } catch (DataException e) { return ActionResult.fail(e.getMessage()); } } /** * DS 保存回调。**匿名可达**(路径在网关白名单内,平台补丁 #9), * 安全边界是 Service 内对回调 JWT 的验签。 * *

返回值格式是 OnlyOffice 定死的 {@code {"error":0}},不能套 ActionResult。 * 且**无论处理成功与否都返回 0**——返回非 0 会让 DS 认为保存失败并无限重试, * 而多数失败(会话不存在、验签不过)重试多少次都不会变好。真正的问题靠日志暴露。 */ @Operation(summary = "DS 保存回调") @PostMapping("/callback") public Map callback(@RequestBody OnlyOfficeCallbackParam param, @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization) { try { onlyOfficeService.handleCallback(param, authorization); } catch (DataException e) { log.warn("OnlyOffice 回调处理失败,key={} status={}:{}", param.getKey(), param.getStatus(), e.getMessage()); } catch (Exception e) { log.error("OnlyOffice 回调处理异常,key={} status={}", param.getKey(), param.getStatus(), e); } return Collections.singletonMap("error", 0); } }