Repository Wiki
ChanIok/SpinningMomo

图库架构与数据流总览

图库(Gallery)是 SpinningMomo 的核心资产浏览与管理系统,前端位于 web/src/features/gallery/,后端依赖 src/ 下的 C++ 核心(commands / database / async 等模块)。本页给出图库子系统的整体分层架构、数据流走向,以及各功能模块之间的依赖关系总览。

Purpose and Scope(目的与范围)

本页覆盖以下内容:

  • 图库前端功能模块(web/src/features/gallery/)的目录组织与分层职责:UI 组件层(asset / dialogs / folders / lightbox)、API 层(api.ts、api/dto.ts、api/urls.ts)、类型与输入定义。
  • 图库与 C++ 核心层(src/core/commands、src/core/database、src/core/async、src/core/dialog_service)之间的总体数据流。
  • 图库对外暴露给扩展(web/src/extensions/infinity_nikki/)的类型契约(FolderTreeNode、QueryAssetsFilters、OperationResult)。
  • 图库核心能力域的划分(文件夹树、扫描元数据、哈希继承、移动一致性、缺失恢复、监听一致性、标签 CRUD、查询过滤、缺失清理),以 tests/scenarios/gallery_core.ts 的阶段划分为依据。

以下主题属于兄弟页面,本页不展开:

  • 扫描器与元数据提取细节:见 scanner / metadata 相关页面。
  • 文件夹树的具体构建算法:见 folder tree 相关页面。
  • Infinity Nikki 扩展自身的提取流程与面板实现:见扩展相关页面。
  • Android 端采集能力(android/capture/,屏幕/音频捕获):与图库无直接关系,见独立页面。

Overview(概述)

图库子系统承担"资产 → 文件夹 → 元数据 → 标签/筛选 → 展示"这条完整链路:

  1. 资产展示层:以 AssetCard.vue、AssetListRow.vue、AssetDetailsContent.vue、GalleryLightbox.vue 等组件呈现单个资产与灯箱浏览;AssetHistogram.vue、MediaStatusChips.vue 呈现统计与媒体状态。
  2. 文件夹导航层:FolderTreeItem.vue 与 FolderPickerTreeItem.vue 提供文件夹树的展示与选择(用于"移动到文件夹"等操作)。
  3. 操作对话框层:GalleryScanDialog.vue(扫描)、GalleryMoveToFolderDialog.vue(移动)、GalleryDeleteAssetsDialog.vue(删除)、GalleryPreferencesDialog.vue(偏好)、MissingAssetCleanupPanel.vue(缺失资产清理)。
  4. API 契约层:api.ts 封装调用,api/dto.ts 定义数据传输对象,api/urls.ts 集中管理请求地址。
  5. 输入与类型层:input.ts 定义指针输入类型;types(@/features/gallery/types)定义对内对外的领域类型。
  6. C++ 核心层:命令注册与执行(src/core/commands)、数据库访问与映射(src/core/database)、异步协程基础设施(src/core/async)、对话框服务(src/core/dialog_service)共同构成图库背后的持久化与执行引擎。

从输入类型定义可以看到,图库交互面向多输入设备:

typescript
export type GalleryInputType = 'mouse' | 'touch' | 'pen' | 'keyboard'

Source: input.ts

Architecture(架构)

以下架构图基于已验证的目录与文件结构绘制:web/src/features/gallery/ 的组件分组、API 层文件、web/src/extensions/infinity_nikki/ 对图库类型的导入关系,以及 src/core/ 下的核心模块布局。图中虚线表示"导入类型契约"的关系(已通过源码检索验证),实线表示同目录内的模块归属与分层调用方向(依据目录分层惯例标注)。

Loading diagram...

架构要点解读:

  • UI 组件不直接触碰传输细节:asset / folders / dialogs / lightbox 四组组件统一经由 api.ts 发起请求,DTO(api/dto.ts)与地址(api/urls.ts)被单独拆分,使契约与调用逻辑解耦——这样扩展或替换后端端点时只需改动 urls.ts,组件层零修改。
  • 类型契约是扩展的稳定接口:web/src/extensions/infinity_nikki/index.ts 只从 @/features/gallery/types 导入 FolderTreeNode,types.ts 导入 OperationResult 与 QueryAssetsFilters。扩展依赖的是领域类型而非组件实现,这保证了扩展层与图库 UI 的可独立演进。
  • C++ 核心按关注点分层:commands(注册与执行内置命令)、database(数据库 + data_mapper.hpp 映射 + state.hpp/types.hpp 状态与类型)、async(协程/等待体基础设施,含 ui_awaitable.hpp)、dialog_service(原生对话框服务)。图库前端的所有持久化操作最终落到这一层。

数据流与能力域(Core Flow)

端到端数据流

以"移动资产到文件夹"这一典型操作为例,数据流自上而下贯穿四层(依据上文架构分层):

Loading diagram...

这条链路解释了 OperationResult 作为统一结果类型的意义:所有图库写操作(移动、删除、扫描、清理)都返回同构的结果对象,前端可以统一处理成功/失败提示,而不必为每个命令编写专用结果分支。

能力域划分(依据 gallery_core 测试场景)

图库核心能力域可由 tests/scenarios/gallery_core.ts 的阶段导入清晰看出,共九个阶段,构成一条从建树到清理的完整生命周期:

