From cefc4d51c9f8fe7d772c433e19d5b3e9b4372f8b Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 18:14:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M4-08=20=E2=80=94=20release=20&=20decis?= =?UTF-8?q?ion=20module=20(changelogs,=20ADR,=20checklist)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add DecisionRecord and ReleaseChecklist Prisma models - ReleaseController: CRUD for changelogs, decisions, checklists - AAPI: /admin-api/release/changelogs, /decisions, /checklists Co-Authored-By: Claude Opus 4.7 --- prisma/schema.prisma | 26 +++++++ src/app.module.ts | 2 + src/modules/release/release.controller.ts | 83 +++++++++++++++++++++++ src/modules/release/release.module.ts | 7 ++ 4 files changed, 118 insertions(+) create mode 100644 src/modules/release/release.controller.ts create mode 100644 src/modules/release/release.module.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1e8203c..1dc1099 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1442,3 +1442,29 @@ model AgentArtifact { @@index([taskId]) @@index([type]) } + +model DecisionRecord { + id String @id @default(cuid()) + title String @db.VarChar(255) + context String? @db.Text + decision String? @db.Text + rationale String? @db.Text + status String @default("proposed") @db.VarChar(32) + createdBy String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([status]) +} + +model ReleaseChecklist { + id String @id @default(cuid()) + version String @db.VarChar(50) + item String @db.VarChar(255) + checked Boolean @default(false) + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([version]) +} diff --git a/src/app.module.ts b/src/app.module.ts index f494791..9a2fe55 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -56,6 +56,7 @@ import { BackupModule } from './modules/backup/backup.module'; import { ReportingModule } from './modules/reporting/reporting.module'; import { ProjectCenterModule } from './modules/project-center/project-center.module'; import { HermesAgentModule } from './modules/hermes-agent/hermes-agent.module'; +import { ReleaseModule } from './modules/release/release.module'; import { JwtAuthGuard } from './common/guards/jwt-auth.guard'; import { RolesGuard } from './common/guards/roles.guard'; @@ -157,6 +158,7 @@ import appleConfig from './config/apple.config'; ReportingModule, ProjectCenterModule, HermesAgentModule, + ReleaseModule, ], providers: [ { provide: APP_GUARD, useClass: RateLimitGuard }, diff --git a/src/modules/release/release.controller.ts b/src/modules/release/release.controller.ts new file mode 100644 index 0000000..106285d --- /dev/null +++ b/src/modules/release/release.controller.ts @@ -0,0 +1,83 @@ +import { Controller, Get, Post, Patch, Delete, Param, Body, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation } 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-release') +@ApiBearerAuth() +@Controller('admin-api/release') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +export class ReleaseController { + constructor(private readonly prisma: PrismaService) {} + + // ═══ Changelogs ═══ + + @Get('changelogs') + @ApiOperation({ summary: '版本日志列表' }) + async listChangelogs() { + return this.prisma.appChangelog.findMany({ orderBy: { createdAt: 'desc' } }); + } + + @Post('changelogs') + @ApiOperation({ summary: '创建版本日志' }) + async createChangelog(@Body() dto: { version: string; title: string; content: string; platform?: string }) { + return this.prisma.appChangelog.create({ data: { ...dto, platform: dto.platform || 'ios' } }); + } + + @Patch('changelogs/:id') + @ApiOperation({ summary: '更新版本日志' }) + async updateChangelog(@Param('id') id: string, @Body() dto: Record) { + return this.prisma.appChangelog.update({ where: { id }, data: dto }); + } + + @Delete('changelogs/:id') + @ApiOperation({ summary: '删除版本日志' }) + async deleteChangelog(@Param('id') id: string) { + await this.prisma.appChangelog.delete({ where: { id } }); + return { ok: true }; + } + + // ═══ Decision Records (ADR) ═══ + + @Get('decisions') + @ApiOperation({ summary: '决策记录列表' }) + async listDecisions() { + return this.prisma.decisionRecord.findMany({ orderBy: { createdAt: 'desc' } }); + } + + @Post('decisions') + @ApiOperation({ summary: '创建决策记录' }) + async createDecision(@Body() dto: { title: string; context?: string; decision?: string; rationale?: string }) { + return this.prisma.decisionRecord.create({ data: dto }); + } + + @Patch('decisions/:id') + @ApiOperation({ summary: '更新决策记录' }) + async updateDecision(@Param('id') id: string, @Body() dto: Record) { + return this.prisma.decisionRecord.update({ where: { id }, data: dto }); + } + + // ═══ Release Checklist ═══ + + @Get('checklists/:version') + @ApiOperation({ summary: '版本回归清单' }) + async listChecklist(@Param('version') version: string) { + return this.prisma.releaseChecklist.findMany({ + where: { version }, + orderBy: { sortOrder: 'asc' }, + }); + } + + @Post('checklists') + @ApiOperation({ summary: '添加检查项' }) + async addChecklistItem(@Body() dto: { version: string; item: string; sortOrder?: number }) { + return this.prisma.releaseChecklist.create({ data: dto }); + } + + @Patch('checklists/:id') + @ApiOperation({ summary: '更新检查项(标记完成)' }) + async toggleChecklist(@Param('id') id: string, @Body() dto: { checked?: boolean; item?: string }) { + return this.prisma.releaseChecklist.update({ where: { id }, data: dto }); + } +} diff --git a/src/modules/release/release.module.ts b/src/modules/release/release.module.ts new file mode 100644 index 0000000..a38e2ff --- /dev/null +++ b/src/modules/release/release.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { ReleaseController } from './release.controller'; + +@Module({ + controllers: [ReleaseController], +}) +export class ReleaseModule {}