Files
photo-live/packages/db-cn/src/index.ts
T
浅念 3681999667 feat(api): scheduled user sync with mock source (T30 semantics)
P4a Task 7 —— cn 侧用户定期同步(mock 源先行,G4 真实接口到位后仅替换
UserSourcePort provider,引擎/契约/表不变)。

数据模型(db-cn 迁移 20260920114149,utf8mb4_0900_bin 逐表校对):
- user_sync_runs:批次账本(trigger cron|manual、state running|success|
  partial|failed、cursor 水位、counts JSON、起止时间与错误摘要);
- user_sync_errors:单批次坏行明细(runId 索引);
- external_user_mappings:本地↔外部 ID 映射(@@unique([sourceSystem,
  externalUserId]),sourceVersion 逐行高水位);
- user_sync_state:单行状态(lastRunAt/lastSuccessAt/cursor/runCount)。

同步引擎(UserSyncEngine):
- Redis SETNX 分布式互斥(TTL 10min,锁值=runId,Lua CAS 释放);
  定时 tick 与手动触发共用,运行中批次复用 runId(202);
- 游标增量 + 周期全量对账(RECONCILE_EVERY_N_RUNS 默认 10);
- 逐页单事务提交(run counts+cursor / state cursor),分页中断→failed
  且水位不前进、会话不动;
- 红线遵守:sync 不写 password_hash/role/album_members/localLocked;
  空/部分页不误伤停用;上游恢复不解除本地锁定;
- 新用户按 pending 建(随机不可用哨兵哈希,激活+重置后方可登录);
- 停用即撤销该用户全部会话;本地预建同名账号走关联而非重建。

Admin API(/admin/user-sync):GET status|runs|errors?runId=、POST runs
(202 {runId});runs 为 startedAt+ID 倒序游标分页。

auth.service:登录对哨兵/损坏哈希加固(等代价 dummy verify,
401 而非 500,时序平坦)。

测试:user-sync.e2e-spec 12 用例(首轮全量/改名幂等/停用撤会话/
恢复不解锁/新增/空页/分页中断/部分无效/手动互斥/增量+对账/API 形状
与权限/pending 登录门);api e2e 22 套件 239 用例全绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 20:01:17 +08:00

23 lines
981 B
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// packages/db-cn/src/index.ts —— CN 侧唯一 Prisma client 出口。
// api(PrismaService)与 worker(Task 20)都从本包导入,schema/迁移只此一份。
// commonjs 包:消费方(api/worker)均为 CJS,tsc 直出无需打包。
// P4a Task 5 起增补用量域共享核心(usage.ts:聚合 + 阈值告警评估——api 与
// worker 的检查点共用,见该文件头)。
export { PrismaClient } from '@prisma/client';
export type { Prisma } from '@prisma/client';
export {
computeUsageAggregates,
evaluateUsage,
evaluateUsageSafe,
USAGE_ALERTS_LIST_LIMIT,
USAGE_THRESHOLDS_SINGLETON_ID,
} from './usage';
export type {
UsageAlbumAggregate,
UsageAggregates,
UsageAlertKind,
} from './usage';
// P4a Task 7:用户同步域单行状态表固定 id(迁移种子;reset.ts TRUNCATE 后
// 重种同值。引擎与 e2e 共用,避免字面量漂移)。
export const USER_SYNC_STATE_SINGLETON_ID = 'user-sync-state-single-row';