feat: add protocol interaction observability

This commit is contained in:
hectorzhao
2026-07-24 10:47:47 +08:00
parent 4f4fa5fcb7
commit 0bfeb0839e
17 changed files with 740 additions and 42 deletions
@@ -15,6 +15,7 @@ import {
SendChainService,
} from './send-chain.service';
import { GatewayDownstreamConnectionEventDto, SmsConfigService } from '../sms-config/sms-config.service';
import { ProtocolLogsService, type ProtocolLogInput } from '../protocol-logs/protocol-logs.service';
@ApiTags('gateway-events')
@Controller('gateway/events')
@@ -22,21 +23,22 @@ export class GatewayEventsController {
constructor(
private readonly sendChain: SendChainService,
private readonly smsConfig: SmsConfigService,
private readonly protocolLogs: ProtocolLogsService,
) {}
@Post('submit-result')
submitResult(@Body() body: GatewaySubmitResultDto) {
return this.sendChain.handleSubmitResult(body);
return this.trackGatewayEvent('submit_resp', body, () => this.sendChain.handleSubmitResult(body));
}
@Post('receipt')
receipt(@Body() body: GatewayReceiptEventDto) {
return this.sendChain.handleReceipt(body);
return this.trackGatewayEvent('deliver_receipt', body, () => this.sendChain.handleReceipt(body));
}
@Post('uplink')
uplink(@Body() body: GatewayUplinkEventDto) {
return this.sendChain.handleUplink(body);
return this.trackGatewayEvent('deliver_uplink', body, () => this.sendChain.handleUplink(body));
}
@Post('dead-letter')
@@ -46,12 +48,12 @@ export class GatewayEventsController {
@Post('inbound/authenticate')
authenticateInbound(@Body() body: GatewayInboundAuthDto) {
return this.sendChain.authenticateInboundApplication(body);
return this.trackGatewayEvent('connect', body, () => this.sendChain.authenticateInboundApplication(body), 'client_to_platform');
}
@Post('inbound/submit')
submitInbound(@Body() body: GatewayInboundSubmitDto) {
return this.sendChain.submitInboundMessage(body);
return this.trackGatewayEvent('submit', body, () => this.sendChain.submitInboundMessage(body), 'client_to_platform');
}
@Post('inbound/connection')
@@ -88,4 +90,46 @@ export class GatewayEventsController {
downstreamRecoveryStatus(@Body() body: GatewayDownstreamRecoveryStatusDto) {
return this.sendChain.recordGatewayDownstreamRecoveryStatus(body);
}
private async trackGatewayEvent<T>(
eventType: string,
body: object,
action: () => Promise<T> | T,
direction: ProtocolLogInput['direction'] = 'channel_to_platform',
) {
const startedAt = Date.now();
const value = body as Record<string, unknown>;
const common: Omit<ProtocolLogInput, 'status'> = {
protocol: 'cmpp',
direction,
eventType,
tenantId: value.tenantId as string,
applicationId: value.applicationId as string,
channelId: value.channelId as string,
account: (value.account ?? value.loginAccount) as string,
messageId: (value.messageId ?? value.platformMessageId) as string,
gatewayMessageId: (value.gatewayMessageId ?? value.msgId ?? value.upstreamMessageId) as string,
phone: (value.phoneNumber ?? value.srcTerminalId ?? value.destinationId) as string,
resultCode: (value.result ?? value.status ?? value.stat) as string,
detail: { sequenceId: value.sequenceId, connectionId: value.connectionId },
};
this.protocolLogs.record({ ...common, status: 'received', durationMs: 0 });
try {
const result = await action();
this.protocolLogs.record({
...common,
status: 'success',
durationMs: Date.now() - startedAt,
});
return result;
} catch (error) {
this.protocolLogs.record({
...common,
status: 'failed',
durationMs: Date.now() - startedAt,
detail: { error: error instanceof Error ? error.message : 'unknown error' },
});
throw error;
}
}
}