在移动端应用程序的生命周期中,从“概念验证(POC)”走向“工业级产品”往往伴随着阵痛。随着 HarmonyOS 6.0 的发布,ArkUI 声明式范式以前所未有的开发效率重塑了前端生态。然而,极高的自由度也带来了极大的架构失控风险。
在这里插入图片描述

在《轻心记 (MoodLite)》最初的开发阶段,为了快速验证想法,应用内充斥着随性而为的“散装逻辑”:网络请求被直接写在按钮的点击事件中,庞大的数组过滤逻辑阻塞在 UI 的 build() 函数里,而用于大模型对话的编排工具也游离于鸿蒙原生生态之外。当数据量突破一定阈值,或是试图增加全局暗黑模式时,整座代码大厦便摇摇欲坠。

在这里插入图片描述
为了打造一个真正高内聚、低耦合的现代化应用底座,MoodLite 开启了 V2.0 的全面重构。本次重构绝不仅是简单的代码格式化,而是一场从数据流向、视觉基建到 AI 编排战略的系统级“刮骨疗毒”。本文将全面复盘这次重构的核心决策与工程实践,勾勒出一幅从混沌走向秩序的原生架构蓝图。


一、打破“状态迷雾”:以领域驱动设计重塑单一事实来源

在 ArkUI 的状态驱动体系下,@State@Prop@Link 是连接数据与视图的桥梁。但在 V1 版本中,这些装饰器被严重滥用。

开发者往往会在“日历页”、“时间轴页”和“统计页”各自维护一份情绪数据的状态副本。一旦用户在详情页修改了某条记录,由于缺乏统一的数据分发枢纽,其他页面的副本便成了“脏数据”,导致应用状态彻底分裂。

1.1 确立 MoodRecord 为绝对核心实体

在 V2.0 中,我们引入了领域驱动设计(DDD)的思想,确立了“单一事实来源(Single Source of Truth, SSOT)”的铁律。所有的局部状态必须被消灭,整个应用的灵魂收敛于一个极其严谨的底层数据模型:MoodRecord

/**
 * 领域模型:单条情绪记录实体
 * 严禁在视图层私自修改该对象的属性,必须通过 Repository 层进行事务操作
 */
export class MoodRecord {
  id: string; 
  timestamp: number; 
  dateStr: string; 
  score: number; // 核心指标:-2 到 2 的整数
  text: string; 
  tags: string[];

  constructor(id: string, timestamp: number, score: number, text: string = "") {
    this.id = id;
    this.timestamp = timestamp;
    this.score = score;
    this.text = text;
    this.dateStr = this.formatDateStr(timestamp);
    this.tags = [];
  }

  private formatDateStr(ts: number): string {
    const date = new Date(ts);
    // ... 日期格式化逻辑
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
  }
}

在这个模型中,dateStr 的引入是典型的“空间换时间”策略。我们拒绝在 UI 层对时间戳进行格式化,而是将其在数据创建时就固化为字符串,为后续极其频繁的日历聚合查询铺平道路。

1.2 视图模型 (ViewModel) 的防腐层作用

底层的 MoodRecord 不能直接丢给 UI 层。UI 需要的是“今日平均分”、“本月连胜天数”等派生数据。

我们在 Model 与 View 之间构建了厚重的 ViewModel 防腐层。所有的“脏活累活”——如浮点数精度保留、数组过滤、极值判定——全都在 ViewModel 的纯 TypeScript 函数中完成。UI 层面退化为绝对的“哑终端”,它只负责接收 ViewModel 吐出的最终字符串或色值,彻底切断了 UI 与复杂业务逻辑的纠缠。


二、算法思维的降维打击:极限压榨长列表与图表性能

对于一款长期使用的日记类应用,性能的深水区在于长列表的无限滚动与年度热力图的高密度渲染。如果在前端滥用高时间复杂度的计算,ArkTS 的单线程模型将迅速耗尽算力。

2.1 抛弃 O ( n 2 ) O(n^2) O(n2):基于哈希映射的聚合重组

在构建时间轴瀑布流时,我们需要将一维的 MoodRecord[] 按天进行分组。早期的实现使用了极其低效的嵌套循环:先提取所有日期,再对每个日期进行 filter

