package jnpf.elnController; import io.swagger.v3.oas.annotations.tags.Tag; import jnpf.base.ActionResult; import jnpf.database.util.TenantDataSourceUtil; import jnpf.elnEntity.template.ElnTemplateForms; import jnpf.elnEntity.template.ElnTemplateVos; import jnpf.elnEntity.formula.ElnFormulaForms; import jnpf.elnEntity.formula.ElnFormulaVos; import jnpf.elnService.formula.ElnTemplateFormulaBindingService; import jnpf.elnService.cross.ElnCrossTemplateScalarReferenceService; import jnpf.elnService.template.ElnTemplateBindingService; import jnpf.elnService.template.ElnTemplateAnnotationContextService; import jnpf.elnService.template.ElnTemplateCodeAssistService; import jnpf.elnService.template.ElnTemplateOperationException; import jnpf.elnService.template.TemplateAnnotationAuthorization; import jnpf.elnService.template.TemplateAnnotationAuthorizer; import jnpf.elnService.template.TemplateAnnotationTicketService; import jnpf.exception.DataException; import jnpf.util.NoDataSourceBind; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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; @Tag(name = "ELN模板标注") @RestController @RequestMapping("/onlyoffice/templateAnnotation") @NoDataSourceBind public class ElnTemplateAnnotationController { private static final String TICKET_PREFIX = "Ticket "; private final AnnotationAuthorizer authorizer; private final ElnTemplateBindingService bindingService; private final ElnTemplateFormulaBindingService formulaBindingService; private final ElnCrossTemplateScalarReferenceService crossTemplateScalarReferenceService; private final ElnTemplateAnnotationContextService annotationContextService; private final ElnTemplateCodeAssistService codeAssistService; @Autowired public ElnTemplateAnnotationController(TemplateAnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService, ElnTemplateFormulaBindingService formulaBindingService, ElnCrossTemplateScalarReferenceService crossTemplateScalarReferenceService, ElnTemplateAnnotationContextService annotationContextService, ElnTemplateCodeAssistService codeAssistService) { this(authorizer::authorize, bindingService, formulaBindingService, crossTemplateScalarReferenceService, annotationContextService, codeAssistService); } ElnTemplateAnnotationController(AnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService) { this(authorizer, bindingService, null, null, bindingService::context); } ElnTemplateAnnotationController(AnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService, ElnTemplateFormulaBindingService formulaBindingService) { this(authorizer, bindingService, formulaBindingService, null, bindingService::context); } ElnTemplateAnnotationController(AnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService, ElnTemplateFormulaBindingService formulaBindingService, ElnCrossTemplateScalarReferenceService crossTemplateScalarReferenceService) { this(authorizer, bindingService, formulaBindingService, crossTemplateScalarReferenceService, auth -> crossTemplateScalarReferenceService == null ? bindingService.context(auth) : crossTemplateScalarReferenceService.decorate(auth, bindingService.context(auth))); } ElnTemplateAnnotationController(AnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService, ElnTemplateFormulaBindingService formulaBindingService, ElnCrossTemplateScalarReferenceService crossTemplateScalarReferenceService, ElnTemplateAnnotationContextService annotationContextService) { this(authorizer, bindingService, formulaBindingService, crossTemplateScalarReferenceService, annotationContextService, new ElnTemplateCodeAssistService()); } ElnTemplateAnnotationController(AnnotationAuthorizer authorizer, ElnTemplateBindingService bindingService, ElnTemplateFormulaBindingService formulaBindingService, ElnCrossTemplateScalarReferenceService crossTemplateScalarReferenceService, ElnTemplateAnnotationContextService annotationContextService, ElnTemplateCodeAssistService codeAssistService) { this.authorizer = authorizer; this.bindingService = bindingService; this.formulaBindingService = formulaBindingService; this.crossTemplateScalarReferenceService = crossTemplateScalarReferenceService; this.annotationContextService = annotationContextService; this.codeAssistService = codeAssistService; } @GetMapping("/context") public ResponseEntity> context( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization) { return execute(authorization, annotationContextService::context); } @PostMapping("/codeSuggestions") public ResponseEntity> suggestCode( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnTemplateForms.CodeSuggestion form) { return execute(authorization, auth -> { ElnTemplateVos.CodeSuggestion result = new ElnTemplateVos.CodeSuggestion(); result.setCode(codeAssistService.suggest(requireBody(form).getText())); return result; }); } @GetMapping("/crossTemplateTextCandidates") public ResponseEntity>> crossTemplateScalarCandidates( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestParam("sourceTemplateId") String sourceTemplateId) { return execute(authorization, auth -> requireCrossTemplateService().candidates(auth, sourceTemplateId)); } @GetMapping("/crossTemplateSourceTemplates") public ResponseEntity>> crossTemplateSourceTemplates( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization) { return execute(authorization, auth -> requireCrossTemplateService().sourceTemplates(auth)); } @PostMapping("/crossTemplateTextBindings/reserve") public ResponseEntity> reserveCrossTemplateScalarBinding( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnTemplateForms.ReserveCrossTemplateScalarBinding form) { return execute(authorization, auth -> requireCrossTemplateService().reserve(auth, requireBody(form))); } @PostMapping("/privateFields/reserve") public ResponseEntity> reservePrivateField( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnTemplateForms.PrivateField form) { return execute(authorization, auth -> bindingService.createPrivateFieldAndReserve(auth, requireBody(form))); } @PostMapping("/bindings/reserve") public ResponseEntity> reserveBinding( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnTemplateForms.ReserveBinding form) { return execute(authorization, auth -> bindingService.reservePrimary(auth, requireBody(form))); } @PostMapping("/bindings/{id}/references/reserve") public ResponseEntity> reserveReference( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.Operation form) { return execute(authorization, auth -> bindingService.reserveReference(auth, id, requireBody(form).getOperationCode())); } @PostMapping("/bindings/{id}/checkboxGroup") public ResponseEntity> changeCheckboxGroup( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.ChangeCheckboxGroup form) { return execute(authorization, auth -> bindingService.changeCheckboxGroup(auth, id, requireBody(form))); } @PostMapping("/checkboxGroups/{groupCode}") public ResponseEntity> changeCheckboxGroupRule( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("groupCode") String groupCode, @RequestBody(required = false) ElnTemplateForms.ChangeCheckboxGroupRule form) { return execute(authorization, auth -> bindingService.changeCheckboxGroupRule( auth, groupCode, requireBody(form))); } @PostMapping("/bindings/{id}/activate") public ResponseEntity> activate( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.Operation form) { return executeVoid(authorization, auth -> bindingService.activate(auth, id, requireBody(form).getOperationCode())); } @PostMapping("/bindings/{id}/cancel") public ResponseEntity> cancel( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.Cancel form) { return executeVoid(authorization, auth -> { ElnTemplateForms.Cancel body = requireBody(form); bindingService.cancel(auth, id, body.getOperationCode(), body.getReason()); }); } @GetMapping("/bindings/{id}/repairContext") public ResponseEntity> repairContext( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id) { return execute(authorization, auth -> bindingService.repairContext(auth, id)); } @PostMapping("/bindings/{id}/repairConfirm") public ResponseEntity> confirmRepair( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.RepairConfirm form) { return executeVoid(authorization, auth -> bindingService.confirmRepair(auth, id, requireBody(form))); } @PostMapping("/bindings/{id}/versionUpgradeCheck") public ResponseEntity> checkUpgrade( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.UpgradeCheck form) { return execute(authorization, auth -> bindingService.checkUpgrade( auth, id, requireBody(form).getTargetFieldVersionId())); } @PostMapping("/bindings/{id}/versionUpgradeConfirm") public ResponseEntity> confirmUpgrade( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.UpgradeConfirm form) { return executeVoid(authorization, auth -> bindingService.confirmUpgrade(auth, id, requireBody(form))); } @PostMapping("/bindings/{id}/removeCheck") public ResponseEntity> checkRemove( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id) { return execute(authorization, auth -> bindingService.checkRemove(auth, id)); } @PostMapping("/bindings/{id}/removeConfirm") public ResponseEntity> confirmRemove( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.RemoveConfirm form) { return executeVoid(authorization, auth -> bindingService.confirmRemove(auth, id, requireBody(form))); } @PostMapping("/bindings/{id}/promoteCheck") public ResponseEntity> checkPromote( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id) { return execute(authorization, auth -> bindingService.checkPromoteReference(auth, id)); } @PostMapping("/bindings/{id}/promoteConfirm") public ResponseEntity> confirmPromote( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnTemplateForms.PromoteConfirm form) { return executeVoid(authorization, auth -> bindingService.confirmPromoteReference(auth, id, requireBody(form))); } @PostMapping("/versionCopy/confirm") public ResponseEntity> confirmVersionCopy( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnTemplateForms.VersionCopyConfirm form) { return executeVoid(authorization, auth -> bindingService.confirmVersionCopy(auth, requireBody(form))); } @GetMapping("/formulas") public ResponseEntity>> formulas( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization) { return execute(authorization, auth -> formulaBindingService.list(auth)); } @GetMapping("/formulaContext") public ResponseEntity> formulaContext( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization) { return execute(authorization, auth -> formulaBindingService.context(auth)); } @PostMapping("/formulaBindings/reserve") public ResponseEntity> reserveFormulaBinding( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnFormulaForms.TemplateBinding form) { return execute(authorization, auth -> formulaBindingService.reserve(auth, requireBody(form))); } @PostMapping("/formulaBindings/{id}/activate") public ResponseEntity> activateFormulaBinding( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnFormulaForms.ActivateFormulaBinding form) { return executeVoid(authorization, auth -> formulaBindingService.activate(auth, id, requireBody(form))); } @PostMapping("/formulaBindings/{id}/cancel") public ResponseEntity> cancelFormulaBinding( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnFormulaForms.CancelFormulaBinding form) { return executeVoid(authorization, auth -> formulaBindingService.cancel(auth, id, requireBody(form))); } @PostMapping("/formulaBindings/{id}/upgradeCheck") public ResponseEntity> checkFormulaUpgrade( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnFormulaForms.TemplateBinding form) { return execute(authorization, auth -> formulaBindingService.checkUpgrade(auth, id, requireBody(form))); } @PostMapping("/formulaBindings/{id}/upgradeConfirm") public ResponseEntity> confirmFormulaUpgrade( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnFormulaForms.UpgradeMappingConfirm form) { return executeVoid(authorization, auth -> formulaBindingService.confirmUpgrade(auth, id, requireBody(form))); } @PostMapping("/formulaBindings/{id}/removeCheck") public ResponseEntity> checkFormulaRemove( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id) { return execute(authorization, auth -> formulaBindingService.checkRemove(auth, id)); } @PostMapping("/formulaBindings/{id}/removeConfirm") public ResponseEntity> confirmFormulaRemove( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @PathVariable("id") String id, @RequestBody(required = false) ElnFormulaForms.RemoveMappingConfirm form) { return executeVoid(authorization, auth -> formulaBindingService.confirmRemove(auth, id, requireBody(form))); } @PostMapping("/formulaBindings/versionCopy/confirm") public ResponseEntity> confirmFormulaVersionCopy( @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization, @RequestBody(required = false) ElnFormulaForms.FormulaVersionCopyConfirm form) { return executeVoid(authorization, auth -> formulaBindingService.confirmVersionCopy(auth, requireBody(form))); } private ResponseEntity> executeVoid(String authorization, AuthorizedVoidOperation operation) { return execute(authorization, auth -> { operation.execute(auth); return null; }); } private ResponseEntity> execute(String authorization, AuthorizedOperation operation) { try { TemplateAnnotationAuthorization context = authorizer.authorize(extractTicket(authorization)); T result = operation.execute(context); return success(result); } catch (InvalidTicketHeaderException | TemplateAnnotationTicketService.InvalidAnnotationTicketException e) { return failure(401, e.getMessage()); } catch (TemplateAnnotationAuthorizer.AnnotationAuthorizationException e) { return failure(e.getStatus(), e.getMessage()); } catch (ElnTemplateOperationException e) { return failure(e.getStatus(), e.getMessage()); } catch (DataException e) { return failure(422, e.getMessage()); } finally { TenantDataSourceUtil.clearLocalTenantInfo(); } } protected ResponseEntity> success(T data) { return ResponseEntity.ok(ActionResult.success(data)); } private static ResponseEntity> failure(int status, String message) { return ResponseEntity.status(HttpStatus.valueOf(status)).body(ActionResult.fail(status, message)); } private static String extractTicket(String authorization) { if (authorization == null || !authorization.startsWith(TICKET_PREFIX)) { throw new InvalidTicketHeaderException(); } String ticket = authorization.substring(TICKET_PREFIX.length()); if (ticket.isEmpty() || !ticket.equals(ticket.trim())) { throw new InvalidTicketHeaderException(); } return ticket; } private static T requireBody(T body) { if (body == null) { throw ElnTemplateOperationException.validation("请求体不能为空"); } return body; } private ElnCrossTemplateScalarReferenceService requireCrossTemplateService() { if (crossTemplateScalarReferenceService == null) throw new DataException("跨模板字段引用服务不可用"); return crossTemplateScalarReferenceService; } interface AnnotationAuthorizer { TemplateAnnotationAuthorization authorize(String ticket); } private interface AuthorizedOperation { T execute(TemplateAnnotationAuthorization authorization); } private interface AuthorizedVoidOperation { void execute(TemplateAnnotationAuthorization authorization); } private static final class InvalidTicketHeaderException extends RuntimeException { private static final long serialVersionUID = 1L; private InvalidTicketHeaderException() { super("标注 Ticket 无效"); } } }