文章目录


在这里插入图片描述

每日一句正能量

不被任何情绪而变得被动就是最好的状态。
情绪可以来,但不被它推着走。愤怒时不攻击,悲伤时不沉溺,焦虑时不乱做决定。

在这里插入图片描述

标记:参与方向三「毕业季 · 鸿蒙同行」文章投稿。


一、课程开始前:我以为“课程设计”就是把课堂知识拼成一个App

这门校企共建 HarmonyOS 课程持续 16 周。

前 7 周以 ArkTS、ArkUI、Stage Model、网络、数据和通知等基础能力为主;第 8 周开始进入团队课程设计;最后两周进行项目验收、视频演示和答辩。

我对课程设计最初的理解非常简单:

选一个功能多的题目,把课堂里学过的接口尽量用进去,最后做出几个好看的页面。

第一次选题评审就暴露了问题。我们提出的题目叫“校园超级服务平台”,计划包含:

  • 校园地图;
  • 课程表;
  • 失物招领;
  • 实验室预约;
  • 校园资讯;
  • 设备报修;
  • AI问答;
  • 手机和平板协同。

企业导师看完后问:

四个人、八周时间,你们最想解决的一个问题是什么?

我们答不上来。

这次评审让我意识到,课程设计不是功能数量比赛。它真正考察的是:

  • 能不能识别真实问题;
  • 能不能控制范围;
  • 能不能完成核心闭环;
  • 能不能解释技术选择;
  • 能不能协作交付;
  • 能不能诚实复盘不足。

二、课程结构:为什么前半程学能力,后半程做工程

在这里插入图片描述

课程大致分为五个阶段。

2.1 基础阶段:第1~4周

内容:

  • ArkTS 基础;
  • ArkUI 组件与布局;
  • 状态管理;
  • 页面路由;
  • Stage Model;
  • UIAbility 生命周期;
  • Git 基本操作。

要求每名学生独立完成一个包含列表、表单、详情和错误状态的小应用。

2.2 平台能力阶段:第5~7周

内容:

  • 网络请求;
  • 本地数据;
  • 通知;
  • 权限;
  • 资源适配;
  • 多端布局;
  • 调试与日志。

这个阶段最重要的改变,是老师不再只验收“能不能运行”,而是要求:

  • 加载状态;
  • 空数据;
  • 错误提示;
  • 重试;
  • 缓存;
  • 日志;
  • 真机或模拟设备验证。

2.3 项目设计阶段:第8~10周

内容:

  • 选题;
  • 需求调研;
  • 原型;
  • 数据模型;
  • 架构;
  • 团队分工;
  • 里程碑;
  • 风险清单。

2.4 迭代开发阶段:第11~14周

每两周检查一次可运行版本,而不是只看PPT进度。

2.5 展示与复盘阶段:第15~16周

提交:

  • 代码;
  • README;
  • 架构图;
  • 测试记录;
  • 演示视频;
  • 答辩PPT;
  • 个人贡献;
  • 项目复盘。

华为开发者联盟提供的知识地图将官方指南、Codelabs、示例代码、API参考、FAQ和视频教程按开发任务旅程组织起来;开发者学堂也通过课程和 Codelabs 提供“学与练”的路径。课程设计阶段,我们不再只依赖课堂PPT,而是学会回到官方文档核对接口、约束和版本差异。


三、项目选题:从“校园超级App”收敛到设备巡检与报修

在这里插入图片描述

我们重新提出了四个选题:

  1. 校园超级App;
  2. AI课堂全自动分析;
  3. 校园设备巡检与报修;
  4. 校园资讯聚合。

最终选择“校园设备巡检与报修”,原因是:

  • 学校实验室、教室和公共区域存在设备巡检需求;
  • 痛点明确;
  • 流程完整;
  • 数据可以模拟或小规模采集;
  • 适合手机执行、平板查看;
  • 可以实践网络、缓存、状态机、离线队列和通知;
  • 八周内能完成核心版本。

3.1 核心用户

  • 学生助管;
  • 实验室管理员;
  • 校园设备维护人员;
  • 管理端教师。

3.2 核心闭环

接收巡检任务
→ 到达设备
→ 查看检查项
→ 填写巡检结果
→ 发现异常
→ 创建报修
→ 弱网时保存本地
→ 网络恢复后同步
→ 管理端查看处理状态

