基于HarmonyOS的AI冰箱食材菜谱应用开发——从对齐到评估的全流程技术实践

一、对齐阶段(Align)

1.1 项目背景与上下文分析

在HarmonyOS生态快速发展的背景下,AI应用正在从概念走向落地。本项目"AI冰箱食材菜谱"是HarmonyOS AI应用集合中的一个典型场景,旨在解决用户日常生活中"冰箱里有食材却不知道做什么菜"的普遍痛点。作为一款面向C端用户的生活辅助类AI应用,它需要兼顾交互的便捷性、数据的结构化以及AI生成内容的实用性。

本项目所在的工程是一个大型的HarmonyOS AI应用项目,整体架构采用ArkTS语言开发,基于ArkUI声明式UI框架构建。项目根目录为 c:\Users\l\DevEcoStudioProjects\MyApplication,通过 entry 模块承载所有AI应用。每个应用遵循统一的"Model + Service + Page"三层架构模式,通过 apps.json 配置文件统一注册路由并展示在首页网格中。

项目上下文的关键信息:

  • 技术栈:HarmonyOS ArkTS + ArkUI(声明式UI框架)
  • API版本:HarmonyOS SDK 6.0.1(API Level 21)
  • 架构模式:MVVM变体(Model定义数据、Service封装逻辑、Page负责UI)
  • 应用注册方式:通过 resources/rawfile/apps/apps.json 配置列表,在 Index.ets 首页中动态加载并渲染网格卡片,点击后通过 router.pushUrl 跳转至对应页面
  • 路由配置:在 resources/base/profile/main_pages.json 中注册页面路由
  • AI能力:当前阶段使用Mock数据模拟AI生成,预留真实大模型API接入接口
    在这里插入图片描述

1.2 需求理解与边界确认

“AI冰箱食材菜谱"的核心需求是:用户输入冰箱中现有的食材以及口味偏好,应用通过AI能力智能推荐可制作的菜谱,并提供菜名、使用食材、步骤、营养信息、难度、预计用时等完整信息,同时还能提示用户"如果想做更多菜还缺什么食材”。

用户输入字段:

字段 类型 说明 示例
现有食材 string 用户冰箱中现有的食材列表 “鸡蛋, 番茄, 葱, 豆腐”
口味偏好 string 用户偏好的口味类型 “清淡, 微辣, 酸甜”

AI输出字段:

字段 类型 说明
recipes string[] 推荐菜谱名称列表
name string 主推荐菜名
ingredients_used string[] 使用的食材列表
steps string[] 烹饪步骤列表
step string 当前步骤描述
action string 操作动作
tip string 烹饪小贴士
calories string 卡路里信息
protein string 蛋白质含量
carbs string 碳水化合物含量
fat string 脂肪含量
difficulty string 难度等级
time string 预计用时
missing_for_more string[] 想做更多菜还缺的食材
dish string 菜品名称
missing string[] 缺少的食材列表
tips string 综合建议

1.3 场景痛点与用户价值

核心痛点分析:

  1. 决策疲劳:面对冰箱中的食材,用户经常陷入"不知道做什么"的困境,导致食材浪费或重复烹饪
  2. 营养不均衡:缺乏专业的营养知识,难以搭配出营养均衡的餐食
  3. 经验依赖:烹饪能力依赖于个人经验积累,新手用户难以充分利用现有食材
  4. 效率低下:手动搜索菜谱并逐一核对食材匹配度,耗时且不精准
  5. 信息碎片化:菜谱、营养信息、烹饪技巧分散在不同平台,缺乏一站式解决方案

目标用户群体:

  • 普通家庭用户:日常做饭的家人,希望快速解决"今天吃什么"的问题
  • 独居年轻人:烹饪经验有限,希望通过AI指导提升烹饪效率
  • 健身/减脂人群:对营养数据有精确需求,需要AI推荐符合健康目标的菜谱
  • 食材管理爱好者:希望通过AI减少食材浪费,优化冰箱食材利用率

1.4 技术约束与规范对齐