重构后,我们将算法底蕴融入前端架构,利用 Map 实现了 O ( n ) O(n) O(n) 复杂度的极速重组:

  buildDayGroups(records: MoodRecord[]): DayGroup[] {
    const map = new Map<string, DayGroup>();
    
    // 一次性线性遍历,利用 dateStr 作为哈希键实现 O(1) 插入与归类
    for (const r of records) {
      let dg = map.get(r.dateStr);
      if (!dg) { 
        dg = { dateStr: r.dateStr, records: [], dominantScore: 0 }; 
        map.set(r.dateStr, dg); 
      }
      dg.records.push(r);
    }
    
    const result: DayGroup[] = [];
    map.forEach((v) => {
      // 组内根据 timestamp 局部排序
      v.records.sort((a, b) => a.timestamp - b.timestamp);
      result.push(v);
    });
    
    // 组间直接利用字符串字典序进行全局排序,极速且安全
    result.sort((a, b) => b.dateStr.localeCompare(a.dateStr));
    return result;
  }

配合 ArkUI 的 LazyForEach 以及严格的 KeyGenerator(强制绑定记录的 UUID),我们实现了按需实例化的极致性能。无论用户滑动 10 条还是 10 万条数据,内存曲线始终保持在一条波澜不惊的水平线上。


三、视觉基建的收敛:打造纯粹的玻璃拟态设计系统

在这里插入图片描述
工程化的架构不仅体现在数据流上,更体现在对 UI 规范的强权统治。
在这里插入图片描述

