package com.baiyue.eventbridge.controller; import com.baiyue.common.core.api.ApiResponse; import com.baiyue.common.event.EventEnvelope; import com.baiyue.eventbridge.dto.BridgeStatus; import com.baiyue.eventbridge.dto.EventReceiveResult; import com.baiyue.eventbridge.dto.EventReplayRequest; import com.baiyue.eventbridge.service.EventInboxService; import com.baiyue.eventbridge.service.EventReplayService; import com.baiyue.eventbridge.security.InternalServiceTokenGuard; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Value; 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.RestController; import org.springframework.web.bind.annotation.PathVariable; /** * 事件桥内部接口。生产环境必须在网关或基础设施层增加 mTLS 与来源白名单。 */ @Tag(name = "事件桥内部接口", description = "跨区域事件的幂等接收与能力状态") @RestController @RequestMapping("/internal/event-bridge") public class EventBridgeController { @Resource private EventInboxService eventInboxService; @Resource private EventReplayService eventReplayService; @Resource private InternalServiceTokenGuard internalServiceTokenGuard; @Value("${baiyue.region:OVERSEAS}") private String region; @Operation(summary = "幂等接收事件", description = "按当前部署区域校验目标区域、事件信封与 payloadHash,幂等持久化后进入租约保护的重试投递队列") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "事件已接收或判定为重复"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "内部服务令牌无效"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "503", description = "内部服务令牌未配置,接收接口关闭") }) @PostMapping("/events") public ApiResponse receive( @Parameter(description = "内部服务共享令牌") @RequestHeader(value = "X-Internal-Service-Token", required = false) String token, @RequestBody EventEnvelope envelope) { internalServiceTokenGuard.verify(token); return ApiResponse.success(eventInboxService.receive(envelope)); } @Operation(summary = "人工补投死信事件") @PostMapping("/events/{eventId}/replay") public ApiResponse replay( @RequestHeader(value = "X-Internal-Service-Token", required = false) String token, @RequestHeader("X-Operator-Id") String operatorId, @PathVariable String eventId, @RequestBody EventReplayRequest request) { internalServiceTokenGuard.verify(token); eventReplayService.replayDead(eventId, operatorId, request.reason()); return ApiResponse.success(null); } @Operation(summary = "查询事件桥能力状态") @ApiResponses(@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功")) @GetMapping("/status") public ApiResponse status() { return ApiResponse.success(new BridgeStatus("运行中", region, true, false)); } }