3.3 明确不做

  • 全校资产管理;
  • 自动识别所有设备故障;
  • 复杂后台管理系统;
  • 真实支付;
  • 未经授权的校园统一身份认证;
  • 大规模设备控制。

范围收敛后,我们第一次感觉项目真正有可能完成。


四、需求调研:我们原本以为用户最关心“页面好不好看”

我们访谈了两位实验室老师和几名学生助管。

最初准备的问题偏功能:

  • 是否需要地图;
  • 是否需要拍照;
  • 是否需要统计图;
  • 是否需要消息推送。

老师更关心的是:

  • 任务有没有漏做;
  • 异常有没有真正提交;
  • 网络不好时记录会不会丢;
  • 谁提交、什么时候提交;
  • 重复报修怎么办;
  • 设备状态不明确时怎样处理。

这次调研让我们把优先级改成:

  1. 数据不丢;
  2. 状态清楚;
  3. 流程可追踪;
  4. 异常可恢复;
  5. 页面再优化。

这也是课程设计带给我的第一个工程收获:真实用户常常不关心我们用了多少新接口,只关心事情能不能可靠完成。


五、项目架构:第一次主动把页面、业务和数据分开

在这里插入图片描述

工程分成五层。

5.1 交互层

  • 任务列表;
  • 设备详情;
  • 巡检表单;
  • 异常报修;
  • 管理看板。

5.2 状态层

  • 加载;
  • 空数据;
  • 错误;
  • 表单;
  • 同步;
  • 筛选。

5.3 业务层

  • 任务状态机;
  • 表单校验;
  • 报修规则;
  • 权限判断;
  • 重试与冲突策略。

5.4 数据层

  • 网络接口;
  • 本地缓存;
  • 离线队列;
  • 数据映射。

5.5 质量层

  • 日志;
  • 单元测试;
  • UI测试;
  • 性能记录;
  • 演示数据。

5.6 工程目录

entry/src/main/ets/
├── pages/
│   ├── TaskListPage.ets
│   ├── TaskDetailPage.ets
│   ├── InspectionFormPage.ets
│   ├── RepairPage.ets
│   └── DashboardPage.ets
├── components/
├── model/
│   ├── InspectionTask.ets
│   ├── RepairOrder.ets
│   └── PageState.ets
├── service/
│   ├── InspectionService.ets
│   ├── RepairService.ets
│   └── TaskStateMachine.ets
├── repository/
│   ├── TaskRepository.ets
│   ├── CacheRepository.ets
│   └── OfflineQueue.ets
└── common/
    ├── Logger.ets
    ├── Result.ets
    └── Constants.ets

六、团队分工:表格写得很清楚,执行时仍然失衡

在这里插入图片描述

团队四人。

成员A:组长与业务层

负责:

  • 需求;
  • 状态机;
  • 项目计划;
  • 接口协调;
  • 答辩。

成员B:ArkUI和多端适配

负责:

  • 手机页面;
  • 平板看板;
  • 公共组件;
  • 视觉统一。

成员C:数据层

负责:

  • 网络;
  • 缓存;
  • 离线队列;
  • 数据映射;
  • 同步日志。

成员D:测试、文档与演示

负责:

  • 测试用例;
  • 缺陷跟踪;
  • README;
  • 演示视频;
  • 材料整理。

这个分工的问题是:测试成员在前半程没有足够开发任务,直到后期才大量介入;组长和UI成员在联调阶段任务过重。

下一轮应该采用“模块主责+交叉评审”:

  • 每个人有主责模块;
  • 每个模块有第二评审人;
  • 测试从需求阶段编写验收条件;
  • 组长不承担所有协调和关键代码。

七、状态机:我们第一次发现“已提交”不是一个简单布尔值

最初任务只有:

completed: boolean

后来发现真实状态至少包含:

export type InspectionStatus =
  'PENDING' |
  'IN_PROGRESS' |
  'WAITING_SYNC' |
  'SUBMITTED' |
  'REJECTED' |
  'CLOSED'

状态流转:

export class TaskStateMachine {
  private static allowed: Map<InspectionStatus, InspectionStatus[]> = new Map([
    ['PENDING', ['IN_PROGRESS']],
    ['IN_PROGRESS', ['WAITING_SYNC', 'SUBMITTED']],
    ['WAITING_SYNC', ['SUBMITTED', 'IN_PROGRESS']],
    ['SUBMITTED', ['REJECTED', 'CLOSED']],
    ['REJECTED', ['IN_PROGRESS']],
    ['CLOSED', []]
  ])

