feat(api): management photo list and account lookup
P1b Task 4: listAlbumPhotos (GET /albums/{albumId}/photos, tag
[photos]) + lookupUsersByAccount (GET /admin/users, tag [admin]).
- management list: all visibility states visible (unpublished/
published/hidden/deleted) for owner/admin; collaborator 403,
outsider/ghost album 404 (canView -> canManage ordering, same
anti-enumeration as members). Items = PhotoRecord + uploader
{userId, account, displayName} (flat-FK second query, redacted).
Keyset (uploadedAt,id) DESC via shared/cursor; visibility filter
validated against PhotoVisibility enum (400 otherwise), categoryId
must belong to the album (ghost/cross-album same-code 400).
pending_upload rows (uploadedAt NULL) excluded: the sort key is
both the contract-required field and the keyset predicate - NULL
can neither be shaped as PhotoRecord nor compared; in-flight
uploads are covered by the uploads history endpoint.
- account lookup (new src/admin module): prefix search over active
users only (credentialStatus=active, sourceStatus!=disabled,
!localLocked - same eligibility as SessionGuard/members.add),
account ASC, no cursor (small cap). $queryRaw LIKE prefix% with
explicit ESCAPE '!': !/%/_/\ escaped to literal matching (Prisma
startsWith escaping is not contractual); utf8mb4_0900_bin makes
the prefix match case-sensitive ('AL' misses 'alice'), documented
in contract + tests. Access policy per contract: any authenticated
staff (deviation from plan's admin/owner), unauthenticated 401.
- e2e: photos-admin (6) + users-lookup (6) cases; api e2e 136 -> 148.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import { APP_FILTER, APP_PIPE } from '@nestjs/core';
|
||||
import { LoggerModule } from 'nestjs-pino';
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import { AlbumsModule } from './albums/albums.module';
|
||||
import { AdminModule } from './admin/admin.module';
|
||||
import { AllExceptionsFilter } from './all-exceptions.filter';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { HealthController } from './health.controller';
|
||||
@@ -38,8 +39,11 @@ import type { ServerRequestWithId } from './shared/request-with-id';
|
||||
UploadsModule,
|
||||
// 公开(观众端)域(Task 22 getPublicAlbum;Task 23 公开照片读)。
|
||||
PublicModule,
|
||||
// 照片管理域(Task 24:可见性生命周期 PATCH/DELETE)。
|
||||
// 照片管理域(Task 24:可见性生命周期 PATCH/DELETE;P1b Task 4 增补
|
||||
// /albums/{albumId}/photos 管理列表)。
|
||||
PhotosModule,
|
||||
// 管理辅助域(P1b Task 4:/admin/users 账号前缀查询)。
|
||||
AdminModule,
|
||||
LoggerModule.forRoot({
|
||||
pinoHttp: {
|
||||
// 与 RequestIdMiddleware 共用同一解析函数:优先透传 x-request-id,
|
||||
|
||||
Reference in New Issue
Block a user