feat(api): batch hide with per-item results and owner transfer
P4a Task 2(A16/A17):
- POST /albums/{albumId}/photos/hide-batch:逐项结果(部分成功)——
不存在/他册 → ok:false PHOTO_NOT_FOUND;非 published(unpublished/
deleted)→ ok:false PHOTO_NOT_PUBLIC;已 hidden → ok:true 幂等 no-op
(同值不产生第二次事件/审计/版本,P1 语义);published → 逐照片事务
(条件更新 + outbox photo.hidden 高优先级 + audit photo.hide)。
results 与请求同序;权限同管理照片列表(仅创建者或管理员,404 防枚举);
单批 1..200(DTO 层 400)。
- POST /admin/albums/{albumId}/transfer:admin-only owner 移交——事务内
条件更新 ownerId+entityVersion+1(乐观重试)+ 成员互换(目标 upsert 为
owner,原 owner 按 keepAsCollaborator 降级 collaborator 或删行)+
outbox album.updated(标准载荷,版本推进)+ audit album.transfer
{from,to,kept}。目标校验同成员邀请资格谓词(active/未锁定/未停用);
自交接 400;photos.uploaderId 不动(A17 历史上传人不变)。
- e2e(batch-ops.e2e-spec,双入口 harness):8 用例覆盖逐项结果/幂等/
入参权限矩阵/ov 传播(真实发布链→隐藏→photos_ov=hidden+公开列表剔除)/
keep 两分支/目标校验/交接后权限(新 owner 管理、旧 owner 保留协作者
可上传)。
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -300,6 +300,23 @@ export class CategoryUpdateRequestDto {
|
||||
translations?: CategoryTranslationDto[];
|
||||
}
|
||||
|
||||
// ── 负责人交接(P4a Task 2)──
|
||||
|
||||
/**
|
||||
* 契约 AlbumTransferRequest(A17):targetUserId 须为可被指定为 owner 的账号
|
||||
* (存在且已开通——credentialStatus=active、!localLocked、sourceStatus≠disabled,
|
||||
* 服务层对存量行校验);keepAsCollaborator=false 时原 owner 移出成员表。
|
||||
*/
|
||||
export class AlbumTransferRequestDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(64) // users.id VarChar(64)
|
||||
targetUserId!: string;
|
||||
|
||||
@IsBoolean()
|
||||
keepAsCollaborator!: boolean;
|
||||
}
|
||||
|
||||
// ── 资产(P1b Task 3)──
|
||||
|
||||
/** 契约 AssetKind:banner(观众页宣传图,PublicAlbum.banner 消费方);cover/logo 建链就绪,展示消费随后续阶段。 */
|
||||
|
||||
Reference in New Issue
Block a user