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

@@ -1,11 +1,11 @@
export type ErrorType =
| 'validation'
| 'not_found'
| 'permission_denied'
| 'conflict'
| 'business'
| 'database'
| 'internal';
| "validation"
| "not_found"
| "permission_denied"
| "conflict"
| "business"
| "database"
| "internal";
export interface ErrorDetails {
[key: string]: unknown;
@@ -40,57 +40,135 @@ export abstract class ApplicationError extends Error {
}
export class ValidationError extends ApplicationError {
readonly type = 'validation' as const;
readonly type = "validation" as const;
readonly statusCode = 400;
constructor(message: string, details?: ErrorDetails) {
super(message, 'MSG_VALIDATION_ERROR', details);
super(message, "MSG_VALIDATION_ERROR", details);
}
}
export class NotFoundError extends ApplicationError {
readonly type = 'not_found' as const;
readonly type = "not_found" as const;
readonly statusCode = 404;
constructor(resource: string, id: string) {
super(`${resource} not found: ${id}`, 'MSG_NOT_FOUND', { resource, id });
super(`${resource} not found: ${id}`, "MSG_NOT_FOUND", { resource, id });
}
}
export class PermissionDeniedError extends ApplicationError {
readonly type = 'permission_denied' as const;
readonly type = "permission_denied" as const;
readonly statusCode = 403;
constructor(permission: string) {
super(`Permission denied: ${permission}`, 'MSG_PERMISSION_DENIED', { permission });
super(`Permission denied: ${permission}`, "MSG_PERMISSION_DENIED", {
permission,
});
}
}
export class ConflictError extends ApplicationError {
readonly type = 'conflict' as const;
readonly type = "conflict" as const;
readonly statusCode = 409;
constructor(message: string, details?: ErrorDetails) {
super(message, 'MSG_CONFLICT', details);
super(message, "MSG_CONFLICT", details);
}
}
export class BusinessError extends ApplicationError {
readonly type = 'business' as const;
readonly type = "business" as const;
readonly statusCode = 422;
constructor(message: string, details?: ErrorDetails) {
super(message, 'MSG_BUSINESS_ERROR', details);
super(message, "MSG_BUSINESS_ERROR", details);
}
}
export class DatabaseError extends ApplicationError {
readonly type = 'database' as const;
readonly type = "database" as const;
readonly statusCode = 500;
constructor(message: string, details?: ErrorDetails) {
super(message, 'MSG_DATABASE_ERROR', details);
super(message, "MSG_DATABASE_ERROR", details);
}
}
export class InternalError extends ApplicationError {
readonly type = 'internal' as const;
readonly type = "internal" as const;
readonly statusCode = 500;
constructor(message: string, details?: ErrorDetails) {
super(message, 'MSG_INTERNAL_ERROR', details);
super(message, "MSG_INTERNAL_ERROR", details);
}
}
/**
* ES 不可用且无降级路径503
* 仲裁依据02-architecture-design.md §6.2
*/
export class EsUnavailableError extends ApplicationError {
readonly type = "internal" as const;
readonly statusCode = 503;
constructor(message: string, details?: ErrorDetails) {
super(message, "MSG_ES_UNAVAILABLE", details);
}
}
/**
* Redis 不可用且无降级路径503
* 仲裁依据02-architecture-design.md §6.2
*/
export class RedisUnavailableError extends ApplicationError {
readonly type = "internal" as const;
readonly statusCode = 503;
constructor(message: string, details?: ErrorDetails) {
super(message, "MSG_REDIS_UNAVAILABLE", details);
}
}
/**
* Push Gateway 不可用503
* 仲裁依据02-architecture-design.md §6.2 + coord M4软失败仅必要时抛出
*/
export class PushGatewayUnavailableError extends ApplicationError {
readonly type = "internal" as const;
readonly statusCode = 503;
constructor(message: string, details?: ErrorDetails) {
super(message, "MSG_PUSH_GATEWAY_UNAVAILABLE", details);
}
}
/**
* 通知模板不存在404
* 仲裁依据02-architecture-design.md §6.2
*/
export class TemplateNotFoundError extends ApplicationError {
readonly type = "not_found" as const;
readonly statusCode = 404;
constructor(templateCode: string) {
super(
`Notification template not found: ${templateCode}`,
"MSG_TEMPLATE_NOT_FOUND",
{ templateCode },
);
}
}
/**
* 模板渲染失败变量缺失等422
* 仲裁依据02-architecture-design.md §6.2
*/
export class TemplateRenderError extends ApplicationError {
readonly type = "business" as const;
readonly statusCode = 422;
constructor(message: string, details?: ErrorDetails) {
super(message, "MSG_TEMPLATE_RENDER_ERROR", details);
}
}
/**
* 通知频率超限429
* 仲裁依据02-architecture-design.md §6.2NotificationPreference.frequency_limit 预留)
*/
export class RateLimitExceededError extends ApplicationError {
readonly type = "business" as const;
readonly statusCode = 429;
constructor(message: string, details?: ErrorDetails) {
super(message, "MSG_RATE_LIMIT_EXCEEDED", details);
}
}