从0到1:HarmonyOS AI公众号选题应用的六阶段开发实践

摘要:本文以 HarmonyOS 平台上的 AI 公众号选题应用为实战案例,详细阐述了一套六阶段开发方法论——对齐(Align)、架构(Architect)、原子化(Atomize)、审批(Approve)、自动化执行(Automate)、评估(Assess)。文章从项目上下文分析出发,贯穿 ArkTS 语法约束、ArkUI 声明式 UI 开发、数据模型设计、服务层封装等核心环节,提供了完整的代码示例和技术决策记录。全文约 10000 字,适合 HarmonyOS 应用开发者、AI 产品经理以及关注结构化开发流程的技术团队阅读。


在这里插入图片描述

一、对齐阶段(Align)

对齐阶段的目标是将模糊需求转化为精确规范。任何开发项目在启动前,都必须完成项目上下文分析和需求理解确认,这是后续所有工作的基石。

1.1 项目上下文分析

1.1.1 现有项目结构

我们的项目是一个 HarmonyOS 应用,使用 ArkTS 语言开发,项目根目录为 MyApplication。通过分析项目目录结构,我们可以看到以下关键路径:

entry/src/main/ets/
├── pages/
│   └── Index.ets                  # 主入口页面,应用列表
├── apps/
│   ├── AI公众号选题/
│   │   ├── AI公众号选题Page.ets    # 选题页面
│   │   ├── AI公众号选题Model.ets   # 数据模型
│   │   └── AI公众号选题Service.ets # 业务服务
│   ├── AI公众号涨粉选题/
│   │   ├── AI公众号涨粉选题Page.ets
│   │   ├── AI公众号涨粉选题Model.ets
│   │   └── AI公众号涨粉选题Service.ets
│   └── ... (80+ 个 AI 应用)

项目使用了 @kit.ArkUI@kit.ArkTS 两大核心 Kit。每个应用都遵循 MVP(Model-View-Presenter)风格的三层架构:Page(UI 层)、Model(数据层)、Service(业务逻辑层)。这种模式保证了代码的高内聚低耦合。

1.1.2 路由配置分析

应用的路由配置位于 entry/src/main/resources/base/profile/main_pages.json,其中注册了所有页面的路径。例如:

{
  "src": [
    "pages/Index",
    "apps/AI公众号选题/AI公众号选题Page",
    "apps/AI公众号涨粉选题/AI公众号涨粉选题Page",
    ...
  ]
}

Index 页面通过 router.pushUrl 跳转到具体应用页面,跳转逻辑在 apps.json 中配置,每个应用包含 icon、title、subtitle、page 路径和分类信息。

1.1.3 技术栈分析
  • 语言:ArkTS(基于 TypeScript 的静态类型方言)
  • UI 框架:ArkUI 声明式 UI
  • 构建工具:Hvigor
  • 依赖管理:oh-package.json5
  • API Kit:@kit.ArkUI(UI 组件)、@kit.ArkTS(基础能力)

项目当前依赖配置(entry/oh-package.json5)为空,说明所有功能均基于 HarmonyOS 原生 API 实现,未引入第三方依赖。

1.2 ArkTS 语法约束

在开始编码之前,必须理解 ArkTS 与标准 TypeScript 的关键差异。以下是在开发过程中遇到的核心约束:

约束项 说明 影响范围
不支持 any/unknown 必须显式指定类型 所有类型声明
不支持解构赋值 需使用临时变量逐字段操作 数据传递
不支持 Function.bind/call/apply this 遵循传统 OOP 风格 回调处理
不支持 for...in 数组使用常规 for 循环 数据遍历
不支持索引签名 使用数组替代 字典数据结构
不支持对象字面量直接作为类型 需显式声明 class/interface 数据模型
不支持 as const 用显式类型标注替代 常量定义
this 仅能在实例方法中使用 静态方法和独立函数中禁用 事件处理

这些约束深刻影响了代码的组织方式。例如,在 AI公众号选题Page.ets 中,我们使用 Record<string, Object> 来存储输入数据,而不是使用解构赋值:

