rwadurian/backend/services/planting-service/src/infrastructure/kafka/event-ack.controller.ts

86 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Controller, Logger } from '@nestjs/common';
import { MessagePattern, Payload, Ctx, KafkaContext } from '@nestjs/microservices';
import { OutboxRepository } from '../persistence/repositories/outbox.repository';
/**
* 事件确认消息结构
*/
interface EventAckMessage {
/** 原始事件的 aggregateId如 orderNo */
eventId: string;
/** 原始事件类型 */
eventType: string;
/** 消费服务名称 */
consumerService: string;
/** 处理结果 */
success: boolean;
/** 错误信息(如果失败) */
error?: string;
/** 确认时间 */
confirmedAt: string;
}
/**
* 事件确认 Kafka 控制器
*
* B方案核心组件监听消费方的确认事件
* 使用 @MessagePattern 装饰器来处理 Kafka 消息
*/
@Controller()
export class EventAckController {
private readonly logger = new Logger(EventAckController.name);
constructor(private readonly outboxRepository: OutboxRepository) {}
/**
* 处理事件确认消息
*
* 消费方reward-service, referral-service, authorization-service
* 成功处理事件后,会发送确认消息到此 topic
*/
@MessagePattern('planting.events.ack')
async handleEventAck(
@Payload() message: EventAckMessage,
@Ctx() context: KafkaContext,
): Promise<void> {
const partition = context.getPartition();
const offset = context.getMessage().offset;
this.logger.debug(
`[ACK] Received ack from ${message.consumerService} for event ${message.eventId} ` +
`[partition=${partition}, offset=${offset}]`,
);
try {
if (message.success) {
// 标记事件为已确认(使用 eventId + eventType 精确匹配)
const confirmed = await this.outboxRepository.markAsConfirmed(
message.eventId,
message.eventType,
);
if (confirmed) {
this.logger.log(
`[ACK] ✓ Event ${message.eventId} (${message.eventType}) confirmed by ${message.consumerService}`,
);
} else {
this.logger.warn(
`[ACK] Event ${message.eventId} (${message.eventType}) not found or already confirmed`,
);
}
} else {
// 消费方处理失败
this.logger.warn(
`[ACK] ✗ Event ${message.eventId} failed in ${message.consumerService}: ${message.error}`,
);
// 不改变状态,等待超时重发
}
} catch (error) {
this.logger.error(
`[ACK] Error processing ack for event ${message.eventId}:`,
error,
);
}
}
}