diff --git a/docs/plans/2026-09-28-广告位轮播-设计方案.md b/docs/plans/2026-09-28-广告位轮播-设计方案.md index 6f89d09..a926a64 100644 --- a/docs/plans/2026-09-28-广告位轮播-设计方案.md +++ b/docs/plans/2026-09-28-广告位轮播-设计方案.md @@ -38,7 +38,7 @@ model ads { id String @id @default(cuid()) title String? @db.VarChar(120) // 后台备注名,不展示观众 - imageObjectKey String @db.VarChar(512) // OSS 终键 photo-live/{env}/ads/{adId}.{ext} + imageObjectKey String @db.VarChar(512) // OSS 终键 photo-live/{env}/ads/{adId}/v{version}.{ext}(版本段:copyIfAbsent 禁止覆盖,换图新键——实施计划 Task 4 偏差登记) linkUrl String @db.VarChar(2048) // 仅 http/https(服务端校验) enabled Boolean @default(true) sortOrder Int @default(0) // 升序;并列按 createdAt 升序 @@ -83,7 +83,7 @@ model ads_ov { 3. `POST /v1/admin/ads` / `PATCH /v1/admin/ads/{id}` 携带 `imageStagingKey`: 服务端同事务内 `head()` 核验(存在 + bytes + contentType 与申明一致,banner complete 同口径)→ `copyIfAbsent()` 到终键 - `ads/{adId}.{ext}`(禁止覆盖语义天然防重复)→ 落库 + 写 outbox。 + `ads/{adId}/v{version}.{ext}`(禁止覆盖语义天然防重复)→ 落库 + 写 outbox。 编辑换图:新终键生效,旧终键对象不回收(OSS 生命周期兜底,与 banner 流同款)。 - StoragePort 现有 presignPut/head/copyIfAbsent 全复用,零新增存储能力。 diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts index 59a9b95..f929a42 100644 --- a/packages/storage/src/index.ts +++ b/packages/storage/src/index.ts @@ -13,6 +13,8 @@ export { OBJECT_KEY_ROOT, variantsObjectKey, watermarkVariantObjectKey, + adsStagingObjectKey, + adObjectKey, } from './keys'; export type { PhotoVariantName } from './keys'; diff --git a/packages/storage/src/keys.ts b/packages/storage/src/keys.ts index c4283aa..26cf027 100644 --- a/packages/storage/src/keys.ts +++ b/packages/storage/src/keys.ts @@ -33,3 +33,14 @@ export function watermarkVariantObjectKey( ): string { return variantsObjectKey(albumId, photoId, configVersion, 'watermark'); } + +/** 广告图 staging 键(两步上传第一步:presignPut 直传位)。 */ +export function adsStagingObjectKey(cuid: string, ext: string): string { + return `${OBJECT_KEY_ROOT}/ads/staging/${cuid}.${ext}`; +} + +/** 广告图终键(版本段=entityVersion:换图必然 bump 版本 → 新键, + * copyIfAbsent 禁止覆盖语义下天然防重复/防串图)。 */ +export function adObjectKey(adId: string, version: number, ext: string): string { + return `${OBJECT_KEY_ROOT}/ads/${adId}/v${version}.${ext}`; +}