// ArkTS 不支持解构赋值,所以使用 Record 存储
@State inputData: Record<string, Object> = {};

// 通过 onChange 回调逐字段赋值,而非解构
TextInput({ placeholder: '请输入领域' })
  .onChange((val: string) => {
    this.inputData['领域'] = val;
  });

1.3 需求理解与确认

1.3.1 原始需求

"AI公众号选题"应用的核心需求是:用户输入领域、目标读者和热点方向三个参数,系统自动生成包括选题列表、标题、切入角度、钩子、文章框架、预估阅读量、难度评级、标题变体、系列文章创意、发布时机和竞品分析等在内的完整选题方案。

1.3.2 边界确认
  • 输入边界:三个必填字段(领域、目标读者、热点方向),均为文本输入
  • 输出边界:13 个结构化输出字段,包含字符串数组和单值字符串
  • 技术边界:当前阶段使用 Mock 数据模拟,后续可对接大模型 API
  • 平台边界:仅支持 HarmonyOS 手机设备
1.3.3 需求理解

通过分析 AI公众号选题Model.ets 的数据模型,我们可以清晰地看到输出的完整结构:

export class AI公众号选题Data {
  topics: string[] = [];           // 推荐选题列表
  title: string = '';              // 主标题
  angle: string = '';              // 切入角度
  hook: string = '';               // 钩子/开头
  framework: string = '';          // 文章框架
  estimated_reads: string = '';    // 预估阅读量
  difficulty: string = '';         // 难度评级
  title_variants: string[] = [];   // 标题变体
  topic: string = '';              // 主题
  titles: string[] = [];           // 标题列表
  series_idea: string = '';        // 系列文章创意
  timing: string = '';             // 发布时机
  competitor_analysis: string = '';// 竞品分析
}

这个模型涵盖了选题策划的全部关键维度,从内容生产(标题、框架、角度)到运营策略(发布时机、竞品分析),再到效果预估(阅读量、难度),形成了一套完整的选题决策支持体系。


二、架构阶段(Architect)

架构阶段的目标是从共识文档到系统架构,再到模块设计和接口规范。良好的架构设计是应对复杂性和变化的关键。

2.1 整体架构设计

2.1.1 三层架构

AI公众号选题应用采用经典的三层架构:

┌─────────────────────────────────────┐
│          Presentation Layer         │
│    AI公众号选题Page.ets (UI)         │
│  - @Component 声明式 UI             │
│  - @State 状态管理                  │
│  - 用户交互与数据展示               │
└──────────────┬──────────────────────┘
               │ 调用
┌──────────────▼──────────────────────┐
│          Service Layer              │
│   AI公众号选题Service.ets (业务逻辑) │
│  - generateData() 核心方法          │
│  - 数据校验与转换                   │
│  - Mock/API 数据生成               │
└──────────────┬──────────────────────┘
               │ 返回
┌──────────────▼──────────────────────┐
│           Model Layer               │
│    AI公众号选题Model.ets (数据模型)  │
│  - AI公众号选题Data 数据类          │
│  - 字段类型定义                     │
│  - 数据序列化                       │
└─────────────────────────────────────┘

设计原则

  1. 单向数据流:Page → Service → Model,数据通过返回值回传
  2. 职责单一:每个文件只负责一个关注点
  3. 接口隔离:Service 对 Page 暴露最小化接口
2.1.2 与项目架构的集成

该应用作为 MyApplication 项目中的一个子应用,需要与主框架集成:

  • 路由集成:在 main_pages.json 中注册页面路径
  • 导航集成:通过 router.back() 返回主页
  • 数据集成:使用 getContext(this).resourceManager 读取资源文件

2.2 模块详细设计

2.2.1 UI 层设计(AI公众号选题Page.ets)

UI 层采用 ArkUI 的声明式语法,整体结构分为三个区域:

Header 区域:包含返回按钮、标题和装饰元素

