feat(msg): 完整实现 msg 消息服务

包含 channels/preferences/templates/grpc/kafka/outbox/push/redis 等完整实现
This commit is contained in:
SpecialX
2026-07-10 19:09:52 +08:00
parent 21530dc7f6
commit 7b7abbb309
51 changed files with 5204 additions and 371 deletions

View File

@@ -0,0 +1,84 @@
import { Controller } from "@nestjs/common";
import { GrpcMethod } from "@nestjs/microservices";
import { PreferencesService } from "../preferences/preferences.service.js";
import { updatePreferencesSchema } from "../preferences/preferences.dto.js";
import type { NotificationPreference } from "../notifications/notifications.schema.js";
/**
* PreferencesGrpcController —— gRPC 入口NotificationPreferenceService 2 RPC
*
* 仲裁依据:
* - msg.proto §NotificationPreferenceServiceGetPreferences / UpdatePreferences
* - proto-loader 默认 keepCase=false字段名 camelCase
*/
interface GrpcPreference {
id: string;
userId: string;
type: string;
channels: string[];
frequencyLimit: number;
quietHoursStart: string;
quietHoursEnd: string;
quietHoursTimezone: string;
enabled: boolean;
createdAt: number;
updatedAt: number;
}
function opt(val: unknown): string | undefined {
if (typeof val === "string" && val.length > 0) return val;
return undefined;
}
function toGrpcPreference(p: NotificationPreference): GrpcPreference {
return {
id: p.id,
userId: p.userId,
type: p.type,
channels: p.channels,
frequencyLimit: p.frequencyLimit ?? 0,
quietHoursStart: p.quietHoursStart ?? "",
quietHoursEnd: p.quietHoursEnd ?? "",
quietHoursTimezone: p.quietHoursTimezone ?? "",
enabled: p.enabled,
createdAt: p.createdAt instanceof Date ? p.createdAt.getTime() : 0,
updatedAt: p.updatedAt instanceof Date ? p.updatedAt.getTime() : 0,
};
}
@Controller()
export class PreferencesGrpcController {
constructor(private readonly service: PreferencesService) {}
@GrpcMethod("NotificationPreferenceService", "GetPreferences")
async getPreferences(
data: Record<string, unknown>,
): Promise<{ preferences: GrpcPreference[] }> {
const prefs = await this.service.getByUserId(String(data.userId ?? ""));
return { preferences: prefs.map(toGrpcPreference) };
}
@GrpcMethod("NotificationPreferenceService", "UpdatePreferences")
async updatePreferences(
data: Record<string, unknown>,
): Promise<Record<string, never>> {
const rawPrefs = Array.isArray(data.preferences) ? data.preferences : [];
const dto = updatePreferencesSchema.parse({
userId: data.userId,
preferences: rawPrefs.map((p: Record<string, unknown>) => ({
type: p.type,
channels: Array.isArray(p.channels) ? p.channels : [],
frequencyLimit: p.frequencyLimit ? Number(p.frequencyLimit) : undefined,
quietHoursStart: opt(p.quietHoursStart),
quietHoursEnd: opt(p.quietHoursEnd),
quietHoursTimezone: opt(p.quietHoursTimezone),
enabled: p.enabled ?? true,
})),
});
await this.service.update(dto);
return {};
}
}