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
+359
View File
@@ -28,6 +28,8 @@ tags:
description: 密码登录会话(签发、滑动续期、退出)
- name: albums
description: 相册创建、列表、详情与配置(工作人员)
- name: assets
description: 相册宣传图/封面/Logo 资产直传(创建者或管理员)
- name: members
description: 相册成员管理(创建者或管理员)
- name: categories
@@ -36,6 +38,8 @@ tags:
description: 直传任务初始化、授权续签、完成核验与会话记录
- name: photos
description: 照片可见性调整与软删除(工作人员)
- name: admin
description: 管理辅助查询
- name: public
description: 观众匿名只读(相册信息与公开照片)
@@ -278,6 +282,105 @@ paths:
'500':
$ref: '#/components/responses/InternalError'
# ─────────────────────────── assets ───────────────────────────
/albums/{albumId}/assets:
parameters:
- $ref: '#/components/parameters/AlbumId'
post:
operationId: initAlbumAssetUpload
tags: [assets]
summary: 初始化资产上传
description: |
为相册宣传图/封面/Logo 创建上传会话,返回 staging 对象的临时直传凭证
(凭证结构与照片直传同形)。无 clientTaskId 幂等键:assetId 即一次性会话,
失败重试重新初始化即可。替换语义:同一 (kind, locale) 再次上传→新行生效,
旧行保留作历史(P1b 无删除端点);公开相册响应始终取该 kind 最新生效行
(P1a 公开服务已按 latest 实现)。
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AssetInitRequest'
responses:
'201':
description: 资产上传会话已创建。
content:
application/json:
schema:
$ref: '#/components/schemas/AssetInitResponse'
'400':
description: 参数校验失败(VALIDATION_FAILED):文件超过大小限额或类型不接受。
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'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':
$ref: '#/components/responses/InternalError'
/albums/{albumId}/assets/{assetId}/complete:
parameters:
- $ref: '#/components/parameters/AlbumId'
- $ref: '#/components/parameters/AssetId'
post:
operationId: completeAlbumAssetUpload
tags: [assets]
summary: 完成资产上传核验
description: |
核验 staging 对象(存在性、字节数与可用完整性信息)后将资产挂到相册生效,
并返回短期签名访问 URL。幂等:已核验通过的资产重复调用返回 200
(URL 重新现签,本端点即签名刷新入口)。
security:
- bearerAuth: []
responses:
'200':
description: 核验通过,资产已生效。
content:
application/json:
schema:
$ref: '#/components/schemas/AssetCompleteResponse'
'400':
description: |
核验失败(UPLOAD_VERIFY_FAILED):对象缺失、字节数与申明不符
或完整性信息不可用。
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'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':
$ref: '#/components/responses/InternalError'
# ─────────────────────────── members ───────────────────────────
/albums/{albumId}/members:
@@ -695,6 +798,69 @@ paths:
# ─────────────────────────── photos ───────────────────────────
/albums/{albumId}/photos:
parameters:
- $ref: '#/components/parameters/AlbumId'
get:
operationId: listAlbumPhotos
tags: [photos]
summary: 管理端照片列表
description: |
相册照片管理列表(区别于公开列表):全部状态可见(unpublished/published/
hidden/deleted),条目含 uploader 脱敏信息。按 uploadedAt 与 ID 稳定倒序
游标分页。
security:
- bearerAuth: []
parameters:
- name: visibility
in: query
description: 可见性过滤;缺省为全部状态。
schema:
$ref: '#/components/schemas/PhotoVisibility'
- name: categoryId
in: query
description: 分类 ID 过滤。
schema:
type: string
- $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/AdminPhotoList'
'400':
description: 查询参数校验失败(VALIDATION_FAILED)。
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'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':
$ref: '#/components/responses/InternalError'
/photos/{photoId}:
parameters:
- $ref: '#/components/parameters/PhotoId'
@@ -768,6 +934,58 @@ paths:
'500':
$ref: '#/components/responses/InternalError'
# ─────────────────────────── admin ───────────────────────────
/admin/users:
get:
operationId: lookupUsersByAccount
tags: [admin]
summary: 账号前缀查询
description: |
邀请协作者的前置查询:按账号前缀搜索激活用户,返回脱敏列表
(id/account/displayName,不含邮箱、手机与状态字段)。
访问策略(P1b 决策):任何已认证工作人员可用——邀请的发起方不限于
创建者/管理员,而泄露面仅为激活用户的最小展示字段、接收方为已认证
内部人员,风险可接受;未认证一律 401。前缀匹配为大小写敏感
(服务端按二进制校对执行 LIKE prefix%);按账号升序返回,无游标
(前缀搜索 + 小上限即取即用)。
security:
- bearerAuth: []
parameters:
- name: accountPrefix
in: query
required: true
description: 账号前缀(≥2 字符以防过宽匹配)。
schema:
type: string
minLength: 2
maxLength: 64
- name: limit
in: query
description: 返回条数上限。
schema:
type: integer
minimum: 1
maximum: 20
default: 10
responses:
'200':
description: 匹配的激活用户列表(无匹配为空数组)。
content:
application/json:
schema:
$ref: '#/components/schemas/AdminUserLookupList'
'400':
description: 查询参数校验失败(VALIDATION_FAILED;accountPrefix 缺失或长度非法)。
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
$ref: '#/components/responses/Unauthorized'
'500':
$ref: '#/components/responses/InternalError'
# ─────────────────────────── public ───────────────────────────
/public/albums/{token}:
@@ -912,6 +1130,13 @@ components:
description: 分类 ID。
schema:
type: string
AssetId:
name: assetId
in: path
required: true
description: 相册资产 ID。
schema:
type: string
UploadId:
name: uploadId
in: path
@@ -1260,6 +1485,71 @@ components:
items:
$ref: '#/components/schemas/Category'
# ── 资产 ──
AssetKind:
type: string
description: |
相册资产类型(技术实施方案 §4 album_assets.kind)。banner 为观众页宣传图
(PublicAlbum.banner,P1b 消费方);cover/logo 建表与上传链路就绪,展示
消费随后续阶段。
enum:
- banner
- cover
- logo
AssetInitRequest:
type: object
description: |
资产上传初始化请求;大小与类型白名单与照片直传一致(UPLOAD_MAX_BYTES
环境策略,逐请求惰性读取)。locale 为可选语言维度:同一 (kind, locale)
的再次上传即替换(新行生效);语言无关的资产(如 logo)可不传。
required: [kind, filename, bytes, contentType]
properties:
kind:
$ref: '#/components/schemas/AssetKind'
locale:
$ref: '#/components/schemas/Locale'
filename:
type: string
maxLength: 255
description: 原始文件名(仅元数据,不作对象唯一标识)。
bytes:
type: integer
format: int64
minimum: 1
description: 申明文件字节数;完成核验时逐一比对。
contentType:
type: string
enum: [image/jpeg, image/png, image/heic]
description: MIME 类型白名单;image/heic 为暂定项,解码能力待 G2 真机验证后确认。
AssetInitResponse:
type: object
required: [assetId, objectKey, credential]
properties:
assetId:
type: string
readOnly: true
description: 资产 ID(完成核验的寻址凭据,一次性会话)。
objectKey:
type: string
readOnly: true
description: staging 对象键(photo-live/{env}/albums/{albumId}/assets/{assetId}.{ext})。
credential:
$ref: '#/components/schemas/UploadCredential'
AssetCompleteResponse:
allOf:
- $ref: '#/components/schemas/SignedUrl'
- type: object
required: [assetId]
properties:
assetId:
type: string
readOnly: true
description: 资产 ID。
# ── 成员 ──
MemberAddRequest:
@@ -1660,6 +1950,75 @@ components:
minimum: 0
description: 累计点赞数。
AdminPhotoUploader:
type: object
description: 管理列表条目内嵌的上传人脱敏信息(不含邮箱、手机等凭据字段)。
required: [userId, account, displayName]
properties:
userId:
type: string
readOnly: true
description: 上传人用户 ID(与 PhotoRecord.uploaderId 一致)。
account:
type: string
readOnly: true
description: 登录账号。
displayName:
type: string
readOnly: true
description: 展示姓名(来自用户资料)。
AdminPhoto:
allOf:
- $ref: '#/components/schemas/PhotoRecord'
- type: object
required: [uploader]
properties:
uploader:
$ref: '#/components/schemas/AdminPhotoUploader'
AdminPhotoList:
allOf:
- $ref: '#/components/schemas/CursorPageMeta'
- type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/AdminPhoto'
# ── 管理辅助 ──
AdminUserLookupItem:
type: object
description: 账号查询条目(脱敏:仅邀请协作者所需的最小字段,不含邮箱/手机/状态)。
required: [id, account, displayName]
properties:
id:
type: string
readOnly: true
description: 用户 ID(用于 POST /albums/{albumId}/members 的 userId)。
account:
type: string
readOnly: true
description: 登录账号。
displayName:
type: string
readOnly: true
description: 展示姓名。
AdminUserLookupList:
type: object
description: 账号前缀查询结果;无游标(前缀搜索 + 小上限即取即用)。
required: [items]
properties:
items:
type: array
description: 匹配的激活用户(按账号升序)。
items:
$ref: '#/components/schemas/AdminUserLookupItem'
# ── 公开(观众端) ──
SignedUrl: