feat(api): embed slots with ov replica and public feed

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-20 22:22:19 +08:00
parent eb51069a12
commit aee4b7ac46
23 changed files with 1890 additions and 10 deletions
@@ -21,7 +21,8 @@
// 保留 likeTotal/countVersion 权威计数);photo.hidden/
// deleted → photos_ov.visibility 置位(行不存在 = 从未复制即不可见,缺席
// 即撤下语义);asset → banner 投影 albums_ov.bannerAssetKey(cover/logo
// 仅推进版本,ov schema 无位);
// 仅推进版本,ov schema 无位);slot-state(P4b Task 2)→ embed_slots_ov
// 全量快照直投(albumPublicToken 弱关联相册、无依赖暂存,见 ③ 注);
// ⑤ sync_state upsert appliedVersion(watermark=false 实体行;水位行预留)。
// ⑥ 实时转发(Task 7):photo 事件**应用事务提交后**发布 OV_PHOTO_APPLIED
// 进程内事件 → realtime 网关向相册房间广播契约 photo.* 消息(stale/
@@ -40,6 +41,7 @@ import type {
CategoryUpdatedPayload,
PhotoPublishedPayload,
PhotoVisibilityChangedPayload,
SlotStateUpdatedPayload,
} from '@photo/contracts';
import type { Prisma } from '@photo/db-ov';
// 值导入(非 import type):构造器注入的 design:paramtypes 依赖类引用,
@@ -168,8 +170,11 @@ export class SyncInboxService {
});
return { outcome: 'stale' };
}
// ③ 依赖检查:非 album 事件须所属相册副本已存在,否则暂存。
if (event.entityType !== 'album') {
// ③ 依赖检查:非 album 事件须所属相册副本已存在,否则暂存。slot-state
// 豁免:展示位副本以 albumPublicToken 弱关联相册(公开读时按 albums_ov
// 现查,缺席=404 与相册下线同形),且解绑事件 albumId 为空串——相册行的
// 到达顺序对副本正确性无影响,暂存反而会让解绑事件无限积压。
if (event.entityType !== 'album' && event.entityType !== 'slot-state') {
const album = await tx.albums_ov.findUnique({
where: { albumId: event.albumId },
select: { albumId: true },
@@ -399,7 +404,31 @@ export class SyncInboxService {
// cover/logo:ov schema 无投影位(YAGNI,schema 注释),仅推进版本账本。
return undefined;
}
// slot-state 预留:受理即 applied(无投影),版本账本照常推进。
// slot-state(P4b Task 2):全量快照直投 embed_slots_ov(版本单调已由 ②
// 保证;同版本重断言同样走这里——整行替换幂等)。albumPublicToken null =
// 解绑(公开端随即 404);无墓碑,下线是 enabled=false 的快照替换。
if (event.entityType === 'slot-state') {
const p = event.payload as unknown as SlotStateUpdatedPayload;
await tx.embed_slots_ov.upsert({
where: { slotToken: p.publicToken },
create: {
slotToken: p.publicToken,
enabled: p.enabled,
albumPublicToken: p.albumPublicToken ?? null,
configVersion: p.configVersion,
settings: p.settings as unknown as Prisma.InputJsonValue,
entityVersion: event.entityVersion,
},
update: {
enabled: p.enabled,
albumPublicToken: p.albumPublicToken ?? null,
configVersion: p.configVersion,
settings: p.settings as unknown as Prisma.InputJsonValue,
entityVersion: event.entityVersion,
},
});
return undefined;
}
return undefined;
}