【共创季稿事节】毕业季·鸿蒙同行:我用 HarmonyOS 做了一个「鸿蒙求职罗盘」,陪自己走完校招这一程

📅 2026/7/12 18:25:22
【共创季稿事节】毕业季·鸿蒙同行:我用 HarmonyOS 做了一个「鸿蒙求职罗盘」,陪自己走完校招这一程
【共创季稿事节】毕业季·鸿蒙同行我用 HarmonyOS 做了一个「鸿蒙求职罗盘」陪自己走完校招这一程投稿方向毕业季·鸿蒙同行。一、写在前面毕设做完了求职才刚刚开始几个月前我完成了自己的毕业设计——一款叫「鸿蒙毕设伴侣」的应用用它管理毕设进度、润色论文、演练答辩。写完那篇复盘文章时我以为自己已经把毕业季这件事讲完整了。但很快我意识到毕设只是毕业季的上半场求职才是下半场而且往往更漫长、更煎熬。投了简历石沉大海、面试被问住哑口无言、offer 迟迟不来的焦虑比赶论文的焦虑更磨人——因为论文的截止日期是确定的而求职的什么时候能上岸是不确定的。我投了不少公司投递记录散落在各个招聘 App 里笔试通知在邮箱面试安排在微信复盘的心得只存在脑子里过几天就忘了当时具体是怎么答的、哪里答得不好。我又想起了做毕设伴侣时的那个念头我是一个正在学鸿蒙的学生为什么不用鸿蒙做点什么来解决我正在经历的问题于是就有了这款「鸿蒙求职罗盘」——一个专门陪应届生走完校招这一程的鸿蒙应用。它管理我的每一次投递帮我把简历里模糊的项目经历改得更专业陪我模拟面试官可能问的每一个刁钻问题也记录下每一次面试后我自己的复盘和反思。这款应用和「鸿蒙毕设伴侣」是姊妹项目——一个负责毕业设计这条线一个负责求职、实习与成长这条线两个应用合在一起正好覆盖了「毕业季·鸿蒙同行」这个方向里我最想讲的故事。先看一眼成品。这是应用在 DevEco Studio 里运行在Mate 80 ProHarmonyOS 6.1.0模拟器上的样子——投递看板页三张统计卡片一目了然总投递、进行中、Offer。下面是具体的投递记录——华为内推二面、某互联网大厂笔试、某创业公司已投递。这是我自己求职季最真实的状态截面几家在谈、几家在等、一家还没消息。二、需求梳理求职季最真实的四个痛点在写代码之前我先诚实地梳理了自己求职这段时间遇到的四个具体痛点投递记录到处乱记进度记不清。投了十几家公司每家进展到哪一步笔试一面二面全靠脑子记经常搞混。简历写不出彩项目经历流水账。我的项目经历描述总是像做了一个xx功能缺乏量化和亮点不知道怎么改。面试没底气不知道会被问什么。尤其是技术面总担心被问到自己没准备的知识点一慌就语无伦次。面完就忘同样的问题下次还是答不好。每次面试完只有一个模糊的感觉还行/感觉不太好没有具体记录下次遇到类似问题还是老样子。这四个痛点对应了应用的四个 Tab看板、简历、面试、我的。和做毕设伴侣时一样我坚持先想清楚自己真实需要什么再决定做什么功能——这是我做这两个项目学到的最重要的一课。三、架构设计复用经验快速搭建3.1 和毕设伴侣同构的技术路线做完毕设伴侣之后我发现AI 出题→作答→点评这套模式几乎可以套用在任何需要陪练的场景——答辩演练是这样模拟面试也是这样。所以这次开发我大量复用了已验证过的架构模式entry/src/main/ets/ ├── entryability/EntryAbility.ets 入口初始化、沉浸式全屏、安全区 ├── common/ │ ├── Theme.ets 深空蓝罗盘风主题 │ ├── AIConfig.ets 模型配置 求职顾问系统提示词 │ └── TimeUtil.ets 相对时间格式化 ├── model/ │ ├── JobStore.ets 数据模型 持久化 状态机配色 │ └── AIService.ets AI 服务多轮对话 单轮任务 └── pages/ ├── Index.ets 四 Tab 主壳 ├── BoardTab.ets 投递看板 ├── ResumeTab.ets AI 简历顾问 ├── InterviewTab.ets 模拟面试 └── MyTab.ets 复盘日记 我的这次开发速度明显比第一次做毕设伴侣快很多——因为架构分层的思路已经跑通过一次AIService的多轮对话封装、任务模板法、JSON/文本解析容错这些方法论都可以直接照搬。这让我真切体会到复用架构经验这件事的价值第一个项目投入的心血会在第二个项目里加速兑现这也是成长的一种具体体现。3.2 四个 Tab 对应四个痛点看板投递状态机管理解决记不清进度简历AI 简历顾问解决写不出彩面试模拟面试陪练解决没底气我的复盘日记解决面完就忘四、投递看板把记不清变成看得见看板要解决的核心问题是十几家公司的投递进度要在一个地方一眼看清。4.1 投递状态机我把求职投递抽象成一个七态状态机exportconstSTAGES:string[][已投递,笔试,一面,二面,三面,Offer,已挂];exportinterfaceJobApp{id:string;company:string;// 公司名position:string;// 岗位stage:string;// 当前状态STAGES 之一note:string;// 备注渠道/内推人等updateTime:number;}每条投递记录点击状态标签会弹出底部选择框选一个新状态就完成流转staticasyncupdateStage(id:string,stage:string):PromiseJobApp[]{constcurawaitJobStore.listApps();for(constaofcur){if(a.idid){a.stagestage;a.updateTimeDate.now();}}constsortedcur.sort((a:JobApp,b:JobApp)b.updateTime-a.updateTime);// 更新后重新按时间排序awaitJobStore.saveApps(sorted);returnsorted;}状态更新的同时刷新updateTime并重新排序这样最近有动态的投递会自动排到列表最前面——这是个小细节但对求职者来说很实用你最关心的永远是最近有进展的那几家而不是按添加时间死板排列的列表。4.2 状态配色一眼分辨紧急程度每个状态对应一个颜色从已投递的中性灰到二面/三面的warning色再到Offer的成功绿、已挂的警示红exportfunctionstageColor(stage:string):string{switch(stage){case已投递:return#647291;case笔试:return#3E8EF7;case一面:return#9E7BFF;case二面:return#F5B93E;case三面:return#FF8A5B;caseOffer:return#2FD9A8;case已挂:return#F76A6A;default:return#647291;}}配合概览区的三张统计卡总投递、进行中、Offer 数打开应用第一眼就能看到自己求职的战况——这种数字化自己进度的感觉比脑子里模糊记着投了好多家进展不知道要踏实得多。五、AI 简历顾问把流水账改成有亮点的表达这是我认为整个应用里对求职最直接有用的一个功能。5.1 四类任务模板简历问题千差万别但常见的求助场景可以归纳成四类润色经历、写自我介绍、STAR 法则改写、岗位匹配分析。我依然沿用任务模板 用户输入的套路privatetasks:QuickTask[][{label:润色经历,tpl:请帮我润色下面这段项目经历描述使其更专业、有量化亮点并指出改进点\n{X}},{label:自我介绍,tpl:我的背景是{X}。请帮我写一段 1 分钟左右的求职自我介绍技术岗。},{label:STAR改写,tpl:请用 STAR 法则情境-任务-行动-结果改写下面这段经历突出个人贡献与量化成果\n{X}},{label:岗位匹配,tpl:这是招聘岗位要求{X}。请分析我应该在简历中突出哪些技能与项目并给出建议。}];5.2 真实效果AI 一针见血地指出问题下图是我把自己简历里一段项目经历贴给 AI 润色的真实效果——我原本写的是一段更像个人博客简介的描述“关注 HarmonyOS、Flutter、人工智能及跨端开发技术喜欢分享技术文章”AI 直接指出了三个问题AI 反馈非常直接定位模糊像博客简介而非简历内容、缺乏量化成果与技术深度、未体现工程能力与业务价值。这三条反馈说实话比我自己反复读十遍都更能一针见血地戳中问题。这也是为什么我把系统提示词里 AI 的人设定得很明确staticreadonlysystemPrompt:string你是一位资深的校招/实习求职顾问兼技术面试官熟悉简历优化、自我介绍、技术面试与 HR 面试的常见套路。请用专业、具体、鼓励性的中文帮助应届生求职回答要可操作、有针对性避免空泛的套话。;“专业、具体、鼓励性”、“可操作、有针对性、避免空泛套话”——这几个关键词是我反复调试后确定的第一版提示词让 AI 给出的建议比较笼统建议增加更多细节这类加上避免空泛的套话这句限制后AI 的反馈明显更犀利、更具体了。提示词里明确禁止什么有时候比说明白要求什么更有效。5.3 多轮追问把改到满意为止首轮任务模板发送后后续的对话就自动进入自由追问模式constpromptthis.messages.length0?this.tasks[this.activeTask].tpl.replace({X},raw)// 首轮套用任务模板:raw;// 后续自由追问拿到 AI 指出问题后我可以接着问那你帮我改一版或者我这个项目具体是做了xx功能怎么量化一路把简历改到自己满意为止。这种多轮打磨的过程本身就是一次很好的自我梳理。六、模拟面试技术面 HR 面双模式陪练面试焦虑的核心是未知——不知道会被问什么。这个功能就是要把未知变成已知提前练。6.1 技术面 / HR 面切换我把面试拆成两种模式因为技术面和 HR 面的问题画像完全不同privatemodes:string[][技术面,HR面];constmodeDescthis.mode0?技术面试官:HR 面试官;constprompt我的求职背景是「${this.topic.trim()}」。请站在${modeDesc}角度预测 5 个最可能被问到的面试问题。只输出问题列表每行一个问题用数字编号不要额外说明。;6.2 真实效果Java 技术栈的面试题预测我输入java作为技术方向切到技术面模式AI 生成的问题相当扎实三个问题分别是——HashMap 的底层实现与 JDK 8 优化点、synchronized 与 ReentrantLock 的区别、Java 内存模型与 volatile 的可见性/有序性/原子性。这几乎就是 Java 后端面试的高频三连问专业度完全不输真实的面试官。切到HR面模式同样输入javaAI 生成的问题风格完全不同自我介绍、为什么选 Java技术选型动机、遇到过最棘手的问题及如何解决——明显能感受到 HR 面更关注表达能力、职业动机和解决问题的思路而不是纯技术细节。同一个技术方向两种模式下 AI 给出的问题画像截然不同这背后靠的就是提示词里站在技术面试官角度和站在 HR 面试官角度这一句话的差异。这让我意识到在提示词里明确身份视角是让大模型输出符合场景预期的一个简单却极其有效的手段。6.3 出题解析与容错和毕设伴侣的答辩演练一样我要求 AI只输出编号问题列表然后用正则清洗privateparseQuestions(text:string):string[]{constlinestext.split(\n);constout:string[][];for(constrawoflines){constlineraw.trim();if(line){continue;}constcleanedline.replace(/^[Qq]?\s*\d\s*[\.\、\:\)\]?\s*/,).trim();// 去除各种编号格式if(cleaned!){out.push(cleaned);}}returnout.slice(0,8);}这套解析逻辑我在两个项目里几乎原封不动地复用了——这也印证了前面说的方法论层面的经验会随着项目积累越来越好用。6.4 作答与点评闭环每道题下面有练习作答写完提交后 AI 会点评constprompt${modeDesc}面试问题${this.questions[i]}\n候选人的回答${this.answer.trim()}\n请作为面试官简要点评这个回答优点、不足、改进建议控制在 150 字内。;出题→作答→点评形成完整闭环相当于把面试现场提前搬到自己房间里反复练习。我练完 Java 并发那道题之后发现自己对synchronized底层锁升级机制讲得含糊这正是复盘价值所在——练习中暴露的漏洞比面试现场暴露的漏洞成本低得多。七、复盘日记把面完就忘变成持续的成长记录这是我认为整个应用里最能体现成长这个主题的功能。7.1 为什么复盘比刷题更重要求职期间我发现一个现象很多人包括曾经的我面试完只会有一个模糊的印象——“感觉还行或感觉挂了”但具体问了什么、哪里答得不好、下次该怎么改进都随着时间被遗忘。没有记录的经验等于没有发生过的经验。所以我设计了「复盘日记」这个模块鼓励自己每场面试后花 5 分钟把具体问题和反思记下来exportinterfaceReviewLog{id:string;company:string;title:string;// 简述如一面复盘content:string;time:number;}7.2 一条真实的复盘记录下图是「我的」页面展示了一条我自己写的真实复盘——华为一面之后的记录复盘内容是这样写的“问了 ArkTS 与 TypeScript 的区别、State/Prop 状态管理原理还问了项目里怎么处理大模型输出不稳定的问题。答得还算流畅但对分布式能力细节回答不够深入需要补一下。”这条记录很朴素但价值巨大——它精确指出了我在分布式能力这个知识点上的薄弱环节。如果没有这条记录我可能过几天就忘了这个具体的薄弱点下一场面试遇到类似问题还是会卡壳。有了它我在准备下一场面试前会专门去补一下鸿蒙分布式能力的细节这就是记录带来的实际改进。页面顶部我特意起了个标题——“求职同行者”配文每一次面试都是一次成长。这不是一句空洞的口号而是我做这个功能时最真实的心态求职路上每一次挫折只要认真复盘都能转化成下一次的底气。7.3 时间线可视化复盘记录用竖向时间线展示每条记录带公司名、标题、内容摘要和相对时间Row({space:10}){Column(){Circle({width:10,height:10}).fill(C.primary)if(ithis.reviews.length-1){Divider().vertical(true).color(C.stroke).strokeWidth(2).height(40)}}.width(14).alignItems(HorizontalAlign.Center)Column({space:4}){Row(){Text(r.company · r.title).fontWeight(FontWeight.Bold).layoutWeight(1)Text(✕).onClick((){this.onDelete(r.id);})}.width(100%)Text(r.content).fontSize(13).fontColor(C.textSub).width(100%)Text(fmtRelative(r.time)).fontSize(11).fontColor(C.textDim)}}随着求职季推进这条时间线会越来越长回头看时能清楚感受到自己面试能力在一次次复盘中不断被打磨的过程——这和毕设伴侣里的成长里程碑时间线是同一套设计思路都是在用最朴素的方式让成长这件抽象的事情变得可视化、可回顾。八、开发环境在 HarmonyOS 6 全系设备上验证这次开发依然是在最新的HarmonyOS 6API 23环境下完成。打开 DevEco Studio 的设备选择器能看到从 Mate 80 Pro、Mate X7、Pura 90 到 MatePad Pro 13、MateBook Pro 的全系列 HarmonyOS 6.1.0 设备我最终选择Mate 80 ProHarmonyOS 6.1.0作为主力调试设备。能在这么完整的一套设备矩阵下验证应用的运行效果对一个还在学习阶段的开发者来说是很难得的资源——这也从侧面印证了鸿蒙生态这几年发展的速度。九、AI 服务复用架构经验的复利效应这一节我想单独展开讲讲复用这件事因为它是这次开发速度明显加快的核心原因。9.1 几乎照搬的 AIService对比「毕设伴侣」和「求职罗盘」的AIService.ets两者的多轮对话封装几乎是一模一样的结构exportclassAIService{staticasyncchat(history:ChatMessage[]):Promisestring{constmessages:ChatMessage[][{role:system,content:AIConfig.systemPrompt}];for(constmofhistory){messages.push(m);}// ... 发起请求、超时控制、异常兜底、finally 释放资源}staticasynctask(prompt:string):Promisestring{returnAIService.chat([{role:user,content:prompt}]);}}唯一的差异只是AIConfig.systemPrompt里的人设描述——一个是毕业设计指导老师一个是校招求职顾问兼技术面试官。核心的网络请求逻辑、超时控制、异常兜底、资源释放全部可以直接复用。9.2 复用带来的真实收益这次开发从搭工程骨架到四个 Tab 全部功能跑通明显比第一次做毕设伴侣快了不少。具体体现在架构分层不用重新设计直接照搬入口层不碰网络、AI 交互收敛在 model 层的分层原则。AI 服务封装可以复制粘贴AIService.chat/task几乎原样搬过来只改提示词。交互模式可以复用任务模板多轮追问用在了简历顾问出题-作答-点评用在了模拟面试都是从毕设伴侣里搬过来的成熟模式。踩过的坑不会再踩Prop装饰器不能漏、自定义组件属性不能跟内置属性方法同名、build()方法只能有一个根容器……这些坑在第一个项目里踩过一次这次写代码时会本能地避开。这让我真切体会到一件事技术成长不是线性的而是有复利效应的。第一个项目投入的时间和踩坑成本会在后续项目里持续兑现。这也是我想对同样在学鸿蒙的同学说的——不要怕做第一个项目慢方法论和踩坑经验积累起来后第二个、第三个项目会明显加速。十、这次踩到的新坑ArkUI 保留字与容器根节点虽然大部分架构是复用的但求职罗盘的开发过程也不是一帆风顺编译时遇到了两个新的报错记录下来。10.1 自定义方法名与框架保留字冲突Methods, properties and accessors in structures decorated by Component and ComponentV2 cannot have name as activeCount.我在BoardTab组件里写了一个统计进行中投递数的方法随手命名为activeCount结果编译报错——activeCount是 ArkUI 框架内部保留的名字大概率与Tabs/动画等内部活跃状态计数机制相关不能被自定义组件的方法或属性使用。解决很简单换个名字// 之前activeCount() —— 与框架保留字冲突privatenotFailedCount():number{returnthis.list.filter((a:JobApp)a.stage!已挂).length;}这个坑告诉我一个经验ArkUI 组件内部方法命名最好避开一些看起来很像框架内置概念的通用词如active、state、current这类命名时稍微具体一点比如notFailedCount而不是activeCount能降低撞上保留字的概率。10.2 build() 方法的根容器规则In an Entry decorated component, the build method can have only one root node, which must be a container component.我在「我的」页面想在Scroll滚动区域之外叠加一个条件渲染的底部弹窗写复盘的表单最初写成了这样// 错误写法build() 里出现了两个平级根节点build(){Scroll(){/* ... */}if(this.showAdd){this.addSheet()}// 和 Scroll 是平级的第二个根节点}build()方法的规则是有且仅有一个根容器组件不能出现两个平级的顶层节点。修复方式是用Stack把两者包裹成一个整体build(){Stack({alignContent:Alignment.Bottom}){Scroll(){/* ... */}if(this.showAdd){this.addSheet()}// 现在是 Stack 内部的子节点合法}.width(100%).height(100%)}这个坑其实我在毕设伴侣的看板页里已经用Stack处理过一次当时是为了叠加新增任务的底部弹窗但这次写「我的」页时因为切换了心思专注在复盘日记的逻辑上一时疏忽又漏掉了这个包裹。这提醒我即使是已经验证过的模式写新页面时也要对照检查一遍不能凭记忆想当然。十一、总结求职是一场值得被认真记录的修行回顾这个项目它对我的意义和「鸿蒙毕设伴侣」一样——技术上是练手情感上是陪伴。从技术角度这次开发验证了一件事当你把一个项目的架构、方法论沉淀下来第二个类似的项目会明显更快、更稳。从投递看板的状态机设计到 AI 顾问的提示词调优再到面试陪练的出题-点评闭环几乎每一处都能看到上一个项目的影子但又结合求职场景做了针对性调整。从意义角度这个应用陪我走过了求职季最焦虑的那段时间。它让我看到自己投递的全貌不再被记不清进度的焦虑困扰它帮我把简历里模糊的表达改得更专业它让我在真正的面试前先在自己的应用里练了一遍又一遍它记录下每一次面试后的反思让这次没答好不再是白白经历的挫折而是变成了下一次的底气。如果你也是一名正在求职的鸿蒙方向应届生我想把这段经历里最真实的几点体会留给你求职的焦虑往往来自不确定而记录是对抗不确定感最好的工具。把投递进度、复盘心得都写下来你会发现自己其实比想象中更有掌控感。让 AI 帮你换个角度看自己。简历也好面试作答也好我们对自己的表达往往有盲区AI 的第三方视角能帮你更快发现问题。技术积累是有复利的。如果你也在做鸿蒙项目不要怕第一个项目做得慢、踩坑多——那些经验会在你做第二个项目时兑现成实实在在的效率提升。毕业季鸿蒙同行。愿每一位正在投简历、赶面试的同学都能把这段求职路走得踏实、走得心里有数最终找到属于自己的方向。如果你对这个项目、对鸿蒙开发、或者对求职这件事本身有任何想聊的欢迎交流——我们都是这条路上的同行者。