修复生成图片OSS签名缓存链路

前端将完整阿里云OSS generated 地址归一为 legacy path 后走 read-url 换签。

platform-oss 为 generated 私有对象 PostObject 和 PutObject 写入 immutable Cache-Control。

补齐 shared-contracts 与 api-server 直传票据字段映射。

更新后端、运维文档和 Hermes 团队记忆,明确不使用服务端磁盘缓存兜底。
This commit is contained in:
2026-06-07 17:19:03 +08:00
parent decded991e
commit d3a3238028
10 changed files with 193 additions and 8 deletions

View File

@@ -140,6 +140,48 @@ describe('assetReadUrlService', () => {
);
});
test('resolveAssetReadUrl exchanges generated Aliyun OSS url for signed url', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
JSON.stringify({
ok: true,
data: {
read: {
objectKey:
'generated-puzzle-assets/puzzle-session-1/candidate-1/asset-1/image.png',
signedUrl: 'https://signed.example.com/puzzle.png',
expiresAt: '2099-01-01T00:10:00Z',
},
},
error: null,
meta: {
apiVersion: '2026-04-08',
routeVersion: '2026-04-08',
latencyMs: 1,
timestamp: '2099-01-01T00:00:00Z',
},
}),
{
status: 200,
headers: {
'Content-Type': 'application/json',
},
},
),
);
await expect(
resolveAssetReadUrl(
'https://genarrative-release.oss-cn-beijing.aliyuncs.com/generated-puzzle-assets/puzzle-session-1/candidate-1/asset-1/image.png',
),
).resolves.toBe('https://signed.example.com/puzzle.png');
expect(globalThis.fetch).toHaveBeenCalledTimes(1);
expect(String(vi.mocked(globalThis.fetch).mock.calls[0]?.[0])).toContain(
'legacyPublicPath=%2Fgenerated-puzzle-assets%2Fpuzzle-session-1%2Fcandidate-1%2Fasset-1%2Fimage.png',
);
});
test('resolveAssetReadUrl does not append cache busting query to OSS signed url', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(