feat(api): listUploads 会话条目内嵌照片预览(现签 thumb/browse)
- 契约 UploadSessionPhoto 增可选 preview(PhotoPreview;旧客户端零 break) - list() 批量查 photo_variants,uploader 视角签发:thumb+browse 齐备且 未隐藏/删除才签(「隐藏是运营动作」口径从严;无变体→字段缺省) - presign 本地 SigV4 无网络开销,TTL 同全局 900s - 顺手修 batch-ops ⑦:交接后上传 bytes 撞查重改 71007(登记旧账清偿) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -652,7 +652,8 @@ describe('batch hide + owner transfer (e2e)', () => {
|
||||
expect(oldOwnerManage.status).toBe(403);
|
||||
expect(oldOwnerManage.body.code).toBe(ErrorCodes.AUTH_FORBIDDEN);
|
||||
|
||||
// 保留协作者仍可上传(成员行有效即「已加入的相册」)。
|
||||
// 保留协作者仍可上传(成员行有效即「已加入的相册」)。bytes=71007
|
||||
// 避开本册 ownerA 已传照片的字节数(init ③.5 查重会 409 拦同 bytes)。
|
||||
const upload = await request(cnBase)
|
||||
.post('/v1/uploads')
|
||||
.set('Authorization', `Bearer ${ownerAToken}`)
|
||||
@@ -660,7 +661,7 @@ describe('batch hide + owner transfer (e2e)', () => {
|
||||
albumId: albumAId,
|
||||
clientTaskId: 'bo-upload-after-transfer',
|
||||
filename: 'after-transfer.jpg',
|
||||
bytes: 1000,
|
||||
bytes: 71007,
|
||||
contentType: 'image/jpeg',
|
||||
});
|
||||
expect(upload.status).toBe(201);
|
||||
|
||||
@@ -876,6 +876,72 @@ describe('uploads renew & complete & history (e2e)', () => {
|
||||
expect(bad.body.code).toBe(ErrorCodes.VALIDATION_FAILED);
|
||||
});
|
||||
|
||||
it('GET /uploads preview:变体齐备且未隐藏/删除 → 现签 thumb/browse;无变体或 hidden → 无 preview 字段', async () => {
|
||||
// C-happy:补齐 thumb/browse 变体 + ready/published → 应签发。
|
||||
await prisma.photo_variants.createMany({
|
||||
data: (['thumb', 'browse'] as const).map((variant) => ({
|
||||
photoId: cHappy.photoId,
|
||||
variant,
|
||||
objectKey: `photo-live/dev/albums/${albumId}/variants/${cHappy.photoId}/1/${variant}.jpg`,
|
||||
bytes: 100,
|
||||
version: 1,
|
||||
width: 480,
|
||||
height: 300,
|
||||
})),
|
||||
});
|
||||
await prisma.photos.update({
|
||||
where: { id: cHappy.photoId },
|
||||
data: { processingState: 'ready', visibility: 'published' },
|
||||
});
|
||||
// C-bytes:变体齐备但 hidden → 不签(「隐藏是运营动作」,历史回退占位)。
|
||||
await prisma.photo_variants.createMany({
|
||||
data: (['thumb', 'browse'] as const).map((variant) => ({
|
||||
photoId: cBytes.photoId,
|
||||
variant,
|
||||
objectKey: `photo-live/dev/albums/${albumId}/variants/${cBytes.photoId}/1/${variant}.jpg`,
|
||||
bytes: 100,
|
||||
version: 1,
|
||||
width: 480,
|
||||
height: 300,
|
||||
})),
|
||||
});
|
||||
await prisma.photos.update({
|
||||
where: { id: cBytes.photoId },
|
||||
data: { processingState: 'ready', visibility: 'hidden' },
|
||||
});
|
||||
|
||||
const res = await listUploads(memberToken, { limit: 10 });
|
||||
expect(res.status).toBe(200);
|
||||
type Item = {
|
||||
clientTaskId: string;
|
||||
photo: {
|
||||
preview?: {
|
||||
thumb: { url: string; expiresAt: string };
|
||||
browse: { url: string; expiresAt: string };
|
||||
};
|
||||
};
|
||||
};
|
||||
const byTask = new Map<string, Item>(
|
||||
(res.body.items as Item[]).map((i) => [i.clientTaskId, i]),
|
||||
);
|
||||
|
||||
// 签发:URL 含变体对象键 + 签名参数;expiresAt 为 ISO 时间串。
|
||||
const happy = byTask.get('C-happy')!;
|
||||
expect(happy.photo.preview).toBeDefined();
|
||||
expect(happy.photo.preview!.thumb.url).toContain(
|
||||
`/variants/${cHappy.photoId}/1/thumb.jpg?`,
|
||||
);
|
||||
expect(happy.photo.preview!.browse.url).toContain(
|
||||
`/variants/${cHappy.photoId}/1/browse.jpg?`,
|
||||
);
|
||||
expect(typeof happy.photo.preview!.thumb.expiresAt).toBe('string');
|
||||
|
||||
// 无变体(未处理)与 hidden → 无 preview 字段(App 回退占位图标)。
|
||||
expect(byTask.get('C-type')!.photo.preview).toBeUndefined();
|
||||
expect(byTask.get('C-bytes')!.photo.preview).toBeUndefined();
|
||||
expect(byTask.get('R1')!.photo.preview).toBeUndefined();
|
||||
});
|
||||
|
||||
it('BullMQ 入队:complete 后 photo-process 队列恰有一条对应 job(重复 complete 不重复入队)', async () => {
|
||||
// C-bytes(重试成功)与 C-happy 各完成一次 → 各入队一次;jobId 以 photoId
|
||||
// 派生去重。等待短暂时间让 enqueue 落 Redis(生产者为 await,理论即时)。
|
||||
|
||||
Reference in New Issue
Block a user