项目名称:智能影记 - Memoria
时间:2026.04.06 - 2026.04.12
核心任务:首页“发现”模块重构、端侧三级推荐策略落地、虚拟事件流架构设计


一、 前言:赋予本地相册“主动感知”的能力

在前面的实训中,我们完成了 Isar 高性能数据库的接入和相册元数据(时间、地点、标签)的落盘。但一个优秀的相册 App 不能仅仅是一个“静态的数据仓库”,它应该像一个懂你的老朋友,能在适当的时候把遗忘的美好主动推送到你面前。

本周我的核心任务,就是重构 HomePage 的“发现”模块,在完全不依赖云端算力、绝对保护隐私的前提下,利用端侧的元数据矩阵,纯本地实现一套“智能回忆推荐引擎”。


二、 业务逻辑:端侧三级渐进式推荐策略

受限于移动端的算力和电池续航,我们不能在每次打开首页时遍历数万张照片跑复杂的推荐算法。因此,我设计了一套基于 Isar 极速查询的“三级渐进式规则引擎”,按照优先级依次生成推荐卡片(最多展示两张,以保证首屏加载速度):

规则 1:时间感知策略 (Time-based)

系统会动态获取当前的 DateTime.now(),通过业务边界条件判断推送逻辑:

  • 年度/月度总结:当日期处于 12.20 - 1.10 时,触发“年度大片”;当每月日期大于 25 号时,触发“月度回顾”。
  • 往年今日:通过一个 for 循环,向前追溯 1 到 5 年的今天。利用 Isar 的 timestampBetween 索引进行毫秒级过滤。

规则 2:内容画像策略 (Content-based)

如果时间策略未完全填满推荐位,系统会提取近期照片的 aiTags 进行画像分析。
我将标签划分为了四个高优分类:宠物 (pets)风景 (scenery)美食 (foods)治愈系 (happy)(结合 AI 提取的 joyScore > 0.6)。如果某一类的照片积攒到了一定阈值(如美食 >= 6 张),就会动态生成对应的聚类卡片。

规则 3:地点保底策略 (Location-based)

最后,通过提取照片的 cityprovince 字段,将同一个地点拍摄的大量照片打包成“城市印记(如:青岛·漫游记)”作为推荐的保底策略。


三、 工程挑战与解决方案

在具体编码落地的过程中,我遇到了两个典型的移动端工程挑战,并针对性地给出了解决方案。

3.1 挑战一:缓存路径失效与异步状态刷新

在移动端(特别是 Android),照片的绝对沙盒路径是可能会因为系统清理缓存而发生变动的。如果在展示推荐卡片时直接读取失效的 Path,会导致 UI 渲染大面积黑屏。

解决方案
我在 home_page.dart_generateDiscoverCards 阶段,引入了 PhotoService().reconcileAccessiblePhotos 机制。在照片喂给 UI 渲染前,先通过永远不变的 assetId 去系统底层换取最新有效路径,同时过滤掉已经被用户在系统相册中物理删除的照片。
配合 Flutter 的 setState,确保了数据清洗和 UI 状态的完美同步。

3.2 挑战二:页面跳转的“数据孤岛”问题

点击推荐卡片后,需要跳转到“事件详情页”查看照片瀑布流并支持“一键生成视频”。但推荐出来的照片集合(比如“我的美食日记”)在底层数据库中并没有真实对应的 EventEntity(事件实体),它们只是一组临时的查询结果集。如果为此单独写一个详情页,会导致严重的代码冗余。

解决方案:虚拟事件模式
采用了经典的 DTO(数据传输对象)设计模式。我在点击事件的 onTap 回调中,利用内存里的查询结果,动态构造了一个 id = '-1' 的虚拟 Event 对象,并为其注入了虚拟的 AI 主题:

// 截取自 home_page.dart 推荐卡片点击逻辑
onTap: () async {
  // ... 前置的路径清洗逻辑
  
  // 1. 构造穿透底层的虚拟 Event (内存对象,不写数据库)
  final virtualEvent = Event(
    id: '-1', // 标识为虚拟事件
    title: cardData['title'],
    season: '智能推荐',
    year: sortedDates.first.year,
    location: '精选回忆',
    startDate: sortedDates.first,
    endDate: sortedDates.last,
    photos: mappedPhotos,
    aiThemes: [
      AITheme(
        id: 'discover_theme',
        emoji: '✨',
        title: cardData['title'],
        subtitle: cardData['tag'],
      )
    ],
  );

  // 2. 完美复用现有的 EventDetailPage,实现视图层解耦
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => EventDetailPage(event: virtualEvent),
    ),
  );
}

通过这种“内存数据伪装”的架构设计,我成功实现了推荐流和原生事件流在 UI 层的完美闭环,复用了全部的下游业务逻辑。


四、 本周总结与下周计划

本周通过对 HomePage 的重构,Memoria 在不需要任何云端推荐算法干预的情况下,拥有了一套“千人千面”的端侧本地推荐引擎。这不仅贯彻了项目的隐私保护初衷,也大幅提升了产品的使用温度。

目前,推荐卡片点击进去后的“一键生成故事”功能已经跑通了基于文本的 LLM 生成。下周,项目将迎来最激动人心的架构换血:纯端侧的 C++ 向量引擎即将并网。 我将配合底层同学,把目前基于“时间与文本标签”的检索彻底替换为“多模态 Embedding 余弦搜索”。

前端的状态机已经搭建完毕,期待下周底层算力的全面注入!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