feat: harden CMPP delivery and platform workflows

This commit is contained in:
hectorzhao
2026-07-20 18:07:29 +08:00
parent 80fb5a8f53
commit f02c33cbb7
61 changed files with 1834 additions and 281 deletions
@@ -1,10 +1,12 @@
import { BadRequestException, Body, Controller, Get, Param, Post, Put, Query, UploadedFile, UseInterceptors } from '@nestjs/common';
import { BadRequestException, Body, Controller, Get, Param, Post, Put, Query, Res, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiTags } from '@nestjs/swagger';
import { RequireRecentAuthentication } from '../auth/require-recent-authentication.decorator';
import { CurrentSessionUserId } from '../auth/current-session-user.decorator';
import { CreateImportProfileDto, CreateReportBatchDto, ImportCommitDto, ReportMaterialsService } from './report-materials.service';
type UploadedWorkbook = { originalname: string; mimetype: string; size: number; buffer: Buffer };
type DownloadResponse = { setHeader(name: string, value: string): void; send(content: Buffer): void };
@ApiTags('report-materials')
@Controller('admin/report-materials')
@@ -16,6 +18,17 @@ export class ReportMaterialsController {
return this.service.listPending({ reportType, tenantId, applicationId });
}
@Get('templates/:reportType')
async downloadTemplate(@Param('reportType') reportType: 'signature' | 'drainage', @CurrentSessionUserId() operatorId: string | undefined, @Res() response: DownloadResponse) {
if (!['signature', 'drainage'].includes(reportType)) throw new BadRequestException('reportType must be signature or drainage');
this.sendWorkbook(response, await this.service.buildOfficialTemplate(reportType, operatorId));
}
@Get('pending/export')
async exportPending(@Query('reportType') reportType: 'signature' | 'drainage' | undefined, @Query('tenantId') tenantId: string | undefined, @Query('applicationId') applicationId: string | undefined, @CurrentSessionUserId() operatorId: string | undefined, @Res() response: DownloadResponse) {
this.sendWorkbook(response, await this.service.exportPending({ reportType, tenantId, applicationId }, operatorId));
}
@Get('import-profiles')
listImportProfiles(@Query('reportType') reportType?: 'signature' | 'drainage') {
return this.service.listImportProfiles(reportType);
@@ -28,8 +41,8 @@ export class ReportMaterialsController {
}
@Post('imports/analyze')
@UseInterceptors(FileInterceptor('file', { limits: { fileSize: 100 * 1024 * 1024, files: 1, fields: 12, parts: 13 } }))
analyzeImport(@UploadedFile() file: UploadedWorkbook, @Body() body: Record<string, string>) {
@UseInterceptors(FileInterceptor('file', { limits: { fileSize: 10 * 1024 * 1024, files: 1, fields: 12, parts: 13 } }))
analyzeImport(@UploadedFile() file: UploadedWorkbook, @Body() body: Record<string, string>, @CurrentSessionUserId() operatorId?: string) {
if (!file) throw new BadRequestException('请选择 XLSX 文件');
return this.service.analyzeImport(file, {
tenantId: body.tenantId,
@@ -39,13 +52,14 @@ export class ReportMaterialsController {
headerRowCount: Number(body.headerRowCount || 1),
dataStartRow: Number(body.dataStartRow || 2),
profileId: body.profileId || undefined,
operatorId,
});
}
@Put('imports/:id/commit')
@RequireRecentAuthentication()
commitImport(@Param('id') id: string, @Body() body: ImportCommitDto) {
return this.service.commitImport(id, body);
commitImport(@Param('id') id: string, @Body() body: ImportCommitDto, @CurrentSessionUserId() operatorId?: string) {
return this.service.commitImport(id, { ...body, operatorId });
}
@Get('batches')
@@ -55,7 +69,14 @@ export class ReportMaterialsController {
@Post('batches')
@RequireRecentAuthentication()
createBatch(@Body() body: CreateReportBatchDto) {
return this.service.createBatch(body);
createBatch(@Body() body: CreateReportBatchDto, @CurrentSessionUserId() operatorId?: string) {
return this.service.createBatch({ ...body, createdById: operatorId });
}
private sendWorkbook(response: DownloadResponse, exported: { fileName: string; content: Buffer }) {
response.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
response.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(exported.fileName)}`);
response.send(exported.content);
}
}