MoodLite 采用了轻量、治愈的极简主义(Minimalism)风格,并大面积运用了具有空间呼吸感的玻璃拟态(Glassmorphism)特效,辅以品牌专属的蜜桃粉(#FF8BA7)作为点缀。在 V1 阶段,这些特效和色值像补丁一样贴得到处都是。

3.1 从魔法数字到 Design Tokens

重构的第一步,是拔除所有的魔法数字。我们建立了一套涵盖排版、圆角、间距的全局设计系统(Design System)。

视觉属性 散装逻辑 (V1.0 痛点) 全局蓝图 (V2.0 规范)
色彩引用 .fontColor('#FF8BA7') .fontColor(BrandColors.PEACH_PINK)
卡片间距 .margin({ top: 15, left: 12 }) .padding(Spacing.LG)
容器圆角 .borderRadius(20) .borderRadius(Radius.CARD)
深色模式 组件内写满 if (isDark) 三元表达式 ThemeManager 统一输出调色板配置

3.2 高阶容器 GlassCard 的降维打击

为了实现物理级别精确的光学玻璃效果(包含高斯模糊、边缘高光、弥散阴影),我们利用 @BuilderParam 封装了应用级的顶级容器 GlassCard

import { Spacing, Radius, BrandColors } from '../styles';

@Component
export struct GlassCard {
  @BuilderParam content: () => void;
  @StorageLink('isDarkMode') isDarkMode: boolean = false;

  build() {
    Stack() {
      // 1. 底层:控制玻璃的折射率与高斯模糊管线
      ContainerSpan()
        .width('100%').height('100%')
        .backgroundColor(this.isDarkMode ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.65)')
        .backdropBlur(40)
        .shadow({ radius: 30, color: this.isDarkMode ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.08)', offsetY: 10 })

      // 2. 中层:业务内容承载区
      Column() { this.content?.() }
      .padding(Spacing.MD)

      // 3. 顶层:防穿透的物理高光边框
      ContainerSpan()
        .width('100%').height('100%')
        .hitTestBehavior(HitTestMode.None) 
        .border({ width: 1, color: this.isDarkMode ? 'rgba(255,255,255,0.08)' : 'rgba(255,255,255,0.4)' })
        .borderRadius(Radius.CARD)
    }
    .clip(true)
    .borderRadius(Radius.CARD)
  }
}

通过这一层封装,业务页面的开发者被完全剥夺了“破坏视觉统一性”的能力。他们只需要调用 GlassCard() { ... },并心安理得地享受系统级别的暗黑模式自适应与 120Hz 的点击回弹动效。品牌标志性的蜜桃粉(#FF8BA7)作为情感焦点的 Accent Color,也在 ThemeManager 的调度下,在每一个图标和进度条中精准绽放。


四、AI 架构的战略转移:深耕华为 Celia (小艺) 开放平台

在 AI 赋能方面,MoodLite 的演进历程是一次深刻的战略修正。

在初期的技术选型中,为了追求快速上线,我们采用了 Dify 等第三方编排工具,通过 HTTP SSE 直连外部大语言模型(如 DeepSeek)。然而,随着应用与 HarmonyOS 生态的融合逐渐加深,这种“游离于系统之外”的架构暴露出明显的短板:

  1. 端侧上下文割裂:第三方 API 无法直接获取设备级的环境信息(如当前处于睡眠模式、运动状态等),导致 AI 的建议往往显得空泛。
  2. 多端流转困难:无法无缝对接鸿蒙的桌面卡片、穿戴设备的语音指令等原生交互入口。
  3. 安全与合规风险:用户的私密情绪日记必须经过脱敏并发送到外部第三方网关,链路过长。

4.1 拥抱原生:接入小艺意图框架 (Insight Intent)

在 V2.0 的重构中,我们做出了一个极具前瞻性的架构决策:全面废弃 Dify 工作流,将 AI 集成战略彻底向华为 Celia (小艺) 开放平台转移。

这一转变使得 MoodLite 从一个“调接口的套壳应用”,蜕变为了真正长在鸿蒙系统底层神经网上的“原生智能体”。

我们首先在 module.json5 中声明了应用的原子化意图:

{
  "module": {
    "insightIntents": [
      {
        "intentName": "RecordMoodIntent",
        "domain": "MedicalHealth",
        "intentVersion": "1.0",
        "srcEntry": "./ets/entryability/IntentAbility.ets"
      }
    ]
  }
}

4.2 场景触发与原生感知

接入小艺开放平台后,AI 分析的流向被彻底逆转。不再是应用去请求 AI,而是 AI 主动驱动应用。

当用户对着手机说:“小艺小艺,我今天感觉特别焦虑,帮我记在轻心记里。”小艺底层的 NLU(自然语言理解)引擎会精准解析出 RecordMoodIntent 意图,并提取出“焦虑”这一核心槽位(Slot)数据。

随后,小艺通过系统底层的 IPC 通道唤醒 MoodLite 的 IntentAbility

// 意图响应枢纽
export default class RecordMoodIntentAbility extends InsightIntentAbility {
  onExecuteInUIAbility(intentName: string, param: Record<string, Object>, pageLoader: window.WindowStage) {
    if (intentName === 'RecordMoodIntent') {
      const emotionText = param['emotion'] as string; // 获取小艺解析的情绪文本
      
      // 1. 调用本地算法模型或小艺原生 API 进行轻量级打分
      const score = CeliaEngine.analyzeSentiment(emotionText);
      
      // 2. 组装实体并静默落盘
      const newRecord = new MoodRecord(generateUUID(), Date.now(), score, emotionText);
      dbHelper.insert(newRecord);

      // 3. 驱动全局 UI 刷新
      AppStorage.setOrCreate('latestRecord', newRecord);
      
      // 4. 向小艺返回执行结果,由小艺通过语音播报给用户反馈
      return {
        code: 0,
        result: {
          displayMessage: `已经为你记录啦,深呼吸,一切都会好起来的。`
        }
      };
    }
  }
}

这种深度的原生融合,不仅将语音指令、负一屏建议、桌面卡片等触点全部打通,更极大地降低了端云通信的延迟。小艺平台提供的原生情感分析和上下文感知能力,赋予了应用远超传统 API 调用的同理心与智能化上限。


五、结语:工程化是写给未来的情书

从 V1.0 的混沌堆砌,到 V2.0 的秩序井然,MoodLite 的架构演进是一次对软件工程哲学的深度践行。

重构从来不是为了炫技。将 MoodRecord 打造成唯一的真理之源,是为了捍卫数据的纯洁;将算法思想引入长列表组装,是为了守住设备的性能底线;强制剥离出 GlassCard 容器并约束设计令牌,是为了守护极简治愈的产品美学;而果断将 AI 大脑切换至 Celia (小艺) 开放平台,则是为了将应用的根系深深扎入 HarmonyOS 的肥沃土壤之中。

优秀的工程架构就像是一份写给未来的情书。当我们为每一块玻璃拟态的倒角注入克制的代码,为每一次跨进程通信设计严密的协议时,我们实际上是在为产品未来五年的演进搭建跑道。在这套宏大而精密的架构蓝图上,开发者终于可以从无尽的代码碎片中解脱出来,全神贯注地去打磨那些触达人心的产品细节。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