2026-09-18 08:54:44 +08:00
|
|
|
|
// services/api/src/photos/album-photos.controller.ts —— /albums/{albumId}/photos
|
2026-09-20 16:36:34 +08:00
|
|
|
|
// 管理列表端点(P1b Task 4)+ 批量隐藏(P4a Task 2)。方法名与契约
|
|
|
|
|
|
// operationId 一一对应(listAlbumPhotos/hidePhotosBatch,tag [photos]——路由
|
|
|
|
|
|
// 挂在相册子路径下,域仍属照片管理)。控制器级 SessionGuard:端点需认证。
|
|
|
|
|
|
// 控制器只做认证用户透传 + 查询参数规整(visibility 本域 dto / categoryId
|
|
|
|
|
|
// 空串归一 / cursor+limit 走 shared/pagination,同契约 bounds);权限与 HTTP
|
|
|
|
|
|
// 映射由 PhotosService 抛 ApiException(404 ALBUM_NOT_FOUND / 403
|
|
|
|
|
|
// AUTH_FORBIDDEN / 400 VALIDATION_FAILED)。注意 DTO 值导入(非 import type):
|
|
|
|
|
|
// @Body 参数类型经 emitDecoratorMetadata 进入设计期元数据。
|
2026-09-18 08:54:44 +08:00
|
|
|
|
import {
|
2026-09-20 16:36:34 +08:00
|
|
|
|
Body,
|
2026-09-18 08:54:44 +08:00
|
|
|
|
Controller,
|
|
|
|
|
|
Get,
|
2026-09-20 16:36:34 +08:00
|
|
|
|
HttpCode,
|
|
|
|
|
|
HttpStatus,
|
2026-09-18 08:54:44 +08:00
|
|
|
|
Param,
|
2026-09-20 16:36:34 +08:00
|
|
|
|
Post,
|
2026-09-18 08:54:44 +08:00
|
|
|
|
Query,
|
|
|
|
|
|
Req,
|
|
|
|
|
|
UseGuards,
|
|
|
|
|
|
} from '@nestjs/common';
|
|
|
|
|
|
import { userOf } from '../albums/albums.controller';
|
|
|
|
|
|
import { AuthenticatedRequest, SessionGuard } from '../auth/session.guard';
|
|
|
|
|
|
import { parseCursor, parseLimit } from '../shared/pagination';
|
2026-09-20 16:36:34 +08:00
|
|
|
|
import { PhotoHideBatchDto, parseVisibilityFilter } from './dto';
|
|
|
|
|
|
import {
|
|
|
|
|
|
PhotosService,
|
|
|
|
|
|
type AdminPhotoListResponse,
|
|
|
|
|
|
type BatchHideResult,
|
|
|
|
|
|
} from './photos.service';
|
2026-09-18 08:54:44 +08:00
|
|
|
|
|
|
|
|
|
|
@Controller('albums/:albumId/photos')
|
|
|
|
|
|
@UseGuards(SessionGuard)
|
|
|
|
|
|
export class AlbumPhotosController {
|
|
|
|
|
|
constructor(private readonly photosService: PhotosService) {}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 管理照片列表:全部可见性状态可见(区别于公开列表仅 published),
|
|
|
|
|
|
* (uploadedAt,id) 稳定倒序键集游标;条目内嵌 uploader 脱敏信息。
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Get()
|
|
|
|
|
|
listAlbumPhotos(
|
|
|
|
|
|
@Param('albumId') albumId: string,
|
|
|
|
|
|
@Query('visibility') visibility: string | undefined,
|
|
|
|
|
|
@Query('categoryId') categoryId: string | undefined,
|
|
|
|
|
|
@Query('cursor') cursor: string | undefined,
|
|
|
|
|
|
@Query('limit') limit: string | undefined,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<AdminPhotoListResponse> {
|
|
|
|
|
|
return this.photosService.listAlbumPhotos(userOf(req), albumId, {
|
|
|
|
|
|
visibility: parseVisibilityFilter(visibility),
|
|
|
|
|
|
// 空串视为缺省(同公开列表 category 规整惯例)。
|
|
|
|
|
|
categoryId: categoryId === '' ? undefined : categoryId,
|
|
|
|
|
|
cursor: parseCursor(cursor),
|
|
|
|
|
|
limit: parseLimit(limit),
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-09-20 16:36:34 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量隐藏(A16):逐项结果(部分成功);单批 1..200(DTO 层 400)。
|
|
|
|
|
|
* 契约 200(非 201:批量结果不是新建资源的表述)。
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Post('hide-batch')
|
|
|
|
|
|
@HttpCode(HttpStatus.OK)
|
|
|
|
|
|
hidePhotosBatch(
|
|
|
|
|
|
@Param('albumId') albumId: string,
|
|
|
|
|
|
@Body() dto: PhotoHideBatchDto,
|
|
|
|
|
|
@Req() req: AuthenticatedRequest,
|
|
|
|
|
|
): Promise<BatchHideResult> {
|
|
|
|
|
|
return this.photosService.hideBatch(userOf(req), albumId, dto.photoIds);
|
|
|
|
|
|
}
|
2026-09-18 08:54:44 +08:00
|
|
|
|
}
|