AI文案润色:HarmonyOS 智能审稿批注应用全流程开发实战
AI文案润色:HarmonyOS 智能审稿批注应用全流程开发实战
摘要:本文以"AI文案润色"应用为案例,详细阐述在 HarmonyOS 生态下,从需求对齐到最终交付的全流程开发实践。文章遵循"对齐→架构→原子化→审批→自动化执行→评估"六阶段方法论,涵盖 ArkTS 语法约束、ArkUI 声明式 UI 开发、@State 状态管理、数据模型设计、服务层抽象、条件渲染与列表渲染、ForEach 列表渲染、Record 数据容器、Hvigor 构建系统等核心技术主题,并配以完整的代码示例和工程实践心得。全文约 10000 字,适合 HarmonyOS 应用开发者、移动端架构师和技术管理者阅读。

一、对齐阶段(Align)
在软件开发中,对齐阶段的目标是将模糊需求转化为精确规范。这一阶段如果做得不够充分,后续的架构设计和编码实现就会不断返工,造成时间和资源的大量浪费。对于"AI文案润色"这一应用,我们需要从项目上下文、需求理解、技术约束三个维度进行深度对齐。
1.1 项目上下文分析
在开始任何开发工作之前,理解项目所处的上下文环境至关重要。本项目是一个运行在 HarmonyOS 操作系统上的 AI 智能助手集合应用,代码仓库位于 c:\Users\l\DevEcoStudioProjects\MyApplication。整个应用以"AI 智能助手"为品牌定位,通过首页网格(Index.ets)聚合了多个 AI 驱动的应用,涵盖健康生活、工作效率、创意娱乐、学习成长、职业发展六大类别。这种应用集合的架构模式,使得每个应用可以独立开发、独立部署,同时又通过统一的首页入口为用户提供一站式的 AI 服务体验。
"AI文案润色"应用被归类为"工作效率"类别,其核心功能是针对用户提供的原始文案,提供 AI 驱动的智能润色、审稿批注和修改建议。在 apps.json 中的注册信息示意如下:
{
"icon": "📱",
"title": "AI文案润色",
"subtitle": "审稿·批注模式",
"color": "#DC2626",
"bg": "#FEF2F2",
"border": "#FECACA",
"page": "apps/AI文案润色/AI文案润色Page",
"cat": "工作效率"
}
从技术栈上看,项目采用 HarmonyOS 的 ArkTS 语言、ArkUI 声明式框架、@kit.ArkUI 和 @kit.ArkTS 核心 Kit 包。项目构建系统为 Hvigor,依赖管理通过 oh-package.json5 完成。所有页面以 @Entry 装饰器标记为入口,通过 router API 实现页面间导航。值得注意的是,项目的 oh-package.json5 中 dependencies 为空,这说明项目完全依赖 HarmonyOS 系统的内置 Kit 能力,无需引入任何第三方依赖库,这大大降低了依赖管理和版本兼容性的复杂度。
1.2 需求理解与边界确认
"AI文案润色"的核心需求是:用户输入待润色文案和目标风格,点击"审稿批注"按钮后,系统 AI 对文案进行智能分析,生成润色结果和修改建议,并以结构化方式展示。结果应包含润色后全文、修改列表(逐条列出原文片段、修改后内容、修改原因和修改类型)、风格对比、字数统计和写作建议。
经过需求分析,我们明确了以下关键边界:
- 用户输入:待润色文案(字符串)、目标风格(字符串,如专业、活泼、正式、口语化、文艺、商务等)
- 业务逻辑:根据输入文案和目标风格生成润色数据,当前阶段使用 Mock 数据模拟 AI 生成行为,后续可接入真实 AI 大模型
- 输出展示:以结构化方式展示完整的润色结果,包含润色后全文、修改列表(原文片段、修改后内容、修改原因、修改类型)、风格对比、字数统计、写作建议
- UI 风格:编辑审稿风格,以红色(#DC2626)为主色调,白色卡片区域展示结果,模拟审稿批注的视觉体验
- 交互方式:点击"审稿批注"按钮触发计算,结果区域通过
if条件渲染控制显隐,初始状态隐藏结果区域,生成后展示完整批注结果 - 数据模型:
AI文案润色Data类定义了 12 个字段,覆盖文案润色的核心要素 - 导航行为:页面顶部提供"← 返回"按钮,使用
router.back()实现返回上一页
1.3 技术约束对齐
在 HarmonyOS 的 ArkTS 环境下,有若干重要的语法约束需要在开发前对齐。这些约束与标准 TypeScript 存在显著差异,如果开发团队之前没有 ArkTS 的开发经验,这些约束可能会成为开发过程中的主要障碍。
不支持索引访问类型:这是 ArkTS 中最具约束力的规则之一。标准 TypeScript 中,我们可以通过 obj["field"] 的方式动态访问对象属性,这在处理 JSON 数据或动态配置时非常方便。但在 ArkTS 中,必须使用显式类型名称,通过 obj.field 语法访问。这要求开发者在设计阶段就明确所有字段名称,无法在运行时动态添加或访问属性。
不支持 any 和 unknown 类型:所有变量必须有显式类型标注。例如,Record<string, Object> 是合法的泛型容器类型,但不能使用 any。这意味着在处理不确定类型的数据时,需要借助类型断言或联合类型来解决问题。
不支持解构赋值:标准 TypeScript 中常见的 const { polished, changes } = data 语法在 ArkTS 中不可用,必须创建临时变量逐字段操作。这虽然增加了代码量,但使数据流动路径更加清晰。
不支持 Function.bind/apply/call:在 ArkTS 中,this 的语义被限制为传统的 OOP 风格,禁止在独立函数中使用 this。这意味着所有依赖 this 的上下文操作都必须通过类的实例方法来完成,不能通过函数式编程中的 bind 或 call 来动态绑定 this。
不支持 for…in 遍历对象:对于数组,必须使用常规的 for 循环或 ForEach 组件进行迭代。这是因为 ArkTS 在编译时就已经确定了对象的布局,运行时遍历属性没有意义。
不支持对象字面量直接作为类型声明:必须显式声明类和接口,然后通过构造函数创建实例。这要求所有数据结构都有明确的类型定义。
不支持在构造函数中声明类字段:必须在类声明内部直接声明字段,而不是在构造函数中通过 this.xxx = xxx 声明。这与标准 TypeScript 的类字段声明方式一致,但 ArkTS 更加严格地强制执行这一规则。
不支持索引签名:不能使用 [key: string]: string 这样的索引签名。应改用数组(arrays)或 Record<K, V> 泛型类型。
不支持解构变量声明:ArkTS 禁止使用 const { a, b } = obj 这种解构语法。在"AI文案润色"中,如果需要从数据对象中提取多个字段,必须逐字段手动赋值。例如,在 Page 层中展示数据的各个字段时,我们直接通过 this.resultData.fieldName 的方式逐个访问,而不是先解构再使用。
这些约束直接影响代码写法,在后续的架构和编码阶段必须严格遵守。在"AI文案润色"的开发中,我们特别关注了 Record<string, Object> 的使用方式——它是 ArkTS 支持的少数几种泛型容器类型之一,用于处理动态键值对场景。
1.4 ArkUI 声明式 UI 规范对齐
在 UI 开发层面,HarmonyOS 的 ArkUI 框架采用声明式编程范式,与传统的命令式 UI 开发有显著差异。我们需要在开发前对齐以下几个关键规范:
@State 装饰器:用于声明组件内部的状态变量,当状态变量发生变化时,ArkUI 框架会自动触发 UI 重新渲染。这是 ArkUI 响应式编程的核心机制。在"AI文案润色"中,我们使用 @State 来管理用户输入、结果数据和界面显隐状态。
@Entry 装饰器:标记页面为应用的入口页面,使其可以被路由系统识别和导航。每个页面组件都必须使用 @Entry 装饰器。
@Component 装饰器:将结构体标记为 ArkUI 组件,使其具备声明式 UI 的能力。@Component 和 @ComponentV2 是 ArkUI 中两种组件装饰器,前者是 V1 版本的标准组件装饰器,后者是 V2 版本的新特性。在"AI文案润色"中,我们使用 @Component 装饰器,与项目中其他应用保持一致。
条件渲染:使用 if/else 语句根据条件控制组件的显示与隐藏,这是 ArkUI 中最常用的渲染控制方式之一。在"AI文案润色"中,我们使用 if (this.showResult && this.resultData !== null) 来控制结果区域的显隐。
列表渲染:使用 ForEach 组件遍历数组并生成对应的 UI 元素,需要提供唯一的键值生成函数。在"AI文案润色"中,我们使用 ForEach 来渲染 changes 数组中的每个修改项。
Scroll 组件:用于创建可滚动的容器,当内容超出屏幕高度时,用户可以通过滚动查看更多内容。在"AI文案润色"中,整个内容区域(输入区域、按钮、结果区域)都包裹在 Scroll 组件中。
Row 和 Column:ArkUI 的线性布局组件,分别对应水平方向和垂直方向的布局。与 Flexbox 布局模型类似,通过 justifyContent 和 alignItems 控制子组件的排列方式。
Blank 组件:用于在 Row 或 Column 中占据剩余空间,实现弹性布局效果。在"AI文案润色"的顶部导航栏中,我们使用 Blank() 组件实现标题的水平居中。
对齐阶段是项目成功的基础。通过以上三个维度的深入对齐,我们为后续的架构设计、编码实现和测试验证奠定了坚实的基础。
二、架构阶段(Architect)
基于对齐阶段达成的共识,我们进入架构设计阶段。目标是设计出一套与现有系统架构一致、可扩展、易维护的技术方案。架构设计是软件开发中最关键的环节之一,它直接决定了代码的可维护性、可测试性和可扩展性。
2.1 整体架构设计
"AI文案润色"应用采用经典的三层架构模式:
┌─────────────────────────────────────────┐
│ UI 表现层 (Page) │
│ AI文案润色Page.ets │
│ - 用户输入采集(文案/目标风格) │
│ - 润色结果展示 │
│ - 状态变量管理 │
│ - 条件渲染与列表渲染 │
├─────────────────────────────────────────┤
│ 业务服务层 (Service) │
│ AI文案润色Service.ets │
│ - 润色数据生成逻辑 │
│ - AI 模型调用封装 │
│ - 输入参数处理 │
│ - 数据转换与格式化 │
├─────────────────────────────────────────┤
│ 数据模型层 (Model) │
│ AI文案润色Model.ets │
│ - 数据实体定义 │
│ - 12 个字段属性约束 │
│ - 默认值初始化 │
│ - 数据完整性保证 │
└─────────────────────────────────────────┘
这种分层架构与项目中其他应用的架构模式保持一致,确保代码风格统一、易于理解和维护。每一层都有自己的明确定义和职责边界:
- UI 表现层:负责用户的交互体验,包括输入采集、结果展示、状态管理等。这一层只关注"如何展示"和"如何交互",不关心"数据从哪里来"和"业务逻辑是什么"。
- 业务服务层:负责核心业务逻辑的实现,包括润色数据生成、AI 模型调用封装、输入参数处理等。这一层是应用的"大脑",处理所有业务规则。
- 数据模型层:负责数据实体的定义和约束,确保数据结构的完整性和类型安全。这一层是应用的数据契约,所有数据流动都基于这个契约进行。
2.2 模块依赖关系
模块之间的依赖关系遵循"单向依赖"原则,即上层依赖下层,下层不依赖上层:
AI文案润色Page.ets
↓ import
AI文案润色Service.ets
↓ import
AI文案润色Model.ets
具体来说:
AI文案润色Page.ets导入AI文案润色Model中的AI文案润色Data类用于类型标注,导入AI文案润色Service中的AI文案润色Service类用于业务逻辑调用AI文案润色Service.ets导入AI文案润色Model中的AI文案润色Data类用于创建和返回数据实例AI文案润色Model.ets是纯数据模型,不依赖任何其他模块
这种单向依赖关系确保了代码的可测试性——我们可以独立测试每一层,而不需要依赖其他层的实现。
2.3 数据模型设计
数据模型是架构设计的核心产出之一。"AI文案润色"的数据模型包含 12 个字段,覆盖文案润色的全部要素:
// AI文案润色Model.ets
export class AI文案润色Data {
polished: string = ''
changes: string[] = []
original: string = ''
revised: string = ''
reason: string = ''
type: string = ''
before: string = ''
after: string = ''
style_comparison: string = ''
word_count: string = ''
tips: string = ''
constructor() {
this.polished = ''
this.changes = []
this.original = ''
this.revised = ''
this.reason = ''
this.type = ''
this.before = ''
this.after = ''
this.style_comparison = ''
this.word_count = ''
this.tips = ''
}
}
这个模型设计体现了以下几个关键设计决策:
字段默认值初始化:每个字段都在声明时指定了默认值(空字符串或空数组),同时在构造函数中再次显式初始化。这种双重初始化策略在 ArkTS 中是必要的,因为 ArkTS 要求在类声明中直接声明字段,而构造函数中的初始化确保了实例的完整性。
数组类型字段:changes 是字符串数组类型,用于存储多个修改项的列表。在 ArkUI 的 UI 渲染中,这些数组通过 ForEach 组件进行迭代渲染,每个修改项以列表形式逐条展示。
纯数据类:AI文案润色Data 是纯数据类,不包含任何业务方法。这种设计保持了数据模型的纯净性,使其可以被多个服务层方法复用。
字段命名策略:字段名采用英文命名(如 polished、changes、original、revised 等),与项目中的命名规范保持一致。这些字段名直接对应 AI 输出 JSON 的键名,便于后续接入真实 AI 模型时的数据映射。
2.4 服务层设计
服务层封装了核心业务逻辑,对外提供统一的接口。在"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 contentVal: string = String(input['content'] || '')
result.polished = '生成结果:' + contentVal
result.changes = ['示例数据1', '示例数据2', '示例数据3']
result.style_comparison = '生成结果:' + contentVal
result.word_count = '生成结果:' + contentVal
result.tips = '生成结果:' + contentVal
return result
}
}
服务层设计的关键决策:
输入参数类型:input: Record<string, Object> 使用 ArkTS 支持的泛型容器类型 Record,用于接收动态的输入参数。Record<K, V> 是 ArkTS 中少数几个支持的实用类型之一,用于表示键值对映射。注意,ArkTS 不支持 Partial<Record<string, Object>> 这样的嵌套实用类型,所以我们在使用时直接使用 Record<string, Object>。
Mock 数据生成:当前阶段使用 Mock 数据模拟 AI 生成行为,contentVal 从输入参数中提取文案内容值,并将其作为生成结果的前缀。这种设计模式使得后续接入真实 AI 模型时,只需替换 generateData 方法的内部实现,而不需要修改调用方代码。
服务实例管理:在 Page 层中,AI文案润色Service 被声明为 private 成员变量,在组件初始化时创建实例。这种设计确保了服务实例的生命周期与页面组件一致,避免了多次创建实例的开销。
2.5 UI 表现层设计
UI 表现层是整个应用的入口,负责用户交互和界面展示。在"AI文案润色"中,UI 层由单一页面 AI文案润色Page 构成,包含以下几个核心区域:
顶部导航栏:包含返回按钮、应用标题和版本标签,采用 Row 水平布局,通过 Blank() 组件实现标题居中。标题区域包含应用名称"AI文案润色"和副标题"审稿 · 批注模式",右上角显示"RB"版本标签。
输入区域:包含文案输入和目标风格输入两个字段,采用 TextInput 组件,每个输入框配有标签文字。输入框使用 backgroundColor('#FFFFFF') 和 borderRadius(4) 创建白色背景、圆角边框的输入区域,边框颜色为 #D1D5DB。
操作按钮:"审稿批注"按钮,使用 Button 组件,以蓝色(#2563EB)为背景色,圆角 6 像素,白色文字,粗体字重,实现醒目的操作按钮视觉效果。
结果展示区域:通过条件渲染控制显隐,展示完整的润色结果,包含润色后全文(Polished)、修改列表(Changes)、原文(Original)、修订版(Revised)、修改原因(Reason)、修改类型(Type)、修改前内容(Before)、修改后内容(After)、写作建议(Tips)等多个字段。
2.6 数据流向设计
"AI文案润色"的数据流向遵循"用户输入 → 状态存储 → 服务调用 → 结果展示"的闭环:
用户输入 (TextInput onChange)
↓
this.inputData['文案'] = val (Record<string, Object>)
this.inputData['目标风格'] = val
↓
点击"审稿批注"按钮 (onClick)
↓
this.service.generateData(this.inputData)
↓
返回 AI文案润色Data 实例
↓
this.resultData = result
↓
this.showResult = true
↓
ArkUI 自动触发 UI 重新渲染
↓
展示润色结果 (if 条件渲染 + ForEach 列表渲染)
这个数据流向设计体现了 ArkUI 声明式编程的核心思想——开发者只需要关注状态(State)的变化,框架自动处理 UI 的更新。当 showResult 从 false 变为 true 时,ArkUI 框架会自动执行条件渲染,展示结果区域。
2.7 异常处理策略
在架构设计中,异常处理是不可忽视的一环。虽然"AI文案润色"当前阶段使用 Mock 数据,但我们需要为后续接入真实 AI 模型预留异常处理机制:
输入校验:在服务层 generateData 方法中,使用 String(input['content'] || '') 对输入参数进行校验和类型转换,确保即使输入字段缺失或为空,也能生成兜底的空字符串值。
空数据保护:在 UI 层,使用 if (this.showResult && this.resultData !== null) 进行双重空值检查,确保在数据未生成时不会尝试访问空对象的属性。showResult 控制展示与否,resultData !== null 确保数据可用。
默认值兜底:数据模型中的所有字段都有默认值,即使在极端情况下(如数据生成失败),UI 层也能展示空数据而非崩溃。
数组空值保护:在 ForEach 列表渲染之前,使用 if (this.resultData.changes) 进行判断,确保数组不为 null 或 undefined,避免 ForEach 在空数组上渲染而导致异常。
三、原子化阶段(Atomize)
原子化阶段的核心任务是将宏观的架构设计分解为更小的、可管理的原子任务。每个原子任务应该有明确的输入输出、清晰的责任边界,并且可以独立完成和测试。原子化分解的目标是让每个任务都能在 1-2 天内完成,避免任务粒度太大导致进度不可控。
3.1 任务分解策略
在"AI文案润色"的开发中,我们将整个开发任务分解为以下原子级任务:
任务 1:数据模型定义与验证
输入:需求文档中关于数据字段的定义
输出:AI文案润色Model.ets 文件
验收标准:
- 定义
AI文案润色Data类,包含 12 个字段 - 每个字段有正确的类型标注
- 所有字段有默认值初始化
- 构造函数中完成属性初始化
- 类可以被其他模块通过
export导入
工作量估算:0.5 人天
任务 2:服务层核心逻辑实现
输入:数据模型定义、业务逻辑规范
输出:AI文案润色Service.ets 文件
验收标准:
- 定义
AI文案润色Service类 - 实现
generateData(input: Record<string, Object>): AI文案润色Data方法 - 正确处理输入参数,提取文案内容值
- 返回符合预期的
AI文案润色Data实例 - 方法签名与 Page 层调用方式匹配
工作量估算:0.5 人天
任务 3:UI 页面搭建——输入区域
输入:UI 设计稿、ArkUI 组件规范
输出:AI文案润色Page.ets 中的输入区域代码
验收标准:
- 包含文案和目标风格两个输入框
- 每个输入框配有标签文字
- 输入框使用
TextInput组件,设置 placeholder - 输入框风格统一(白色背景、圆角边框)
onChange事件正确更新inputData状态
工作量估算:1 人天
任务 4:UI 页面搭建——按钮与交互
输入:交互设计规范
输出:AI文案润色Page.ets 中的按钮和交互逻辑
验收标准:
- "审稿批注"按钮样式正确(蓝色背景、圆角、白色文字)
- 按钮
onClick事件调用服务层方法 - 正确更新
resultData和showResult状态 - 页面顶部导航栏包含返回按钮、标题和副标题
工作量估算:0.5 人天
任务 5:UI 页面搭建——结果展示区域
输入:UI 设计稿、数据模型定义
输出:AI文案润色Page.ets 中的结果展示区域代码
验收标准:
- 使用
if条件渲染控制结果显示 - 展示润色后全文、修改列表、原文、修订版、修改原因、修改类型、修改前后对比、写作建议
- 使用
ForEach组件渲染数组类型字段(changes 修改列表) - 结果区域包含"批注结果"标题
- 结果卡片使用白色背景、圆角边框
工作量估算:1 人天
任务 6:导航与页面集成
输入:路由配置规范
输出:页面路由集成
验收标准:
- 页面顶部返回按钮调用
router.back()正常返回 - 页面在
apps.json中正确注册 - 从首页可以正常导航到该页面
工作量估算:0.5 人天
任务 7:UI 细节打磨与适配
输入:UI 设计规范
输出:UI 细节优化
验收标准:
- 页面背景色正确(#FAFAFA)
- 各组件间距、边距符合设计规范
- 文本颜色、字体大小、字重正确
- 在 HarmonyOS 模拟器上显示正常
工作量估算:0.5 人天
3.2 任务依赖关系
原子化任务之间存在依赖关系,需要按正确的顺序执行:
任务 1 (Model) ──→ 任务 2 (Service) ──→ 任务 3 (UI 输入区域)
│ │
│ ↓
└──────────→ 任务 5 (UI 结果展示)
↑
任务 4 (按钮交互) ───┘
任务 6 (导航集成) ── 依赖于任务 3/4/5 完成
任务 7 (UI 打磨) ── 依赖于所有 UI 任务完成
任务 1(数据模型)是基础依赖,必须先完成。任务 2(服务层)依赖于任务 1。任务 3、4、5(UI 层)可以并行进行,但都依赖于任务 2。任务 6(导航集成)和任务 7(UI 打磨)在最后阶段进行。
3.3 任务优先级排序
根据依赖关系和业务价值,我们对任务进行优先级排序:
| 优先级 | 任务 | 原因 |
|---|---|---|
| P0 | 任务 1 (Model) | 基础依赖,所有其他任务都依赖它 |
| P0 | 任务 2 (Service) | 核心逻辑,UI 层依赖它 |
| P0 | 任务 3 (UI 输入区域) | 用户交互入口,必须优先完成 |
| P0 | 任务 4 (按钮交互) | 核心交互逻辑 |
| P0 | 任务 5 (UI 结果展示) | 核心功能展示 |
| P1 | 任务 6 (导航集成) | 页面集成,影响用户体验 |
| P1 | 任务 7 (UI 打磨) | 细节优化,提升品质感 |
3.4 原子化分解的价值
原子化分解不仅仅是任务拆分,更是一种风险管理策略。通过将大任务分解为小任务,我们可以:
- 降低风险:每个小任务的风险可控,即使某个任务延期,也不会影响整体进度
- 提高可测试性:每个原子任务都有明确的验收标准,可以独立测试
- 便于并行开发:无依赖关系的任务可以并行进行,提高开发效率
- 增强进度可视化:通过原子任务的完成情况,可以精确跟踪项目进度
- 便于代码审查:小粒度的变更更容易审查,提高代码质量
四、审批阶段(Approve)
审批阶段是对前面三个阶段(对齐、架构、原子化)的成果进行审核和批准,确保所有设计决策符合项目要求,不存在遗漏或冲突。审批阶段是质量门控的关键环节,通过的审批意味着项目可以从设计阶段进入编码阶段。
4.1 审批清单
在"AI文案润色"的审批阶段,我们建立了以下审批清单:
4.1.1 对齐阶段审批项
- 需求理解是否准确?—— 是,核心需求(用户输入文案 → AI 智能润色)已明确
- 边界条件是否清晰?—— 是,当前阶段使用 Mock 数据,后续可接入真实 AI 模型
- 技术约束是否已对齐?—— 是,ArkTS 语法约束、ArkUI 声明式 UI 规范已对齐
- 项目上下文是否充分理解?—— 是,项目架构、技术栈、依赖关系已分析
- 是否存在需求歧义?—— 否,需求已收敛,无歧义
4.1.2 架构阶段审批项
- 三层架构是否与现有项目一致?—— 是,与项目中其他应用的架构模式一致
- 数据模型是否完整覆盖需求?—— 是,12 个字段覆盖文案润色核心要素
- 服务层接口是否清晰?—— 是,
generateData方法输入输出类型明确 - 数据流向是否合理?—— 是,符合"用户输入 → 状态存储 → 服务调用 → 结果展示"闭环
- 是否存在过度设计?—— 否,架构简单清晰,未引入不必要的抽象
- 模块依赖关系是否遵循单向依赖?—— 是,Page → Service → Model 单向依赖
4.1.3 原子化阶段审批项
- 任务分解是否合理?—— 是,7 个任务粒度适中,每个任务 0.5~1 人天
- 任务依赖关系是否明确?—— 是,依赖关系图清晰
- 验收标准是否可衡量?—— 是,每个任务有具体的验收标准
- 优先级排序是否合理?—— 是,P0 任务优先,P1 任务后置
4.2 代码审查要点
在审批阶段,除了对设计文档进行审核外,我们还建立了代码审查的要点清单,供后续编码阶段的代码审查使用:
ArkTS 语法合规性审查:
- 是否使用了
any或unknown类型?—— 禁止使用 - 是否存在
is运算符?—— 必须替换为instanceof - 是否存在解构赋值?—— 禁止使用
- 是否存在
Function.bind/apply/call?—— 禁止使用 - 是否存在
for...in遍历?—— 必须替换为常规 for 循环 - 是否存在索引签名?—— 禁止使用,改用数组或
Record - 是否存在对象字面量直接作为类型声明?—— 必须使用类或接口
- 是否存在
as const断言?—— 禁止使用,改用显式类型标注 - 是否存在
#开头的私有标识符?—— 必须改用private关键字
ArkUI 规范审查:
- 状态变量是否使用
@State装饰器?—— 必须使用 - 是否有不必要的
width、height动画操作?—— 禁止在动画中改变布局属性 - 列表渲染是否提供唯一键值?——
ForEach必须提供键值生成函数 - 条件渲染逻辑是否正确?—— 使用
if而非visibility属性 - 是否使用了
Scroll组件包裹长内容?—— 必须使用,确保内容可滚动
安全审查:
- 是否存在硬编码敏感信息?—— 确认无 API Key、密码等敏感信息
- 输入参数是否进行类型转换?——
String(input['content'] || '')确保类型安全 - 是否存在
catch子句中的类型标注?—— 必须省略
4.3 审批流程
在"AI文案润色"的开发中,我们采用了以下审批流程:
- 自审:开发者完成设计后,首先进行自我审查,对照审批清单逐项检查
- 互审:将设计文档提交给另一位团队成员进行交叉审查,从不同视角发现问题
- 终审:技术负责人进行最终审批,确认所有事项已解决后,批准进入编码阶段
审批通过后,所有设计文档(对齐文档、架构文档、原子化任务列表)被标记为"已批准"状态,作为后续开发工作的正式依据。任何对已批准设计的变更都需要重新提交审批,确保设计变更的可追溯性。
4.4 审批阶段的常见问题
在审批阶段,我们经常遇到以下问题,需要特别注意:
需求遗漏:在审查过程中发现某些需求未被覆盖。例如,在"AI文案润色"的初始设计中,我们遗漏了"字数统计"(word_count)字段的展示,在审批阶段被及时发现并补充。
技术方案冲突:新应用的设计与现有项目架构存在冲突。例如,如果新应用使用了不同的状态管理方案,需要与现有架构对齐。
过度设计:开发者为"未来可能的需求"做了过多的设计,导致当前实现复杂度增加。审批阶段需要严格遵循"只做当前需要做的事"原则。
五、自动化执行阶段(Automate)
自动化执行阶段是将设计转化为实际代码的过程。在"AI文案润色"的开发中,我们通过自动化工具和规范化的编码流程,确保代码质量和开发效率。
5.1 环境搭建与构建系统
HarmonyOS 应用开发使用 Hvigor 构建系统,它是华为自研的构建工具,基于 Gradle 但针对 HarmonyOS 进行了优化。项目的构建配置位于 oh-package.json5 文件中:
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {}
}
从配置可以看出,项目没有任何第三方依赖,所有功能都基于 HarmonyOS 内置的 Kit 能力。这包括 @kit.ArkUI(UI 框架)和 @kit.ArkTS(基础能力)。
5.2 数据模型层编码实现
数据模型层是应用的基础,定义了数据结构和类型约束。在"AI文案润色"中,数据模型层的完整实现如下:
// entry/src/main/ets/apps/AI文案润色/AI文案润色Model.ets
export class AI文案润色Data {
polished: string = ''
changes: string[] = []
original: string = ''
revised: string = ''
reason: string = ''
type: string = ''
before: string = ''
after: string = ''
style_comparison: string = ''
word_count: string = ''
tips: string = ''
constructor() {
this.polished = ''
this.changes = []
this.original = ''
this.revised = ''
this.reason = ''
this.type = ''
this.before = ''
this.after = ''
this.style_comparison = ''
this.word_count = ''
this.tips = ''
}
}
这段代码展示了 ArkTS 中数据模型的标准写法。有几个关键点值得注意:
字段声明与初始化:ArkTS 要求类字段必须在类声明内部声明,而不是在构造函数中声明。同时,每个字段都需要有显式的类型标注。这与标准 TypeScript 不同,在标准 TypeScript 中,你可以在构造函数中通过 this.field = value 动态创建字段。
双重初始化:字段在声明时设置了默认值,在构造函数中又再次初始化。这种模式在 ArkTS 中很常见,虽然没有严格的双重初始化要求,但这样做可以确保实例的字段始终有值。
数组类型:changes: string[] = [] 声明了一个字符串数组,并初始化为空数组。注意,ArkTS 不支持索引签名,所以使用数组类型来存储多个修改项。在后续的 UI 渲染中,这个数组将通过 ForEach 组件进行遍历。
类和接口的导出:使用 export class 语法导出数据模型类,使其可以被其他模块导入使用。ArkTS 不支持 export default 语法,必须使用命名导出。
5.3 服务层编码实现
服务层封装了业务逻辑,对外提供统一的接口。在"AI文案润色"中,服务层代码如下:
// 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 contentVal: string = String(input['content'] || '')
result.polished = '生成结果:' + contentVal
result.changes = ['示例数据1', '示例数据2', '示例数据3']
result.style_comparison = '生成结果:' + contentVal
result.word_count = '生成结果:' + contentVal
result.tips = '生成结果:' + contentVal
return result
}
}
服务层实现的关键技术点:
输入参数类型处理:input: Record<string, Object> 接收用户输入的键值对。Record<K, V> 是 ArkTS 支持的 TypeScript 实用类型之一,用于表示键值对映射。注意,由于 ArkTS 不支持 any 类型,我们使用 Object 作为值的类型。
类型转换:String(input['content'] || '') 将输入值转换为字符串。这里使用了 String() 全局函数和 || 逻辑运算符,确保在输入值为空或未定义时使用空字符串兜底。注意,这里使用的是 input['content'] 的索引访问,这在 Record<string, Object> 类型上是允许的,因为 Record 类型是 ArkTS 支持的容器类型,不违反"不支持索引访问类型"的约束。
Mock 数据模式:当前阶段使用 Mock 数据,所有字段值基于 contentVal 生成。这种模式允许我们在不接入真实 AI 模型的情况下,完成 UI 开发和验证。后续接入真实 AI 模型时,只需替换 generateData 方法内部的实现逻辑。
服务实例的私有化:private model: AI文案润色Data 声明了一个私有成员变量,用于在服务实例内部持有数据模型。这种设计体现了面向对象编程的封装原则。
5.4 UI 表现层编码实现
UI 表现层是整个应用的入口,实现了完整的用户交互界面。以下是完整的页面代码:
// 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() {
// 顶部导航栏
Row() {
Text('← 返回')
.fontSize(13)
.fontColor('#DC2626')
.onClick(() => { router.back() })
Blank()
Column() {
Text('📱 AI文案润色').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#1A1A1A')
Text('审稿 · 批注模式').fontSize(9).fontColor('#2563EB').margin({ top: 2 })
}
Blank()
Text('RB').fontSize(12).fontColor('#DC2626')
.border({ width: 1, color: '#DC2626', radius: 4 })
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
}
.width('100%').padding({ left: 20, right: 20, top: 16, bottom: 14 })
.backgroundColor('#FAFAFA')
Scroll() {
Column() {
// 输入区域
Column() {
Text('📝 文案')
.fontSize(11).fontColor('#DC2626').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入文案' })
.fontSize(13).height(40).backgroundColor('#FFFFFF')
.borderRadius(4).border({ width: 1, color: '#D1D5DB' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['文案'] = val })
Text('📝 目标风格')
.fontSize(11).fontColor('#DC2626').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入目标风格' })
.fontSize(13).height(40).backgroundColor('#FFFFFF')
.borderRadius(4).border({ width: 1, color: '#D1D5DB' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['目标风格'] = val })
}
.width('100%').padding(18).backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E5E7EB' }).margin({ top: 6 })
// 审稿批注按钮
Button('📱 审稿批注')
.width('100%').height(50).backgroundColor('#2563EB')
.borderRadius(6).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('#1A1A1A').margin({ bottom: 12 })
// 润色后全文
Row() {
Text('Polished: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.polished).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 修改列表
Text('Changes').fontSize(13).fontWeight(FontWeight.Bold)
.fontColor('#333333').margin({ top: 10, bottom: 6 })
if (this.resultData.changes) {
ForEach(this.resultData.changes, (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('Original: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.original).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 修订版
Row() {
Text('Revised: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.revised).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 修改原因
Row() {
Text('Reason: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.reason).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 修改类型
Row() {
Text('Type: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.type).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 修改前/后对比
Row() {
Text('Before: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.before).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
Row() {
Text('After: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.after).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 写作建议
Row() {
Text('Tips: ').fontSize(12)
.fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.tips).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
}
.width('100%').padding(18).backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E5E7EB' }).margin({ bottom: 20 })
}
}
.width('100%').padding({ left: 18, right: 18, bottom: 40 })
}
.layoutWeight(1)
}
.width('100%').height('100%').backgroundColor('#FAFAFA')
}
}
5.5 关键技术实现详解
5.5.1 @State 状态管理机制
在 ArkUI 中,@State 装饰器是实现响应式 UI 的核心机制。当被 @State 装饰的变量发生变化时,ArkUI 框架会自动重新渲染依赖该变量的 UI 组件。
在"AI文案润色"中,我们使用了三个状态变量:
@State inputData: Record<string, Object> = {}
@State resultData: AI文案润色Data | null = null
@State showResult: boolean = false
inputData:存储用户输入的文案和目标风格值,通过TextInput的onChange事件更新resultData:存储服务层生成的结果数据,初始为null,生成后更新为AI文案润色Data实例showResult:控制结果展示区域的显隐,初始为false,生成后更新为true
值得注意的是,resultData 的类型标注为 AI文案润色Data | null,这是 ArkTS 中联合类型的使用方式。由于 ArkTS 不支持 any 和 unknown 类型,联合类型成为处理可选值的主要手段。
5.5.2 条件渲染
条件渲染是 ArkUI 中控制组件显隐的主要方式。在"AI文案润色"中,结果展示区域通过 if 语句控制:
if (this.showResult && this.resultData !== null) {
// 结果展示区域
}
这个条件语句有两个作用:
this.showResult控制是否展示结果——初始时showResult为false,结果区域不展示;点击生成按钮后showResult变为true,结果区域展示this.resultData !== null进行空值保护——确保在数据未生成时不会访问空对象的属性
ArkUI 的条件渲染是声明式的,开发者只需要声明渲染条件,框架会自动处理组件的创建和销毁。这在性能上优于传统的 show/hide 切换,因为不满足条件的组件根本不会被创建。
5.5.3 列表渲染
对于数组类型字段(修改列表 changes),我们使用 ForEach 组件进行列表渲染:
ForEach(this.resultData.changes, (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())
ForEach 组件接收三个参数:
- 数据源数组:
this.resultData.changes - UI 生成函数:
(item: string, index: number) => void,用于生成每个数组元素对应的 UI - 键值生成函数:
(item: string, index: number) => string,用于生成每个列表项的唯一标识,帮助 ArkUI 框架进行高效的列表更新
使用 index.toString() 作为键值在当前场景中是可接受的,因为列表数据在生成后不会动态变化。如果列表数据可能会增删改,建议使用数据本身的唯一标识作为键值,以避免列表更新时的性能问题。
5.5.4 布局系统
ArkUI 使用 Row 和 Column 作为主要的布局容器,分别对应水平方向和垂直方向的线性布局。
在"AI文案润色"中,页面采用垂直布局(Column)作为根容器,内部包含:
- 水平布局(
Row)的顶部导航栏:使用Blank()组件实现标题居中,左右两侧分别放置返回按钮和版本标签 - 可滚动区域(
Scroll):包裹输入区域、按钮和结果展示区域,确保内容超出屏幕高度时可以滚动查看 - 嵌套的
Column布局:输入区域和结果区域各自使用Column作为容器,内部按垂直方向排列各个子元素
布局参数的使用:
.width('100%').height('100%') // 宽高百分比
.padding({ left: 20, right: 20 }) // 内边距
.margin({ top: 18, bottom: 14 }) // 外边距
.layoutWeight(1) // 权重分配剩余空间
.border({ width: 1, color: '#E5E7EB', radius: 4 }) // 边框样式
layoutWeight(1) 是 ArkUI 中非常重要的布局属性,它让 Scroll 组件占据父容器中除其他固定高度组件外的所有剩余空间,确保页面内容可以自适应填充。
5.5.5 事件处理
在 ArkUI 中,事件处理通过链式调用的方式绑定:
Button('📱 审稿批注')
.onClick(() => {
this.resultData = this.service.generateData(this.inputData)
this.showResult = true
})
onClick 回调函数中使用箭头函数,这是 ArkTS 的要求——不支持函数表达式,必须使用箭头函数。箭头函数自动捕获当前 this 上下文,使得在回调中可以访问组件的状态变量和服务实例。
TextInput 的输入事件处理:
TextInput({ placeholder: '请输入文案' })
.onChange((val: string) => { this.inputData['文案'] = val })
onChange 回调接收当前输入框的值,并将其存储到 inputData 状态中。注意,这里使用了对象属性访问语法 this.inputData['文案'],这是在 ArkTS 中允许的——它是在 Record<string, Object> 类型上的索引访问,而不是在任意对象上的索引访问。
5.5.6 导航实现
页面顶部提供了返回按钮,用于导航回上一页:
Text('← 返回')
.fontSize(13)
.fontColor('#DC2626')
.onClick(() => { router.back() })
router.back() 是 @kit.ArkUI 提供的路由 API,用于返回上一页。注意,router 的导入语句为 import { router } from '@kit.ArkUI',这是 HarmonyOS 的标准导入方式。
5.6 自动化测试
自动化测试是保障代码质量的重要手段。在"AI文案润色"的开发中,虽然当前阶段主要是 Mock 数据,但我们仍然需要建立测试框架,为后续的真实 AI 模型接入做好准备。
5.6.1 单元测试
服务层 generateData 方法的单元测试应该覆盖以下场景:
- 正常输入:传递有效的文案内容和目标风格值,验证返回数据各字段正确
- 空输入:传递空对象或空字符串,验证返回数据的兜底行为
- 部分输入:只传递部分字段(如只有文案),验证其他字段的默认值处理
5.6.2 UI 测试
UI 测试应该覆盖以下交互场景:
- 初始状态:页面加载时,输入区域应显示,结果区域应隐藏
- 输入交互:在输入框中输入文字,验证状态变量是否正确更新
- 生成操作:点击"审稿批注"按钮,验证结果区域是否正确展示
- 空数据保护:在
resultData为null时,验证条件渲染正确处理
5.6.3 构建与部署
HarmonyOS 应用的构建通过 DevEco Studio 或命令行完成:
# 构建 HAP 包
hvigorw assembleHap
# 运行测试
hvigorw runTest
# 构建产物输出
hvigorw assembleApp
构建产物为 HAP(HarmonyOS Ability Package)格式,可以部署到 HarmonyOS 模拟器或真机上进行测试。
六、评估阶段(Assess)
评估阶段是对整个开发过程和成果进行回顾和评估,总结经验教训,为后续项目提供参考。评估应该是客观、全面、有建设性的,既要肯定成绩,也要指出不足。
6.1 项目成果评估
6.1.1 功能完整性
"AI文案润色"应用实现了以下功能:
- ✅ 用户输入采集:支持文案和目标风格两个输入参数
- ✅ AI 文案润色:基于输入参数生成润色结果(Mock 数据)
- ✅ 结果展示:结构化展示完整的润色结果,包含 10+ 个数据字段
- ✅ 修改列表:支持
ForEach列表渲染展示多条修改建议 - ✅ 交互反馈:点击按钮触发润色,结果区域动态展示
- ✅ 页面导航:支持从首页跳入和返回
6.1.2 代码质量评估
代码规范性:代码严格遵守 ArkTS 语法约束和 ArkUI 开发规范,无任何语法违规行为。类型标注完整,所有变量都有明确的类型声明。
架构清晰度:三层架构(Page → Service → Model)职责划分清晰,模块依赖关系遵循单向依赖原则。代码结构简洁,易于理解和维护。
代码复用性:数据模型 AI文案润色Data 和服务层 AI文案润色Service 是独立模块,可在其他页面或应用中被复用。服务层的方法设计支持后续接入真实 AI 模型。
性能表现:页面使用 Scroll 组件包裹内容,支持长内容滚动。条件渲染确保结果区域在不需要时不会被创建,减少内存占用。@State 驱动的响应式更新确保 UI 只更新必要的部分。
6.2 关键技术决策回顾
在"AI文案润色"的开发过程中,我们做出了一系列关键技术决策,以下是这些决策的回顾和评估:
决策 1:使用 Record<string, Object> 而非具体类型
决策内容:输入参数使用 Record<string, Object> 类型,而非定义具体的输入参数接口。
决策理由:输入参数来自 UI 层的 TextInput 组件,键值对是动态的,使用 Record<string, Object> 提供灵活性。同时,ArkTS 不支持索引签名,Record 是替代索引签名的官方推荐方式。
评估结果:✅ 合理。这种设计在灵活性和类型安全之间取得了平衡。后续如果需要更强的类型约束,可以定义专门的输入参数接口。
决策 2:Mock 数据替代真实 AI 模型
决策内容:当前阶段使用 Mock 数据模拟 AI 生成行为,后续接入真实 AI 模型。
决策理由:在项目初期,UI 开发和业务逻辑可以并行进行。Mock 数据允许我们在不依赖外部 AI 服务的情况下完成 UI 开发和验证。
评估结果:✅ 合理。这种"先 Mock,后接入"的策略是应用开发的常见模式,可以加速开发进度。后续接入真实 AI 模型时,只需替换 generateData 方法的内部实现。
决策 3:使用 if 条件渲染而非显隐控制
决策内容:使用 if (this.showResult && this.resultData !== null) 控制结果展示,而非使用 visibility 属性。
决策理由:if 条件渲染在组件不需要时根本不会创建组件,性能更好;而 visibility 只是隐藏组件,组件仍然存在。
评估结果:✅ 合理。对于"AI文案润色"这种结果在生成前完全不需要展示的场景,if 条件渲染是最优选择。
决策 4:使用 ForEach 进行列表渲染
决策内容:使用 ForEach 组件渲染 changes 数组中的修改项,而非手动创建多个 Text 组件。
决策理由:changes 数组的长度是动态的,使用 ForEach 可以自动根据数组长度生成对应数量的 UI 元素,无需手动管理。
评估结果:✅ 合理。ForEach 是 ArkUI 中处理动态列表的标准方式,提供了键值管理和高效更新的能力。
6.3 经验教训与改进建议
6.3.1 经验总结
ArkTS 语法约束需要提前对齐:在 HarmonyOS 开发中,ArkTS 的语法约束与标准 TypeScript 差异较大。如果团队没有 ArkTS 开发经验,建议在项目开始前进行专门的 ArkTS 语法培训,或编写一份 ArkTS 语法速查手册。例如,在"AI文案润色"的开发中,我们特别注意了 Record<string, Object> 的正确使用、ForEach 的键值生成函数、以及 if 条件渲染的模式。
声明式 UI 的开发范式转变:从传统的命令式 UI(如 Android 的 XML + Java)转向声明式 UI(如 ArkUI),需要开发者的思维模式进行转变。在声明式 UI 中,开发者只需要关注"状态"和"UI 之间的映射关系",不需要手动操作 UI 的增删改。
分层架构的价值:三层架构在"AI文案润色"这样的小型应用中,可能看起来有些"过度设计"。但在实际开发中,分层架构的价值体现在:
- 可测试性:每一层可以独立测试
- 可维护性:修改某一层不会影响其他层
- 可扩展性:后续接入真实 AI 模型时,只需修改服务层
条件渲染与列表渲染的最佳实践:在 ArkUI 中,条件渲染使用 if 语句,列表渲染使用 ForEach 组件。这两个模式是 ArkUI 声明式 UI 的核心能力,掌握它们可以高效地构建动态 UI。
6.3.2 改进建议
接入真实 AI 模型:当前的 Mock 数据只能展示 UI 交互效果,没有实际的 AI 润色能力。推荐接入 HarmonyOS 的 AI 能力(如 Core AI Kit)或第三方 AI 大模型 API(如 OpenAI、文心一言等),实现真正的智能文案润色。
增强输入校验:当前对用户输入的校验较为简单,建议增加更多校验逻辑,如输入长度限制、特殊字符过滤、必填字段检查等。例如,可以在用户点击"审稿批注"按钮前,检查文案是否为空,若为空则给出提示。
优化用户体验:可以考虑增加加载动画、骨架屏、错误提示等用户体验优化。在 AI 生成过程中,加载动画可以提供更好的等待体验。可以使用 @State 控制加载状态,在生成过程中展示加载动画,生成完成后切换为结果展示。
离线缓存:将用户生成的润色结果缓存到本地,方便用户随时查看历史记录。可以使用 HarmonyOS 的 @ohos.data.preferences 或 @ohos.data.distributedKVStore 实现数据持久化。
分享功能:文案润色的最终目的是用于实际写作,可以集成复制到剪贴板或分享功能,让用户一键复制润色结果。
增强结果展示样式:当前结果展示采用简单的文本行布局,可以进一步优化为更丰富的 UI 样式,如使用卡片式布局展示修改列表,使用高亮对比展示修改前后的差异,使用进度条或图表展示风格对比和字数统计等。
6.4 与同类应用的对比
"AI文案润色"作为 HarmonyOS 生态中的 AI 应用,与同类应用(如 Grammarly、写作猫等)相比,有其独特的优势:
平台优势:深度集成 HarmonyOS 生态,可以利用 HarmonyOS 的分布式能力、多端适配能力等特性,为用户提供跨设备的无缝体验。
轻量化:作为应用集合中的一员,"AI文案润色"体积小、启动快,用户可以即开即用。三个核心文件加起来不到 300 行代码,却构成了一个完整的、可运行的 AI 应用。
安全可控:所有代码运行在 HarmonyOS 的沙箱环境中,用户数据不会泄露到第三方平台。后续接入 AI 模型时,也可以选择本地 AI 推理,进一步保障数据安全。
审稿批注模式:与其他文案润色工具不同,"AI文案润色"特别强调"审稿·批注模式"的定位,提供逐条修改建议的展示方式,类似于审稿人的批注风格,让用户不仅能得到润色结果,还能理解每处修改的原因和逻辑。
6.5 未来展望
"AI文案润色"应用虽然当前是一个简单的 Mock 数据演示,但其架构设计和代码结构为后续的演进奠定了良好基础。以下是我们对未来的展望:
短期目标(1-2 个月):
- 接入真实 AI 模型,实现真正的智能文案润色
- 增加输入校验和错误处理
- 优化 UI 交互体验,增加加载动画
- 支持更多润色维度(如语气调整、句式优化等)
中期目标(3-6 个月):
- 增加历史记录功能,支持用户查看和管理已润色的文案
- 支持文案模板,用户可以选择不同场景的润色模板
- 集成复制和分享功能
- 支持批量润色,同时处理多段文案
长期目标(6-12 个月):
- 利用 HarmonyOS 的分布式能力,实现跨设备协作(如手机输入、平板预览)
- 集成多模态 AI 能力,支持图文混排的文案润色
- 构建用户反馈系统,持续优化 AI 润色质量
- 支持插件化扩展,用户可以根据需要安装不同的润色风格包
结语
本文以"AI文案润色"应用为案例,详细阐述了在 HarmonyOS 生态下,从需求对齐到最终交付的全流程开发实践。通过"对齐→架构→原子化→审批→自动化执行→评估"六阶段方法论,我们系统地完成了从需求分析到代码实现的全过程。
在开发过程中,我们深刻体会到 HarmonyOS 的 ArkTS 语言和 ArkUI 框架在声明式 UI 开发方面的强大能力,也认识到 ArkTS 语法约束带来的挑战。通过严格遵循平台规范、合理设计架构、精心编写代码,我们最终交付了一个高质量的 AI 应用。
"AI文案润色"的完整代码位于 c:\Users\l\DevEcoStudioProjects\MyApplication\entry\src\main\ets\apps\AI文案润色\ 目录下,包含三个核心文件:
AI文案润色Page.ets:UI 表现层,实现用户交互和界面展示,约 200 行代码AI文案润色Service.ets:业务服务层,封装润色数据生成逻辑,约 30 行代码AI文案润色Model.ets:数据模型层,定义数据结构,约 30 行代码
这三个文件加起来不到 300 行代码,却构成了一个完整的、可运行的 AI 应用。这正是 HarmonyOS 应用架构的魅力所在——轻量、高效、可组合。
AI 文案润色技术正在改变人们的写作方式。从简单的语法检查到深度的风格优化,从单一的语言修正到多维度的写作建议,AI 正逐步成为每个人身边的全能写作助手。HarmonyOS 作为新一代智能终端操作系统,为 AI 应用的创新提供了广阔的舞台。
希望本文的实践经验能够对 HarmonyOS 应用开发者有所启发和帮助。在 AI 时代,将 AI 能力与移动应用深度融合,将为用户带来前所未有的智能体验。
作者:HarmonyOS 应用开发团队
版本:v1.0
日期:2026 年 7 月
版权声明:本文为 HarmonyOS 应用开发技术博客,欢迎转载,请注明出处。
更多推荐


所有评论(0)