fix(contracts): quality review fixes for p1a contracts

- status enum active|archived -> active|offline (align requirement docs)
- UploadSessionState session-state semantics documented (no failed state)
- maxLength: clientTaskId 128 / filename 255 / cursor 512
- PublicPhoto width/height required (layout placeholders)
- reusable Unauthorized/InternalError responses (hybrid with inline op-specific codes)
- PublicAlbum.translations -> translation (single resolved object)
- contentType enum [jpeg/png/heic] (heic provisional pending G2)
- minLength 1 on title/name; publicToken pattern ^[0-9a-z]{20,32}$
- CursorPageMeta hasMore/nextCursor invariant documented
- public locale: silent fallback to defaultLocale (tech doc section 11)
- plan: upload_sessions state comment aligned; P1b adds management photo list + account lookup endpoints

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-17 18:06:54 +08:00
parent 1dae00eaef
commit f6448bd006
3 changed files with 152 additions and 581 deletions
+75 -370
View File
@@ -410,7 +410,7 @@ export interface components {
/** @description 请求追踪 ID。 */
readonly requestId: string;
};
/** @description 游标分页元信息;各列表响应以 allOf 内嵌本结构(条目 schema 在业务端点中定义)。 */
/** @description 游标分页元信息;各列表响应以 allOf 内嵌本结构(条目 schema 在业务端点中定义)。不变式:hasMore=true 当且仅当 nextCursor≠null,两者必须成对填充。 */
CursorPageMeta: {
/** @description 下一页游标;null 表示没有更多数据。 */
readonly nextCursor: string | null;
@@ -522,11 +522,11 @@ export interface components {
/** @description 创建者用户 ID。 */
readonly ownerId: string;
/**
* @description 相册状态。
* @description 相册状态;offline 表示下线(停止公开访问与新授权签发)。
* @default active
* @enum {string}
*/
readonly status: "active" | "archived";
readonly status: "active" | "offline";
defaultLocale: components["schemas"]["Locale"];
/** @description 活动 IANA 时区。 */
timezone: string;
@@ -637,6 +637,9 @@ export interface components {
};
/**
* @description 上传会话状态(服务端视角;本地任务状态见技术实施方案 §5.5)。
* 会话没有 failed 状态:完成核验失败属于单次请求错误(UPLOAD_VERIFY_FAILED),
* 会话保持 pending 可重试;cancelled 为用户主动取消;
* expired 为超过 expiresAt 未完成(核验时推导或由补偿扫描落定)。
* @enum {string}
*/
UploadSessionState: "pending" | "confirmed" | "cancelled" | "expired";
@@ -664,8 +667,11 @@ export interface components {
* @description 申明文件字节数;完成核验时逐一比对。
*/
bytes: number;
/** @description MIME 类型;仅接受图片类型(JPEG/PNG/HEIC)。 */
contentType: string;
/**
* @description MIME 类型白名单;image/heic 为暂定项,解码能力待 G2 真机验证后确认。
* @enum {string}
*/
contentType: "image/jpeg" | "image/png" | "image/heic";
};
/** @description staging 对象的临时直传凭证;仅可写入指定 objectKey,不可访问 originals/variants。 */
UploadCredential: {
@@ -833,7 +839,7 @@ export interface components {
* @description 活动结束时间;未定为 null。
*/
readonly endAt: string | null;
translations: components["schemas"]["PublicAlbumTranslation"];
translation: components["schemas"]["PublicAlbumTranslation"];
/** @description 分类列表(按 sortOrder 升序)。 */
categories: components["schemas"]["PublicCategory"][];
/** @description 封面图签名 URL;未设置为 null。 */
@@ -841,14 +847,14 @@ export interface components {
/** @description P1a 占位 null;浏览计数由后续 /visits 端点提供(技术实施方案 §7.1)。 */
readonly viewCount: null;
};
/** @description 公开照片条目(仅 published 照片;URL 为短期签名,需在过期前刷新)。 */
/** @description 公开照片条目(仅 published 照片,处理完成即有宽高;URL 为短期签名,需在过期前刷新)。 */
PublicPhoto: {
/** @description 照片 ID。 */
readonly photoId: string;
/** @description 浏览图宽度(像素);用于列表占位,减少加载跳动。 */
readonly width?: number;
readonly width: number;
/** @description 浏览图高度(像素)。 */
readonly height?: number;
readonly height: number;
thumb: components["schemas"]["SignedUrl"];
browse: components["schemas"]["SignedUrl"];
/** @description 累计点赞数。 */
@@ -867,7 +873,26 @@ export interface components {
items: components["schemas"]["PublicPhoto"][];
};
};
responses: never;
responses: {
/** @description 未认证:令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 */
Unauthorized: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
InternalError: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
};
parameters: {
/** @description 相册 ID。 */
AlbumId: string;
@@ -932,15 +957,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
refreshSession: {
@@ -970,24 +987,8 @@ export interface operations {
"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"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
logout: {
@@ -1015,24 +1016,8 @@ export interface operations {
"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"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
listAlbums: {
@@ -1067,24 +1052,8 @@ export interface operations {
"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"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
createAlbum: {
@@ -1118,24 +1087,8 @@ export interface operations {
"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"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
getAlbum: {
@@ -1159,15 +1112,7 @@ export interface operations {
"application/json": components["schemas"]["Album"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 相册不存在,或当前用户非成员不可见(ALBUM_NOT_FOUND,避免枚举)。 */
404: {
headers: {
@@ -1177,15 +1122,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
updateAlbum: {
@@ -1222,15 +1159,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 仅创建者或管理员可修改(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1249,15 +1178,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
listAlbumMembers: {
@@ -1295,15 +1216,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 相册不存在,或当前用户不可见(ALBUM_NOT_FOUND)。 */
404: {
headers: {
@@ -1313,15 +1226,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
addAlbumMember: {
@@ -1358,15 +1263,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 仅创建者或管理员可管理成员(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1394,15 +1291,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
removeAlbumMember: {
@@ -1426,15 +1315,7 @@ export interface operations {
};
content?: never;
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无管理权限,或试图移除创建者(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1453,15 +1334,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
createCategory: {
@@ -1498,15 +1371,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 仅创建者或管理员可管理分类(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1525,15 +1390,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
deleteCategory: {
@@ -1555,15 +1412,7 @@ export interface operations {
};
content?: never;
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无所属相册的管理权限(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1582,15 +1431,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
updateCategory: {
@@ -1627,15 +1468,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无所属相册的管理权限(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1654,15 +1487,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
listUploads: {
@@ -1697,24 +1522,8 @@ export interface operations {
"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"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
initUpload: {
@@ -1748,15 +1557,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无该相册的上传权限(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1778,15 +1579,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
renewUploadCredential: {
@@ -1810,15 +1603,7 @@ export interface operations {
"application/json": components["schemas"]["UploadInitResponse"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 */
403: {
headers: {
@@ -1840,15 +1625,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
completeUpload: {
@@ -1884,15 +1661,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无权操作该会话或会话不存在(AUTH_FORBIDDEN,不区分以避免枚举)。 */
403: {
headers: {
@@ -1911,15 +1680,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
deletePhoto: {
@@ -1941,15 +1702,7 @@ export interface operations {
};
content?: never;
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无该照片的管理权限(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -1968,15 +1721,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
updatePhoto: {
@@ -2013,15 +1758,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 未认证(AUTH_TOKEN_EXPIRED)。 */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @description 无该照片的管理权限(AUTH_FORBIDDEN)。 */
403: {
headers: {
@@ -2040,15 +1777,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
getPublicAlbum: {
@@ -2093,15 +1822,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
listPublicPhotos: {
@@ -2152,15 +1873,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
getPublicPhoto: {
@@ -2198,15 +1911,7 @@ export interface operations {
"application/json": components["schemas"]["Error"];
};
};
/** @description 服务器内部错误(INTERNAL_ERROR)。 */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
500: components["responses"]["InternalError"];
};
};
}