diff --git a/.nvmrc b/.nvmrc index 209e3ef..2bd5a0a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 +22 diff --git a/docs/plans/2026-09-17-P1a-后端核心链路.md b/docs/plans/2026-09-17-P1a-后端核心链路.md index 0d0d550..9ce0cc4 100644 --- a/docs/plans/2026-09-17-P1a-后端核心链路.md +++ b/docs/plans/2026-09-17-P1a-后端核心链路.md @@ -253,8 +253,9 @@ Expected: `src/generated/openapi.d.ts` 生成;redocly 0 错误。 **Files:** - Modify: `packages/contracts/openapi.yaml` +- Modify: `packages/contracts/tsconfig.json`(exclude src/__tests__,Task 4 审查遗留)、`packages/contracts/package.json`(exports 补 `./package.json`)、根 `package.json` engines(node >=22.12)与 `.nvmrc`(22)——ESM 策略 (a):contracts ESM-only + jest 源码映射(Node 20 已 EOL,2026-09 决策) -- [ ] **Step 1: 补全 P1a 路径与 schema**(字段以技术实施方案 §4/§10 为准): +- [ ] **Step 1: 补全 P1a 路径与 schema**(字段以技术实施方案 §4/§10 为准)。附加要求(Task 4 质量审查遗留):列表响应一律经 allOf 消费 CursorPageMeta;消费后**删除 redocly.yaml 的 no-unused-components 豁免**;auth 三条路径补 400 ErrorResponse;保留 hasMore/nextCursor 成对填充约定: - `POST /albums`(title 多语言必填、时间、时区默认 Asia/Shanghai、地点、开关默认值 upload/public=true、download=true、original=false、defaultLocale) - `GET /albums`、`GET /albums/{id}`、`PATCH /albums/{id}`(仅创建者/管理员可写;开关字段独立) - `POST/GET/DELETE /albums/{id}/members`;`POST /albums/{id}/categories` + `PATCH/DELETE /categories/{id}` @@ -285,7 +286,7 @@ export interface OutboxEvent { } ``` -- [ ] **Step 2: 单测**:类型测试——合法 `OutboxEvent{type:'photo.published'}` 编译通过,非法 type 用 `@ts-expect-error` 验证编译期拒绝。 +- [ ] **Step 2: 单测**:类型测试——合法 `OutboxEvent{type:'photo.published'}` 编译通过,非法 type 用 `@ts-expect-error` 验证编译期拒绝;**枚举同步测试**(Task 4 审查遗留):断言 `ErrorCode` 与 `components['schemas']['Error']['code']` 的 enum 双向可赋值。 ```bash pnpm --filter @photo/contracts test diff --git a/package.json b/package.json index 2a76b12..c0c6099 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "photo-live", "private": true, "packageManager": "pnpm@10.7.1", - "engines": { "node": ">=20" }, + "engines": { "node": ">=22.12" }, "scripts": { "lint": "turbo run lint", "typecheck": "turbo run typecheck", diff --git a/packages/contracts/openapi.yaml b/packages/contracts/openapi.yaml index da65712..dfd4187 100644 --- a/packages/contracts/openapi.yaml +++ b/packages/contracts/openapi.yaml @@ -14,6 +14,11 @@ info: `src/errors.ts` 的 `ErrorCodes` 一一对应),客户端按 code 映射多语言文案, `message` 仅供诊断。 - 列表接口统一游标分页,元信息见 `components.schemas.CursorPageMeta`。 + - 业务端点一律 Bearer 认证;`/public/**` 匿名访问(`security: []`)。 + - 状态码通用映射:401 → AUTH_TOKEN_EXPIRED、500 → INTERNAL_ERROR; + 其余状态码对应的 code 在各响应的 description 中标注。 + - 多语言内容按「请求 locale → 相册 defaultLocale」回退解析为单语言对象, + 响应携带 `resolvedLocale` 供界面判断实际语言。 servers: - url: /v1 @@ -21,8 +26,22 @@ servers: tags: - name: auth description: 密码登录会话(签发、滑动续期、退出) + - name: albums + description: 相册创建、列表、详情与配置(工作人员) + - name: members + description: 相册成员管理(创建者或管理员) + - name: categories + description: 相册分类管理(创建者或管理员) + - name: uploads + description: 直传任务初始化、授权续签、完成核验与会话记录 + - name: photos + description: 照片可见性调整与软删除(工作人员) + - name: public + description: 观众匿名只读(相册信息与公开照片) paths: + # ─────────────────────────── auth ─────────────────────────── + /auth/password: post: operationId: loginWithPassword @@ -43,10 +62,24 @@ paths: application/json: schema: $ref: '#/components/schemas/LoginResponse' + '400': + description: 参数校验失败(VALIDATION_FAILED,经全局校验管道)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - $ref: '#/components/responses/ErrorResponse' + description: 账号或密码错误(AUTH_INVALID_CREDENTIALS)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': - $ref: '#/components/responses/ErrorResponse' + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /auth/refresh: post: @@ -63,10 +96,24 @@ paths: application/json: schema: $ref: '#/components/schemas/RefreshResponse' + '400': + description: 参数校验失败(VALIDATION_FAILED,经全局校验管道)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - $ref: '#/components/responses/ErrorResponse' + description: 令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': - $ref: '#/components/responses/ErrorResponse' + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /auth/logout: post: @@ -79,10 +126,915 @@ paths: responses: '204': description: 已退出,无响应体。 + '400': + description: 请求体不可解析(VALIDATION_FAILED,经全局校验管道)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '401': - $ref: '#/components/responses/ErrorResponse' + description: 令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': - $ref: '#/components/responses/ErrorResponse' + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── albums ─────────────────────────── + + /albums: + post: + operationId: createAlbum + tags: [albums] + summary: 创建相册 + description: | + 创建相册并写入多语言内容;创建者自动成为 owner 成员。 + 初始默认值:uploadEnabled/publicEnabled/downloadEnabled=true、 + originalDownloadEnabled=false、status=active、watermarkVersion=0。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AlbumCreateRequest' + responses: + '201': + description: 相册已创建(新相册 categories 为空数组)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Album' + '400': + description: 参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + get: + operationId: listAlbums + tags: [albums] + summary: 相册列表 + description: 当前用户可见(参与)的相册列表;管理员可见全部。按创建时间倒序。 + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/CursorQuery' + - name: limit + in: query + description: 每页条数。 + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + responses: + '200': + description: 相册列表页。 + content: + application/json: + schema: + $ref: '#/components/schemas/AlbumList' + '400': + description: 查询参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /albums/{albumId}: + parameters: + - $ref: '#/components/parameters/AlbumId' + get: + operationId: getAlbum + tags: [albums] + summary: 相册详情 + description: 相册完整配置、多语言内容与分类列表。 + security: + - bearerAuth: [] + responses: + '200': + description: 相册详情。 + content: + application/json: + schema: + $ref: '#/components/schemas/Album' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户非成员不可见(ALBUM_NOT_FOUND,避免枚举)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + patch: + operationId: updateAlbum + tags: [albums] + summary: 更新相册配置 + description: | + 部分更新:仅提交的字段被修改,各开关互相独立。 + translations 为全量替换(须包含 defaultLocale 对应条目)。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AlbumUpdateRequest' + responses: + '200': + description: 更新后的相册详情。 + content: + application/json: + schema: + $ref: '#/components/schemas/Album' + '400': + description: 参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 仅创建者或管理员可修改(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── members ─────────────────────────── + + /albums/{albumId}/members: + parameters: + - $ref: '#/components/parameters/AlbumId' + post: + operationId: addAlbumMember + tags: [members] + summary: 添加相册成员 + description: 将已开通的同事添加为协作者;仅创建者或管理员可操作。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MemberAddRequest' + responses: + '201': + description: 成员已添加。 + content: + application/json: + schema: + $ref: '#/components/schemas/Member' + '400': + description: 参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 仅创建者或管理员可管理成员(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: 该用户已是相册成员(MEMBER_CONFLICT)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + get: + operationId: listAlbumMembers + tags: [members] + summary: 相册成员列表 + description: 相册全部成员(含 owner)。 + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/CursorQuery' + - name: limit + in: query + description: 每页条数。 + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + responses: + '200': + description: 成员列表页。 + content: + application/json: + schema: + $ref: '#/components/schemas/MemberList' + '400': + description: 查询参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /albums/{albumId}/members/{userId}: + parameters: + - $ref: '#/components/parameters/AlbumId' + - $ref: '#/components/parameters/UserId' + delete: + operationId: removeAlbumMember + tags: [members] + summary: 移除相册成员 + description: | + 移除协作者;仅创建者或管理员可操作。不能移除创建者(403)。 + 目标成员不存在时幂等返回 204。 + security: + - bearerAuth: [] + responses: + '204': + description: 已移除,无响应体。 + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无管理权限,或试图移除创建者(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── categories ─────────────────────────── + + /albums/{albumId}/categories: + parameters: + - $ref: '#/components/parameters/AlbumId' + post: + operationId: createCategory + tags: [categories] + summary: 创建分类 + description: 在相册下创建分类;仅创建者或管理员可操作。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CategoryCreateRequest' + responses: + '201': + description: 分类已创建。 + content: + application/json: + schema: + $ref: '#/components/schemas/Category' + '400': + description: 参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 仅创建者或管理员可管理分类(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /categories/{categoryId}: + parameters: + - $ref: '#/components/parameters/CategoryId' + patch: + operationId: updateCategory + tags: [categories] + summary: 更新分类 + description: 部分更新;translations 为全量替换。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CategoryUpdateRequest' + responses: + '200': + description: 更新后的分类。 + content: + application/json: + schema: + $ref: '#/components/schemas/Category' + '400': + description: 参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无所属相册的管理权限(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 分类不存在,或所属相册不存在/不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + delete: + operationId: deleteCategory + tags: [categories] + summary: 删除分类 + description: 删除分类(分类下仍有照片时是否允许删除由服务端策略决定,400 拒绝)。 + security: + - bearerAuth: [] + responses: + '204': + description: 已删除,无响应体。 + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无所属相册的管理权限(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 分类不存在,或所属相册不存在/不可见(ALBUM_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── uploads ─────────────────────────── + + /uploads: + post: + operationId: initUpload + tags: [uploads] + summary: 初始化上传任务 + description: | + 幂等初始化:预创建上传会话与照片 ID,返回 staging 对象的临时直传凭证。 + 相同 clientTaskId 且参数一致时返回原会话,不重复创建照片; + 参数不一致时返回 409(UPLOAD_SESSION_CONFLICT)。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UploadInitRequest' + responses: + '201': + description: 上传会话已创建(幂等重复调用返回原会话)。 + content: + application/json: + schema: + $ref: '#/components/schemas/UploadInitResponse' + '400': + description: 参数校验失败(VALIDATION_FAILED):文件超过大小限额或类型不接受。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无该相册的上传权限(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: | + 相册已关闭上传(ALBUM_UPLOAD_DISABLED),或同 clientTaskId 已存在 + 且参数不一致(UPLOAD_SESSION_CONFLICT)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + get: + operationId: listUploads + tags: [uploads] + summary: 上传会话记录 + description: 当前用户的上传会话历史(新在前),用于任务队列恢复与状态展示。 + security: + - bearerAuth: [] + parameters: + - $ref: '#/components/parameters/CursorQuery' + - name: limit + in: query + description: 每页条数。 + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + responses: + '200': + description: 会话记录列表页。 + content: + application/json: + schema: + $ref: '#/components/schemas/UploadSessionList' + '400': + description: 查询参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /uploads/{uploadId}/renew: + parameters: + - $ref: '#/components/parameters/UploadId' + post: + operationId: renewUploadCredential + tags: [uploads] + summary: 续签上传凭证 + description: 为仍处于 pending 状态的会话重新签发直传凭证(结构同初始化响应)。 + security: + - bearerAuth: [] + responses: + '200': + description: 新凭证已签发(credential 为新值,其余字段回显原会话)。 + content: + application/json: + schema: + $ref: '#/components/schemas/UploadInitResponse' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: | + 相册已关闭上传(ALBUM_UPLOAD_DISABLED),或会话状态不允许续签 + (已完成/取消/过期,UPLOAD_SESSION_CONFLICT)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /uploads/{uploadId}/complete: + parameters: + - $ref: '#/components/parameters/UploadId' + post: + operationId: completeUpload + tags: [uploads] + summary: 完成核验 + description: | + 核验 staging 对象(存在性、字节数与可用完整性信息)后进入处理流水线。 + 幂等:核验通过的会话重复调用返回相同结果。 + security: + - bearerAuth: [] + responses: + '200': + description: 核验通过,照片进入处理流水线。 + content: + application/json: + schema: + $ref: '#/components/schemas/UploadCompleteResponse' + '400': + description: | + 核验失败(UPLOAD_VERIFY_FAILED):对象缺失、字节数与申明不符 + 或完整性信息不可用。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: 会话状态不允许完成(已取消或已过期,UPLOAD_SESSION_CONFLICT)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── photos ─────────────────────────── + + /photos/{photoId}: + parameters: + - $ref: '#/components/parameters/PhotoId' + patch: + operationId: updatePhoto + tags: [photos] + summary: 调整照片可见性 + description: 在 hidden 与 published 间切换(隐藏/恢复发布);按相册角色与上传人共同判定权限。 + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PhotoVisibilityUpdateRequest' + responses: + '200': + description: 更新后的照片记录。 + content: + application/json: + schema: + $ref: '#/components/schemas/PhotoRecord' + '400': + description: 参数校验失败(VALIDATION_FAILED,visibility 取值非法)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无该照片的管理权限(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 照片不存在(PHOTO_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + delete: + operationId: deletePhoto + tags: [photos] + summary: 软删除照片 + description: 标记 deleted(软删除),公开端点即刻不可见;物理删除走独立运维流程。 + security: + - bearerAuth: [] + responses: + '204': + description: 已软删除,无响应体。 + '401': + description: 未认证(AUTH_TOKEN_EXPIRED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: 无该照片的管理权限(AUTH_FORBIDDEN)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 照片不存在(PHOTO_NOT_FOUND)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + # ─────────────────────────── public ─────────────────────────── + + /public/albums/{token}: + parameters: + - $ref: '#/components/parameters/AlbumPublicToken' + get: + operationId: getPublicAlbum + tags: [public] + summary: 公开相册信息 + description: | + 观众页入口:按「请求 locale → defaultLocale」回退解析为单语言对象。 + publicToken 不可枚举,但持有链接者均可访问。 + security: [] + parameters: + - $ref: '#/components/parameters/LocaleQuery' + responses: + '200': + description: 相册公开信息。 + content: + application/json: + schema: + $ref: '#/components/schemas/PublicAlbum' + '400': + description: 查询参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在或未公开(ALBUM_NOT_PUBLIC,不区分以避免枚举)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /public/albums/{token}/photos: + parameters: + - $ref: '#/components/parameters/AlbumPublicToken' + get: + operationId: listPublicPhotos + tags: [public] + summary: 公开照片列表 + description: | + 仅返回公开相册中 published 的照片,按 publishedAt 与 ID 稳定倒序游标分页。 + 签名 URL 为短期凭证,过期后凭同一入口刷新。 + security: [] + parameters: + - name: category + in: query + description: 分类 ID 过滤(来自 PublicAlbum.categories 的 id)。 + schema: + type: string + - $ref: '#/components/parameters/LocaleQuery' + - $ref: '#/components/parameters/CursorQuery' + - name: limit + in: query + description: 每页张数(技术实施方案 §7 建议 30-60)。 + schema: + type: integer + minimum: 1 + maximum: 60 + default: 30 + responses: + '200': + description: 公开照片列表页。 + content: + application/json: + schema: + $ref: '#/components/schemas/PublicPhotoList' + '400': + description: 查询参数校验失败(VALIDATION_FAILED)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: 相册不存在或未公开(ALBUM_NOT_PUBLIC,不区分以避免枚举)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /public/albums/{token}/photos/{photoId}: + parameters: + - $ref: '#/components/parameters/AlbumPublicToken' + - $ref: '#/components/parameters/PhotoId' + get: + operationId: getPublicPhoto + tags: [public] + summary: 公开单图 + description: | + 深度链接定位单图,同时作为签名 URL 过期后的刷新入口。 + 服务端校验相册与照片归属;隐藏/删除后的旧链接返回 404。 + security: [] + responses: + '200': + description: 单图信息(含新签名的 thumb/browse URL)。 + content: + application/json: + schema: + $ref: '#/components/schemas/PublicPhotoDetail' + '404': + description: | + 相册不存在或未公开(ALBUM_NOT_PUBLIC),或照片不存在、未发布、 + 不属于该相册(PHOTO_NOT_PUBLIC)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: 服务器内部错误(INTERNAL_ERROR)。 + content: + application/json: + schema: + $ref: '#/components/schemas/Error' components: securitySchemes: @@ -90,15 +1042,65 @@ components: type: http scheme: bearer - responses: - ErrorResponse: - description: 错误响应(稳定 code;客户端按 code 映射文案,message 仅供诊断)。 - content: - application/json: - schema: - $ref: '#/components/schemas/Error' + parameters: + AlbumId: + name: albumId + in: path + required: true + description: 相册 ID。 + schema: + type: string + UserId: + name: userId + in: path + required: true + description: 用户 ID。 + schema: + type: string + CategoryId: + name: categoryId + in: path + required: true + description: 分类 ID。 + schema: + type: string + UploadId: + name: uploadId + in: path + required: true + description: 上传会话 ID。 + schema: + type: string + PhotoId: + name: photoId + in: path + required: true + description: 照片 ID。 + schema: + type: string + AlbumPublicToken: + name: token + in: path + required: true + description: 相册公开令牌(不可枚举,持有即可访问)。 + schema: + type: string + CursorQuery: + name: cursor + in: query + description: 分页游标(上一页响应的 nextCursor)。 + schema: + type: string + LocaleQuery: + name: locale + in: query + description: 请求语言;缺省时按相册 defaultLocale 回退。 + schema: + $ref: '#/components/schemas/Locale' schemas: + # ── 通用 ── + Error: type: object required: [code, message, requestId] @@ -144,6 +1146,15 @@ components: readOnly: true description: 是否还有下一页。 + Locale: + type: string + description: P1a 支持的语言。 + enum: + - zh-CN + - en + + # ── 认证 ── + LoginRequest: type: object required: [account, password] @@ -176,3 +1187,763 @@ components: format: date-time readOnly: true description: 续期后的过期时间。 + + # ── 相册 ── + + AlbumTranslation: + type: object + required: [locale, title] + description: 相册多语言条目(创建/更新与响应共用;未填的可选字段存储为 null)。 + properties: + locale: + $ref: '#/components/schemas/Locale' + title: + type: string + description: 相册标题。 + description: + type: [string, 'null'] + description: 相册描述。 + venue: + type: [string, 'null'] + description: 场地名称。 + address: + type: [string, 'null'] + description: 场地地址。 + + AlbumCreateRequest: + type: object + required: [translations, defaultLocale, startAt] + properties: + translations: + type: array + minItems: 1 + description: 多语言内容;必须包含 defaultLocale 对应条目。 + items: + $ref: '#/components/schemas/AlbumTranslation' + defaultLocale: + $ref: '#/components/schemas/Locale' + startAt: + type: string + format: date-time + description: 活动开始时间(UTC 存储,展示按 timezone)。 + endAt: + type: [string, 'null'] + format: date-time + description: 活动结束时间;null 或缺省表示未定。 + timezone: + type: string + default: Asia/Shanghai + description: 活动 IANA 时区。 + examples: + - Asia/Shanghai + downloadEnabled: + type: boolean + default: true + description: 允许下载浏览版本。 + originalDownloadEnabled: + type: boolean + default: false + description: 允许下载原图。 + + AlbumUpdateRequest: + type: object + minProperties: 1 + description: 部分更新:仅提交的字段被修改,各开关互相独立;endAt 传 null 清除。 + properties: + translations: + type: array + minItems: 1 + description: 全量替换多语言内容;须包含 defaultLocale 对应条目。 + items: + $ref: '#/components/schemas/AlbumTranslation' + defaultLocale: + $ref: '#/components/schemas/Locale' + startAt: + type: string + format: date-time + description: 活动开始时间。 + endAt: + type: [string, 'null'] + format: date-time + description: 活动结束时间;null 清除。 + timezone: + type: string + description: 活动 IANA 时区。 + uploadEnabled: + type: boolean + description: 是否允许新上传任务。 + publicEnabled: + type: boolean + description: 是否公开可见。 + downloadEnabled: + type: boolean + description: 允许下载浏览版本。 + originalDownloadEnabled: + type: boolean + description: 允许下载原图。 + + AlbumSummary: + type: object + description: 相册列表条目(完整开关与多语言标题,管理列表无需二次请求详情)。 + required: + - id + - publicToken + - ownerId + - status + - defaultLocale + - timezone + - startAt + - endAt + - uploadEnabled + - publicEnabled + - downloadEnabled + - originalDownloadEnabled + - watermarkVersion + - translations + - createdAt + - updatedAt + properties: + id: + type: string + readOnly: true + description: 相册 ID。 + publicToken: + type: string + readOnly: true + description: 公开访问令牌(不可枚举)。 + ownerId: + type: string + readOnly: true + description: 创建者用户 ID。 + status: + type: string + readOnly: true + default: active + description: 相册状态。 + enum: + - active + - archived + defaultLocale: + $ref: '#/components/schemas/Locale' + timezone: + type: string + description: 活动 IANA 时区。 + startAt: + type: string + format: date-time + description: 活动开始时间。 + endAt: + type: [string, 'null'] + format: date-time + description: 活动结束时间;null 表示未定。 + uploadEnabled: + type: boolean + description: 是否允许新上传任务。 + publicEnabled: + type: boolean + description: 是否公开可见。 + downloadEnabled: + type: boolean + description: 允许下载浏览版本。 + originalDownloadEnabled: + type: boolean + description: 允许下载原图。 + watermarkVersion: + type: integer + readOnly: true + minimum: 0 + default: 0 + description: 当前水印配置版本。 + translations: + type: array + description: 各语言内容。 + items: + $ref: '#/components/schemas/AlbumTranslation' + createdAt: + type: string + format: date-time + readOnly: true + description: 创建时间。 + updatedAt: + type: string + format: date-time + readOnly: true + description: 最近更新时间。 + + AlbumList: + allOf: + - $ref: '#/components/schemas/CursorPageMeta' + - type: object + required: [items] + properties: + items: + type: array + items: + $ref: '#/components/schemas/AlbumSummary' + + Album: + allOf: + - $ref: '#/components/schemas/AlbumSummary' + - type: object + required: [categories] + properties: + categories: + type: array + description: 相册分类(按 sortOrder 升序)。 + items: + $ref: '#/components/schemas/Category' + + # ── 成员 ── + + MemberAddRequest: + type: object + required: [userId] + properties: + userId: + type: string + description: 被添加用户的 ID(须为已开通账号)。 + role: + type: string + default: collaborator + description: 成员角色;P1a 仅协作者(owner 由创建流程产生)。 + enum: + - collaborator + + Member: + type: object + required: [userId, account, displayName, role, status] + properties: + userId: + type: string + readOnly: true + description: 用户 ID。 + account: + type: string + readOnly: true + description: 登录账号。 + displayName: + type: string + readOnly: true + description: 展示姓名(来自用户资料同步)。 + role: + type: string + readOnly: true + description: 相册角色。 + enum: + - owner + - collaborator + status: + type: string + readOnly: true + description: 成员状态;P1a 仅 active,预留扩展。 + enum: + - active + + MemberList: + allOf: + - $ref: '#/components/schemas/CursorPageMeta' + - type: object + required: [items] + properties: + items: + type: array + items: + $ref: '#/components/schemas/Member' + + # ── 分类 ── + + CategoryTranslation: + type: object + required: [locale, name] + properties: + locale: + $ref: '#/components/schemas/Locale' + name: + type: string + description: 分类名称。 + + CategoryCreateRequest: + type: object + required: [translations] + properties: + sortOrder: + type: integer + default: 0 + description: 排序序号(升序)。 + translations: + type: array + minItems: 1 + description: 分类多语言名称。 + items: + $ref: '#/components/schemas/CategoryTranslation' + + CategoryUpdateRequest: + type: object + minProperties: 1 + description: 部分更新;translations 为全量替换。 + properties: + sortOrder: + type: integer + description: 排序序号(升序)。 + translations: + type: array + minItems: 1 + description: 全量替换多语言名称。 + items: + $ref: '#/components/schemas/CategoryTranslation' + + Category: + type: object + required: [id, albumId, sortOrder, translations] + properties: + id: + type: string + readOnly: true + description: 分类 ID。 + albumId: + type: string + readOnly: true + description: 所属相册 ID。 + sortOrder: + type: integer + description: 排序序号(升序)。 + translations: + type: array + description: 各语言名称。 + items: + $ref: '#/components/schemas/CategoryTranslation' + + # ── 上传 ── + + UploadSessionState: + type: string + description: 上传会话状态(服务端视角;本地任务状态见技术实施方案 §5.5)。 + enum: + - pending + - confirmed + - cancelled + - expired + + PhotoProcessingState: + type: string + description: 照片服务端处理状态(技术实施方案 §5.5「服务端处理」链路)。 + enum: + - pending_upload + - uploaded + - processing + - ready + - processing_failed + + PhotoVisibility: + type: string + description: 照片对外可见性(技术实施方案 §5.5「对外可见性」;deleted 为软删除)。 + enum: + - unpublished + - published + - hidden + - deleted + + UploadInitRequest: + type: object + required: [albumId, clientTaskId, filename, bytes, contentType] + properties: + albumId: + type: string + description: 目标相册 ID。 + categoryId: + type: string + description: 目标分类 ID;缺省为相册默认分类。 + clientTaskId: + type: string + description: 客户端任务幂等键;同一任务的重试必须复用(会话与照片 ID 均复用)。 + filename: + type: string + description: 原始文件名(仅元数据,不作对象唯一标识)。 + bytes: + type: integer + format: int64 + minimum: 1 + description: 申明文件字节数;完成核验时逐一比对。 + contentType: + type: string + description: MIME 类型;仅接受图片类型(JPEG/PNG/HEIC)。 + + UploadCredential: + type: object + description: staging 对象的临时直传凭证;仅可写入指定 objectKey,不可访问 originals/variants。 + required: [mode, url, method, headers, expiresAt] + properties: + mode: + type: string + readOnly: true + default: presigned + description: 凭证模式;P1a 仅预签名 URL,预留 STS 扩展。 + enum: + - presigned + url: + type: string + readOnly: true + description: 预签名上传 URL。 + method: + type: string + readOnly: true + description: 上传 HTTP 方法。 + enum: + - PUT + headers: + type: object + readOnly: true + description: 上传时必须携带的请求头(如 Content-Type)。 + additionalProperties: + type: string + expiresAt: + type: string + format: date-time + readOnly: true + description: 凭证过期时间;过期后续签(renew)再重试。 + + UploadInitResponse: + type: object + required: [uploadId, photoId, objectKey, credential] + properties: + uploadId: + type: string + readOnly: true + description: 上传会话 ID。 + photoId: + type: string + readOnly: true + description: 预创建的照片 ID(重试复用,不重复建照片)。 + objectKey: + type: string + readOnly: true + description: staging 对象键(photo-live/{env}/staging/{uploadId}/{photoId}.{ext})。 + credential: + $ref: '#/components/schemas/UploadCredential' + + UploadCompleteResponse: + type: object + required: [photoId, processingState] + properties: + photoId: + type: string + readOnly: true + description: 照片 ID。 + processingState: + $ref: '#/components/schemas/PhotoProcessingState' + + UploadSessionPhoto: + type: object + required: [photoId, processingState, visibility] + description: 会话关联照片的当前状态(会话记录内嵌,客户端据此展示服务端进度)。 + properties: + photoId: + type: string + readOnly: true + description: 照片 ID。 + processingState: + $ref: '#/components/schemas/PhotoProcessingState' + visibility: + $ref: '#/components/schemas/PhotoVisibility' + + UploadSessionRecord: + type: object + required: + - uploadId + - albumId + - clientTaskId + - state + - expectedBytes + - contentType + - createdAt + - photo + properties: + uploadId: + type: string + readOnly: true + description: 上传会话 ID。 + albumId: + type: string + readOnly: true + description: 目标相册 ID。 + clientTaskId: + type: string + readOnly: true + description: 客户端任务幂等键。 + state: + $ref: '#/components/schemas/UploadSessionState' + expectedBytes: + type: integer + format: int64 + readOnly: true + description: 申明文件字节数。 + contentType: + type: string + readOnly: true + description: 申明 MIME 类型。 + createdAt: + type: string + format: date-time + readOnly: true + description: 会话创建时间。 + photo: + $ref: '#/components/schemas/UploadSessionPhoto' + + UploadSessionList: + allOf: + - $ref: '#/components/schemas/CursorPageMeta' + - type: object + required: [items] + properties: + items: + type: array + items: + $ref: '#/components/schemas/UploadSessionRecord' + + # ── 照片(管理端) ── + + PhotoVisibilityUpdateRequest: + type: object + required: [visibility] + properties: + visibility: + type: string + description: 目标可见性;仅允许 hidden 与 published 间切换。 + enum: + - hidden + - published + + PhotoRecord: + type: object + description: 管理端照片记录(技术实施方案 §4 photos 表字段)。 + required: + - photoId + - albumId + - categoryId + - uploaderId + - visibility + - processingState + - bytes + - width + - height + - shotAt + - uploadedAt + - publishedAt + - likeCount + properties: + photoId: + type: string + readOnly: true + description: 照片 ID。 + albumId: + type: string + readOnly: true + description: 所属相册 ID。 + categoryId: + type: [string, 'null'] + readOnly: true + description: 分类 ID;null 表示未分类。 + uploaderId: + type: string + readOnly: true + description: 上传人用户 ID。 + visibility: + $ref: '#/components/schemas/PhotoVisibility' + processingState: + $ref: '#/components/schemas/PhotoProcessingState' + bytes: + type: integer + format: int64 + readOnly: true + description: 原图字节数。 + width: + type: [integer, 'null'] + readOnly: true + description: 原图宽度(像素);处理前为 null。 + height: + type: [integer, 'null'] + readOnly: true + description: 原图高度(像素);处理前为 null。 + shotAt: + type: [string, 'null'] + format: date-time + readOnly: true + description: 拍摄时间(EXIF);无有效值时为 null。 + uploadedAt: + type: string + format: date-time + readOnly: true + description: 上传完成时间。 + publishedAt: + type: [string, 'null'] + format: date-time + readOnly: true + description: 发布时间;未发布为 null。 + likeCount: + type: integer + readOnly: true + minimum: 0 + description: 累计点赞数。 + + # ── 公开(观众端) ── + + SignedUrl: + type: object + description: 私有对象的短期签名 URL;不得写入持久分享链接,过期后经原入口刷新。 + required: [url, expiresAt] + properties: + url: + type: string + readOnly: true + description: 签名访问 URL。 + expiresAt: + type: string + format: date-time + readOnly: true + description: URL 失效时间。 + + PublicAlbumTranslation: + type: object + description: 按请求 locale 回退解析后的单语言相册内容(locale 为实际返回语言)。 + required: [locale, title, description, venue, address] + properties: + locale: + $ref: '#/components/schemas/Locale' + title: + type: string + description: 相册标题。 + description: + type: [string, 'null'] + description: 相册描述;无内容为 null。 + venue: + type: [string, 'null'] + description: 场地名称;无内容为 null。 + address: + type: [string, 'null'] + description: 场地地址;无内容为 null。 + + PublicCategory: + type: object + description: 公开分类(名称已按请求 locale 回退解析)。 + required: [id, name, sortOrder] + properties: + id: + type: string + readOnly: true + description: 分类 ID。 + name: + type: string + readOnly: true + description: 分类名称。 + sortOrder: + type: integer + readOnly: true + description: 排序序号(升序)。 + + PublicAlbum: + type: object + description: 观众端相册信息(不泄露工作人员信息与内部存储键)。 + required: + - publicToken + - resolvedLocale + - defaultLocale + - timezone + - startAt + - endAt + - translations + - categories + - banner + - viewCount + properties: + publicToken: + type: string + readOnly: true + description: 相册公开令牌。 + resolvedLocale: + $ref: '#/components/schemas/Locale' + defaultLocale: + $ref: '#/components/schemas/Locale' + timezone: + type: string + readOnly: true + description: 活动 IANA 时区(展示用,不随请求语言变化)。 + startAt: + type: string + format: date-time + readOnly: true + description: 活动开始时间(UTC,客户端按时区格式化)。 + endAt: + type: [string, 'null'] + format: date-time + readOnly: true + description: 活动结束时间;未定为 null。 + translations: + $ref: '#/components/schemas/PublicAlbumTranslation' + categories: + type: array + description: 分类列表(按 sortOrder 升序)。 + items: + $ref: '#/components/schemas/PublicCategory' + banner: + description: 封面图签名 URL;未设置为 null。 + anyOf: + - $ref: '#/components/schemas/SignedUrl' + - type: 'null' + viewCount: + type: 'null' + readOnly: true + description: P1a 占位 null;浏览计数由后续 /visits 端点提供(技术实施方案 §7.1)。 + + PublicPhoto: + type: object + description: 公开照片条目(仅 published 照片;URL 为短期签名,需在过期前刷新)。 + required: [photoId, thumb, browse, likeCount, publishedAt] + properties: + photoId: + type: string + readOnly: true + description: 照片 ID。 + width: + type: integer + readOnly: true + description: 浏览图宽度(像素);用于列表占位,减少加载跳动。 + height: + type: integer + readOnly: true + description: 浏览图高度(像素)。 + thumb: + $ref: '#/components/schemas/SignedUrl' + browse: + $ref: '#/components/schemas/SignedUrl' + likeCount: + type: integer + readOnly: true + minimum: 0 + description: 累计点赞数。 + publishedAt: + type: string + format: date-time + readOnly: true + description: 发布时间(列表排序依据)。 + + PublicPhotoDetail: + allOf: + - $ref: '#/components/schemas/PublicPhoto' + - type: object + required: [categoryId] + properties: + categoryId: + type: [string, 'null'] + readOnly: true + description: 所属分类 ID;未分类为 null。 + + PublicPhotoList: + allOf: + - $ref: '#/components/schemas/CursorPageMeta' + - type: object + required: [items] + properties: + items: + type: array + items: + $ref: '#/components/schemas/PublicPhoto' diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 965ade2..fe672e4 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -12,7 +12,8 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, - "./openapi.yaml": "./openapi.yaml" + "./openapi.yaml": "./openapi.yaml", + "./package.json": "./package.json" }, "scripts": { "gen": "openapi-typescript openapi.yaml -o src/generated/openapi.d.ts", diff --git a/packages/contracts/redocly.yaml b/packages/contracts/redocly.yaml index 3290633..c2373f5 100644 --- a/packages/contracts/redocly.yaml +++ b/packages/contracts/redocly.yaml @@ -1,5 +1,2 @@ extends: - recommended -rules: - # 基座阶段 CursorPageMeta 为前瞻定义(Task 5 的列表端点消费),暂时无引用。 - no-unused-components: off diff --git a/packages/contracts/src/generated/openapi.d.ts b/packages/contracts/src/generated/openapi.d.ts index c1ada0a..dfdc810 100644 --- a/packages/contracts/src/generated/openapi.d.ts +++ b/packages/contracts/src/generated/openapi.d.ts @@ -64,6 +64,337 @@ export interface paths { patch?: never; trace?: never; }; + "/albums": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 相册列表 + * @description 当前用户可见(参与)的相册列表;管理员可见全部。按创建时间倒序。 + */ + get: operations["listAlbums"]; + put?: never; + /** + * 创建相册 + * @description 创建相册并写入多语言内容;创建者自动成为 owner 成员。 + * 初始默认值:uploadEnabled/publicEnabled/downloadEnabled=true、 + * originalDownloadEnabled=false、status=active、watermarkVersion=0。 + */ + post: operations["createAlbum"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/albums/{albumId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + /** + * 相册详情 + * @description 相册完整配置、多语言内容与分类列表。 + */ + get: operations["getAlbum"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * 更新相册配置 + * @description 部分更新:仅提交的字段被修改,各开关互相独立。 + * translations 为全量替换(须包含 defaultLocale 对应条目)。 + */ + patch: operations["updateAlbum"]; + trace?: never; + }; + "/albums/{albumId}/members": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + /** + * 相册成员列表 + * @description 相册全部成员(含 owner)。 + */ + get: operations["listAlbumMembers"]; + put?: never; + /** + * 添加相册成员 + * @description 将已开通的同事添加为协作者;仅创建者或管理员可操作。 + */ + post: operations["addAlbumMember"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/albums/{albumId}/members/{userId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + /** @description 用户 ID。 */ + userId: components["parameters"]["UserId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * 移除相册成员 + * @description 移除协作者;仅创建者或管理员可操作。不能移除创建者(403)。 + * 目标成员不存在时幂等返回 204。 + */ + delete: operations["removeAlbumMember"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/albums/{albumId}/categories": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 创建分类 + * @description 在相册下创建分类;仅创建者或管理员可操作。 + */ + post: operations["createCategory"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/categories/{categoryId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 分类 ID。 */ + categoryId: components["parameters"]["CategoryId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * 删除分类 + * @description 删除分类(分类下仍有照片时是否允许删除由服务端策略决定,400 拒绝)。 + */ + delete: operations["deleteCategory"]; + options?: never; + head?: never; + /** + * 更新分类 + * @description 部分更新;translations 为全量替换。 + */ + patch: operations["updateCategory"]; + trace?: never; + }; + "/uploads": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * 上传会话记录 + * @description 当前用户的上传会话历史(新在前),用于任务队列恢复与状态展示。 + */ + get: operations["listUploads"]; + put?: never; + /** + * 初始化上传任务 + * @description 幂等初始化:预创建上传会话与照片 ID,返回 staging 对象的临时直传凭证。 + * 相同 clientTaskId 且参数一致时返回原会话,不重复创建照片; + * 参数不一致时返回 409(UPLOAD_SESSION_CONFLICT)。 + */ + post: operations["initUpload"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/uploads/{uploadId}/renew": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 上传会话 ID。 */ + uploadId: components["parameters"]["UploadId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 续签上传凭证 + * @description 为仍处于 pending 状态的会话重新签发直传凭证(结构同初始化响应)。 + */ + post: operations["renewUploadCredential"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/uploads/{uploadId}/complete": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 上传会话 ID。 */ + uploadId: components["parameters"]["UploadId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + /** + * 完成核验 + * @description 核验 staging 对象(存在性、字节数与可用完整性信息)后进入处理流水线。 + * 幂等:核验通过的会话重复调用返回相同结果。 + */ + post: operations["completeUpload"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/photos/{photoId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 照片 ID。 */ + photoId: components["parameters"]["PhotoId"]; + }; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * 软删除照片 + * @description 标记 deleted(软删除),公开端点即刻不可见;物理删除走独立运维流程。 + */ + delete: operations["deletePhoto"]; + options?: never; + head?: never; + /** + * 调整照片可见性 + * @description 在 hidden 与 published 间切换(隐藏/恢复发布);按相册角色与上传人共同判定权限。 + */ + patch: operations["updatePhoto"]; + trace?: never; + }; + "/public/albums/{token}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + }; + cookie?: never; + }; + /** + * 公开相册信息 + * @description 观众页入口:按「请求 locale → defaultLocale」回退解析为单语言对象。 + * publicToken 不可枚举,但持有链接者均可访问。 + */ + get: operations["getPublicAlbum"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/public/albums/{token}/photos": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + }; + cookie?: never; + }; + /** + * 公开照片列表 + * @description 仅返回公开相册中 published 的照片,按 publishedAt 与 ID 稳定倒序游标分页。 + * 签名 URL 为短期凭证,过期后凭同一入口刷新。 + */ + get: operations["listPublicPhotos"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/public/albums/{token}/photos/{photoId}": { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + /** @description 照片 ID。 */ + photoId: components["parameters"]["PhotoId"]; + }; + cookie?: never; + }; + /** + * 公开单图 + * @description 深度链接定位单图,同时作为签名 URL 过期后的刷新入口。 + * 服务端校验相册与照片归属;隐藏/删除后的旧链接返回 404。 + */ + get: operations["getPublicPhoto"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; } export type webhooks = Record; export interface components { @@ -86,6 +417,11 @@ export interface components { /** @description 是否还有下一页。 */ readonly hasMore: boolean; }; + /** + * @description P1a 支持的语言。 + * @enum {string} + */ + Locale: "zh-CN" | "en"; LoginRequest: { /** @description 账号(用户名或邮箱)。 */ account: string; @@ -108,19 +444,448 @@ export interface components { */ readonly expiresAt: string; }; - }; - responses: { - /** @description 错误响应(稳定 code;客户端按 code 映射文案,message 仅供诊断)。 */ - ErrorResponse: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["Error"]; + /** @description 相册多语言条目(创建/更新与响应共用;未填的可选字段存储为 null)。 */ + AlbumTranslation: { + locale: components["schemas"]["Locale"]; + /** @description 相册标题。 */ + title: string; + /** @description 相册描述。 */ + description?: string | null; + /** @description 场地名称。 */ + venue?: string | null; + /** @description 场地地址。 */ + address?: string | null; + }; + AlbumCreateRequest: { + /** @description 多语言内容;必须包含 defaultLocale 对应条目。 */ + translations: components["schemas"]["AlbumTranslation"][]; + defaultLocale: components["schemas"]["Locale"]; + /** + * Format: date-time + * @description 活动开始时间(UTC 存储,展示按 timezone)。 + */ + startAt: string; + /** + * Format: date-time + * @description 活动结束时间;null 或缺省表示未定。 + */ + endAt?: string | null; + /** + * @description 活动 IANA 时区。 + * @default Asia/Shanghai + * @example Asia/Shanghai + */ + timezone: string; + /** + * @description 允许下载浏览版本。 + * @default true + */ + downloadEnabled: boolean; + /** + * @description 允许下载原图。 + * @default false + */ + originalDownloadEnabled: boolean; + }; + /** @description 部分更新:仅提交的字段被修改,各开关互相独立;endAt 传 null 清除。 */ + AlbumUpdateRequest: { + /** @description 全量替换多语言内容;须包含 defaultLocale 对应条目。 */ + translations?: components["schemas"]["AlbumTranslation"][]; + defaultLocale?: components["schemas"]["Locale"]; + /** + * Format: date-time + * @description 活动开始时间。 + */ + startAt?: string; + /** + * Format: date-time + * @description 活动结束时间;null 清除。 + */ + endAt?: string | null; + /** @description 活动 IANA 时区。 */ + timezone?: string; + /** @description 是否允许新上传任务。 */ + uploadEnabled?: boolean; + /** @description 是否公开可见。 */ + publicEnabled?: boolean; + /** @description 允许下载浏览版本。 */ + downloadEnabled?: boolean; + /** @description 允许下载原图。 */ + originalDownloadEnabled?: boolean; + }; + /** @description 相册列表条目(完整开关与多语言标题,管理列表无需二次请求详情)。 */ + AlbumSummary: { + /** @description 相册 ID。 */ + readonly id: string; + /** @description 公开访问令牌(不可枚举)。 */ + readonly publicToken: string; + /** @description 创建者用户 ID。 */ + readonly ownerId: string; + /** + * @description 相册状态。 + * @default active + * @enum {string} + */ + readonly status: "active" | "archived"; + defaultLocale: components["schemas"]["Locale"]; + /** @description 活动 IANA 时区。 */ + timezone: string; + /** + * Format: date-time + * @description 活动开始时间。 + */ + startAt: string; + /** + * Format: date-time + * @description 活动结束时间;null 表示未定。 + */ + endAt: string | null; + /** @description 是否允许新上传任务。 */ + uploadEnabled: boolean; + /** @description 是否公开可见。 */ + publicEnabled: boolean; + /** @description 允许下载浏览版本。 */ + downloadEnabled: boolean; + /** @description 允许下载原图。 */ + originalDownloadEnabled: boolean; + /** + * @description 当前水印配置版本。 + * @default 0 + */ + readonly watermarkVersion: number; + /** @description 各语言内容。 */ + translations: components["schemas"]["AlbumTranslation"][]; + /** + * Format: date-time + * @description 创建时间。 + */ + readonly createdAt: string; + /** + * Format: date-time + * @description 最近更新时间。 + */ + readonly updatedAt: string; + }; + AlbumList: components["schemas"]["CursorPageMeta"] & { + items: components["schemas"]["AlbumSummary"][]; + }; + Album: components["schemas"]["AlbumSummary"] & { + /** @description 相册分类(按 sortOrder 升序)。 */ + categories: components["schemas"]["Category"][]; + }; + MemberAddRequest: { + /** @description 被添加用户的 ID(须为已开通账号)。 */ + userId: string; + /** + * @description 成员角色;P1a 仅协作者(owner 由创建流程产生)。 + * @default collaborator + * @enum {string} + */ + role: "collaborator"; + }; + Member: { + /** @description 用户 ID。 */ + readonly userId: string; + /** @description 登录账号。 */ + readonly account: string; + /** @description 展示姓名(来自用户资料同步)。 */ + readonly displayName: string; + /** + * @description 相册角色。 + * @enum {string} + */ + readonly role: "owner" | "collaborator"; + /** + * @description 成员状态;P1a 仅 active,预留扩展。 + * @enum {string} + */ + readonly status: "active"; + }; + MemberList: components["schemas"]["CursorPageMeta"] & { + items: components["schemas"]["Member"][]; + }; + CategoryTranslation: { + locale: components["schemas"]["Locale"]; + /** @description 分类名称。 */ + name: string; + }; + CategoryCreateRequest: { + /** + * @description 排序序号(升序)。 + * @default 0 + */ + sortOrder: number; + /** @description 分类多语言名称。 */ + translations: components["schemas"]["CategoryTranslation"][]; + }; + /** @description 部分更新;translations 为全量替换。 */ + CategoryUpdateRequest: { + /** @description 排序序号(升序)。 */ + sortOrder?: number; + /** @description 全量替换多语言名称。 */ + translations?: components["schemas"]["CategoryTranslation"][]; + }; + Category: { + /** @description 分类 ID。 */ + readonly id: string; + /** @description 所属相册 ID。 */ + readonly albumId: string; + /** @description 排序序号(升序)。 */ + sortOrder: number; + /** @description 各语言名称。 */ + translations: components["schemas"]["CategoryTranslation"][]; + }; + /** + * @description 上传会话状态(服务端视角;本地任务状态见技术实施方案 §5.5)。 + * @enum {string} + */ + UploadSessionState: "pending" | "confirmed" | "cancelled" | "expired"; + /** + * @description 照片服务端处理状态(技术实施方案 §5.5「服务端处理」链路)。 + * @enum {string} + */ + PhotoProcessingState: "pending_upload" | "uploaded" | "processing" | "ready" | "processing_failed"; + /** + * @description 照片对外可见性(技术实施方案 §5.5「对外可见性」;deleted 为软删除)。 + * @enum {string} + */ + PhotoVisibility: "unpublished" | "published" | "hidden" | "deleted"; + UploadInitRequest: { + /** @description 目标相册 ID。 */ + albumId: string; + /** @description 目标分类 ID;缺省为相册默认分类。 */ + categoryId?: string; + /** @description 客户端任务幂等键;同一任务的重试必须复用(会话与照片 ID 均复用)。 */ + clientTaskId: string; + /** @description 原始文件名(仅元数据,不作对象唯一标识)。 */ + filename: string; + /** + * Format: int64 + * @description 申明文件字节数;完成核验时逐一比对。 + */ + bytes: number; + /** @description MIME 类型;仅接受图片类型(JPEG/PNG/HEIC)。 */ + contentType: string; + }; + /** @description staging 对象的临时直传凭证;仅可写入指定 objectKey,不可访问 originals/variants。 */ + UploadCredential: { + /** + * @description 凭证模式;P1a 仅预签名 URL,预留 STS 扩展。 + * @default presigned + * @enum {string} + */ + readonly mode: "presigned"; + /** @description 预签名上传 URL。 */ + readonly url: string; + /** + * @description 上传 HTTP 方法。 + * @enum {string} + */ + readonly method: "PUT"; + /** @description 上传时必须携带的请求头(如 Content-Type)。 */ + readonly headers: { + [key: string]: string; }; + /** + * Format: date-time + * @description 凭证过期时间;过期后续签(renew)再重试。 + */ + readonly expiresAt: string; + }; + UploadInitResponse: { + /** @description 上传会话 ID。 */ + readonly uploadId: string; + /** @description 预创建的照片 ID(重试复用,不重复建照片)。 */ + readonly photoId: string; + /** @description staging 对象键(photo-live/{env}/staging/{uploadId}/{photoId}.{ext})。 */ + readonly objectKey: string; + credential: components["schemas"]["UploadCredential"]; + }; + UploadCompleteResponse: { + /** @description 照片 ID。 */ + readonly photoId: string; + processingState: components["schemas"]["PhotoProcessingState"]; + }; + /** @description 会话关联照片的当前状态(会话记录内嵌,客户端据此展示服务端进度)。 */ + UploadSessionPhoto: { + /** @description 照片 ID。 */ + readonly photoId: string; + processingState: components["schemas"]["PhotoProcessingState"]; + visibility: components["schemas"]["PhotoVisibility"]; + }; + UploadSessionRecord: { + /** @description 上传会话 ID。 */ + readonly uploadId: string; + /** @description 目标相册 ID。 */ + readonly albumId: string; + /** @description 客户端任务幂等键。 */ + readonly clientTaskId: string; + state: components["schemas"]["UploadSessionState"]; + /** + * Format: int64 + * @description 申明文件字节数。 + */ + readonly expectedBytes: number; + /** @description 申明 MIME 类型。 */ + readonly contentType: string; + /** + * Format: date-time + * @description 会话创建时间。 + */ + readonly createdAt: string; + photo: components["schemas"]["UploadSessionPhoto"]; + }; + UploadSessionList: components["schemas"]["CursorPageMeta"] & { + items: components["schemas"]["UploadSessionRecord"][]; + }; + PhotoVisibilityUpdateRequest: { + /** + * @description 目标可见性;仅允许 hidden 与 published 间切换。 + * @enum {string} + */ + visibility: "hidden" | "published"; + }; + /** @description 管理端照片记录(技术实施方案 §4 photos 表字段)。 */ + PhotoRecord: { + /** @description 照片 ID。 */ + readonly photoId: string; + /** @description 所属相册 ID。 */ + readonly albumId: string; + /** @description 分类 ID;null 表示未分类。 */ + readonly categoryId: string | null; + /** @description 上传人用户 ID。 */ + readonly uploaderId: string; + visibility: components["schemas"]["PhotoVisibility"]; + processingState: components["schemas"]["PhotoProcessingState"]; + /** + * Format: int64 + * @description 原图字节数。 + */ + readonly bytes: number; + /** @description 原图宽度(像素);处理前为 null。 */ + readonly width: number | null; + /** @description 原图高度(像素);处理前为 null。 */ + readonly height: number | null; + /** + * Format: date-time + * @description 拍摄时间(EXIF);无有效值时为 null。 + */ + readonly shotAt: string | null; + /** + * Format: date-time + * @description 上传完成时间。 + */ + readonly uploadedAt: string; + /** + * Format: date-time + * @description 发布时间;未发布为 null。 + */ + readonly publishedAt: string | null; + /** @description 累计点赞数。 */ + readonly likeCount: number; + }; + /** @description 私有对象的短期签名 URL;不得写入持久分享链接,过期后经原入口刷新。 */ + SignedUrl: { + /** @description 签名访问 URL。 */ + readonly url: string; + /** + * Format: date-time + * @description URL 失效时间。 + */ + readonly expiresAt: string; + }; + /** @description 按请求 locale 回退解析后的单语言相册内容(locale 为实际返回语言)。 */ + PublicAlbumTranslation: { + locale: components["schemas"]["Locale"]; + /** @description 相册标题。 */ + title: string; + /** @description 相册描述;无内容为 null。 */ + description: string | null; + /** @description 场地名称;无内容为 null。 */ + venue: string | null; + /** @description 场地地址;无内容为 null。 */ + address: string | null; + }; + /** @description 公开分类(名称已按请求 locale 回退解析)。 */ + PublicCategory: { + /** @description 分类 ID。 */ + readonly id: string; + /** @description 分类名称。 */ + readonly name: string; + /** @description 排序序号(升序)。 */ + readonly sortOrder: number; + }; + /** @description 观众端相册信息(不泄露工作人员信息与内部存储键)。 */ + PublicAlbum: { + /** @description 相册公开令牌。 */ + readonly publicToken: string; + resolvedLocale: components["schemas"]["Locale"]; + defaultLocale: components["schemas"]["Locale"]; + /** @description 活动 IANA 时区(展示用,不随请求语言变化)。 */ + readonly timezone: string; + /** + * Format: date-time + * @description 活动开始时间(UTC,客户端按时区格式化)。 + */ + readonly startAt: string; + /** + * Format: date-time + * @description 活动结束时间;未定为 null。 + */ + readonly endAt: string | null; + translations: components["schemas"]["PublicAlbumTranslation"]; + /** @description 分类列表(按 sortOrder 升序)。 */ + categories: components["schemas"]["PublicCategory"][]; + /** @description 封面图签名 URL;未设置为 null。 */ + banner: components["schemas"]["SignedUrl"] | null; + /** @description P1a 占位 null;浏览计数由后续 /visits 端点提供(技术实施方案 §7.1)。 */ + readonly viewCount: null; + }; + /** @description 公开照片条目(仅 published 照片;URL 为短期签名,需在过期前刷新)。 */ + PublicPhoto: { + /** @description 照片 ID。 */ + readonly photoId: string; + /** @description 浏览图宽度(像素);用于列表占位,减少加载跳动。 */ + readonly width?: number; + /** @description 浏览图高度(像素)。 */ + readonly height?: number; + thumb: components["schemas"]["SignedUrl"]; + browse: components["schemas"]["SignedUrl"]; + /** @description 累计点赞数。 */ + readonly likeCount: number; + /** + * Format: date-time + * @description 发布时间(列表排序依据)。 + */ + readonly publishedAt: string; + }; + PublicPhotoDetail: components["schemas"]["PublicPhoto"] & { + /** @description 所属分类 ID;未分类为 null。 */ + readonly categoryId: string | null; + }; + PublicPhotoList: components["schemas"]["CursorPageMeta"] & { + items: components["schemas"]["PublicPhoto"][]; }; }; - parameters: never; + responses: never; + parameters: { + /** @description 相册 ID。 */ + AlbumId: string; + /** @description 用户 ID。 */ + UserId: string; + /** @description 分类 ID。 */ + CategoryId: string; + /** @description 上传会话 ID。 */ + UploadId: string; + /** @description 照片 ID。 */ + PhotoId: string; + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + AlbumPublicToken: string; + /** @description 分页游标(上一页响应的 nextCursor)。 */ + CursorQuery: string; + /** @description 请求语言;缺省时按相册 defaultLocale 回退。 */ + LocaleQuery: components["schemas"]["Locale"]; + }; requestBodies: never; headers: never; pathItems: never; @@ -149,8 +914,33 @@ export interface operations { "application/json": components["schemas"]["LoginResponse"]; }; }; - 401: components["responses"]["ErrorResponse"]; - 500: components["responses"]["ErrorResponse"]; + /** @description 参数校验失败(VALIDATION_FAILED,经全局校验管道)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 账号或密码错误(AUTH_INVALID_CREDENTIALS)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; }; }; refreshSession: { @@ -171,8 +961,33 @@ export interface operations { "application/json": components["schemas"]["RefreshResponse"]; }; }; - 401: components["responses"]["ErrorResponse"]; - 500: components["responses"]["ErrorResponse"]; + /** @description 参数校验失败(VALIDATION_FAILED,经全局校验管道)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; }; }; logout: { @@ -191,8 +1006,1207 @@ export interface operations { }; content?: never; }; - 401: components["responses"]["ErrorResponse"]; - 500: components["responses"]["ErrorResponse"]; + /** @description 请求体不可解析(VALIDATION_FAILED,经全局校验管道)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listAlbums: { + parameters: { + query?: { + /** @description 分页游标(上一页响应的 nextCursor)。 */ + cursor?: components["parameters"]["CursorQuery"]; + /** @description 每页条数。 */ + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 相册列表页。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AlbumList"]; + }; + }; + /** @description 查询参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createAlbum: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AlbumCreateRequest"]; + }; + }; + responses: { + /** @description 相册已创建(新相册 categories 为空数组)。 */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Album"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getAlbum: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 相册详情。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Album"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户非成员不可见(ALBUM_NOT_FOUND,避免枚举)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updateAlbum: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AlbumUpdateRequest"]; + }; + }; + responses: { + /** @description 更新后的相册详情。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Album"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 仅创建者或管理员可修改(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listAlbumMembers: { + parameters: { + query?: { + /** @description 分页游标(上一页响应的 nextCursor)。 */ + cursor?: components["parameters"]["CursorQuery"]; + /** @description 每页条数。 */ + limit?: number; + }; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 成员列表页。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MemberList"]; + }; + }; + /** @description 查询参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + addAlbumMember: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MemberAddRequest"]; + }; + }; + responses: { + /** @description 成员已添加。 */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Member"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 仅创建者或管理员可管理成员(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 该用户已是相册成员(MEMBER_CONFLICT)。 */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + removeAlbumMember: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + /** @description 用户 ID。 */ + userId: components["parameters"]["UserId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 已移除,无响应体。 */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无管理权限,或试图移除创建者(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册 ID。 */ + albumId: components["parameters"]["AlbumId"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CategoryCreateRequest"]; + }; + }; + responses: { + /** @description 分类已创建。 */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Category"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 仅创建者或管理员可管理分类(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deleteCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 分类 ID。 */ + categoryId: components["parameters"]["CategoryId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 已删除,无响应体。 */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无所属相册的管理权限(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 分类不存在,或所属相册不存在/不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updateCategory: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 分类 ID。 */ + categoryId: components["parameters"]["CategoryId"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CategoryUpdateRequest"]; + }; + }; + responses: { + /** @description 更新后的分类。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Category"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无所属相册的管理权限(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 分类不存在,或所属相册不存在/不可见(ALBUM_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listUploads: { + parameters: { + query?: { + /** @description 分页游标(上一页响应的 nextCursor)。 */ + cursor?: components["parameters"]["CursorQuery"]; + /** @description 每页条数。 */ + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 会话记录列表页。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UploadSessionList"]; + }; + }; + /** @description 查询参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + initUpload: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UploadInitRequest"]; + }; + }; + responses: { + /** @description 上传会话已创建(幂等重复调用返回原会话)。 */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UploadInitResponse"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED):文件超过大小限额或类型不接受。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无该相册的上传权限(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** + * @description 相册已关闭上传(ALBUM_UPLOAD_DISABLED),或同 clientTaskId 已存在 + * 且参数不一致(UPLOAD_SESSION_CONFLICT)。 + */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + renewUploadCredential: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 上传会话 ID。 */ + uploadId: components["parameters"]["UploadId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 新凭证已签发(credential 为新值,其余字段回显原会话)。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UploadInitResponse"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** + * @description 相册已关闭上传(ALBUM_UPLOAD_DISABLED),或会话状态不允许续签 + * (已完成/取消/过期,UPLOAD_SESSION_CONFLICT)。 + */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + completeUpload: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 上传会话 ID。 */ + uploadId: components["parameters"]["UploadId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 核验通过,照片进入处理流水线。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["UploadCompleteResponse"]; + }; + }; + /** + * @description 核验失败(UPLOAD_VERIFY_FAILED):对象缺失、字节数与申明不符 + * 或完整性信息不可用。 + */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 会话状态不允许完成(已取消或已过期,UPLOAD_SESSION_CONFLICT)。 */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + deletePhoto: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 照片 ID。 */ + photoId: components["parameters"]["PhotoId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 已软删除,无响应体。 */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无该照片的管理权限(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 照片不存在(PHOTO_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + updatePhoto: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 照片 ID。 */ + photoId: components["parameters"]["PhotoId"]; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["PhotoVisibilityUpdateRequest"]; + }; + }; + responses: { + /** @description 更新后的照片记录。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PhotoRecord"]; + }; + }; + /** @description 参数校验失败(VALIDATION_FAILED,visibility 取值非法)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 未认证(AUTH_TOKEN_EXPIRED)。 */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 无该照片的管理权限(AUTH_FORBIDDEN)。 */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 照片不存在(PHOTO_NOT_FOUND)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getPublicAlbum: { + parameters: { + query?: { + /** @description 请求语言;缺省时按相册 defaultLocale 回退。 */ + locale?: components["parameters"]["LocaleQuery"]; + }; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 相册公开信息。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PublicAlbum"]; + }; + }; + /** @description 查询参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在或未公开(ALBUM_NOT_PUBLIC,不区分以避免枚举)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + listPublicPhotos: { + parameters: { + query?: { + /** @description 分类 ID 过滤(来自 PublicAlbum.categories 的 id)。 */ + category?: string; + /** @description 请求语言;缺省时按相册 defaultLocale 回退。 */ + locale?: components["parameters"]["LocaleQuery"]; + /** @description 分页游标(上一页响应的 nextCursor)。 */ + cursor?: components["parameters"]["CursorQuery"]; + /** @description 每页张数(技术实施方案 §7 建议 30-60)。 */ + limit?: number; + }; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 公开照片列表页。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PublicPhotoList"]; + }; + }; + /** @description 查询参数校验失败(VALIDATION_FAILED)。 */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 相册不存在或未公开(ALBUM_NOT_PUBLIC,不区分以避免枚举)。 */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + getPublicPhoto: { + parameters: { + query?: never; + header?: never; + path: { + /** @description 相册公开令牌(不可枚举,持有即可访问)。 */ + token: components["parameters"]["AlbumPublicToken"]; + /** @description 照片 ID。 */ + photoId: components["parameters"]["PhotoId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description 单图信息(含新签名的 thumb/browse URL)。 */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PublicPhotoDetail"]; + }; + }; + /** + * @description 相册不存在或未公开(ALBUM_NOT_PUBLIC),或照片不存在、未发布、 + * 不属于该相册(PHOTO_NOT_PUBLIC)。 + */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description 服务器内部错误(INTERNAL_ERROR)。 */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; }; }; } diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index 5a9444e..2fdfe10 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -10,5 +10,6 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true }, - "include": ["src"] + "include": ["src"], + "exclude": ["src/__tests__"] }