Row() {
  Text('← 返回')
    .fontSize(13)
    .fontColor('#1A1A1A')
    .onClick(() => { router.back() });
  Blank();
  Column() {
    Text('📱 AI公众号选题')
      .fontSize(17)
      .fontWeight(FontWeight.Bold)
      .fontColor('#1A1A1A');
    Text('WHITEBOARD · 白板')
      .fontSize(9)
      .fontColor('#6B7280')
      .margin({ top: 2 });
  }
  Blank();
  Text('📋')
    .fontSize(22);
}
.width('100%')
.padding({ left: 20, right: 20, top: 16, bottom: 14 })
.backgroundColor('#F9FAFB');

输入区域:三个输入字段,使用 TextInput 组件

结果展示区域:条件渲染,使用 if (this.showResult && this.resultData !== null) 控制显示

状态管理方面,使用了三个 @State 变量:

@State inputData: Record<string, Object> = {};      // 输入数据
@State resultData: AI公众号选题Data | null = null;    // 结果数据
@State showResult: boolean = false;                  // 是否展示结果

这里有一个值得注意的 ArkTS 约束:@State 变量的类型必须显式声明。由于不支持 any 类型,我们使用了 Record<string, Object> 作为输入数据的容器类型。

2.2.2 数据模型层设计(AI公众号选题Model.ets)

数据模型层定义了 AI公众号选题Data 类,所有字段都有默认值并显式初始化:

export class AI公众号选题Data {
  topics: string[] = [];
  title: string = '';
  angle: string = '';
  hook: string = '';
  framework: string = '';
  estimated_reads: string = '';
  difficulty: string = '';
  title_variants: string[] = [];
  topic: string = '';
  titles: string[] = [];
  series_idea: string = '';
  timing: string = '';
  competitor_analysis: string = '';

  constructor() {
    // 显式初始化确保所有字段有默认值
    this.topics = [];
    this.title = '';
    // ... 其余字段初始化
  }
}

设计考量:

  • 默认值初始化:ArkTS 要求类字段必须有初始值,或者在构造函数中赋值
  • 数组类型string[] 是标准数组类型,不支持索引签名
  • 不可变性:类实例化后,字段通过对象属性访问(obj.field),不支持 obj["field"] 索引访问
2.2.3 服务层设计(AI公众号选题Service.ets)

服务层封装了核心业务逻辑,对外暴露 generateData 方法:

export class AI公众号选题Service {
  private model: AI公众号选题Data;

  constructor() {
    this.model = new AI公众号选题Data();
  }

  generateData(input: Record<string, Object>): AI公众号选题Data {
    let result: AI公众号选题Data = new AI公众号选题Data();
    let fieldVal: string = String(input['field'] || '');

    // Mock 数据生成
    result.topics = ['示例数据1', '示例数据2', '示例数据3'];
    result.title_variants = ['示例数据1', '示例数据2', '示例数据3'];
    result.series_idea = '生成结果:' + fieldVal;
    result.timing = '生成结果:' + fieldVal;
    result.competitor_analysis = '生成结果:' + fieldVal;
    return result;
  }
}

设计要点:

  • 解耦:Page 不直接操作 Model,通过 Service 中介
  • 可测试性:Service 可以独立于 UI 进行单元测试
  • 可扩展性:后续可替换为真实 API 调用,Page 层无需修改

2.3 数据流设计

用户输入 → inputData (Record) → Button Click → 
  service.generateData(inputData) → 
    构造 AI公众号选题Data 实例 →
    填充 Mock 数据 → 
    返回 resultData →
    赋值给 @State resultData →
    UI 自动刷新 →
    条件渲染展示结果

ArkUI 的响应式编程模型使得数据流自动驱动 UI 更新。当 @State resultData 被赋值时,UI 自动重新渲染,无需手动操作 DOM。

2.4 异常处理策略

// 在页面中,结果展示使用了安全的空值检查
if (this.showResult && this.resultData !== null) {
  // 安全地访问 resultData 的字段
  if (this.resultData.topics) {
    ForEach(this.resultData.topics, ...);
  }
}

ArkTS 的 catch 子句不支持类型标注(不支持 any/unknown),因此异常处理时省略类型标注:

