From b3176b8ead6c4f16394e0f2b61a007bdaa9f2483 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 18:23:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M4-10=20=E2=80=94=20admin=20notificatio?= =?UTF-8?q?n=20deepening=20(cost=20alerts,=20import=20failures,=20key=20ex?= =?UTF-8?q?pirations)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add scope field to Notification model (user/admin) - AdminNotificationsController: list, send, mark read - Generate endpoints: cost-alert, import-failure, key-expiring Co-Authored-By: Claude Opus 4.7 --- prisma/schema.prisma | 1 + src/app.module.ts | 2 + .../admin-notifications.controller.ts | 88 +++++++++++++++++++ .../admin-notifications.module.ts | 7 ++ 4 files changed, 98 insertions(+) create mode 100644 src/modules/admin-notifications/admin-notifications.controller.ts create mode 100644 src/modules/admin-notifications/admin-notifications.module.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0577cb4..11e739e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -586,6 +586,7 @@ model Notification { title String @db.VarChar(255) content String? @db.Text data Json? + scope String @default("user") @db.VarChar(16) readAt DateTime? createdAt DateTime @default(now()) diff --git a/src/app.module.ts b/src/app.module.ts index b187fff..c999267 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -58,6 +58,7 @@ import { ProjectCenterModule } from './modules/project-center/project-center.mod import { HermesAgentModule } from './modules/hermes-agent/hermes-agent.module'; import { ReleaseModule } from './modules/release/release.module'; import { ComplianceModule } from './modules/compliance/compliance.module'; +import { AdminNotificationsModule } from './modules/admin-notifications/admin-notifications.module'; import { JwtAuthGuard } from './common/guards/jwt-auth.guard'; import { RolesGuard } from './common/guards/roles.guard'; @@ -161,6 +162,7 @@ import appleConfig from './config/apple.config'; HermesAgentModule, ReleaseModule, ComplianceModule, + AdminNotificationsModule, ], providers: [ { provide: APP_GUARD, useClass: RateLimitGuard }, diff --git a/src/modules/admin-notifications/admin-notifications.controller.ts b/src/modules/admin-notifications/admin-notifications.controller.ts new file mode 100644 index 0000000..090a500 --- /dev/null +++ b/src/modules/admin-notifications/admin-notifications.controller.ts @@ -0,0 +1,88 @@ +import { Controller, Get, Post, Patch, Param, Body, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation, ApiBody } from '@nestjs/swagger'; +import { PrismaService } from '../../infrastructure/database/prisma.service'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; + +@ApiTags('admin-notifications-v2') +@ApiBearerAuth() +@Controller('admin-api/admin-notifications') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +export class AdminNotificationsController { + constructor(private readonly prisma: PrismaService) {} + + @Get() + @ApiOperation({ summary: 'Admin 通知列表' }) + async list() { + return this.prisma.notification.findMany({ + where: { scope: 'admin' }, + orderBy: { createdAt: 'desc' }, + take: 100, + }); + } + + @Post('send') + @ApiOperation({ summary: '发送 Admin 通知' }) + @ApiBody({ schema: { type: 'object', required: ['title', 'type'], properties: { + title: { type: 'string' }, type: { type: 'string' }, content: { type: 'string' }, data: { type: 'object' }, + } } }) + async send(@Body() dto: { title: string; type: string; content?: string; data?: any }) { + return this.prisma.notification.create({ + data: { + userId: 'admin', + type: dto.type, + title: dto.title, + content: dto.content || '', + data: dto.data || null, + scope: 'admin', + }, + }); + } + + @Post(':id/read') + @ApiOperation({ summary: '标记 Admin 通知已读' }) + async markRead(@Param('id') id: string) { + return this.prisma.notification.update({ where: { id }, data: { readAt: new Date() } }); + } + + // ═══ Predefined admin notification generators ═══ + + @Post('generate/cost-alert') + @ApiOperation({ summary: '生成成本预警通知' }) + async generateCostAlert(@Body() dto: { cost: number; threshold: number; period: string }) { + return this.prisma.notification.create({ + data: { + userId: 'admin', type: 'cost_alert', scope: 'admin', + title: `成本预警:${dto.period}成本 ¥${dto.cost} 超过阈值 ¥${dto.threshold}`, + content: `${dto.period} AI 调用成本已达到 ¥${dto.cost},超过预设阈值 ¥${dto.threshold}。`, + data: dto, + }, + }); + } + + @Post('generate/import-failure') + @ApiOperation({ summary: '生成导入失败通知' }) + async generateImportFailure(@Body() dto: { importId: string; failedCount: number; totalCount: number }) { + return this.prisma.notification.create({ + data: { + userId: 'admin', type: 'import_failure', scope: 'admin', + title: `导入任务大量失败:${dto.failedCount}/${dto.totalCount}`, + content: `导入任务 ${dto.importId} 中 ${dto.failedCount}/${dto.totalCount} 项失败。`, + data: dto, + }, + }); + } + + @Post('generate/key-expiring') + @ApiOperation({ summary: '生成 API Key 到期通知' }) + async generateKeyExpiring(@Body() dto: { keyName: string; expiresInDays: number }) { + return this.prisma.notification.create({ + data: { + userId: 'admin', type: 'key_expiring', scope: 'admin', + title: `API Key「${dto.keyName}」即将在 ${dto.expiresInDays} 天后到期`, + content: `请尽快续期或更换密钥。`, + data: dto, + }, + }); + } +} diff --git a/src/modules/admin-notifications/admin-notifications.module.ts b/src/modules/admin-notifications/admin-notifications.module.ts new file mode 100644 index 0000000..930c42c --- /dev/null +++ b/src/modules/admin-notifications/admin-notifications.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { AdminNotificationsController } from './admin-notifications.controller'; + +@Module({ + controllers: [AdminNotificationsController], +}) +export class AdminNotificationsModule {}