// services/api/src/admin/users-lookup.controller.ts —— /admin/users 端点 // (P1b Task 4 建立;P4a Task 1 契约统一为 listAdminUsers 双模式)。 // 方法名与契约 operationId 一一对应(listAdminUsers)。 // 控制器级 SessionGuard:访问策略=任何已认证工作人员(P1b 决策,见服务文件 // 头——管理列表模式的仅 admin 判定由 Task 6 补齐)。控制器只做查询参数规整, // 语义在服务层。 // 过渡期边界(Task 6 收口):契约 P4a 起 accountPrefix 可选(缺省进入管理 // 列表模式:query/status 过滤 + 游标分页 + 仅 admin);当前实现仅前缀模式, // 缺 accountPrefix 仍 400——管理列表模式随 Task 6 账号管理落地。 // limit 边界已对齐统一契约:缺省 20、上下界 1..100(P1b 时为 10/1..20; // 邀请搜索调用方显式传 limit=10,行为不变)。 import { Controller, Get, HttpStatus, Query, UseGuards, } from '@nestjs/common'; import { ErrorCodes } from '@photo/contracts'; import { ApiException } from '../api-exception'; import { SessionGuard } from '../auth/session.guard'; import { validationFailed } from '../albums/support'; import { UsersLookupService, type AdminUserListResponse } from './users-lookup.service'; /** 契约 listAdminUsers.limit:缺省 20,上下界 1..100(P4a 统一分页 bounds)。 */ export const DEFAULT_LIST_LIMIT = 20; export const MAX_LIST_LIMIT = 100; /** accountPrefix 查询参数(前缀模式):2..64(契约 bounds)。 */ export function parseAccountPrefix(raw: string | undefined): string { if (raw === undefined || raw === '') { // Task 6 起此分支消失(缺省进入管理列表模式);当前实现仅前缀模式。 throw validationFailed('accountPrefix 必填(2-64 字符)'); } if (raw.length < 2 || raw.length > 64) { throw validationFailed('accountPrefix 须为 2-64 字符'); } return raw; } /** limit 查询参数:缺省 20,须为 1..100 的十进制整数(契约 bounds)。 */ export function parseListLimit(raw: string | undefined): number { if (raw === undefined || raw === '') { return DEFAULT_LIST_LIMIT; } if (!/^\d+$/.test(raw)) { throw new ApiException( ErrorCodes.VALIDATION_FAILED, 'limit 必须是 1-100 的整数', HttpStatus.BAD_REQUEST, ); } const limit = Number.parseInt(raw, 10); if (limit < 1 || limit > MAX_LIST_LIMIT) { throw new ApiException( ErrorCodes.VALIDATION_FAILED, 'limit 必须是 1-100 的整数', HttpStatus.BAD_REQUEST, ); } return limit; } @Controller('admin/users') @UseGuards(SessionGuard) export class UsersLookupController { constructor(private readonly usersLookupService: UsersLookupService) {} /** 用户查询/管理列表(统一响应 AdminUserList;当前仅前缀模式,见文件头)。 */ @Get() listAdminUsers( @Query('accountPrefix') accountPrefix: string | undefined, @Query('limit') limit: string | undefined, ): Promise { return this.usersLookupService.lookup( parseAccountPrefix(accountPrefix), parseListLimit(limit), ); } }