// services/api/src/albums/assets.controller.ts —— /albums/{albumId}/assets // 端点(P1b Task 3)。方法名与契约 operationId 一一对应(initAlbumAssetUpload/ // completeAlbumAssetUpload)。控制器级 SessionGuard:两端点全需认证。控制器只做 // 认证用户与路径参数透传;权限与 HTTP 映射由 AssetsService 抛 ApiException // (404 ALBUM_NOT_FOUND / 403 AUTH_FORBIDDEN / 400 VALIDATION_FAILED 或 // UPLOAD_VERIFY_FAILED)。 import { Body, Controller, HttpCode, HttpStatus, Param, Post, Req, UseGuards, } from '@nestjs/common'; import { AuthenticatedRequest, SessionGuard } from '../auth/session.guard'; import { userOf } from './albums.controller'; import { AssetInitRequestDto } from './dto'; import { AssetsService, type AssetCompleteResponse, type AssetInitResponse, } from './assets.service'; @Controller('albums/:albumId/assets') @UseGuards(SessionGuard) export class AssetsController { constructor(private readonly assetsService: AssetsService) {} /** 契约 201:会话行已建(每次 init 新行新键,无幂等键)。 */ @Post() initAlbumAssetUpload( @Param('albumId') albumId: string, @Body() dto: AssetInitRequestDto, @Req() req: AuthenticatedRequest, ): Promise { return this.assetsService.init(userOf(req), albumId, dto); } /** 完成核验:幂等(已生效行现签新 URL 返回,即签名刷新入口)。 */ @Post(':assetId/complete') @HttpCode(HttpStatus.OK) completeAlbumAssetUpload( @Param('albumId') albumId: string, @Param('assetId') assetId: string, @Req() req: AuthenticatedRequest, ): Promise { return this.assetsService.complete(userOf(req), albumId, assetId); } }