try {
  // 可能抛出异常的代码
} catch {
  // ArkTS 中省略类型标注
  console.error('发生错误');
}

三、原子化阶段(Atomize)

原子化阶段将任务分解为更小的、可管理的原子任务,便于高效执行和跟踪。

3.1 任务分解方法论

对于 AI公众号选题应用,我们将整个开发任务分解为以下原子任务:

3.1.1 基础设施任务
任务 ID 任务描述 预估工时 依赖
T-001 创建应用目录结构 0.5h
T-002 配置路由(main_pages.json) 0.5h T-001
T-003 配置应用元数据(apps.json) 0.5h T-001
3.1.2 数据模型任务
任务 ID 任务描述 预估工时 依赖
T-004 定义 AI公众号选题Data 类 1h T-001
T-005 定义所有字段类型和默认值 0.5h T-004
T-006 实现构造函数初始化 0.5h T-005
3.1.3 服务层任务
任务 ID 任务描述 预估工时 依赖
T-007 创建 AI公众号选题Service 类 1h T-004
T-008 实现 generateData 方法 2h T-007
T-009 实现 Mock 数据生成逻辑 1h T-008
3.1.4 UI 层任务
任务 ID 任务描述 预估工时 依赖
T-010 实现 Header 组件 1h T-001
T-011 实现输入表单(三个字段) 2h T-010
T-012 实现提交按钮 1h T-011
T-013 实现结果展示区域 2h T-009, T-012
T-014 实现条件渲染逻辑 1h T-013
T-015 样式优化和适配 1.5h T-014

3.2 ArkTS 特有约束下的任务调整

在原子化分解过程中,需要针对 ArkTS 的语法约束进行任务调整:

3.2.1 数据展示的 ForEach 处理

由于 ArkTS 不支持 for...in 循环,遍历数组必须使用 ForEach 组件或常规 for 循环。在 UI 中,我们使用 ForEach

