feat(contracts): p1b asset upload, management photo list, account lookup

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-18 08:23:59 +08:00
parent 47fb81ad31
commit c2a9cc51f3
2 changed files with 744 additions and 0 deletions
+385
View File
@@ -118,6 +118,60 @@ export interface paths {
patch: operations["updateAlbum"];
trace?: never;
};
"/albums/{albumId}/assets": {
parameters: {
query?: never;
header?: never;
path: {
/** @description 相册 ID。 */
albumId: components["parameters"]["AlbumId"];
};
cookie?: never;
};
get?: never;
put?: never;
/**
* 初始化资产上传
* @description 为相册宣传图/封面/Logo 创建上传会话,返回 staging 对象的临时直传凭证
* (凭证结构与照片直传同形)。无 clientTaskId 幂等键:assetId 即一次性会话,
* 失败重试重新初始化即可。替换语义:同一 (kind, locale) 再次上传→新行生效,
* 旧行保留作历史(P1b 无删除端点);公开相册响应始终取该 kind 最新生效行
* (P1a 公开服务已按 latest 实现)。
*/
post: operations["initAlbumAssetUpload"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/albums/{albumId}/assets/{assetId}/complete": {
parameters: {
query?: never;
header?: never;
path: {
/** @description 相册 ID。 */
albumId: components["parameters"]["AlbumId"];
/** @description 相册资产 ID。 */
assetId: components["parameters"]["AssetId"];
};
cookie?: never;
};
get?: never;
put?: never;
/**
* 完成资产上传核验
* @description 核验 staging 对象(存在性、字节数与可用完整性信息)后将资产挂到相册生效,
* 并返回短期签名访问 URL。幂等:已核验通过的资产重复调用返回 200
* (URL 重新现签,本端点即签名刷新入口)。
*/
post: operations["completeAlbumAssetUpload"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/albums/{albumId}/members": {
parameters: {
query?: never;
@@ -294,6 +348,31 @@ export interface paths {
patch?: never;
trace?: never;
};
"/albums/{albumId}/photos": {
parameters: {
query?: never;
header?: never;
path: {
/** @description 相册 ID。 */
albumId: components["parameters"]["AlbumId"];
};
cookie?: never;
};
/**
* 管理端照片列表
* @description 相册照片管理列表(区别于公开列表):全部状态可见(unpublished/published/
* hidden/deleted),条目含 uploader 脱敏信息。按 uploadedAt 与 ID 稳定倒序
* 游标分页。
*/
get: operations["listAlbumPhotos"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/photos/{photoId}": {
parameters: {
query?: never;
@@ -321,6 +400,32 @@ export interface paths {
patch: operations["updatePhoto"];
trace?: never;
};
"/admin/users": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* 账号前缀查询
* @description 邀请协作者的前置查询:按账号前缀搜索激活用户,返回脱敏列表
* (id/account/displayName,不含邮箱、手机与状态字段)。
* 访问策略(P1b 决策):任何已认证工作人员可用——邀请的发起方不限于
* 创建者/管理员,而泄露面仅为激活用户的最小展示字段、接收方为已认证
* 内部人员,风险可接受;未认证一律 401。前缀匹配为大小写敏感
* (服务端按二进制校对执行 LIKE prefix%);按账号升序返回,无游标
* (前缀搜索 + 小上限即取即用)。
*/
get: operations["lookupUsersByAccount"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/public/albums/{token}": {
parameters: {
query?: never;
@@ -573,6 +678,45 @@ export interface components {
/** @description 相册分类(按 sortOrder 升序)。 */
categories: components["schemas"]["Category"][];
};
/**
* @description 相册资产类型(技术实施方案 §4 album_assets.kind)。banner 为观众页宣传图
* (PublicAlbum.banner,P1b 消费方);cover/logo 建表与上传链路就绪,展示
* 消费随后续阶段。
* @enum {string}
*/
AssetKind: "banner" | "cover" | "logo";
/**
* @description 资产上传初始化请求;大小与类型白名单与照片直传一致(UPLOAD_MAX_BYTES
* 环境策略,逐请求惰性读取)。locale 为可选语言维度:同一 (kind, locale)
* 的再次上传即替换(新行生效);语言无关的资产(如 logo)可不传。
*/
AssetInitRequest: {
kind: components["schemas"]["AssetKind"];
locale?: components["schemas"]["Locale"];
/** @description 原始文件名(仅元数据,不作对象唯一标识)。 */
filename: string;
/**
* Format: int64
* @description 申明文件字节数;完成核验时逐一比对。
*/
bytes: number;
/**
* @description MIME 类型白名单;image/heic 为暂定项,解码能力待 G2 真机验证后确认。
* @enum {string}
*/
contentType: "image/jpeg" | "image/png" | "image/heic";
};
AssetInitResponse: {
/** @description 资产 ID(完成核验的寻址凭据,一次性会话)。 */
readonly assetId: string;
/** @description staging 对象键(photo-live/{env}/albums/{albumId}/assets/{assetId}.{ext})。 */
readonly objectKey: string;
credential: components["schemas"]["UploadCredential"];
};
AssetCompleteResponse: components["schemas"]["SignedUrl"] & {
/** @description 资产 ID。 */
readonly assetId: string;
};
MemberAddRequest: {
/** @description 被添加用户的 ID(须为已开通账号)。 */
userId: string;
@@ -790,6 +934,35 @@ export interface components {
/** @description 累计点赞数。 */
readonly likeCount: number;
};
/** @description 管理列表条目内嵌的上传人脱敏信息(不含邮箱、手机等凭据字段)。 */
AdminPhotoUploader: {
/** @description 上传人用户 ID(与 PhotoRecord.uploaderId 一致)。 */
readonly userId: string;
/** @description 登录账号。 */
readonly account: string;
/** @description 展示姓名(来自用户资料)。 */
readonly displayName: string;
};
AdminPhoto: components["schemas"]["PhotoRecord"] & {
uploader: components["schemas"]["AdminPhotoUploader"];
};
AdminPhotoList: components["schemas"]["CursorPageMeta"] & {
items: components["schemas"]["AdminPhoto"][];
};
/** @description 账号查询条目(脱敏:仅邀请协作者所需的最小字段,不含邮箱/手机/状态)。 */
AdminUserLookupItem: {
/** @description 用户 ID(用于 POST /albums/{albumId}/members 的 userId)。 */
readonly id: string;
/** @description 登录账号。 */
readonly account: string;
/** @description 展示姓名。 */
readonly displayName: string;
};
/** @description 账号前缀查询结果;无游标(前缀搜索 + 小上限即取即用)。 */
AdminUserLookupList: {
/** @description 匹配的激活用户(按账号升序)。 */
items: components["schemas"]["AdminUserLookupItem"][];
};
/** @description 私有对象的短期签名 URL;不得写入持久分享链接,过期后经原入口刷新。 */
SignedUrl: {
/** @description 签名访问 URL。 */
@@ -900,6 +1073,8 @@ export interface components {
UserId: string;
/** @description 分类 ID。 */
CategoryId: string;
/** @description 相册资产 ID。 */
AssetId: string;
/** @description 上传会话 ID。 */
UploadId: string;
/** @description 照片 ID。 */
@@ -1193,6 +1368,119 @@ export interface operations {
500: components["responses"]["InternalError"];
};
};
initAlbumAssetUpload: {
parameters: {
query?: never;
header?: never;
path: {
/** @description 相册 ID。 */
albumId: components["parameters"]["AlbumId"];
};
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["AssetInitRequest"];
};
};
responses: {
/** @description 资产上传会话已创建。 */
201: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["AssetInitResponse"];
};
};
/** @description 参数校验失败(VALIDATION_FAILED):文件超过大小限额或类型不接受。 */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @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"];
};
};
500: components["responses"]["InternalError"];
};
};
completeAlbumAssetUpload: {
parameters: {
query?: never;
header?: never;
path: {
/** @description 相册 ID。 */
albumId: components["parameters"]["AlbumId"];
/** @description 相册资产 ID。 */
assetId: components["parameters"]["AssetId"];
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description 核验通过,资产已生效。 */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["AssetCompleteResponse"];
};
};
/**
* @description 核验失败(UPLOAD_VERIFY_FAILED):对象缺失、字节数与申明不符
* 或完整性信息不可用。
*/
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @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"];
};
};
500: components["responses"]["InternalError"];
};
};
listAlbumMembers: {
parameters: {
query?: {
@@ -1695,6 +1983,67 @@ export interface operations {
500: components["responses"]["InternalError"];
};
};
listAlbumPhotos: {
parameters: {
query?: {
/** @description 可见性过滤;缺省为全部状态。 */
visibility?: components["schemas"]["PhotoVisibility"];
/** @description 分类 ID 过滤。 */
categoryId?: string;
/** @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"]["AdminPhotoList"];
};
};
/** @description 查询参数校验失败(VALIDATION_FAILED)。 */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
/** @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"];
};
};
500: components["responses"]["InternalError"];
};
};
deletePhoto: {
parameters: {
query?: never;
@@ -1792,6 +2141,42 @@ export interface operations {
500: components["responses"]["InternalError"];
};
};
lookupUsersByAccount: {
parameters: {
query: {
/** @description 账号前缀(≥2 字符以防过宽匹配)。 */
accountPrefix: string;
/** @description 返回条数上限。 */
limit?: number;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description 匹配的激活用户列表(无匹配为空数组)。 */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["AdminUserLookupList"];
};
};
/** @description 查询参数校验失败(VALIDATION_FAILED;accountPrefix 缺失或长度非法)。 */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["Error"];
};
};
401: components["responses"]["Unauthorized"];
500: components["responses"]["InternalError"];
};
};
getPublicAlbum: {
parameters: {
query?: {