  static canTransfer(
    from: InspectionStatus,
    to: InspectionStatus
  ): boolean {
    return this.allowed.get(from)?.includes(to) ?? false
  }
}

这个状态机解决了:

  • 未开始不能直接关闭;
  • 离线记录处于待同步;
  • 驳回后可以重新处理;
  • 已关闭任务不能继续修改。

它也成为答辩时最容易体现“项目不是页面拼接”的证据。


八、ArkUI 页面:从成功状态扩展到完整页面状态

export type PageState<T> =
  | { status: 'loading' }
  | { status: 'success', data: T }
  | { status: 'empty' }
  | { status: 'error', message: string }

@Entry
@Component
struct TaskListPage {
  @State state: PageState<InspectionTask[]> = {
    status: 'loading'
  }

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

  build() {
    Column() {
      if (this.state.status === 'loading') {
        LoadingProgress()
      } else if (this.state.status === 'empty') {
        Text('暂无巡检任务')
      } else if (this.state.status === 'error') {
        Column() {
          Text(this.state.message)
          Button('重试')
            .onClick(() => this.loadTasks())
        }
      } else {
        List({ space: 12 }) {
          ForEach(
            this.state.data,
            (item: InspectionTask) => {
              ListItem() {
                TaskCard({ task: item })
              }
            },
            (item: InspectionTask) => item.id
          )
        }
      }
    }
    .width('100%')
    .height('100%')
  }

  private async loadTasks(): Promise<void> {
    this.state = { status: 'loading' }

    try {
      const tasks: InspectionTask[] =
        await TaskRepository.getInstance().list()

      this.state = tasks.length > 0
        ? { status: 'success', data: tasks }
        : { status: 'empty' }
    } catch (error) {
      this.state = {
        status: 'error',
        message: '任务加载失败,请稍后重试'
      }
    }
  }
}

课程前半程,我只会展示成功数据;课程结束时,我开始把错误和恢复当成页面的一部分。


九、离线队列:项目最有价值的技术点,也是返工最多的部分

设备间或地下实验室网络不稳定。我们设计本地离线队列:

export interface PendingOperation {
  id: string
  businessId: string
  type: 'SUBMIT_INSPECTION' | 'CREATE_REPAIR'
  payload: string
  createdAt: number
  expireAt: number
  retryCount: number
}

export class OfflineQueue {
  private items: PendingOperation[] = []

  enqueue(item: PendingOperation): void {
    const existed = this.items.some(
      (current: PendingOperation) => current.id === item.id
    )

    if (!existed) {
      this.items.push(item)
    }
  }

  async flush(
    sender: (item: PendingOperation) => Promise<boolean>
  ): Promise<void> {
    const remained: PendingOperation[] = []

    for (const item of this.items) {
      if (item.expireAt < Date.now()) {
        continue
      }

      const success: boolean = await sender(item)
      if (!success) {
        remained.push({
          ...item,
          retryCount: item.retryCount + 1
        })
      }
    }

    this.items = remained
  }
}

第一版遗漏了:

  • 应用重启后的持久化;
  • 服务端幂等;
  • 记录过期提示;
  • 已成功但客户端超时;
  • 多次点击重复提交;
  • 用户查看同步状态。

企业导师评审后,我们增加:

  • 本地持久化;
  • 请求ID;
  • 队列去重;
  • 最大重试;
  • 同步状态;
  • 手动重试;
  • 冲突提示。

十、项目时间线:第13周的联调危机

在这里插入图片描述

第8周:选题评审

从超级App收敛到巡检与报修。

第9周:用户访谈

确认数据可靠性高于页面丰富度。

第10周:架构评审

确定状态机和离线同步。

第11周:V0.3

完成任务列表和详情。

第12周:V0.6

完成报修、本地缓存和手机端核心流程。

第13周:联调危机

数据接口字段发生变化:

旧字段:status = 0 / 1 / 2
新字段:healthStatus = NORMAL / WARNING / OFFLINE

问题包括:

  • UI使用旧字段;
  • 本地缓存使用旧模型;
  • 测试数据没有更新;
  • 统计页面映射失败;
  • 没有接口版本记录。

团队花了两天统一模型和数据映射。

第14周:V1.0

补充:

  • 弱网;
  • 测试;
  • 平板看板;
  • 空状态;
  • 同步状态。

