feat(ov): public read api on replicas with hot sorting
P2 Task 6(ov 公开读 API + viewer 切换):
- ov 装配 PublicOvModule(仅 app-ov.module):/public/albums/{token}、
/photos、/photos/{id} 三端点同契约读 ov 副本——translations/categories
自 JSON 列解析(防御性过滤),banner 经 ov 侧 STORAGE_PORT_OV 预签名
(同本地 MinIO;只读属约定,真实 OSS 只读凭证 P5),viewCount 取
album_stats 权威累计(无行默认 0;计数端点 Task 8 接线)
- 排序:latest=发布时间 keyset(shared/cursor.ts 同 cn);hot=likeTotal
desc + photoId desc 决胜,顺序按 `${albumId}|${category}` 短期快照
(内存 TTL 默认 30s,HOT_SORT_TTL_MS 惰性读取)——计数逐请求现读、
顺序冻结窗口内一致,偏移游标(base64url 整数)在快照内严格稳定;
非法 sort 400 VALIDATION_FAILED(cn 不感知该参数)
- 契约:viewCount 放宽为 integer|null(cn null、ov 权威值);
listPublicPhotos 增 sort 枚举(latest|hot,缺省 latest);
PhotoPublishedPayload 增 categoryId——补上 Tasks 3/4 管线缺口
(payloads/publish.service/sync-inbox 投影 + 关联测试随改)
- e2e(services/api/test/public-ov.e2e-spec.ts,7 用例):双入口同进程
起 cn+ov,cn API 种子→真实发送管线投递;断言相册/列表/单图与 cn
深等(仅签名 URL 剔除)、banner 真字节、参数矩阵 400/404、hot 排序
TTL 内快照(计数新/顺序旧)+TTL 过期翻转+偏移翻页、隐藏/下线/紧急
停用逐态撤下
- viewer 切 ov:API 基址 VITE_API_BASE(缺省 http://127.0.0.1:8081,
空串=同源网关;Vite 只读 apps/viewer/.env*,根 .env 对前端无效),
移除 /v1 dev 代理(CORS_ORIGINS 增 127.0.0.1:5175 直连两形);
playwright webServer 增 dev:ov;e2e 夹具 flushSync(albumId)=cn
bootstrap 快照 + deliverPendingOnce(sender 新导出)经真实管线投影
种子,deleteSeededPhoto 改软删让墓碑可同步;14 个 Playwright 全绿
(journey 管理/staffB 腿仍走 cn 8080)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
import { expect, test, type Page } from '@playwright/test';
|
||||
import { cleanupSeededAlbums, seedAlbum, seedCategory, seedPhoto } from './helpers';
|
||||
import {
|
||||
cleanupSeededAlbums,
|
||||
flushSync,
|
||||
seedAlbum,
|
||||
seedCategory,
|
||||
seedPhoto,
|
||||
} from './helpers';
|
||||
|
||||
// Task 7 瀑布流 Playwright(真实后端):35 张已发布照片(公开列表默认页 30)→
|
||||
// 初始 30 + 滚动取第 2 页 5 张;分类 chips 过滤;新照片轮询提示(__viewerPollMs
|
||||
// 注入缩短间隔,见 PhotoFeed pollIntervalMs 的 dev/test 覆盖钩子)。
|
||||
// 种子直种 cn 侧,seedFeedAlbum/新增照片后 flushSync 投影到 ov(viewer 读 ov)。
|
||||
|
||||
test.afterAll(async () => {
|
||||
await cleanupSeededAlbums();
|
||||
@@ -39,6 +46,7 @@ async function seedFeedAlbum(alias: string): Promise<{ albumId: string; token: s
|
||||
publishedAt: new Date(base - (i + 1) * 60_000),
|
||||
});
|
||||
}
|
||||
await flushSync(album.id);
|
||||
return { albumId: album.id, token: album.publicToken };
|
||||
}
|
||||
|
||||
@@ -109,7 +117,7 @@ test('new-photo poll hint appears; click refreshes to include new photos', async
|
||||
await expect(page.locator('.cell')).toHaveCount(35);
|
||||
await expect(page.locator('.new-photo-bar')).toHaveCount(0);
|
||||
|
||||
// 轮询期间直种 2 张更新的照片(publishedAt 晚于全部既有)。
|
||||
// 轮询期间直种 2 张更新的照片(publishedAt 晚于全部既有)并同步到 ov。
|
||||
const base = Date.now();
|
||||
for (let i = 0; i < 2; i++) {
|
||||
await seedPhoto(albumId, `poll-new-${i}`, {
|
||||
@@ -119,6 +127,7 @@ test('new-photo poll hint appears; click refreshes to include new photos', async
|
||||
publishedAt: new Date(base + (i + 1) * 60_000),
|
||||
});
|
||||
}
|
||||
await flushSync(albumId);
|
||||
|
||||
// 提示条出现(不自动插入——仍 35 张)。
|
||||
await expect(page.locator('.new-photo-bar')).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
Reference in New Issue
Block a user