feat(sync): cn event coverage, bootstrap and outbox sender

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-18 15:21:41 +08:00
parent 3d6e9ce175
commit 65726702f3
43 changed files with 3333 additions and 181 deletions
+19 -2
View File
@@ -29,8 +29,10 @@
// presign 为 SigV4 本地计算,无网络副作用。
import { Inject, Injectable } from '@nestjs/common';
import type { components } from '@photo/contracts';
import type { Prisma } from '@photo/db-cn';
import type { StoragePort } from '@photo/storage';
import type { AuthUser } from '../auth/session.guard';
import { buildAssetUpdatedPayload } from '../sync/payloads';
import { STORAGE_PORT } from '../uploads/storage.provider';
import {
assetsObjectKey,
@@ -164,13 +166,28 @@ export class AssetsService {
);
}
// ② 事务:条件更新抢占(并发 complete 恰一人胜出)+ 胜者独占 audit。
// ② 事务:条件更新抢占(并发 complete 恰一人胜出,state pending→effective
// 迁移同时自增 entityVersion 并写 outbox asset.updated——P2 Task 3 起仅
// 生效迁移产生事件,幂等重复 complete 不再发)+ 胜者独占 audit。
await this.prisma.$transaction(async (tx) => {
const race = await tx.album_assets.updateMany({
where: { id: asset.id, state: 'pending' },
data: { state: 'effective' },
data: { state: 'effective', entityVersion: { increment: 1 } },
});
if (race.count === 1) {
const effective = await tx.album_assets.findUniqueOrThrow({
where: { id: asset.id },
});
await tx.outbox_events.create({
data: {
type: 'asset.updated',
albumId,
entityVersion: effective.entityVersion,
payload: buildAssetUpdatedPayload(
effective,
) as unknown as Prisma.InputJsonValue,
},
});
await tx.audit_logs.create({
data: {
actorId: user.id,