用 HarmonyOS 开发助手做一个元服务:岁时·中国节的开发与上架记录
文章目录
前言
「岁时·中国节」最初只是一个很朴素的想法:打开元服务就能知道下一个传统节日还有多久,不必再去日历里翻日期。真正开始写之后,事情很快变得复杂起来。农历怎么换算,除夕怎么算,节日资料放在哪里,桌面卡片又该展示什么,这些都不是生成一个页面就能解决的。
这次我没有从空白工程直接开写,而是先整理项目规划书、数据结构、页面要求和参考物料,再让 HarmonyOS 开发助手参与代码生成和修改。它确实省下了不少搭骨架、查错误的时间,但需求边界、代码核对和真机验收仍然要由开发者把关。
下面记录的是这个项目从规划到上架的实际过程,代码和目录也都来自最终工程。
先看成品
打开首页,最近节日和倒计时会直接出现在眼前,继续向下可以查看近期节日与全年节日。进入详情页后,节日由来、习俗和诗词会集中展示,祝福语则可以按长辈、亲友、恋人、同事客户等对象筛选。遇到合适的文案,可以直接复制、收藏,或者生成粒子祝福卡。桌面上的 2*2 万能卡片也会显示最近节日,点击后直接拉起对应页面。

这些功能看起来分散,底层其实围绕两件事展开:一套可离线运行的节日日期计算,以及一份统一维护的节日内容数据。页面、卡片和祝福语功能都建立在这两层之上。
开发助手在这个项目里做了什么
我使用的是集成在 DevEco Studio 中的 HarmonyOS 开发助手。在这个项目里,它主要帮我搭页面和模块骨架、根据编译日志定位问题,以及在真机验证后做局部修改。代码生成只是其中一环,真正有价值的是它能持续读取工程上下文,不必每次都从头解释目录和模块关系。
安装和能力说明可以直接查看华为的安装文档与开发助手介绍。如果需要查看插件版本和兼容信息,可以再到 JetBrains Marketplace 确认。
在 DevEco Studio 中安装
在 macOS 上,打开 DevEco Studio 后进入:
DevEco Studio > Preferences > Plugins
搜索 HarmonyOS Dev Assistant,安装后重启 IDE。如果使用离线插件包,则在插件设置中选择从磁盘安装。
首次打开助手时,需要使用已实名认证的华为开发者账号登录,并根据提示完成相关协议确认。之后先配置 AI 模型,再开始生成代码。助手支持通过供应商添加模型,也支持通过 URL、API Key 和模型名称手动配置。
配置完成后建议先执行一次模型测试。模型没有测试通过时就直接开始生成工程,后面遇到的问题很难判断究竟来自提示词、模型配置,还是工程本身。
我给助手划了一条边界
“做一个节日元服务”并不是一条合格的开发指令。它没有说明农历日期怎么处理,也没有交代除夕跨年、节日数据结构和万能卡片的限制。助手可以很快生成一个看起来完整的页面,但这种完整往往只停留在界面层。
所以我的分工很明确:助手负责搭骨架、读日志和改代码,我负责定义需求、判断方案并验收结果。这个边界一旦模糊,后面就会花更多时间修补看似能用、实际经不起验证的实现。
先把需求写成助手能读懂的物料
如果直接从一句需求开始生成,最常见的结果是功能边界飘忽不定:不同节日各写一套页面,规划能力被当成已实现能力,页面结构也会随着提示词不断变化。
我先写了一份项目规划书,把项目定位、MVP 范围、节日数据模型、页面路由和工程目录放在一起,同时写清 HarmonyOS Kit 与 API 约束、万能卡片配置、验收标准以及能力不可用时的降级方案。规划书不需要追求篇幅,真正重要的是让助手回答三个问题:要创建哪些文件,每个文件负责什么,这一版明确不做什么。
物料目录怎么组织
在项目的 entry/src/main/ets 目录下,我新建了 物料 文件夹,放入规划书、节日数据参考和提示词资料:
entry/src/main/ets/物料/
├── 岁时中国节_元服务项目规划书.md
├── Festival参考.md
├── 岁时中国节_App图标提示词.md
└── 待补充物料.md
我把这些物料直接放进工程,助手读取上下文时就能一起看到。需求有变化时,规划、代码和素材也在同一个地方,不必来回翻找散落的文档。
项目没有采用“一个节日一个页面”的写法。所有节日信息统一放在 model/festivals.ets,页面根据节日 ID 读取数据。以后新增节日时主要补充数据,不需要再复制一套页面逻辑。
让助手分阶段开发
物料准备完成后,我给助手的第一条提示词是:
需求如下:该元服务项目的项目说明文档以及相关的参考格式及内容已经引入。
请根据项目规划书和物料中的其他文件,对当前项目进行开发。
请先读取相关物料,输出开发计划,确认目录结构后再分阶段修改代码。
我没有让助手一次性生成所有功能,而是按下面的顺序推进:
| 阶段 | 主要内容 | 验收重点 |
|---|---|---|
| 数据层 | Festival 接口、节日数据、祝福语分类 | 类型正确,节日信息可查询 |
| 核心页面 | 首页、详情页、祝福语列表 | 页面能打开,路由和数据绑定正常 |
| 日期能力 | 农历转公历、最近节日、倒计时 | 春节、端午、中秋等日期抽样核对 |
| 元服务入口 | FormExtensionAbility、桌面卡片 | 卡片能刷新,点击能跳转 |
| 交互增强 | 收藏、复制、粒子祝福卡、深链 | 真机操作完整,失败有降级 |
| 发布准备 | 签名、构建、AGC 信息 | 构建成功,发布资料完整 |
助手生成计划后,我会逐项检查待办内容。涉及文件创建、删除、重命名和系统能力调用时,先确认计划,再让它执行,能够明显减少后续返工。
关键实现一:离线农历与节日倒计时
节日提醒是这个项目的核心功能。工程没有依赖网络请求,而是在 adapters/LunarCalendar.ets 中内置 1900—2100 年的农历信息表,提供农历转公历、公历转农历和日期差计算。
节日服务再基于这个日期引擎计算最近节日:
export interface FestivalCountdown {
festival: Festival;
solarDate: Date;
daysLeft: number;
}
static getNearest(from: Date): FestivalCountdown {
let nearest: FestivalCountdown | null = null;
for (const f of FESTIVALS) {
const d: Date = FestivalService.getNextSolarDate(f, from);
const left: number = LunarCalendar.daysBetween(from, d);
if (nearest === null || left < nearest.daysLeft) {
nearest = { festival: f, solarDate: d, daysLeft: left };
}
}
const fallback: Festival = FESTIVALS[0];
const fallbackDate: Date = FestivalService.getNextSolarDate(fallback, from);
return nearest ?? { festival: fallback, solarDate: fallbackDate, daysLeft: 0 };
}
这段计算真正麻烦的地方在跨年。某个节日如果今年已经过去,目标日期必须切换到下一年,否则首页会出现负数倒计时。除夕还要单独处理,因为它并不固定对应某个农历日期,工程里按下一年春节的前一天反推。
首页加载时会同时计算最近节日、今天的节日、未来 60 天节日和全年节日列表。页面只消费结果,不直接碰农历算法。后续即使替换日期实现,受影响的也主要是服务层。
关键实现二:数据驱动的详情页
节日数据统一使用 Festival 接口描述,字段包括节日 ID、农历或公历日期、主题色、简介、由来、习俗、诗词和祝福语。
当前工程的目录结构如下:
entry/src/main/ets/
├── adapters/
│ └── LunarCalendar.ets
├── common/
│ ├── AppConfig.ets
│ ├── PreferencesUtil.ets
│ └── viewmodels/
├── components/
│ ├── FestivalCard.ets
│ ├── BlessingList.ets
│ └── ParticleBlessing.ets
├── entryability/
│ └── EntryAbility.ets
├── entryformability/
│ └── EntryFormAbility.ets
├── features/
│ ├── blessing/BlessingService.ets
│ ├── festival/FestivalService.ets
│ └── share/ShareService.ets
├── form/
│ └── FestivalFormCard.ets
├── model/
│ ├── Festival.ets
│ └── festivals.ets
├── pages/
│ ├── Index.ets
│ └── BlessingCard.ets
└── 物料/
首页 Index.ets 只负责组合导航、列表和详情入口;FestivalService 负责日期和节日查询;BlessingService 负责分类、随机抽取和收藏;PreferencesUtil 负责本地持久化。这种分层比把所有逻辑写在页面的 build() 方法里更容易调试。
祝福语筛选逻辑很简单,但它体现了数据驱动页面的好处:
static filter(f: Festival, category: BlessingCategory): Blessing[] {
if (category === 'general') {
return f.blessings;
}
return f.blessings.filter((b: Blessing): boolean => b.category === category);
}
页面不需要知道每个节日有哪些祝福语,只需要把当前节日和分类传给服务层即可。
关键实现三:万能卡片与元服务入口
元服务的 module.json5 中开启了免安装能力,并注册 EntryFormAbility:
{
"module": {
"type": "entry",
"mainElement": "EntryAbility",
"installationFree": true,
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"type": "form",
"exported": true,
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
EntryFormAbility 每次生成卡片数据时,都会调用 FestivalService.getNearest(),因此卡片显示的是当前最近节日,而不是写死的内容:
private buildData(): FormCardData {
const now: Date = new Date();
const nearest = FestivalService.getNearest(now);
const dateText =
(nearest.solarDate.getMonth() + 1) + '月' + nearest.solarDate.getDate() + '日';
const countdownText = nearest.daysLeft === 0
? '就是今天'
: '还有 ' + nearest.daysLeft + ' 天';
return {
formName: nearest.festival.name,
formDate: dateText,
formCountdown: countdownText,
formSummary: nearest.festival.summary,
formPrimary: nearest.festival.theme.primary,
formSecondary: nearest.festival.theme.secondary,
formAction: ShareService.buildDeepLink(nearest.festival.id, '')
};
}
卡片 UI 使用基础组件和主题色渐变,没有把 Canvas 粒子动画等复杂内容放进卡片。卡片的职责是快速展示和拉起元服务,复杂交互仍然放在主页面中,这也是元服务卡片开发时需要遵守的边界。
当前卡片配置为 2*2,每天 09:00 更新:
{
"name": "festivalCard",
"src": "./ets/form/FestivalFormCard.ets",
"uiSyntax": "arkts",
"updateEnabled": true,
"scheduledUpdateTime": "09:00",
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
关键实现四:深链与分享降级
当前版本没有把系统分享能力当作必选前提,而是统一生成 festival:// 深链。用户复制祝福语时,可以同时复制祝福内容和深链;对端打开深链后,由 EntryAbility.onNewWant() 解析节日 ID,再交给页面消费。
深链格式示例:
festival://festival?id=zhongqiu&blessing=%E4%B8%AD%E7%A7%8B%E5%AE%89%E5%BA%B7
生成深链的代码如下:
static buildDeepLink(festivalId: string, blessing: string): string {
if (blessing.length === 0) {
return AppConfig.FES_DEEP_LINK_PREFIX + festivalId;
}
return AppConfig.FES_DEEP_LINK_PREFIX + festivalId
+ '&blessing=' + encodeURIComponent(blessing);
}
祝福语写进 URI 前必须经过 encodeURIComponent,否则空格、中文和特殊字符都可能破坏链接。接收端也不能默认外部 URI 永远正确,解析失败时直接返回空结果,至少不要让一条错误链接把页面带进白屏。
这套方案并不复杂,但降级效果很实际:即使设备、系统版本或相关权益不满足,核心的“复制祝福语”和“打开节日详情”仍然可用。
真机报错与修复方法
代码生成完成后,我先在 DevEco Studio 中构建,再连接真机运行。这个步骤不能省略,因为元服务卡片、签名、深链和部分系统能力在模拟环境中的表现可能与真机不同。
遇到问题时,我给助手的提示词不是简单地说“帮我修复”,而是把错误信息、触发步骤和期望结果一起提供:
当前工程在真机运行时出现以下问题:
1. 触发路径:从桌面卡片点击进入详情页。
2. 实际现象:页面未打开/出现错误提示。
3. 日志或编译错误如下:
<粘贴完整错误信息>
请先定位根因,再只修改必要文件。修改后说明:
- 修改了哪些文件;
- 为什么这样修改;
- 如何在真机上复现和验证。
让助手修复代码后,仍然要重新构建并走完整操作路径。编译通过只能说明语法和类型基本正确,不能代替功能验收。
打包与上架
功能确认后,可以自己完成打包,也可以让开发助手辅助执行构建和发布准备。无论使用哪种方式,签名和 AGC 配置都需要开发者自己确认。
上架前我检查了以下内容:
- AGC 项目、元服务名称和包名对应一致。
- 调试签名与发布签名没有混用。
installationFree、卡片扩展能力和深链配置符合工程实际。- 元服务图标、简介、隐私声明和截图资料完整。
- 构建产物在真机上重新安装或体验过。
- 卡片点击、详情页跳转、复制、收藏和祝福卡操作均可用。
- 关闭网络后,核心节日查询功能仍能正常工作。
签名配置完成并且项目验证通过后,再生成发布版本并上传 AGC。不要把签名信息直接写进提示词或公开文章,模型只需要知道工程中哪些配置需要检查,不需要接触敏感凭据。


审核结果
「岁时·中国节」首次提交后,我在一天左右收到了审核通过的邮件。

审核速度会受到资料完整度、应用类型和平台审核节奏影响,这个时间只能作为本次项目的记录,不能当作固定承诺。
写在最后
这个项目做完后,我对开发助手的判断反而更具体了。物料越清楚,生成结果越容易收敛;开发过程拆得越细,出了问题越容易定位。先跑通数据和页面,再接卡片、深链和交互,比一开始就追求“全部生成”稳得多。
还有一点不能混淆:规划书里写过的能力,不等于工程里已经落地。规划书怎么写、发布前怎么验收,都应该回到实际代码和真机结果上。哪些功能已经可用,哪些做了降级,需要逐项确认。
HarmonyOS 开发助手很适合做开发过程中的协作工具。它能加快代码生成、问题定位和重复修改,但架构怎么取舍、API 是否用对、真机是否跑通,最后仍然是开发者的责任。
更多推荐


所有评论(0)