在开发过程中,需要严格遵守ArkTS语言的语法约束。ArkTS是HarmonyOS的声明式编程语言,基于TypeScript但做了大量静态化改造,以下是开发中需要特别注意的关键约束:

  • 不支持 anyunknown 类型:所有变量必须显式指定具体类型
  • 不支持解构赋值和展开运算符(除数组展开到rest参数外):需要使用显式字段赋值
  • 不支持 Function.applyFunction.callFunction.bindthis 语义限制为传统OOP风格
  • 不支持索引访问对象字段(obj["field"]:必须使用 obj.field 语法
  • 不支持 for...in 遍历对象:数组使用常规 for 循环
  • 不支持 in 运算符:使用 instanceof 替代
  • 不支持 is 运算符:使用 instanceof + as 转换
  • 所有 import 语句必须在文件最前面:不能分散在代码中
  • 不支持对象字面量直接作为类型:必须显式声明类或接口

这些约束深刻影响了代码的编写方式,我们在后续的代码实现中会逐一体现。


二、架构阶段(Architect)

2.1 整体架构设计

AI冰箱食材菜谱应用遵循MVVM(Model-View-ViewModel)架构模式的变体,采用"Model + Service + Page"三层架构。这种架构选择基于以下考量:

  1. 与现有项目架构对齐:整个项目的所有AI应用均采用此模式,保持了代码风格的一致性和可维护性
  2. 关注点分离:数据模型、业务逻辑、UI展示各司其职,降低耦合度
  3. 可测试性:Service层独立于UI,便于单元测试和Mock数据替换
  4. 可扩展性:后续接入真实AI大模型API时,只需修改Service层,UI层无需变化

架构层次图:

┌─────────────────────────────────────────────────────┐
│                    Page (View)                      │
│   AI冰箱食材菜谱Page.ets                            │
│   @State 数据绑定  |  @Builder 组件  |  build()     │
├─────────────────────────────────────────────────────┤
│                  Service (ViewModel)                │
│   AI冰箱食材菜谱Service.ets                         │
│   generateData() - AI生成逻辑                       │
│   Mock数据 / 真实API调用                            │
├─────────────────────────────────────────────────────┤
│                  Model (Data)                       │
│   AI冰箱食材菜谱Model.ets                           │
│   AI冰箱食材菜谱Data - 数据实体定义                 │
└─────────────────────────────────────────────────────┘

2.2 核心数据流设计

数据流遵循"单向数据流"原则,确保数据变化可预测、可追踪:

用户输入 (TextInput onChange)
      ↓
inputData: Record<string, Object> (状态变量)
      ↓
Button onClick → service.generateData(inputData)
      ↓
AI冰箱食材菜谱Data (返回结果)
      ↓
this.resultData = 返回结果
      ↓
@State 驱动 → UI 自动重新渲染
      ↓
ForEach 循环渲染列表 / Text 显示字段

关键设计决策:

  • 使用 @State 装饰器:所有UI绑定的数据都通过 @State 标注,当数据变化时,ArkUI框架自动触发组件的重新渲染,无需手动操作DOM
  • 使用 Record<string, Object> 收集输入:由于ArkTS不支持动态属性名,使用 Record<string, Object> 类型作为通用输入容器,这是ArkTS中推荐的动态数据收集方式
  • Service层返回全新对象:每次调用 generateData() 都返回一个新的 AI冰箱食材菜谱Data 实例,避免引用传递带来的副作用

2.3 Model层设计

Model层定义数据实体,是整个应用的数据基础。在 AI冰箱食材菜谱Model.ets 中,我们定义了 AI冰箱食材菜谱Data 类:

// 文件路径: entry/src/main/ets/apps/AI冰箱食材菜谱/AI冰箱食材菜谱Model.ets

export class AI冰箱食材菜谱Data {
  recipes: string[] = []
  name: string = ''
  ingredients_used: string[] = []
  steps: string[] = []
  step: string = ''
  action: string = ''
  tip: string = ''
  calories: string = ''
  protein: string = ''
  carbs: string = ''
  fat: string = ''
  difficulty: string = ''
  time: string = ''
  missing_for_more: string[] = []
  dish: string = ''
  missing: string[] = []
  tips: string = ''

  constructor() {
    this.recipes = []
    this.name = ''
    this.ingredients_used = []
    this.steps = []
    this.step = ''
    this.action = ''
    this.tip = ''
    this.calories = ''
    this.protein = ''
    this.carbs = ''
    this.fat = ''
    this.difficulty = ''
    this.time = ''
    this.missing_for_more = []
    this.dish = ''
    this.missing = []
    this.tips = ''
  }
}

设计要点说明:

  1. 所有字段显式初始化:ArkTS要求类字段必须在声明时或在构造函数中初始化,不能使用 ! 断言。我们采用"声明时初始化"的方式,确保每个字段都有默认值
  2. 数组类型明确声明string[] 而非 Array<string>,遵循ArkTS的类型标注规范
  3. 构造函数双重初始化:虽然字段已在声明时初始化,但构造函数中再次赋值,确保在类的实例化过程中不会出现空值风险
  4. 不支持对象类型的调用签名:如果Model需要行为方法,应使用 class 方法而非对象类型中的调用签名

2.4 Service层设计

Service层负责AI数据生成逻辑,是连接用户输入和AI能力的桥梁。在 AI冰箱食材菜谱Service.ets 中实现:

// 文件路径: entry/src/main/ets/apps/AI冰箱食材菜谱/AI冰箱食材菜谱Service.ets

import { AI冰箱食材菜谱Data } from './AI冰箱食材菜谱Model'

export class AI冰箱食材菜谱Service {
  private model: AI冰箱食材菜谱Data

  constructor() {
    this.model = new AI冰箱食材菜谱Data()
  }

  // 生成AI冰箱食材菜谱数据
  generateData(input: Record<string, Object>): AI冰箱食材菜谱Data {
    let result: AI冰箱食材菜谱Data = new AI冰箱食材菜谱Data()
    // Mock data generation based on input
    let ingredientsVal: string = String(input['ingredients'] || '')

    result.recipes = ['示例数据1', '示例数据2', '示例数据3']
    result.missing_for_more = ['示例数据1', '示例数据2', '示例数据3']
    result.tips = '生成结果:' + ingredientsVal
    return result
  }
}

设计要点说明:

  1. 输入参数类型:使用 Record<string, Object> 作为通用输入类型,兼容ArkTS不支持动态属性名的限制
  2. Mock数据策略:当前阶段使用硬编码的示例数据,后续接入真实AI大模型API时,只需替换 generateData 方法的内部实现,对外接口保持不变
  3. 类型转换安全:从 Record<string, Object> 中取值时,使用 String() 进行显式类型转换,避免类型不匹配
  4. 私有字段 model:Service内部持有Model实例,用于内部数据处理,遵循ArkTS的OOP风格

2.5 Page层设计

Page层是UI展示层,使用ArkUI的声明式语法构建界面。在 AI冰箱食材菜谱Page.ets 中实现:

// 文件路径: entry/src/main/ets/apps/AI冰箱食材菜谱/AI冰箱食材菜谱Page.ets

import { AI冰箱食材菜谱Data } from './AI冰箱食材菜谱Model'
import { AI冰箱食材菜谱Service } from './AI冰箱食材菜谱Service'
import { router } from '@kit.ArkUI'

@Entry
@Component
struct AI冰箱食材菜谱Page {
  @State inputData: Record<string, Object> = {}
  @State resultData: AI冰箱食材菜谱Data | null = null
  @State showResult: boolean = false
  private service: AI冰箱食材菜谱Service = new AI冰箱食材菜谱Service()

  build() {
    Column() {
      // 冰箱Header
      Row() {
        Text('← 返回')
          .fontSize(13)
          .fontColor('#64748B')
          .onClick(() => { router.back() })
        Blank()
        Column() {
          Text('📱 AI冰箱食材菜谱')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#334155')
          Text('冰箱备忘录')
            .fontSize(10)
            .fontColor('#94A3B8')
            .margin({ top: 2 })
        }
        Blank()
        Text('🧊').fontSize(22)
      }
      .width('100%')
      .padding({ left: 20, right: 20, top: 16, bottom: 14 })
      .backgroundColor('#F1F5F9')

      // ... 后续UI组件
    }
    .width('100%').height('100%')
    .backgroundColor('#F1F5F9')
  }
}

设计要点说明:

  1. @Entry@Component 装饰器:标识这是一个页面入口组件,ArkUI框架会自动识别并管理其生命周期
  2. @State 装饰的三个状态变量
    • inputData:收集用户输入,类型为 Record<string, Object>
    • resultData:AI生成的菜谱数据,可为 null 表示尚未生成
    • showResult:控制结果区域的显示/隐藏
  3. Service实例作为私有字段private service 在组件中保持单例,避免重复创建
  4. router.back() 返回导航:ArkUI提供的路由API,用于返回上一层页面

2.6 输入区域设计

输入区域由两个 TextInput 组件构成,分别收集"现有食材"和"口味偏好":

// 便签输入区域
Column() {
  Text('📌 现有食材')
    .fontSize(11)
    .fontColor('#475569')
    .margin({ top: 6, bottom: 3 })
  TextInput({ placeholder: '请输入现有食材' })
    .fontSize(13)
    .height(40)
    .backgroundColor('#FFFFFF')
    .borderRadius(4)
    .border({ width: 1, color: '#E2E8F0' })
    .padding({ left: 12, right: 12 })
    .onChange((val: string) => { this.inputData['现有食材'] = val })

  Text('📌 口味偏好')
    .fontSize(11)
    .fontColor('#475569')
    .margin({ top: 6, bottom: 3 })
  TextInput({ placeholder: '请输入口味偏好' })
    .fontSize(13)
    .height(40)
    .backgroundColor('#FFFFFF')
    .borderRadius(4)
    .border({ width: 1, color: '#E2E8F0' })
    .padding({ left: 12, right: 12 })
    .onChange((val: string) => { this.inputData['口味偏好'] = val })
}

设计要点说明:

  1. TextInputonChange 回调:与 @State inputData 联动,每次输入变化时实时更新状态
  2. 使用字符串键名访问this.inputData['现有食材'] 虽然是索引访问,但这里 inputData 的类型是 Record<string, Object>,是标准库中支持索引访问的特殊类型(类似 Int32Array),符合ArkTS约束
  3. 视觉设计:使用圆角边框、浅色背景、统一的字体大小和间距,保持与整体应用风格一致

2.7 结果展示区域设计

结果展示区域使用条件渲染,在用户点击"烹饪!"按钮后显示:

Button('📱  烹饪!')
  .width('100%')
  .height(50)
  .backgroundColor('#F59E0B')
  .borderRadius(8)
  .fontColor('#FFFFFF')
  .fontSize(16)
  .fontWeight(FontWeight.Bold)
  .margin({ top: 18, bottom: 14 })
  .onClick(() => {
    this.resultData = this.service.generateData(this.inputData)
    this.showResult = true
  })

if (this.showResult && this.resultData !== null) {
  Column() {
    Text('📋 今日菜单')
      .fontSize(15)
      .fontWeight(FontWeight.Bold)
      .fontColor('#334155')
      .margin({ bottom: 12 })

    // 推荐菜谱列表
    Text('Recipes')
      .fontSize(13)
      .fontWeight(FontWeight.Bold)
      .fontColor('#333333')
      .margin({ top: 10, bottom: 6 })
    if (this.resultData.recipes) {
      ForEach(this.resultData.recipes, (item: string, index: number) => {
        Row() {
          Text('• ').fontSize(12).fontColor('#666666')
          Text(item).fontSize(12).fontColor('#333333')
        }
        .width('100%')
        .padding({ top: 2, bottom: 2 })
      }, (item: string, index: number) => index.toString())
    }

    // 营养信息展示
    Row() {
      Text('Calories: ').fontSize(12).fontWeight(FontWeight.Medium).fontColor('#666666')
      Text(this.resultData.calories).fontSize(12).fontColor('#333333')
    }
    // ... 其他字段
  }
  // ...
}

设计要点说明:

  1. 条件渲染模式:使用 if (this.showResult && this.resultData !== null) 控制结果区域的显示,在用户未操作时保持界面简洁
  2. ForEach 渲染列表:ArkUI中渲染动态列表的标准方式,需要提供两个回调函数(内容渲染和key生成)
  3. null 安全判断:由于 resultData 的类型是 AI冰箱食材菜谱Data | null,使用前必须进行 null 检查,这是ArkTS类型安全的要求
  4. @State 驱动UI更新:当 onClick 中更新 resultDatashowResult 时,ArkUI框架自动检测状态变化并重新渲染组件树

2.8 应用注册与路由配置

AI冰箱食材菜谱应用通过 apps.json 注册到首页:

{
  "icon": "🍳",
  "title": "AI冰箱食材菜谱",
  "subtitle": "冰箱食材",
  "color": "#10B981",
  "bg": "#ECFDF5",
  "border": "#A7F3D0",
  "page": "apps/AI冰箱食材菜谱/AI冰箱食材菜谱Page",
  "cat": "健康生活"
}

配置关键点:

  • page 字段指向页面文件的模块路径,ArkUI框架根据此路径自动加载对应的 @Entry 组件
  • cat 字段为"健康生活"分类,在首页中通过分类标签筛选展示
  • 配色方案(colorbgborder)采用绿色系,传达健康、自然的视觉感知

Index.ets 中,通过读取 apps.json 文件并解析,动态生成应用网格:

aboutToAppear(): void {
  this.loadApps()
}

private loadApps(): void {
  try {
    let ctx: Context = getContext(this)
    let mgr = ctx.resourceManager
    let data: Uint8Array = mgr.getRawFileContentSync('apps/apps.json')
    let decoder: util.TextDecoder = util.TextDecoder.create('utf-8')
    let jsonStr: string = decoder.decodeToString(data)
    let rawList: AppJsonItem[] = JSON.parse(jsonStr) as AppJsonItem[]
    // ... 构建应用列表
  } catch {
    this.apps = this.getDefaultApps()
  }
}

设计要点说明:

  1. getRawFileContentSync 读取rawfile:HarmonyOS提供的同步读取原始文件API,适用于读取静态配置文件
  2. TextDecoder 解码:将二进制数据(Uint8Array)解码为UTF-8字符串
  3. JSON.parse 解析:ArkTS支持标准JSON解析,但解析结果需要显式类型断言(as AppJsonItem[]
  4. try/catch 异常处理:捕获文件读取或解析异常,降级使用空列表,保证应用不会因配置加载失败而崩溃

三、原子化阶段(Atomize)

3.1 任务分解原则

原子化阶段的核心目标是将"AI冰箱食材菜谱"应用的整体开发任务,拆解为最小可执行、可验证的原子任务。每个原子任务满足以下条件:

  • 独立性:可独立开发,不依赖其他任务的完成
  • 可验证:有明确的完成标准和验收方式
  • 可估算:工作量可预估,便于排期和跟踪
  • 价值驱动:每个原子任务交付后都能产生可感知的价值

原子化分解的过程本身也是一次深度的设计复审。当我们将一个看似简单的"AI冰箱食材菜谱"应用拆解为7个原子任务时,实际上是在重新审视整个系统的边界和职责。例如,Model层和Service层的分离,意味着我们清晰地认识到"数据定义"和"数据生成"是两个不同的关注点,应当由不同的模块负责。这种分解方式不仅有助于开发阶段的并行推进,也为后续的维护和扩展奠定了良好的基础。

从项目管理角度来说,原子化任务分解的价值在于:每个任务都有明确的交付物和验收标准,开发进度可量化追踪;任务之间的依赖关系清晰,排期更加科学合理;即使某个任务出现延误,也不会影响其他独立任务的正常推进。这种"化整为零"的策略,正是应对复杂软件开发的有效方法论。

3.2 原子任务列表

任务1:创建Model数据模型

  • 描述:定义 AI冰箱食材菜谱Data 类,包含所有AI输出字段
  • 文件AI冰箱食材菜谱Model.ets
  • 验收标准:类定义完整,所有字段类型正确,默认值合理,可通过 new AI冰箱食材菜谱Data() 实例化
  • 预估工时:0.5人天

任务2:实现Service服务层

  • 描述:实现 AI冰箱食材菜谱Service 类,包含 generateData 方法
  • 文件AI冰箱食材菜谱Service.ets
  • 验收标准:Service可被实例化,generateData 接受 Record<string, Object> 输入,返回 AI冰箱食材菜谱Data 实例
  • 预估工时:0.5人天

任务3:构建Page页面UI

  • 描述:使用ArkUI声明式语法构建完整的页面UI,包括Header、输入区域、按钮、结果展示区域
  • 文件AI冰箱食材菜谱Page.ets
  • 验收标准:页面可正常渲染,文本输入可正常收集数据,点击按钮可调用Service并展示结果,结果区域包含所有定义字段
  • 预估工时:1.5人天

任务4:注册路由配置

  • 描述:在 main_pages.json 中注册页面路由路径
  • 文件entry/src/main/resources/base/profile/main_pages.json
  • 验收标准:页面路由注册正确,可通过 router.pushUrl 正常跳转
  • 预估工时:0.1人天

任务5:集成到应用列表

  • 描述:在 apps.json 中添加AI冰箱食材菜谱的配置项
  • 文件entry/src/main/resources/rawfile/apps/apps.json
  • 验收标准:应用在首页网格中正常显示,点击可跳转至对应页面
  • 预估工时:0.1人天

任务6:ArkTS语法适配与编译验证

  • 描述:检查代码是否符合ArkTS语法约束,确保编译通过
  • 检查项:无 any 类型、无解构赋值、无 Function.bind、无 for...in、无索引签名等
  • 验收标准DevEco Studio 编译无错误,无语法警告
  • 预估工时:0.3人天

任务7:UI交互优化与视觉对齐

  • 描述:优化UI细节,包括颜色、字体、间距、圆角、动画等,与整体应用风格保持一致
  • 验收标准:界面美观、交互流畅、与平台其他应用风格一致
  • 预估工时:0.5人天

3.3 任务依赖关系图

任务1 (Model) ──→ 任务2 (Service) ──→ 任务3 (Page) ──→ 任务6 (语法验证)
                                                          │
任务4 (路由) ───────────────────────────────────────────────┘
                                                          │
任务5 (应用列表) ──────────────────────────────────────────┘
                                                          │
                                                    任务7 (UI优化)

任务1和任务2是任务3的前置依赖,任务4、任务5、任务6可并行进行,任务7是最终的收尾优化。

3.4 风险识别与应对策略

风险项 影响 概率 应对策略
ArkTS语法兼容性问题 编译失败 编写代码时严格遵循ArkTS约束,使用DevEco Studio的实时语法检查
页面布局在不同屏幕尺寸上的适配 显示异常 使用相对布局(layoutWeight、百分比宽度)而非绝对像素值
第三方依赖缺失 编译失败 本项目不依赖第三方库,所有API均使用HarmonyOS标准库
路由配置错误 页面无法跳转 配置后立即测试验证,确保路径与文件名一致

四、审批阶段(Approve)

4.1 代码审查清单

在代码提交前,需要对以下方面进行审查:

1. 架构合规性审查

  • 是否遵循Model + Service + Page三层架构?
  • 是否与现有AI应用的代码风格一致?
  • 是否有不必要的依赖引入?
  • 是否遵循了模块化设计原则?

2. ArkTS语法合规性审查

  • 所有变量类型是否显式声明?(无 any/unknown
  • 是否使用了类字段声明而非构造函数内声明?
  • 是否有解构赋值或解构参数?
  • 是否有 Function.bindFunction.applyFunction.call 调用?
  • 是否有 for...in 遍历?
  • 是否有 obj["field"] 索引访问(除 Record 类型外)?
  • 所有 import 语句是否在文件最前面?
  • 是否有 is 运算符?(应使用 instanceof
  • 返回类型是否已显式标注?(函数返回类型推断受限时)

3. UI/UX审查

  • 页面布局是否自适应不同屏幕尺寸?
  • 颜色方案是否与主题一致?(支持浅色/深色模式)
  • 是否使用了合理的字体大小和间距?
  • 交互反馈是否清晰?(按钮点击、数据加载等)
  • 是否有良好的空状态和错误状态处理?

4. 安全性审查

  • 是否有敏感信息泄露风险?(无硬编码密钥等)
  • 输入数据是否进行了类型安全转换?
  • 是否避免了使用 eval 或动态代码执行?

4.2 关键决策记录

在AI冰箱食材菜谱应用的开发过程中,我们遇到了多个需要权衡和决策的技术选型问题。以下是几个关键决策的详细记录,包括背景分析、方案对比和最终选择理由。

决策1:使用 Record<string, Object> 作为通用输入类型

  • 背景:ArkTS不支持动态属性名和索引签名,但页面需要收集不同类型的数据(食材、口味偏好等),且未来可能增加更多输入字段
  • 方案A:使用 Record<string, Object> 作为输入容器,通过 onChange 回调收集数据
  • 方案B:为每个输入字段创建独立的 @State 变量(如 @State ingredients: string = ''@State taste: string = ''
  • 方案C:定义一个固定的输入接口,将所有输入字段作为接口属性
  • 选择理由:方案A更通用,便于后续扩展更多输入字段,且与Service层的接口参数类型一致。方案B虽然类型更精确,但每增加一个字段就需要新增一个状态变量,维护成本高。方案C定义了固定接口,但失去了灵活性。最终选择方案A,因为它平衡了类型安全和扩展性。

决策2:使用Mock数据而非真实AI API

  • 背景:项目处于早期开发阶段,真实AI大模型API尚未接入,但需要验证UI交互的完整性和正确性
  • 方案A:Service层返回硬编码示例数据(Mock数据)
  • 方案B:直接返回空数据或报错,等待真实API接入后再开发UI
  • 方案C:使用本地规则引擎模拟AI生成,不依赖外部API
  • 选择理由:方案A使用的Mock数据可以使用户界面和交互逻辑得到完整验证,且后续真实API接入时只需修改Service层,UI层无需任何变化。方案B会导致开发阻塞,无法并行推进。方案C过度设计,对于验证UI交互的需求来说过于复杂。

决策3:使用条件渲染而非导航到新页面展示结果

  • 背景:AI生成的结果需要在用户操作后展示,有两种常见的展示方式
  • 方案A:在当前页面使用 if 条件渲染展示结果区域
  • 方案B:跳转到一个新的结果页面,通过路由参数传递数据
  • 选择理由:方案A减少页面跳转开销,交互更流畅,符合"单页应用"的体验设计理念。方案B适合数据量极大或需要独立分享的场景,但对于"AI冰箱食材菜谱"这种轻量级应用,在同一页面展示结果更加直观和高效。

决策4:使用 ForEach 而非 LazyForEach 渲染列表

  • 背景:菜谱列表、食材列表、步骤列表等需要在UI中渲染
  • 方案A:使用 ForEach 渲染所有列表项
  • 方案B:使用 LazyForEach 实现懒加载渲染
  • 选择理由:当前数据量较小(通常不超过10条),ForEach 完全满足需求且实现更简单。LazyForEach 适用于数据量较大(数百条以上)的场景,过早优化会增加复杂度。未来如果数据量增长,可以无缝切换到 LazyForEach

4.3 质量门控标准

门控项 标准 验证方式
编译通过 DevEco Studio编译无错误 自动编译检查
语法合规 无ArkTS语法违规 代码审查 + 编译检查
功能完整 所有字段可正常输入和展示 手动测试
交互流畅 按钮点击响应正常,结果展示无闪烁 手动测试
风格统一 与平台其他应用视觉风格一致 视觉审查

五、自动化执行阶段(Automate)

5.1 自动化脚本设计

在整体开发流程中,自动化是提升效率的关键。虽然每个应用的代码可以手动编写,但通过自动化工具可以大幅提升开发效率并减少人为错误。

自动化代码生成的核心逻辑:

  1. 读取模板文件(Model、Service、Page的代码模板)
  2. 根据应用名称和字段配置,替换模板中的占位符
  3. 生成三个目标文件(Model.ets、Service.ets、Page.ets)
  4. 自动更新 apps.json 配置
Logo

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

更多推荐