rwadurian/backend/services/contribution-service/src/application/event-handlers/user-synced.handler.ts

134 lines
5.0 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 { Injectable, Logger } from '@nestjs/common';
import { CDCEvent } from '../../infrastructure/kafka/cdc-consumer.service';
import { SyncedDataRepository } from '../../infrastructure/persistence/repositories/synced-data.repository';
import { ContributionAccountRepository } from '../../infrastructure/persistence/repositories/contribution-account.repository';
import { ContributionAccountAggregate } from '../../domain/aggregates/contribution-account.aggregate';
import { UnitOfWork } from '../../infrastructure/persistence/unit-of-work/unit-of-work';
/**
* 用户 CDC 事件处理器
* 处理从身份服务同步过来的用户数据
*/
@Injectable()
export class UserSyncedHandler {
private readonly logger = new Logger(UserSyncedHandler.name);
constructor(
private readonly syncedDataRepository: SyncedDataRepository,
private readonly contributionAccountRepository: ContributionAccountRepository,
private readonly unitOfWork: UnitOfWork,
) {}
async handle(event: CDCEvent): Promise<void> {
const { op, before, after } = event.payload;
this.logger.log(`[CDC] User event received: op=${op}, seq=${event.sequenceNum}`);
this.logger.debug(`[CDC] User event payload: ${JSON.stringify(after || before)}`);
try {
switch (op) {
case 'c': // create
case 'r': // read (snapshot)
await this.handleCreate(after, event.sequenceNum);
break;
case 'u': // update
await this.handleUpdate(after, event.sequenceNum);
break;
case 'd': // delete
await this.handleDelete(before);
break;
default:
this.logger.warn(`[CDC] Unknown CDC operation: ${op}`);
}
} catch (error) {
this.logger.error(`[CDC] Failed to handle user CDC event, op=${op}, seq=${event.sequenceNum}`, error);
throw error;
}
}
private async handleCreate(data: any, sequenceNum: bigint): Promise<void> {
if (!data) {
this.logger.warn(`[CDC] User create: empty data received`);
return;
}
// 兼容不同的字段命名CDC 使用 snake_case
const userId = data.user_id ?? data.id;
const accountSequence = data.account_sequence ?? data.accountSequence;
const phone = data.phone_number ?? data.phone ?? null;
const status = data.status ?? 'ACTIVE';
this.logger.log(`[CDC] User create: userId=${userId}, accountSequence=${accountSequence}, phone=${phone}, status=${status}`);
if (!userId || !accountSequence) {
this.logger.warn(`[CDC] Invalid user data: missing user_id or account_sequence`, { data });
return;
}
await this.unitOfWork.executeInTransaction(async () => {
// 保存同步的用户数据
this.logger.log(`[CDC] Upserting synced user: ${accountSequence}`);
await this.syncedDataRepository.upsertSyncedUser({
originalUserId: BigInt(userId),
accountSequence,
phone,
status,
sourceSequenceNum: sequenceNum,
});
// 为用户创建算力账户(如果不存在)
const existingAccount = await this.contributionAccountRepository.findByAccountSequence(accountSequence);
if (!existingAccount) {
const newAccount = ContributionAccountAggregate.create(accountSequence);
await this.contributionAccountRepository.save(newAccount);
this.logger.log(`[CDC] Created contribution account for user: ${accountSequence}`);
} else {
this.logger.debug(`[CDC] Contribution account already exists for user: ${accountSequence}`);
}
});
this.logger.log(`[CDC] User synced successfully: ${accountSequence}`);
}
private async handleUpdate(data: any, sequenceNum: bigint): Promise<void> {
if (!data) {
this.logger.warn(`[CDC] User update: empty data received`);
return;
}
// 兼容不同的字段命名CDC 使用 snake_case
const userId = data.user_id ?? data.id;
const accountSequence = data.account_sequence ?? data.accountSequence;
const phone = data.phone_number ?? data.phone ?? null;
const status = data.status ?? 'ACTIVE';
this.logger.log(`[CDC] User update: userId=${userId}, accountSequence=${accountSequence}, status=${status}`);
if (!userId || !accountSequence) {
this.logger.warn(`[CDC] Invalid user update data: missing user_id or account_sequence`, { data });
return;
}
await this.syncedDataRepository.upsertSyncedUser({
originalUserId: BigInt(userId),
accountSequence,
phone,
status,
sourceSequenceNum: sequenceNum,
});
this.logger.log(`[CDC] User updated successfully: ${accountSequence}`);
}
private async handleDelete(data: any): Promise<void> {
if (!data) {
this.logger.warn(`[CDC] User delete: empty data received`);
return;
}
const accountSequence = data.account_sequence ?? data.accountSequence;
// 用户删除一般不处理,保留历史数据
this.logger.log(`[CDC] User delete event received: ${accountSequence} (not processed, keeping history)`);
}
}