浅念
66d1eb3e9f
feat(embed,ui04): E01 v2 contract skeleton — displayMode/scrollSpeed/reactionsEnabled, v2 loader entry and fixed 195x280 shell
...
- contracts: EmbedSlotSettings optional v2 fields (carousel default) + SlotSettingsPayload; gen + redocly green
- api: DTO validation + normalization full-set (cn manage + ov public projection share normalizeStoredSettings)
- embed-loader: params v2 parse/merge/url family, loader-v2.js fixed shell (border/radius/#10161E/safe-area, resize ignored, stack gap 12), dual-artifact build+sync; v1 source frozen
- viewer: EmbedPage v=2 branch, EmbedStream (viewport/track/real anchors/collapse/empty), stream.ts pure fns, i18n zh/en
- admin: types + utils (create default vertical-stream, clampScrollSpeed, v1/v2 snippet selection)
- tests: loader 36 (+9 v2), viewer 222, admin 82, api unit 51, v1 embed e2e 20, v2 E01 gate 2 (195x280 desktop+mobile cross-origin, no footer)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 12:50:28 +08:00
浅念
8ee69598bf
feat(mobile): UI-03 photos and share pages (17-20)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 02:45:34 +08:00
浅念
ab51616c70
feat(mobile): UI-03 capture and upload pages (13-16)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 02:06:23 +08:00
浅念
8bd09eefb9
feat(mobile): UI-03 forms and collaboration pages (09-12)
...
- Page 09 create: grouped form (Event details / Description & languages),
required default-language selector, zh-CN/en tabs with per-locale
title/description/venue/address, explicit timezone field, bottom save
button kept keyboard-reachable, create mode has no Access tab
- Page 10 edit: Details/Access tabs sharing one draft; Access has four
switches with explanations; original downloads constrained by downloads
(disabled + forced off in PATCH); unsaved-changes PopScope confirm;
save commits all changes in one minimal PATCH
- Page 11 members: Owner group + Collaborators list with initial avatars
and per-row remove; full-width Invite colleague opens account-search
bottom sheet (debounced prefix search, race guard, already-added state);
remove confirm names the member
- Page 12 categories: extracted to standalone page with per-row edit/
delete menu and New category sheet (language tabs + name + sort order);
delete confirm mentions photo impact; collaborator browse-only; old
inline section removed from album detail
- l10n: new form/members/categories keys both locales; regenerated
- tests: form 14, members 6, categories 7, detail nav 1; full suite green
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 01:23:02 +08:00
浅念
5715c608d8
feat(mobile): UI-03 album workspace pages (05-08)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 00:57:59 +08:00
浅念
95e8417553
feat(mobile): UI-03 theme calibration and account pages (01-04)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-22 00:28:39 +08:00
浅念
2a489aa16a
docs: UI-02 acceptance record and visual verification
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 23:10:58 +08:00
浅念
160e7e5f1a
feat(viewer): UI-02 heart particles, interaction notices, share and download
...
Task 5 (V05 §5.4/§6): decorative heart particles on the lightbox stage
(local like clicks + others' WS bursts for the current photo), compact
cross-photo interaction notices above the action bar (click to locate),
navigator.share-first share orchestration with clipboard fallback for
album and photo deep links (lang param), and download failures attributed
to the photo that triggered them. Self-echo dedup stays in the realtime
router (own batches never produce particles/notices); particles cap at
12 with visibility/unmount cleanup and are suppressed under reduced
motion; notices cap at 3 with ~4s auto-dismiss and throttled SR summary.
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 22:20:19 +08:00
浅念
89f45896b7
feat(viewer): UI-02 dark immersive photo viewer with thumbnail strip
...
V04 (方案 §5):PhotoLightbox 模板重构为深色沉浸布局——顶栏(关闭/位置
计数/⋯ 减少动画菜单)+ 舞台(contain 大图 + 边缘 ‹› + 粒子/互动占位层)+
缩略图条 + 底部操作栏;业务逻辑(序列导航/缩放手势/签名刷新/下载/点赞)
原样保留。
- 新增 viewer/position.ts:§5.2 位置计数真实回退(solo→照片预览;无
total 未到末页→已加载 N 张;全部加载→i/n;不伪造总数)
- 新增 ThumbnailStrip:当前 ±3 窗口(≤7 项)、主色 2px 描边、thumb 变体、
滚入可见(遵守减少动画)、点击选择不新增历史
- 新增 PhotoActionBar:Like(coral 心形+计数+连续点赞提示)/Share/
Download 均分大触控;下载关闭整项移除;原图二级菜单(▾ + 长按)
- 模态可用性:body 滚动锁、焦点进入/困住/归还触发元素、Esc 先收菜单、
菜单内左右键不抢占、dvh + 安全区、chrome 区手势/滚轮隔离
- 分享 V04 最小实现(复制当前深链,support/clipboard 从 AlbumHeader
提取共用);navigator.share 编排属 Task 5
- zh-CN/en 新词条;vitest 新增 23 例(位置回退/缩略图/操作栏/焦点/滚锁)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:58:09 +08:00
浅念
3d89fcdb99
feat(viewer): UI-02 responsive waterfall with like badges
...
Task 3 (V03 瀑布流重排, 方案 §4.3):
- responsiveColumns 断点:<600px 双列 gap4 / 600-959px 三列 gap6 / ≥960px 四列
gap8;PhotoFeed 经既有 ResizeObserver 容器测量整体重排
- 照片卡片去卡片化:图片边到边 + 4px 圆角,无白色厚包裹/无常驻文件名;
右下角点赞角标(半透明暗底渐变、白心 16px、12px tabular-nums 计数),
@click.stop 独立交互不冒泡开大图;计数经 Intl 分组
- 新照片提示改白色 pill(viewer.newPhotos,接口仅 hasNew 不伪造数量);
refreshFromHint 不再强制回顶(用户浏览位置保持)
- 首屏骨架按 skeletonSources 竖/横/方比例走真实瀑布流放置(非均匀网格);
宽高缺失/NaN/Infinity 回退 4:3 安全比例(无 NaN/无限高度)
- 保留懒加载 IO、游标分页、photoId 稳定键、翻页失败底部重试与已到底文案
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:34:56 +08:00
浅念
9c3915b387
feat(viewer): UI-02 visual foundation, top bar and album info
...
V01+V02 (Task 2 of the UI-02 viewer redesign plan):
- viewer-tokens.css: UI-02 §3.1 design tokens scoped to .viewer-shell
(never :root), so embed routes consume none of them
- App.vue: normal routes get a sticky white top bar (PhotoLive brand +
compact locale switcher + lightweight "..." menu holding the
reduce-motion toggle); global 16px content padding removed so banner
and photos run edge-to-edge; embed shell branch untouched
- LocaleSwitcher: compact "EN/中文 + caret" trigger with a proper menu
(current language checked), 44px touch target, Escape/outside close
- AlbumHeader (new): banner (2.4:1 mobile, 320px desktop cap,
object-fit contain, collapses on missing/failed), 2-line expandable
title + share button (clipboard copy with selectable-text fallback),
3-line expandable pre-line description, meta row (Intl-formatted
view count with em-dash fallback for unknown, album-timezone dates
with full range on title, venue), Latest/Popular 44px tabs and
category pills from the API with horizontal scroll + overflow fade
- ActivityPage: delegates the info area to AlbumHeader; sequence
reset/sort-lock/category-by-id semantics unchanged; desktop content
capped at 1120px
- i18n: viewer.* strings (brand/share/views/readMore/latest/popular/
allCategories/noPhotos/newPhotos/reduceMotion/settings) in zh-CN+en
- tests: viewer-shell (token source + normal/embed isolation) and
album-header (8 cases) specs; activity/i18n specs updated for the
menu switcher and the em-dash unknown view count; e2e selectors
refreshed
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:22:36 +08:00
浅念
ac23a4c40e
feat(admin): UI-01 embed slot config form and live preview
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 19:26:48 +08:00
浅念
ddf26a177f
feat(embed,viewer): UI-01 complete carousel, loader config and accessibility
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 18:55:15 +08:00
浅念
1f67b7c818
feat(mobile): UI-01 upload center, capture and auxiliary pages
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 17:20:43 +08:00
浅念
f6929ae18f
feat(mobile): UI-01 album cards, photo grid and full viewer
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 15:55:44 +08:00
浅念
c94b9e5e58
feat(mobile): UI-01 theme system, shared components and navigation
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 14:55:29 +08:00
浅念
8f4ad11bd5
feat(contracts,api): UI-01 cover, preview, scope=own and embed config extensions
...
契约扩展(全部可选/可空,旧客户端零感知)+ cn/ov 实现 + e2e 权限矩阵:
- AlbumSummary += cover(kind=cover state=effective 最新行的短期签名 URL,
列表/详情按页批量组装非 N+1;未配置为 null)
- AdminPhoto += preview{thumb,browse}(变体齐备且未删除才签发;隐藏仅
admin/owner;协作者仅自己上传,scope=own 服务端过滤 uploaderId,
成员资格逐请求裁决)
- listAlbumPhotos += scope=all|own(缺省 all 行为不变:仅 owner/admin,
协作者 403;own 任何有效成员)
- EmbedSlotSettings += imageFit/offsetX/offsetBottom/zIndex(cn PATCH 校验
→ slot-state 投影 → ov 公开回读;settings 为 Json 列免迁移,读侧
normalizeStoredSettings 兜旧形状默认值)
- Flutter 模型同步(SignedUrl/PhotoPreview/AlbumPhotoScope,可空容错)
e2e:ui01-contracts 9 例(封面选取/字节取回/N+1 计数断言、preview 权限
矩阵、scope 过滤与 403/404/400)+ embed ⑨ 配置链路;全量 e2e 257 通过。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 14:09:35 +08:00
浅念
5cdf828ff5
docs: G1 gate closure recorded in master plan and P5 draft
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 10:17:16 +08:00
浅念
7385785504
feat(i18n): T15 full-stack language audit with coverage assertions
...
- viewer/admin 错误码→文案映射按契约 ErrorCodes 枚举全集登记(机器守护
spec 遍历枚举断言无缺漏 + 映射键在 zh/en 词条均存在 + locale 键集
递归相等);mobile 镜像补齐 DOWNLOAD_DISABLED(P4a 增补漏登)
- 漏译修复:viewer.photoRemoved en 侧缺失(审计测试捕获);
DOWNLOAD_DISABLED 观众侧专属文案 + useDownload 专属提示接线;
admin apiMsg 增 RATE_LIMITED/ALBUM_NOT_PUBLIC/DOWNLOAD_DISABLED/
SYNC_AUTH_FAILED 四码词条
- 运行时抽查:admin 主旅程 en 版 e2e(登录→相册管理全英文 + 偏好
持久);embed iframe lang=en 渲染与深链跟随 e2e(zh 侧既有用例覆盖)
- contracts 新增 ./errors 浏览器安全子路径(errors.ts 纯 TS);
admin 增 @photo/contracts devDep(仅测试消费)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 23:44:27 +08:00
浅念
0238f505c4
feat(admin): embed slot management with code snippet
...
P4b Task 5:/system/embed-slots 管理页(Task 8/9 页面模式:useCursorList
游标累积 + 对话框治理)。列表(名称/绑定相册/启停/configVersion/更新时间,
启停开关行内 PATCH → configVersion+1 toast 提示)+ 新建/编辑对话框
(设置表单位置/尺寸/间隔/语言/默认收起,intervalMs 契约区间夹取;编辑按
整体替换语义提交完整 settings)+ 绑定相册对话框(相册标题/ID 搜索选中 →
确认框携带 A22 文案「configVersion 将递增,官网无需改动」;清空即解绑)+
嵌入代码卡({EMBED_ORIGIN}/embed/v1/loader.js + data-slot 令牌,一键复制;
EMBED_ORIGIN=VITE_EMBED_ORIGIN 缺省本机 viewer dev,.env.example 登记)。
- API 层 src/api/embedSlots.ts + types/api/embedSlot.d.ts(契约镜像)
- vitest 9 例:表单→载荷(缺省+夹取)、编辑载荷收敛、snippet 拼接、
环境基址兜底、绑定变更分类、zh/en 键一致 + A22 短语断言
- Playwright e2e(真实 cn+ov 后端):新建→绑定(gov-photos 直种 +
seed-fixture 新增 sync-flush 模式经 bootstrap/真实发送管线投影 photos_ov)
→嵌入代码卡含 token→ov 公开 feed 返回相册照片→换绑 configVersion+1 +
feed 切换(A22)→停用 feed 404;admin playwright 配置补 ov webServer
- i18n zh/en(embedSlotsManage.* + menus.system.embedSlots)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 23:25:59 +08:00
浅念
8f2c21d25a
feat(viewer): embed carousel iframe with visibility pause
...
P4b Task 4:宿主 loader 以 iframe 载入的轮播页(/embed/:slotToken,裸布局)。
- src/embed/feed.ts:纯函数集——A22 旧响应裁决(dispatch seq + configVersion
双闸:过期/回看丢弃、等值差分合并、升值全量重置)、照片队列差分(新照片
追加队尾、缺席淘汰、thumb 签名刷新)、interval/高度夹取、轮询退避;
- src/embed/useEmbedFeed.ts:取数编排(If-None-Match → 304 零处理)、轮询
(查询 interval 覆盖 → EMBED_POLL_INTERVAL_MS,连续 5 失败 ×5 退避,成功
归零;404/410 静默 collapse 隐藏自身)、A23 不可见暂停(document 隐藏 +
IntersectionObserver 滚出视口 + 本地收起;恢复立即校准)、轮播 intervalMs
切换;__embedPollMs e2e 注入点;
- src/embed/postMessage.ts:上行 resize/collapse(referrer origin 收敛,缺失
退 '*'——载荷无敏感数据,宿主侧 loader source+origin 双校验)、下行 expand
(event.source === window.parent 白名单);
- EmbedPage.vue:交叉淡入淡出(仅 thumb 小图省流量)、点击 window.open 单图
深链(noopener)、计数圆点、「照片即将上线」空态卡、×收起;lang 查询
(auto→相册 defaultLocale)驱动嵌入词条子集;
- api client:fetchPublicEmbedSlot(304/ETag 分支,不走 apiRequest);
- e2e:宿主夹具页 public/__test__/embed-host.html(仅测试资产)+ embed.spec 7
用例(轮换/新标签深链/收起记忆/窄屏 sm/轮询增量/换绑重置/空态/幽灵槽);
helpers 增展示位 admin API 种子 + flushOutbox;
- vitest 18 用例(裁决矩阵/队列差分/夹取/退避/304/可见性状态机)。
配套修正:api CORS exposedHeaders ETag(跨源浏览器可读,304 语义成立);
contracts 新增浏览器安全子路径 ./constants(根出口 re-export internal-sync
的 node:crypto 会打进浏览器包);eslint ignore embed-loader 产物拷贝
(Task 3 起 viewer lint 红,先修)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 23:02:07 +08:00
浅念
ddb4c8175f
feat(embed): versioned loader with postMessage whitelist
...
packages/embed-loader:手写零依赖加载器(esbuild bundle+minify→dist/v1/loader.js,
IIFE target es2018 4.7KB)。自发现 EMBED_ORIGIN(currentScript 兜底扫描)、
data-slot 必填缺失静默 no-op、__photoliveEmbedV1 标记去重+同槽去重、
窄屏 <480px 强制 sm+默认收起、postMessage 双向白名单(source+origin 严格
校验,resize 夹取 60-640)、sessionStorage 收起记忆、零全局零 console
全静默。sync 脚本携 sha256 校验拷贝至 viewer publicDir(两份产物入库);
src/params.js 为 loader/单测共用参数源(11 vitest 用例)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 22:31:40 +08:00
浅念
f08d68bebf
feat(admin): batch ops, transfer, watermark, usage and qr share pages
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 21:10:30 +08:00
浅念
618d1d796c
feat(admin): account governance and user sync pages
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 20:26:53 +08:00
浅念
2bb6f291d3
feat(ov,viewer): two-tier download with wechat guidance
...
P4a Task 4(T12 收口):ov 域公开照片下载端点 + viewer 下载 UI。
- interactions 增 downloads:GET /public/albums/{token}/photos/{photoId}
/download?variant=browse|original。两级开关(A18):browse 需
downloadEnabled;original 需叠加 originalDownloadEnabled(级联,browse 关
则两者均 403 DOWNLOAD_DISABLED)。水印相册 browse 现签 watermark pattern
key(browse-key 解析抽取 shared/ov-browse-key 与 public-ov 复用);原图
现签 originalObjectKey(SIGNED_URL_TTL)。来源 IP 固定窗限速
(DOWNLOAD_WINDOW_REQUESTS/DOWNLOAD_WINDOW_SECONDS,env 可调);零浏览
计数写入(排除规则)。STORAGE_PORT_OV 经新 StorageOvModule 与 public-ov
共享同一实例。
- 契约微调登记:PublicAlbum 增 readOnly 必返 downloadEnabled/
originalDownloadEnabled(cn+ov 同形返回;public e2e 精确键断言同步)。
- viewer:lightbox 下载入口随开关渲染(无/单按钮/分体菜单两选项)、过期
凭证静默重取一次、微信内置浏览器(MicroMessenger UA)出保存图片指引
浮层(复制深链 + 反馈)替代直接下载;i18n zh/en 词条。
- tests:api e2e +7(开关矩阵 + URL/字节级断言、水印键一致、隐藏 404、
限速 429、零计数行);viewer vitest +9(UA/矩阵/菜单/重取/失败/指引/
复制);viewer Playwright +3(入口矩阵、微信 UA 浮层、真实端点 200 +
签名 URL 模式)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 18:32:34 +08:00
浅念
8a8ad8a18d
feat(contracts): p4a admin governance, watermark, download and usage endpoints
...
P4a Task 1 契约扩展批次(36 → 51 operations,gen/redocly/枚举同步守护绿):
- photos:POST /albums/{albumId}/photos/hide-batch(photoIds 1..200,逐项
BatchHideResult {photoId, ok, code?},部分成功语义,A16);
- admin:POST /admin/albums/{albumId}/transfer(owner 交接,历史上传人不动,
keepAsCollaborator,A17);
- albums:POST/GET /albums/{albumId}/watermark-jobs(WatermarkConfig 版本化
+ 历史重生成任务 state/progress/total/failed,原子切换、原图永不覆盖,
A18/A20);
- interactions:GET /public/albums/{token}/photos/{photoId}/download
(variant=browse|original 两级开关校验;**新增错误码 DOWNLOAD_DISABLED**,
errors.ts 与 Error enum 两侧同步,A18);
- admin 账号治理:GET /admin/users 统一为 listAdminUsers 双模式 + PATCH
/admin/users/{userId} 与 reset-password / activate / deactivate;
- admin 用户同步:GET /admin/user-sync/{status,runs,errors} + POST runs
手动补拉(互斥复用进行中批次,A29);
- admin 用量:GET /admin/usage/summary + PUT thresholds(null 清除、
即刻重评估,A23)。
偏差登记①——/admin/users 路径冲突(P1b lookupUsersByAccount):P4a 决策
统一为双模式端点(前缀模式沿用 P1b 语义与任何已认证工作人员可用;管理
列表模式 query/status 过滤 + 游标 + 仅 admin),响应统一 AdminUserList
(AdminUser 字段超集,游标元信息随行)。消费方同步:admin 邀请搜索
(fetchLookupUsers/类型镜像,行为不变)与 api 过渡实现(仍仅前缀模式、
limit 对齐 1..100 缺省 20;管理列表模式与仅 admin 判定随 Task 6 落地,
users-lookup e2e 已按统一形状修订)。
偏差登记②——apps/mobile 手写镜像未随本次更新:其邀请流程仍按旧 3 字段
投影消费(AdminUserList 为超集,运行时兼容,mobile IT 不经该路径);列
为 P4a 跟进项,仅当其 e2e 破坏时处理。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 15:56:38 +08:00
浅念
d41be367f8
fix(mobile,it): integration suite stabilization on real device
...
Root causes found & fixed across 29 phase-1 + 3 phase-2 runs:
- MSYS path mangling for adb shell device paths (per-command MSYS_NO_PATHCONV + winpath)
- adb reverse tcp:9000: presign URLs embed server-side localhost MinIO host,
unreachable from emulator (uploads hung forever)
- compose --profile ov omitted: Docker restarts stranded ov side (ov-api 500s)
- soft keyboard occlusion on dialog buttons (dismissKeyboard + ensureVisible)
- stacked-route BackButton ambiguity (goBack must tap topmost hit-testable)
- bottom-sheet entrance animation race (wait for hit-testable option)
- language leg: drive LocaleSettings provider (sheet tap flaky, covered by unit tests)
- password page: self-pops on success (no extra goBack)
- bg verify: server-side authoritative assertion (fresh process = fresh memory DB)
- flutter test uninstall-reinstall wipes app data: image dir fallback to /data/local/tmp
- run-as cp failures no longer silently swallowed
- phase-2 missing IT_STAFF_ACCOUNT define
Phase 1: all 12 legs green (login→album→category→invite→3 uploads via native
WorkManager→server history→ov visible→hide sync→network fault/recovery→
password change→relogin→locale switch)
Phase 2: bg-home + kill-restart green (A04/A05 emulator-level)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 15:03:17 +08:00
浅念
fb3c278e50
test(mobile): integration suite against real backend on mumu
...
P3 Task 11 (WRITE & COMPILE phase; device run pending MuMu availability).
- integration_test/p3_flow_test.dart: login -> album (zh+en) -> category
-> invite -> enqueue 3 real JPEGs -> queue completed -> server history
(sourceDeviceId) -> ov public API sees 3 -> hide via API + UI sync ->
DIO fault-gate offline (waitingNetwork -> retry -> 4 completed) ->
password change (session kept) -> relogin -> in-app locale smoke.
- integration_test/p3_background_test.dart: IT_PHASE=prepare/verify spec
for HOME-background continuation and force-stop kill-restart recovery
(2 x ~14MB noise JPEGs give a deterministic mid-flight window).
- helpers: dart-define config, fault-injecting DIO adapter, app harness
(picker/notification/locale provider overrides), UI step library with
live-binding polling (no pumpAndSettle).
- scripts/run-mobile-it.sh: device detect (exit 2), backend ensure
(compose + api/ov/worker/sync, exit 3), idempotent per-run user seed
(no DB reset), APK build/install + adb push of testdata, phase 1 /
--background phase 2 orchestration with server-side confirmed-count
truth via curl.
- services/api seed:it script (create-if-absent staff accounts).
- debug-only network security config: cleartext HTTP permitted to
10.0.2.2/127.0.0.1/localhost only (release unaffected).
- testdata: 3 x ~120KB + 2 x ~14MB generated JPEGs.
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 05:35:16 +08:00
浅念
8f3966d7b5
feat(mobile): in-app locale switching
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 05:00:11 +08:00
浅念
955f9b13c1
feat(mobile): upload queue ui and continuous capture page
...
Task 9 (P3 plan): queue tab replaces the stub, continuous capture page
enqueues per shot, album detail gains an upload FAB, and the upload
engine lifecycle is now bound to the login session.
- Engine scope: session account drives UploadEngine start/stop; wired
from app boot, login success, logout, and 401 eviction. Tasks stay in
the store across logout (accountId hard isolation).
- Queue tab: album x state x time filter bar, task rows with thumbnail,
state chip, localized error copy, attempt count, per-row progress via
a broadcast progress hub, state-dependent actions (re-pick retry,
retryNow, cancel, delete), batch retry bar, 200ms-debounced live
refresh from drift table updates.
- Capture page: camera preview via injected CaptureSource, one enqueue
per shutter press, persistent album/category/badges overlay with a
switch-only-affects-new-shots hint, gallery multi-pick, exit confirm
while uploads are pending, permission-denied and init-failure views.
- POST_NOTIFICATIONS runtime permission via a small MethodChannel
(photolive/permissions) + PermissionPlugin.kt: single runtime
permission need, avoids permission_handler dependency; requested once
on first enqueue, silent degradation when denied or unavailable.
- Album detail FAB: capture-and-upload / pick-from-gallery sheet.
- l10n: ~50 new keys in zh/en arbs; tests: +45 (filter matrix, engine
scope lifecycle, permission gate, queue tab, capture page, FAB entry);
existing page tests updated to inject session/task-store fakes.
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 04:47:31 +08:00
浅念
42e1c2e34b
feat(mobile): ios background upload bridge (compile-gated at G3)
...
Swift 侧(协议与 Android UploadPlugin.kt 逐字一致,编译/真机留 G3):
- UploadBridge.swift:通道/方法/事件/失败码常量 + registerTask 参数解析
(镜像 UploadProtocol.kt)
- UploadEventBus.swift:单调 seq + 终态 ack 缓存重放(镜像
UploadEventBus.kt),iOS 增强——未 ack 终态落盘(系统后台拉起投递的
回调在进程重启后同样重放,技术方案 §6.2 持久化原生回调)
- UploadTaskStore.swift:taskId→URLSession taskIdentifier 映射 plist 持久化
(Application Support;对应 Android WorkManager 自持映射)
- UploadPlugin.swift:URLSession background
(com.photolive.photo_live.uploads)+ delegate 失败分类(2xx→completed+etag
/ 403→needsRenew / 4xx→HTTP_4XX / 网络→重试上限后 NETWORK,镜像
UploadPutExecutor/UploadWorker)、原生重试(5 次上限、10s 指数退避)、
进度 500ms 节流、queryTask/cancelTask/ackEvents、静默取消
- AppDelegate.swift:implicit engine 通道注册 + 启动重建会话 +
handleEventsForBackgroundURLSession completionHandler 暂存
- Info.plist:UIBackgroundModes 有意不声明(background URLSession 无需;
计划措辞按技术方案 §6.2 修正,见 README-ios.md)
- project.pbxproj 登记新 Swift 文件;README-ios.md(Xcode 步骤 + G3 验证
清单:实际文件上传/持久化回调窗口/服务端补偿/重启重联校准/强制退出/
低电量延迟/100 张批量)
Dart:NativeTransport 选择矩阵并入 iOS(Platform.isAndroid || isIOS,
FORCE_DART_TRANSPORT 覆盖不变)——协议两侧一致,无新通道代码。
flutter analyze 0 issue;flutter test 219 全绿。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 03:42:49 +08:00
浅念
4e35bdc0b2
feat(mobile): android background upload bridge via workmanager
...
Kotlin 侧(MethodChannel photolive/uploads + EventChannel photolive/uploads/events):
- UploadPlugin:registerTask 入队 WorkManager OneTimeWorkRequest(unique
work=upload-task-{taskId},expedited + OutOfQuotaPolicy 回退、CONNECTED
约束、指数退避 10s 起)、cancelTask / queryTask / ackEvents
- UploadWorker(CoroutineWorker):dataSync 前台服务 + 进度通知,
OkHttp PUT(ETag 捕获、64KB 分片进度),结局分类:2xx→completed、
403→needsRenew、4xx→failed{HTTP_4XX}、网络/5xx→Result.retry(上限
5 次后 failed{NETWORK|SERVER_ERROR})、缺文件→failed{SOURCE_MISSING}
- UploadEventBus:终态事件单调 seq + 未 ack 缓存重放(上限 200),
重注册/取消作废旧缓存——进程重启/切后台恢复不丢完成信号
- Manifest:FOREGROUND_SERVICE(_DATA_SYNC)/POST_NOTIFICATIONS 权限 +
SystemForegroundService tools:node=merge 补 dataSync 类型(已验证
合并产物)
Dart 侧:Transport 接口(init/complete/renew 留引擎,仅 PUT 委托)——
DartHttpTransport(Task 6 逻辑抽出,403→renew 异常归一)+ NativeTransport
(registerTask→事件流终态/进度映射、终态 ack、取消联动);引擎传输选择:
Android 缺省原生,dart-define FORCE_DART_TRANSPORT=true 强制 Dart 路径;
失败分类与 dio 路径语义对齐(401 短退避/429 网络桶/SOURCE_MISSING 不
自动重试等)
测试:gradle testDebugUnitTest 29 例(executor MockWebServer 分类×10、
协议解析×11、事件总线×8);flutter test 219 全绿(新增 NativeTransport
事件映射/取消/ack/并发路由、引擎×Transport 委托与 needsRenew 重注册、
传输选择矩阵);flutter analyze 0;debug apk 构建通过
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 03:22:42 +08:00
浅念
3ddb0ea2d4
feat(mobile): upload engine with preparation, batching states and retries
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 02:50:40 +08:00
浅念
d96e56f653
feat(mobile): drift upload task store with state machine
...
- upload_tasks 表(accountId 隔离 + 三个复合索引)、状态列枚举转换器、
datetime 文本落库(UTC 归一,亚秒精度)
- 纯状态机(task_state.dart):合法迁移表 + canTransition/assertTransition,
completed/cancelled 终态
- TaskStore 仓储:create(clientTaskId 同账号同相册应用侧唯一)、
updateState(迁移守护+旁路字段)、账号隔离读路径(硬守护)、
countsByState 徽章、cleanupCandidates/markCleanable(仅终态可清理——
评审缺口 1 硬守护,未完成源文件永不清理)
- 测试 26 例(状态机表驱动全边 + 守护/隔离/筛选/保留策略);
sqlite3 3.x build hooks 在 flutter test 宿主与 Android APK 构建均验证
(libsqlite3.so 已打包全 ABI,运行时验证留 Task 11)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 02:12:39 +08:00
浅念
bbea1dd364
feat(mobile): album management pages with qr share
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 01:56:38 +08:00
浅念
8be21b856b
feat(mobile): login, password change and logout screens
...
P3 Task 3 移动端(对接 Task 2 api client + 本任务新增的后端契约):
- 登录页实装:账号/密码表单(空值客户端校验)→ api.login(会话自动
持久化)→ pushReplacement /home;错误码本地化(AUTH_INVALID_
CREDENTIALS / AUTH_RATE_LIMITED(含稍后再试文案)/ 网络 / 校验);
提交加载态防重复提交。
- 修改密码页(设置页入口):当前/新/确认三字段,客户端校验(≥8 位、
两次一致)→ api.changePassword → toast + pop;成功后本地会话不变
(服务端保留当前会话);旧密码错误(401 AUTH_INVALID_CREDENTIALS)
显示「当前密码错误」且不触发 401 驱逐。
- 退出登录:设置页按钮 → api.logout 尽力而为 → 清会话 → 清栈回
/login;App 壳 initState 注册 onUnauthorized(业务 401 → 清栈 /login),
根 navigatorKey 接线;password-change 的 401 豁免会话驱逐(拦截器
豁免集合与令牌附加分离)。
- l10n:新增 16 组 zh/en 文案;错误码 → 文案解析扩展
(core/error_l10n.dart,errorCode→AppLocalizations)。
- 测试:ApiClient.changePassword 单测 ×2(Bearer+请求体;旧密码 401
不驱逐);widget 测试 ×12(登录 5:空值/成功跳转/凭据错误/限速文案/
加载禁用;改密 4:过短/不一致/旧密码错误文案+会话保留/成功 toast+pop
且令牌不变;设置 2:改密入口跳转/登出清会话回登录;App 壳 1:401
清栈回 /login)。flutter analyze 0 issue;flutter test 81/81。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 01:19:57 +08:00
浅念
f975321b67
feat(mobile): api client, session storage and contract-aligned models
...
P3 计划 Task 2(apps/mobile):
- core/config.dart:API_BASE/VIEWER_BASE 经 --dart-define 覆盖,缺省
10.0.2.2 模拟器宿主回环;viewer 相册链接构造 {VIEWER_BASE}/a/{token}。
- core/error_codes.dart:errors.ts 17 码 1:1 镜像(含注释),另设 4 个
客户端本地伪码(NETWORK_ERROR/NETWORK_TIMEOUT/CANCELLED/HTTP_ERROR)。
- core/api_client.dart:dio 封装——Bearer 注入(登录端点豁免)、401
驱逐(清会话 + onUnauthorized 回调去重,App 壳注册跳 /login)、
DioException/错误体 {code,message,requestId} 归一 AppException、
通用游标分页助手;auth/albums/members/categories/admin/uploads/photos
全端点方法;riverpod provider 出口。
- core/session_store.dart:SessionStore 抽象 + flutter_secure_storage
实现(token/expiresAt/account 三键,encode/decode 纯函数可测)。
- features/{auth,albums,uploads,photos}/models.dart:手写 fromJson/
toJson,逐字段对齐 openapi.yaml(类注释标注契约 schema/operationId);
locale 按字符串透传(zh-CN 标识符不兼容),业务枚举 Dart 化并在
未知值 fail-fast。
- 测试 66 项:17 码表驱动 + 网络/超时/取消 + 兜底映射、游标边界
(含 CursorPageMeta 不变式违约容错)、模型往返(fixture 转写自
openapi.yaml schema 与 api e2e 断言,来源在测试头注释)、拦截器
行为(手写 FakeAdapter:Bearer 注入/401 驱逐/登录豁免/并发去重/
尽力而为 logout/refresh 滑动续期)。flutter analyze 零告警。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 00:40:02 +08:00
浅念
8bc50e07ef
feat(mobile): flutter scaffold with env script and baseline nav
...
- scripts/flutter-env.sh: toolchain PATH/JAVA_HOME/ANDROID_HOME + pub 镜像 env
- apps/mobile: flutter create (com.photolive.photo_live, android+ios)
- Android minSdk 24 / targetSdk 36;INTERNET 权限(main manifest,debug+release)
- iOS Info.plist:本地网络 ATS 例外 + 相册/相机用途说明(zh+en)
- 依赖接入:riverpod / drift(+sqlite3_flutter_libs) / dio / secure_storage /
image_picker / camera / qr_flutter / path_provider / path / intl
- l10n:l10n.yaml + app_zh/app_en arb(20 键),gen-l10n(zh 模板,非空 getter)
- 基线:Material 3 主题、/login→/home 命名路由、底部导航(相册/队列/设置桩)、
locale provider 脚手架(Task 10 完成切换)
- flutter analyze 零问题;widget 测试通过;debug apk 构建成功
(MuMu 冒烟因宿主 commit 内存耗尽未完成,Task 2 重试)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-19 00:22:04 +08:00
浅念
cdead49838
feat(viewer): likes, bubbles, visit reporting and hot sorting
...
Task 10 viewer 互动化:
- 点赞:useLikes 批次状态机(500ms 防抖 / 5 次满额即冲 / 失败同 batchId
退避重试 3 次后回滚丢弃),乐观计数 + 服务端 total 权威校准
(countVersion 单调),大图与格子双入口;
- WS 实时:RealtimeClient 纯状态机(半频心跳、指数退避重连、三败降级、
重连成功 resync 第 1 页重取),消息路由器做 likes.burst 自身过滤
(originBatchIds 求交)、隐藏页暂停与减少动画下仅校准不出气泡;
photo.hidden/deleted 即时移出序列并回退邻居大图;降级后 8s 轮询接管;
- 浏览上报:visitorId 跨标签 BroadcastChannel first-writer-wins 协调,
可见时上报、requestId 持久幂等、网络失败指数重试;
- 排序:最新/热门 chips(热门走 ov 冷快照),大图打开期间禁用切换;
顶栏「减少动画」偏好开关;
- 契约包新增 ./realtime 子路径出口(浏览器面不可引桶出口——其连带
internal-sync 的 node:crypto 会被 Vite 外置化致整页崩溃);
- e2e 注入钩子 __viewerWsUrl/__viewerWsRetryBaseMs/__viewerRealtimePollMs/
__viewerBubbleMs 与 .activity[data-connection] 连接态锚。
vitest:viewer 105(新增 36:likes 批次 8、visitor 协调 5、上报门控 4、
路由 8、WS 状态机 7、排序 4);Playwright:viewer 21(新增 likes.spec 7、
journey 补互动腿断言)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 19:52:27 +08:00
浅念
0fcf18cc5c
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 >
2026-09-18 17:05:56 +08:00
浅念
40f8f37ca4
test(e2e): p1b cross-frontend playwright journey
...
- viewer playwright.config: third webServer boots admin dev (5176,
reuseExisting) so one config serves both frontends
- journey.spec.ts: admin UI (login, zh+en album, real-jpeg banner via
file chooser, category, staffB invite by account search, logout) ->
staffB API uploads x3 (init/presign-PUT/complete) -> worker
PublishService.processPhoto x3 -> public list verify -> viewer UI
(banner bytes, zh->en switch, waterfall 3, chip filter 1, lightbox
zoom/close, deep link) -> admin hide -> viewer 2 + hidden deep-link
404 notice -> empty album state
- cleanup: journey album domain rows + staffB account via Prisma
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 12:54:18 +08:00
浅念
50c59ca9d4
feat(admin): album management pages with members and categories
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 12:33:46 +08:00
浅念
a6cc190751
feat(admin): art-design-pro integration with local session auth
...
P1b Task 9: apps/admin based on art-design-pro v3.0.2 (tag, commit
d224dd4; source recorded in apps/admin/SOURCE.md), trimmed to
layout/login/i18n/request/table-form core. Login wired to real
POST /v1/auth/password (single-token session), Bearer header on all
requests, 401 interceptor logout+redirect, request-driven sliding
renewal (refresh at ~80% TTL, single-flight), best-effort
POST /v1/auth/logout on user-menu logout. One protected stub page
(相册管理). Dev port 5176 strictPort + /v1 proxy to 8080. Admin keeps
its own eslint9 flat config; root eslint ignores apps/admin/**.
vitest unit tests for renewal pacing + Playwright e2e (guard redirect,
localized wrong-password error, login lands on stub, token persists
across reload, logout clears session).
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 11:47:32 +08:00
浅念
90fe64f637
feat(viewer): photo viewer with zoom, navigation and deep link
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 11:04:05 +08:00
浅念
d0b79dd0c9
feat(viewer): waterfall feed with lazy load, filter and new-photo hint
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 10:23:27 +08:00
浅念
adc45b1563
feat(viewer): album page with locale fallback and timezone display
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:52:22 +08:00
浅念
04b5641901
chore(viewer): move dev ports to 5175/5176 (user decision; avoids cms project conflict on 5173)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:18:27 +08:00
浅念
f9cb72ed06
feat(viewer): vite vue3 scaffold with routing and i18n
...
- apps/viewer (@photo/viewer): Vite 5 + Vue 3.4 + TS 最小骨架(无 Pinia/UI 库)
- router: /a/:token 活动页 + /a/:token/p/:photoId 单图深链(复用活动页组件)
- i18n: vue-i18n 9,zh-CN/en 词条,localStorage(viewer.locale) 持久 +
navigator.language 检测兜底 zh-CN;顶栏语言切换组件;文案全量走 i18n
- api client: 类型化 fetch 封装(契约生成类型直连),错误信封 → ApiError,
错误码 → i18n key 映射表(src/api/errors.ts)
- scripts: dev/build/preview/test(vitest 10 例)/test:e2e(Playwright 冒烟,
webServer 自拉 dev)/lint/vue-tsc 全部接入 turbo
- root: +@playwright/test、+eslint-plugin-vue(flat config 收敛到 apps/**/*.vue)、
pnpm.onlyBuiltDependencies 放行 esbuild
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:12:45 +08:00