Files
baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/controller/EventOperationsController.java
T

57 lines
3.0 KiB
Java
Raw Normal View History

2026-09-29 18:47:37 +08:00
package com.baiyue.lastleg.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.common.core.outbox.OutboxOperations;
import com.baiyue.common.core.outbox.EventOperationsTokenGuard;
import com.baiyue.common.event.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
/** Internal-only operator recovery. It cannot mutate the original event or call a consumer directly. */
@RestController
@RequestMapping("/internal/lastleg/event-operations")
public class EventOperationsController {
private final EventOperationsTokenGuard guard;
private final OutboxOperations operations;
public EventOperationsController(JdbcTemplate jdbc, PlatformTransactionManager transactions,
ObjectMapper mapper, @Value("${baiyue.event-delivery.operations-token:}") String token) {
guard = new EventOperationsTokenGuard(token);
operations = new OutboxOperations(jdbc, transactions,
new OutboxOperations.Layout("ll_event_outbox", "id", "status", Set.of(EventTypes.OVERSEAS_CONTAINER_UNLOADED, EventTypes.OVERSEAS_DELIVERY_COMPLETED, EventTypes.OVERSEAS_INBOUND_ORDER_DETAINED_CHANGED, EventTypes.WALLET_COMMAND_REQUESTED, EventTypes.BUSINESS_FEE_SOURCE_CHANGED, EventTypes.COMPANY_VEHICLE_COST_SUBMITTED)),
2026-09-29 18:47:37 +08:00
json -> {
try {
EventEnvelope envelope = mapper.readValue(json, EventEnvelope.class).requireValid();
if (!EventPayloadHash.matches(EventPayloadCanonicalizer.canonicalize(envelope.payload()), envelope.payloadHash())) {
throw new IllegalArgumentException("Hash mismatch");
}
return new OutboxOperations.Identity(envelope.eventId(), envelope.eventType(), envelope.payloadHash());
} catch (Exception invalid) {
throw new BizException(ResultCode.CONFLICT, "原事件格式或摘要无效,禁止人工重试");
}
});
}
@GetMapping("/{entryId}")
public ApiResponse<OutboxOperations.Entry> inspect(
@RequestHeader(value = "X-Event-Operations-Token", required = false) String token,
@PathVariable String entryId) {
guard.require(token);
return ApiResponse.success(operations.inspect(entryId));
}
@PostMapping("/{entryId}/retry")
public ApiResponse<OutboxOperations.RetryResult> retry(
@RequestHeader(value = "X-Event-Operations-Token", required = false) String token,
@PathVariable String entryId, @RequestBody OutboxOperations.RetryRequest request) {
guard.require(token);
return ApiResponse.success(operations.retry(entryId, request));
}
}