Files
photo-live/apps/admin/src/api/albums.ts
T
浅念 8a8ad8a18d feat(contracts): p4a admin governance, watermark, download and usage endpoints
P4a Task 1 契约扩展批次(36 → 51 operations,gen/redocly/枚举同步守护绿):

- photos:POST /albums/{albumId}/photos/hide-batch(photoIds 1..200,逐项
  BatchHideResult {photoId, ok, code?},部分成功语义,A16);
- admin:POST /admin/albums/{albumId}/transfer(owner 交接,历史上传人不动,
  keepAsCollaborator,A17);
- albums:POST/GET /albums/{albumId}/watermark-jobs(WatermarkConfig 版本化
  + 历史重生成任务 state/progress/total/failed,原子切换、原图永不覆盖,
  A18/A20);
- interactions:GET /public/albums/{token}/photos/{photoId}/download
  (variant=browse|original 两级开关校验;**新增错误码 DOWNLOAD_DISABLED**,
  errors.ts 与 Error enum 两侧同步,A18);
- admin 账号治理:GET /admin/users 统一为 listAdminUsers 双模式 + PATCH
  /admin/users/{userId} 与 reset-password / activate / deactivate;
- admin 用户同步:GET /admin/user-sync/{status,runs,errors} + POST runs
  手动补拉(互斥复用进行中批次,A29);
- admin 用量:GET /admin/usage/summary + PUT thresholds(null 清除、
  即刻重评估,A23)。

偏差登记①——/admin/users 路径冲突(P1b lookupUsersByAccount):P4a 决策
统一为双模式端点(前缀模式沿用 P1b 语义与任何已认证工作人员可用;管理
列表模式 query/status 过滤 + 游标 + 仅 admin),响应统一 AdminUserList
(AdminUser 字段超集,游标元信息随行)。消费方同步:admin 邀请搜索
(fetchLookupUsers/类型镜像,行为不变)与 api 过渡实现(仍仅前缀模式、
limit 对齐 1..100 缺省 20;管理列表模式与仅 admin 判定随 Task 6 落地,
users-lookup e2e 已按统一形状修订)。

偏差登记②——apps/mobile 手写镜像未随本次更新:其邀请流程仍按旧 3 字段
投影消费(AdminUserList 为超集,运行时兼容,mobile IT 不经该路径);列
为 P4a 跟进项,仅当其 e2e 破坏时处理。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 15:56:38 +08:00

124 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 相册域接口(对齐 @photo/contracts:albums/assets/members/categories/photos
* 与 /admin/users 账号前缀查询)
*
* - 列表端点均为游标分页(cursor + limit;nextCursor 为 null 表示到底)
* - 更新为 PATCH 部分语义;translations 数组全量替换
* - 204 端点(移除成员/删除分类/删除照片)无响应体
* - 资产直传 PUT 走 staging 预签名 URL(裸 fetch,不带会话头),见
* views/albums 内的上传组件
*/
import request from '@/utils/http'
/** 相册列表(管理视角:当前用户可见,创建时间倒序) */
export function fetchAlbumList(params: { cursor?: string; limit?: number }) {
return request.get<Api.Album.ListResponse>({ url: '/v1/albums', params })
}
/** 相册详情(完整配置 + 多语言内容 + 分类) */
export function fetchAlbum(albumId: string) {
return request.get<Api.Album.Detail>({ url: `/v1/albums/${albumId}` })
}
/** 创建相册(创建者自动成为 owner 成员) */
export function fetchCreateAlbum(data: Api.Album.CreatePayload) {
return request.post<Api.Album.Detail>({ url: '/v1/albums', data })
}
/** 更新相册(部分更新;translations 全量替换,须含 defaultLocale 条目) */
export function fetchUpdateAlbum(albumId: string, data: Api.Album.UpdatePayload) {
return request.patch<Api.Album.Detail>({ url: `/v1/albums/${albumId}`, data })
}
/** 初始化资产上传(banner/cover/logo;返回 staging 直传凭证) */
export function fetchInitAlbumAsset(albumId: string, data: Api.Album.AssetInitPayload) {
return request.post<Api.Album.AssetInitResponse>({
url: `/v1/albums/${albumId}/assets`,
data
})
}
/** 完成资产上传核验(幂等;返回短期签名 URL,即预览/刷新入口) */
export function fetchCompleteAlbumAsset(albumId: string, assetId: string) {
return request.post<Api.Album.AssetCompleteResponse>({
url: `/v1/albums/${albumId}/assets/${assetId}/complete`
})
}
/** 成员列表(含 owner) */
export function fetchAlbumMembers(albumId: string, params: { cursor?: string; limit?: number }) {
return request.get<Api.Album.MemberListResponse>({
url: `/v1/albums/${albumId}/members`,
params
})
}
/** 添加成员(按用户 ID;重复添加 409 MEMBER_CONFLICT) */
export function fetchAddAlbumMember(albumId: string, userId: string) {
return request.post<Api.Album.Member>({
url: `/v1/albums/${albumId}/members`,
data: { userId, role: 'collaborator' }
})
}
/** 移除成员(owner 不可移除 403;目标不存在幂等 204) */
export function fetchRemoveAlbumMember(albumId: string, userId: string) {
return request.del<void>({ url: `/v1/albums/${albumId}/members/${userId}` })
}
/** 账号前缀查询(邀请协作者前置;≥2 字符,激活用户;P4a 契约统一 AdminUserList) */
export function fetchLookupUsers(accountPrefix: string, limit = 10) {
return request.get<Api.Album.UserLookupListResponse>({
url: '/v1/admin/users',
params: { accountPrefix, limit },
showErrorMessage: false
})
}
/** 创建分类 */
export function fetchCreateCategory(albumId: string, data: Api.Album.CategoryCreatePayload) {
return request.post<Api.Album.Category>({
url: `/v1/albums/${albumId}/categories`,
data
})
}
/** 更新分类(translations 全量替换) */
export function fetchUpdateCategory(categoryId: string, data: Api.Album.CategoryUpdatePayload) {
return request.patch<Api.Album.Category>({ url: `/v1/categories/${categoryId}`, data })
}
/** 删除分类(204;分类下有照片时服务端 400 拒绝) */
export function fetchDeleteCategory(categoryId: string) {
return request.del<void>({ url: `/v1/categories/${categoryId}` })
}
/** 管理照片列表(全状态可见 + uploader 脱敏;uploadedAt 倒序游标) */
export function fetchAlbumPhotos(
albumId: string,
params: {
visibility?: Api.Album.PhotoVisibility
categoryId?: string
cursor?: string
limit?: number
}
) {
return request.get<Api.Album.AdminPhotoListResponse>({
url: `/v1/albums/${albumId}/photos`,
params
})
}
/** 调整照片可见性(仅 hidden ↔ published) */
export function fetchUpdatePhotoVisibility(photoId: string, visibility: 'hidden' | 'published') {
return request.patch<Api.Album.AdminPhoto>({
url: `/v1/photos/${photoId}`,
data: { visibility }
})
}
/** 软删除照片(204;deleted 为员工侧终态) */
export function fetchDeletePhoto(photoId: string) {
return request.del<void>({ url: `/v1/photos/${photoId}` })
}