第16周:答辩

项目完成核心闭环,但仍有明显不足。


十一、项目界面与演示

在这里插入图片描述

演示流程:

  1. 手机端打开当天巡检任务;
  2. 进入门禁设备详情;
  3. 发现设备离线;
  4. 填写报修;
  5. 关闭网络;
  6. 提交后显示“待同步”;
  7. 恢复网络;
  8. 离线队列自动补传;
  9. 平板看板刷新异常数量;
  10. 管理端状态变为“已提交”。

配套演示视频脚本已单独输出。由于没有真实课程工程和设备,本次不伪造可播放视频文件。


十二、教师指导:给答案不如不断追问边界

校内教师主要指导:

  • 课程目标;
  • 需求范围;
  • 文档规范;
  • 团队过程;
  • 答辩表达;
  • 学术和材料真实性。

企业导师主要指导:

  • 工程分层;
  • 接口边界;
  • 异常处理;
  • 测试;
  • 日志;
  • 用户体验;
  • 真实上线差距。

几个印象最深的问题:

如果网络成功但客户端超时,重试会不会创建两条报修?

为什么状态判断写在页面里,而不是业务层?

平板看板是简单放大手机页面,还是承担不同任务?

你们的性能数据如何测量?

哪些代码是每个人真正完成的?

这些问题迫使我们从“项目展示”走向“工程解释”。


十三、成绩评价:分数是结果,评价摘要更值得复盘

在这里插入图片描述

文中的成绩单是示意材料,不能作为正式成绩证明。

匿名化样例评分:

评价项 权重 得分
需求与选题 20 17
技术实现 30 26
团队协作 20 15
测试与文档 15 11
展示与答辩 15 13
总评 100 82

比总分更重要的是评价:

  • 痛点真实;
  • 核心闭环完整;
  • 弱网处理有思考;
  • 团队分工后期失衡;
  • 测试介入太晚;
  • 性能证据不足;
  • 复盘比较诚实。

十四、最大的收获一:从“会接口”到“会设计状态”

课程开始时,我衡量能力的方式是:

  • 会多少组件;
  • 会多少Kit;
  • 能写多少页面。

课程结束后,我更关心:

  • 状态有哪些;
  • 状态如何流转;
  • 异常如何恢复;
  • 数据来自哪里;
  • 页面何时更新;
  • 请求会不会重复;
  • 缓存是否过期;
  • 用户是否知道同步结果。

这比记住更多接口更接近工程能力。


十五、最大的收获二:学会把技术选择讲成“取舍”

例如,为什么做离线队列?

不是因为它“技术含量高”,而是因为现场网络不稳定,巡检记录不能丢。

为什么不做实时设备控制?

因为课程项目没有真实设备授权和安全条件,范围会失控。

为什么平板只做管理看板?

因为平板更适合集中查看,不适合在设备现场填写所有细节。

为什么保留UNKNOWN状态?

因为服务端旧数据、版本变化和异常输入都可能出现,不能把未知强行当正常。

答辩真正有效的表达不是“用了什么”,而是“为什么这样做、放弃了什么”。


十六、最大的收获三:团队协作必须有可见资产

我们后期建立了四张表:

  1. 任务看板;
  2. 接口字段表;
  3. 风险清单;
  4. 缺陷与测试表。

接口字段表:

字段
类型
是否为空
枚举
默认值
负责人
变更日期
影响模块

风险清单:

风险
概率
影响
负责人
处理方案
截止时间
状态

这些工具看似不如写代码有成就感,却直接减少了返工。


十七、遗憾一:选题收敛得太晚

前两周仍然舍不得删除:

  • 地图;
  • AI问答;
  • 校园资讯;
  • 服务卡片;
  • 跨设备接续。

最终很多功能没有完成,前期讨论也浪费时间。

下一次应该在选题阶段坚持:

一个核心用户
一个核心问题
一个完整闭环
一个鸿蒙特色能力
一组可验证数据

十八、遗憾二:测试成员介入太晚

测试成员前期主要写文档,第13周才集中写用例,结果发现:

  • 状态非法流转;
  • 表单重复提交;
  • 缓存模型兼容;
  • 页面空状态;
  • 快速切换请求覆盖。

如果测试从需求阶段介入,可以提前把验收条件写成用例。

下一轮应该采用:

需求完成 = 代码 + 测试 + 日志 + 文档