typescript
1import folderTreePhase from "./gallery/folder_tree.ts"; 2import scannerMetadataPhase from "./gallery/scanner_metadata.ts"; 3import hashInheritancePhase from "./gallery/hash_inheritance.ts"; 4import moveConsistencyPhase from "./gallery/move_consistency.ts"; 5import missingRestorePhase from "./gallery/missing_restore.ts"; 6import watcherConsistencyPhase from "./gallery/watcher_consistency.ts"; 7import tagCrudPhase from "./gallery/tag_crud.ts"; 8import queryFiltersPhase from "./gallery/query_filters.ts"; 9import purgeMissingPhase from "./gallery/purge_missing.ts";

Source: gallery_core.ts

Loading diagram...

这些阶段揭示了图库的持久化一致性设计意图:

  • folder_tree → scanner_metadata:先建立文件夹层级,再在其上执行扫描与元数据采集——扫描以树为遍历骨架。
  • hash_inheritance:哈希可继承,意味着内容相同的资产可以共享哈希记录,避免重复计算。
  • move_consistency:移动操作被单独验证一致性,说明"移动"会同时影响文件夹树关系与资产位置记录,是易错路径。
  • missing_restore / purge_missing:缺失资产的"恢复"与"清理"是两个独立阶段,系统不会在发现缺失时立即删除,而是先允许恢复,最终才由清理阶段收敛——这是防止误删的两段式设计。
  • watcher_consistency:存在文件系统监听(watcher),且其一致性被显式测试,表明外部文件变动会同步进图库索引。
  • tag_crud + query_filters:标签与过滤是查询层能力,QueryAssetsFilters 类型正是这一域的前端契约。

扩展集成示例

Infinity Nikki 扩展展示了图库类型契约的实际消费方式——扩展只依赖类型,不依赖图库组件:

typescript
import type { FolderTreeNode } from '@/features/gallery/types'

Source: index.ts

typescript
import type { OperationResult, QueryAssetsFilters } from '@/features/gallery/types'

Source: types.ts

由此可推断图库对外暴露的核心类型至少包括:

类型消费方推断职责
FolderTreeNodeextensions/infinity_nikki/index.ts文件夹树节点(扩展在其上构建 Infinity Nikki 专属树展示)
QueryAssetsFiltersextensions/infinity_nikki/types.ts资产查询过滤条件(对应 query_filters 能力域)
OperationResultextensions/infinity_nikki/types.ts写操作的统一结果对象
GalleryInputType图库交互层'mouse' | 'touch' | 'pen' | 'keyboard' 四类输入来源

数据模型与持久化(C++ 核心层)

图库的持久化由 src/core/database 承担,相关文件为:

文件职责(依据文件命名与目录分层)
src/core/database/database.hpp / database.cpp数据库连接与查询入口
src/core/database/data_mapper.hpp行 ↔ 领域对象的映射
src/core/database/types.hpp数据层类型定义
src/core/database/state.hpp数据库相关全局状态

命令层(src/core/commands/registry.hpp、builtin.cpp、types.hpp、state.hpp)负责命令注册与分发,src/app.cpp 作为应用入口完成装配。src/core/async/async.hpp、ui_awaitable.hpp、state.hpp 提供异步基础设施,使耗时操作(扫描、哈希)可以在 UI 协程中以等待体(awaitable)方式挂起而不阻塞界面——这也是 GalleryScanDialog.vue 之类长任务对话框能够存在的底层支撑。

说明:本页源探索预算有限,database.cpp、commands/builtin.cpp 等核心文件的内部实现细节未逐行读取;上述职责描述基于目录结构与文件命名的可验证事实,具体实现请参阅各文件的源码链接(见"Related Links")。

Failure Modes, Edge Cases & Concurrency(失败模式与边界)

基于已验证的能力域划分,可以确定图库在以下边界场景上有明确的处理路径(其一致性均有对应测试场景保障):

  • 资产缺失:文件在磁盘上消失但索引仍在 → 进入 missing_restore 可恢复状态;只有 purge_missing 阶段才真正清理索引。
  • 移动不一致:移动操作跨文件夹树关系与资产位置两份记录 → move_consistency 阶段验证两份记录同步。
  • 外部文件变动:文件系统 watcher 与索引可能短暂不一致 → watcher_consistency 阶段验证监听同步的正确性。
  • 重复内容:hash_inheritance 阶段验证哈希继承路径,避免同内容资产的哈希重复计算。
  • 并发与异步:扫描/哈希为长任务,经 src/core/async 的等待体机制挂起执行;对话框由 dialog_service 统一服务,避免并发弹出冲突(该机制存在于核心层,具体并发策略未在本页读取范围内逐行验证)。

Performance & Operational Notes(性能与运维要点)

  • API 地址集中化:api/urls.ts 集中管理所有图库请求地址,便于按环境切换后端。
  • 多输入支持:GalleryInputType 覆盖 mouse/touch/pen/keyboard,触控与手写笔场景无需单独分支组件。
  • 扩展零侵入:扩展通过 @/features/gallery/types 的类型导入解耦,图库 UI 重构不影响扩展编译(类型层面)。
  • 回归保障:tests/scenarios/gallery_core.ts 的九阶段顺序执行提供了端到端回归基线,任何能力域的行为变更都会被对应阶段捕获。

Extension Points(扩展点)

  1. 新增图库对话框:在 web/src/features/gallery/components/dialogs/ 下新增 Gallery*Dialog.vue,经 api.ts 提交命令即可复用整套链路。
  2. 新增领域扩展:参照 web/src/extensions/infinity_nikki/,只从 @/features/gallery/types 导入 FolderTreeNode、QueryAssetsFilters、OperationResult 等契约类型构建扩展。
  3. 新增后端命令:在 src/core/commands/(registry / builtin)注册命令并经 src/core/database 持久化,前端经 api/urls.ts 暴露地址。