Files
photo-live/.env.example
T
浅念 85b2da3db5 feat(ov): idempotent batched likes and realtime broadcast
P2 Task 7(点赞批次端点 + ov 实时网关):
- interactions 模块(仅 ov 装配):POST /public/albums/{token}/photos/
  {photoId}/likes——相册不可见/照片未发布 404、delta 1..20 与
  sessionId/batchId 1..64 校验 400;单事务 like_batches 唯一插入
  (P2002=幂等重试,batchAccepted=true 不重加)→ photos_ov.likeTotal/
  countVersion 原子自增回读 → outbox_stat_events(kind=likes) 兜底;
  Redis ov(REDIS_URL_OV)INCRBY+TTL 会话/IP 双窗口限流(默认
  500/2000,超限 429 RATE_LIMITED),幂等重试先于限流不耗额度
- 过期批次重放边界(24h 清理后的 batchId 重插会再次计入):接受该
  边界——无永久账本可查(保留全量批次行违背清理策略),会话/IP 窗口
  上限即重放天然封顶(同一 sessionId 重放挤占自身额度),偏差已在
  likes.service 头注释登记,Task 8 统计侧按清理后语义处理
- realtime:ContractWsAdapter(契约 {type,...} 帧分发,ws 与 HTTP 同
  端口挂 /v1/realtime)+ RealtimeGateway 相册房间模型——subscribe 逐
  相册校验 isAlbumVisibleOv,失败回 error 帧且连接保持;应用层 ping→
  pong 回显 ts;传输层 HEARTBEAT_MS 周期 ping 踢死连接;点赞提交经
  EventEmitter2(单进程,多实例 Redis pub/sub 登记 P5)触发
  likes.burst 500ms 合并窗(delta 聚合、originBatchIds 供客户端过滤
  自身批次);sync-inbox 应用事务提交后发 OV_PHOTO_APPLIED →
  photo.published/hidden/deleted 按房间广播(stale/duplicate/暂存/
  缺席即撤下不转发)
- 契约:REALTIME_WS_PATH、ServerMessage 增 error 帧(穷尽锁 7 成员
  + 类型测试)、LIKE_SESSION_WINDOW_DELTA/LIKE_IP_WINDOW_DELTA/
  LIKE_BURST_MERGE_MS 常量与 .env.example
- e2e(likes-realtime.e2e-spec.ts,6 用例):A09 三批累加/幂等重试/
  outbox 行、小额度 429、ping/pong+幽灵 token error 帧连接保持、
  A10 双房间隔离(先证非空调订)、合并窗单 burst(delta 求和+双
  originBatchIds)、cn 隐藏→房间收 photo.hidden+隐藏后点赞 404;
  凡进程内起 AppOvModule 的 spec(sync×2/public-ov/emergency)补装
  ContractWsAdapter 与生产 main-ov 一致——api e2e 168(162+6)

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

58 lines
3.0 KiB
Bash
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.
DATABASE_URL_CN=mysql://root:root@127.0.0.1:3306/photo_cn?connection_limit=10
REDIS_URL_CN=redis://127.0.0.1:6379
S3_ENDPOINT=http://127.0.0.1:9000
S3_REGION=us-east-1
S3_ACCESS_KEY=photo-dev
S3_SECRET_KEY=photo-dev-secret
S3_BUCKET=photo-dev
SIGNED_URL_TTL_SECONDS=900
AUTH_SESSION_TTL_SECONDS=2592000
UPLOAD_MAX_BYTES=52428800
PORT=8080
# viewer(P2 Task 6 起直连 ov 8081,跨源)需两形 origin:Playwright/本机访问
# 常走 127.0.0.1,浏览器 Origin 头按实际 host 发出——localhost 与 127.0.0.1
# 是两个不同 origin,缺一则直连 fetch 被 CORS 拦截。admin(5176)走自身
# /v1 代理同源,不依赖此清单。
CORS_ORIGINS=http://localhost:5175,http://127.0.0.1:5175,http://localhost:5176
LOG_LEVEL=info
ADMIN_ACCOUNT=admin
ADMIN_PASSWORD=admin-secret-change-me
# ── P2 双地域仿真(ov 域)──
# 本任务(Task 1)即被读取的键:内部同步 HMAC 密钥与 ov 双库连接。
SYNC_INTERNAL_KEY=dev-sync-secret-change-me
DATABASE_URL_OV=mysql://root:root@127.0.0.1:3309/photo_ov?connection_limit=10
REDIS_URL_OV=redis://127.0.0.1:6381
# P2 Task 2:ov 入口监听端口(main-ov.ts 读取;与 cn PORT=8080 同机双入口)。
PORT_OV=8081
# P2 Task 3:sync 发送器(services/sync)推送目标——ov 入口基址(不含路径,
# 端点路径由发送器拼接为 /v1/internal/sync/content-batches)。
OV_API_URL=http://127.0.0.1:8081
# P2 Task 4:对账调度器(services/sync reconciler)拉取 reconcile/digest 与
# 触发 bootstrap 的 cn 入口基址(不含路径)。
CN_API_URL=http://127.0.0.1:8080
# 以下为 P2 决策初值(D07/D11)的 env 覆盖键:默认值见
# packages/contracts/src/constants.ts(HEARTBEAT_MS 见 src/realtime.ts),
# 此处登记供运维可见与调整;SYNC_BATCH_SIZE/SYNC_PUSH_INTERVAL_MS 自 Task 3
# 起、SYNC_RECONCILE_INTERVAL_MS 自 Task 4 起被 services/sync 读取,其余为
# 前瞻登记(Task 7+ 逐个接线)。
SYNC_BATCH_SIZE=100
SYNC_PUSH_INTERVAL_MS=2000
SYNC_RECONCILE_INTERVAL_MS=300000
# P2 Task 6:ov 公开列表 sort=hot 的排序快照 TTL(毫秒,缺省 30000)——
# 顺序冻结窗口(点赞数仍逐请求现读),由 services/api public-ov 惰性读取;
# e2e 以短 TTL 加速翻转断言。另:viewer 的 API 基址是构建期变量
# VITE_API_BASE(缺省 http://127.0.0.1:8081 ov 入口;空串=同源网关)——
# Vite 只读 apps/viewer/.env*,根 .env 对前端无效(见 apps/viewer/src/api/client.ts)。
HOT_SORT_TTL_MS=30000
STAT_RETURN_INTERVAL_MS=60000
VISIT_WINDOW_MINUTES=30
LIKE_BATCH_WINDOW_HOURS=24
# P2 Task 7:点赞基础限速(Redis ov 滑窗,窗口=LIKE_BATCH_WINDOW_HOURS)与
# likes.burst 实时合并窗口——sessionId 窗口累计 delta 上限 / 来源 IP 上限 /
# 同照片批次合并毫秒数(0=不合并)。默认值见 packages/contracts/src/constants.ts。
LIKE_SESSION_WINDOW_DELTA=500
LIKE_IP_WINDOW_DELTA=2000
LIKE_BURST_MERGE_MS=500
VISIT_REQUEST_TTL_MINUTES=35