十九、遗憾三:性能数据缺乏说服力

我们只记录了少量手工数据:

  • 首屏加载;
  • 列表刷新;
  • 离线补传。

但没有统一:

  • 测试设备;
  • 网络条件;
  • 样本数量;
  • 平均值;
  • 最大值;
  • 优化前后版本。

答辩中虽然给出了数据,却难以证明可重复。

下一轮应建立测试基线:

设备:
系统版本:
应用版本:
网络:
数据量:
执行次数:
平均值:
P95:
内存:
结果:

二十、遗憾四:项目缺少长期用户验证

我们只邀请少量同学和教师试用,没有真实运行一个学期,也没有接入真实设备和学校系统。

因此,不能夸大为:

  • 已经提升校园巡检效率;
  • 已经减少设备故障;
  • 已经在学校正式部署。

准确的结论应该是:

教学原型完成了核心流程验证,小规模试用能够发现交互和状态问题,但尚未经过真实长期部署验证。


二十一、收获与遗憾对照

在这里插入图片描述

收获

  • 建立工程分层;
  • 学会异常设计;
  • 学会Code Review;
  • 学会接口协作;
  • 学会测试和日志;
  • 学会答辩表达;
  • 学会诚实描述项目边界。

遗憾

  • 范围收敛晚;
  • 测试介入晚;
  • 性能数据不足;
  • 缺少长期验证;
  • 团队负载不均;
  • 官方文档核对不够及时;
  • 演示素材准备集中在最后一周。

二十二、如果重新做一次,我会怎样安排

第8周

  • 访谈;
  • 选题;
  • 范围;
  • 成功指标。

第9周

  • 原型;
  • 状态机;
  • 数据模型;
  • 测试验收条件。

第10周

  • 架构;
  • 接口契约;
  • 分工;
  • 环境和自动化。

第11~12周

  • 核心闭环;
  • 每周可运行版本;
  • 测试同步编写。

第13周

  • 弱网;
  • 异常;
  • 性能基线。

第14周

  • 多端适配;
  • 用户试用;
  • 缺陷修复。

第15周

  • 冻结功能;
  • 测试;
  • 视频;
  • 文档。

第16周

  • 答辩;
  • 复盘;
  • 资产归档。

二十三、课程成果如何沉淀成求职资产

课程结束后,我们整理:

  • README;
  • 项目介绍;
  • 架构图;
  • 代码片段;
  • 测试报告;
  • 视频;
  • 个人贡献;
  • 失败复盘;
  • V2.0计划。

简历项目描述不再写:

使用ArkTS和ArkUI完成校园巡检系统。

而写成:

面向校园实验室弱网巡检场景,负责任务状态机与离线同步模块;通过本地队列、请求幂等标识和同步状态提示保障记录可恢复,并将手机执行端与平板管理看板分工设计;课程样例完成20轮弱网流程测试,核心记录无丢失。

数据必须来自真实测试,不能为了简历好看编造。


二十四、课程设计对我的真正影响

这门课程没有让我在16周内成为高级HarmonyOS工程师。

它让我完成了一个更重要的转变:

  • 从个人作业到团队项目;
  • 从成功路径到异常路径;
  • 从接口调用到架构边界;
  • 从功能截图到测试证据;
  • 从临时演示到资产沉淀;
  • 从追求高分到诚实复盘。

这些能力不只适用于HarmonyOS,也适用于后续实习、毕业设计和任何软件工程项目。


二十五、总结

回看这次校企合作鸿蒙课程设计,最值得保留的不是最终82分,也不是答辩那天顺利跑通的演示,而是我们第一次经历了一个接近真实工程的小型周期:

  • 选题被否定;
  • 范围被收缩;
  • 接口发生变化;
  • 代码被评审;
  • 弱网暴露问题;
  • 团队任务失衡;
  • 测试发现缺陷;
  • 答辩被追问;
  • 项目留下遗憾。

课程设计的价值不在于证明我们已经可以独立上线商业应用,而在于让我们提前看到学校作业和企业工程之间的距离,并学会用更可靠的方法缩短这段距离。

“毕业季 · 鸿蒙同行”对我来说,是一次从课堂知识到工程实践的过渡。收获让我更有信心,遗憾则提醒我:下一次面对真实项目时,应该更早控制范围、更早测试、更早沟通,并用真正可复现的证据评价成果。


转载自:https://blog.csdn.net/u014727709/article/details/163017075
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