// 展示选题列表
if (this.resultData.topics) {
  ForEach(this.resultData.topics, (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 的第三个参数是键值生成函数,用于优化列表渲染性能。在 ArkTS 中,index.toString() 是推荐的键值生成方式。

3.2.2 条件渲染的 null 检查

ArkTS 中,条件渲染必须显式检查 null:

// ArkTS 不支持可选链操作符 ?. 的完整语义
// 需要显式检查 null
if (this.showResult && this.resultData !== null) {
  // 安全访问
}
3.2.3 事件处理中的 this 绑定

ArkTS 不支持 Function.bind,但 Arrow Function 自动捕获外层 this:

// 使用箭头函数,无需 bind
.onClick(() => {
  this.resultData = this.service.generateData(this.inputData);
  this.showResult = true;
});

3.3 任务依赖图

T-001 (目录结构)
  ├── T-002 (路由配置)
  ├── T-003 (应用元数据)
  └── T-004 (数据模型)
        └── T-005 (字段类型)
              └── T-006 (构造函数)
                    └── T-007 (Service 类)
                          └── T-008 (generateData)
                                └── T-009 (Mock 数据)
T-010 (Header)
  └── T-011 (输入表单)
        └── T-012 (提交按钮)
              └── T-013 (结果展示)
                    └── T-014 (条件渲染)
                          └── T-015 (样式优化)

3.4 原子化任务的优势

  1. 并行开发:T-002/T-003/T-004 可以并行推进
  2. 风险隔离:单个任务失败不影响整体
  3. 精确评估:每个任务 0.5-2h,便于进度跟踪
  4. 质量可控:每个任务可独立验证

四、审批阶段(Approve)

审批阶段对前面阶段的成果进行审核和批准,确保符合质量要求。

4.1 ArkTS 语法合规审查

在审批阶段,代码必须通过 ArkTS 编译器的严格检查。以下是关键的审查点:

4.1.1 类型安全检查

审查规则:所有变量必须有显式类型,禁止使用 any/unknown

通过示例

// ✅ 正确:显式类型声明
@State inputData: Record<string, Object> = {};
@State resultData: AI公众号选题Data | null = null;
private service: AI公众号选题Service = new AI公众号选题Service();

// ❌ 错误:隐式 any 类型
// @State inputData = {};  // 编译错误
4.1.2 类声明规范

审查规则:类字段必须在类声明中初始化,构造函数中不能声明新字段。

通过示例

export class AI公众号选题Data {
  // ✅ 正确:在类声明中声明并初始化所有字段
  topics: string[] = [];
  title: string = '';
  angle: string = '';
  // ...

  constructor() {
    // ✅ 正确:构造函数中只赋值,不声明
    this.topics = [];
    this.title = '';
    this.angle = '';
    // ...
  }
}
4.1.3 对象字面量使用规范

审查规则:对象字面量必须有可推断的类型,不能直接用作类型声明。

通过示例

// ✅ 正确:使用 Record 类型
@State inputData: Record<string, Object> = {};

// ✅ 正确:在 ForEach 中使用的数组字面量元素类型一致
ForEach(['数据源','分析中','就绪'], (item: string) => { ... });
4.1.4 函数类型规范

审查规则:使用箭头函数替代函数表达式,不支持函数声明上的属性。

通过示例

// ✅ 正确:使用箭头函数作为回调
.onChange((val: string) => {
  this.inputData['领域'] = val;
});

// ✅ 正确:ForEach 中使用箭头函数
ForEach(this.resultData.topics, (item: string, index: number) => {
  // ...
}, (item: string, index: number) => index.toString());

4.2 架构合规审查

4.2.1 三层架构检查

规则:Page 层不能直接修改 Model 层数据,必须通过 Service 层。

通过检查

// ✅ 正确:Page 通过 Service 获取数据
onClick(() => {
  this.resultData = this.service.generateData(this.inputData);
  this.showResult = true;
});

// ❌ 错误:Page 直接构造 Model
// this.resultData = new AI公众号选题Data();
// this.resultData.topics = [...];  // 违反分层原则
4.2.2 路由配置检查

规则:所有页面必须在 main_pages.json 中注册。

检查通过AI公众号选题Page 已在 main_pages.json 中注册为 "apps/AI公众号选题/AI公众号选题Page"

4.2.3 资源引用检查

规则:UI 中的文字应使用 $r 引用资源文件,而非硬编码。

当前状态:由于项目规模较小,部分文字直接使用了字面量。在正式发布前,建议将常用文字移至 resources/base/element/string.json 中。

4.3 验收标准

4.3.1 功能验收
验收项 标准 状态
输入字段 三个输入框正常工作
数据生成 点击按钮后生成数据
结果展示 13 个字段完整展示
返回导航 点击返回可回到主页
空状态处理 无数据时不展示结果
4.3.2 代码质量验收
验收项 标准 状态
类型安全 无 any/unknown 类型
编译通过 无编译错误
代码规范 遵循 ArkTS 语法约束
命名规范 类名大写开头,变量驼峰
4.3.3 ArkTS 专有检查清单
□ 无解构赋值语句
□ 无 Function.bind/call/apply 调用
□ 无 for...in 循环
□ 无索引签名
□ 无对象字面量直接作为类型
□ 所有类字段显式初始化
□ 所有 import 在文件顶部
□ 无 as const 断言
□ catch 子句无类型标注
□ 箭头函数替代函数表达式

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

自动化执行阶段利用自动化工具和流程执行任务,提高开发效率和代码质量。

5.1 代码生成自动化

5.1.1 数据模型代码生成

对于 AI公众号选题Data 这样的数据模型,可以编写代码生成模板,自动生成重复的字段声明和构造函数初始化代码:

// 模板示例(伪代码)
// 输入:字段列表
// 输出:自动生成的类代码

// 字段定义
const FIELDS = [
  { name: 'topics', type: 'string[]', default: '[]' },
  { name: 'title', type: 'string', default: "''" },
  { name: 'angle', type: 'string', default: "''" },
  // ...
];

// 自动生成代码
function generateModelClass(fields) {
  let classCode = 'export class AI公众号选题Data {\n';
  for (let f of fields) {
    classCode += `  ${f.name}: ${f.type} = ${f.default};\n`;
  }
  classCode += '\n  constructor() {\n';
  for (let f of fields) {
    classCode += `    this.${f.name} = ${f.default};\n`;
  }
  classCode += '  }\n}\n';
  return classCode;
}
5.1.2 UI 代码模板化

结果展示区域的代码具有高度重复性——每个字段都使用类似的 Row/Text 结构。我们可以通过数据驱动的方式来减少重复代码:

// 定义展示字段配置
interface DisplayField {
  label: string;
  key: string;
  isArray: boolean;
}

const DISPLAY_FIELDS: DisplayField[] = [
  { label: 'Title', key: 'title', isArray: false },
  { label: 'Angle', key: 'angle', isArray: false },
  { label: 'Hook', key: 'hook', isArray: false },
  { label: 'Framework', key: 'framework', isArray: false },
  { label: 'Estimated reads', key: 'estimated_reads', isArray: false },
  { label: 'Difficulty', key: 'difficulty', isArray: false },
  { label: 'Topic', key: 'topic', isArray: false },
  { label: 'Series idea', key: 'series_idea', isArray: false },
  { label: 'Timing', key: 'timing', isArray: false },
  { label: 'Competitor analysis', key: 'competitor_analysis', isArray: false },
];

// 使用 ForEach 自动生成展示行
ForEach(DISPLAY_FIELDS, (field: DisplayField) => {
  Row() {
    Text(field.label + ': ')
      .fontSize(12)
      .fontWeight(FontWeight.Medium)
      .fontColor('#666666');
    // 动态访问字段值(ArkTS 中需使用类型转换)
    Text((this.resultData as Record<string, Object>)[field.key] as string)
      .fontSize(12)
      .fontColor('#333333');
  }
  .width('100%')
  .padding({ top: 4, bottom: 4 });
}, (field: DisplayField) => field.key);

注意:在 ArkTS 中,由于不支持索引访问类型(obj["field"]),上述代码中动态字段访问需要谨慎处理。标准做法是直接访问具名字段。

5.2 构建自动化

5.2.1 Hvigor 构建配置

HarmonyOS 应用使用 Hvigor 作为构建工具,构建配置位于项目根目录和模块目录的 build-profile.json5 中。构建过程自动处理:

  1. ArkTS 编译:将 .ets 文件编译为方舟字节码
  2. 资源编译:处理 resources 目录下的资源文件
  3. 打包:生成 HAP(HarmonyOS Ability Package)包
5.2.2 持续集成流程

对于团队开发,建议配置以下 CI 流程:

# 伪代码:CI 流程配置
stages:
  - lint:       # ArkTS 语法检查
    - hvigor lint
  - build:      # 编译构建
    - hvigor assembleDebug
  - test:       # 单元测试
    - hvigor test
  - deploy:     # 部署到测试环境
    - hvigor assembleRelease

5.3 数据 Mock 自动化

当前 Service 层使用 Mock 数据,后续可以结构化地生成更真实的模拟数据:

export class AI公众号选题Service {
  // Mock 数据模板
  private mockTemplates: Record<string, string[]> = {
    topics: [
      '2024年最值得关注的AI趋势',
      '如何用ChatGPT提升10倍工作效率',
      '从0到1搭建个人知识体系',
      // ...
    ],
    angles: [
      '从行业趋势切入,结合具体案例',
      '从用户痛点出发,提供解决方案',
      '从数据报告出发,分析深层原因',
      // ...
    ],
    hooks: [
      '你知道吗?90%的人都忽略了这一点',
      '如果你只有时间读一篇文章,请读这篇',
      '我花了3个月时间,终于搞懂了这个问题',
      // ...
    ],
  };

  generateMockData(input: Record<string, Object>): AI公众号选题Data {
    let result: AI公众号选题Data = new AI公众号选题Data();
    let field: string = String(input['领域'] || '');
    let reader: string = String(input['目标读者'] || '');
    let hot: string = String(input['热点方向'] || '');

    // 根据输入生成更真实的 Mock 数据
    result.topics = this.pickRandom(this.mockTemplates.topics, 3);
    result.title = `${field}${this.pickRandom(this.mockTemplates.hooks, 1)[0]}`;
    result.angle = `针对${reader},从${hot}角度切入`;
    // ...

    return result;
  }

  private pickRandom(arr: string[], count: number): string[] {
    let result: string[] = [];
    let copy: string[] = arr.slice();
    for (let i: number = 0; i < count && copy.length > 0; i++) {
      let idx: number = Math.floor(Math.random() * copy.length);
      result.push(copy[idx]);
      copy.splice(idx, 1);
    }
    return result;
  }
}

5.4 测试自动化

5.4.1 单元测试

Service 层可以独立编写单元测试:

// 单元测试示例(使用 HarmonyOS 测试框架)
import { describe, it, expect } from '@ohos/hypium';
import { AI公众号选题Service } from '...';

describe('AI公众号选题Service', () => {
  it('generateData_should_return_valid_data', () => {
    let service = new AI公众号选题Service();
    let input: Record<string, Object> = {
      '领域': '人工智能',
      '目标读者': '技术从业者',
      '热点方向': '大模型'
    };
    let result = service.generateData(input);
    expect(result.topics.length).assertGreaterThan(0);
    expect(result.title).assertNotEmpty();
    expect(result.angle).assertNotEmpty();
  });
});
5.4.2 UI 自动化测试

ArkUI 支持 UI 自动化测试,可以验证组件的交互行为:

// UI 测试示例
import { UIAbility } from '@kit.AbilityKit';
import { Driver, ON } from '@kit.UITestKit';

describe('AI公众号选题Page', () => {
  it('should_display_result_after_click', async () => {
    let driver = await Driver.create();
    // 输入领域
    let inputField = await driver.findComponent(ON.text('请输入领域'));
    await inputField.inputText('人工智能');
    // 点击按钮
    let button = await driver.findComponent(ON.text('📱  📋 整理白板'));
    await button.click();
    // 验证结果展示
    let result = await driver.findComponent(ON.text('📋 白板内容'));
    expect(result).assertIsTrue();
  });
});

六、评估阶段(Assess)

评估阶段对整个过程和成果进行评估,总结经验教训,为后续项目提供参考。

6.1 技术选型评估

6.1.1 ArkTS vs TypeScript
维度 ArkTS TypeScript 对项目的影响
类型安全 更严格,无 any 灵活,允许 any 代码更可靠,但开发效率略低
运行时性能 编译为方舟字节码,性能优 解释执行 用户体验更好
生态系统 仅 HarmonyOS 全平台 第三方库支持有限
学习曲线 需适应新约束 标准 团队需额外培训

评价:对于 HarmonyOS 原生应用开发,ArkTS 的严格类型系统在长期维护中带来了显著收益。虽然初期开发需要适应其约束,但编译阶段的错误检查有效避免了运行时异常。

6.1.2 架构模式评估

MVP(Model-View-Presenter)模式在本次项目中表现良好:

  • 优势:职责清晰,各层独立可测试
  • 改进空间:对于小型应用,可以简化 Service 层,将简单逻辑直接放在 Page 中
  • 建议:当应用复杂度增加时,可以考虑引入状态管理框架

6.2 代码质量评估

6.2.1 代码量统计
AI公众号选题Page.ets:    ~195 行
AI公众号选题Model.ets:    ~31 行
AI公众号选题Service.ets:  ~23 行
总计:                     ~249 行
6.2.2 圈复杂度分析
  • AI公众号选题Page.ets:包含 1 个 @Component、1 个 build() 方法、多个条件渲染分支
  • AI公众号选题Model.ets:纯数据类,圈复杂度为 1
  • AI公众号选题Service.ets:包含 1 个公开方法,圈复杂度为 1
6.2.3 代码复用率
  • 三个文件之间无重复代码(职责分离良好)
  • 与其他 AI 应用共享
Logo

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

更多推荐