一个应用准备提交新版本时最容易拖慢团队的有时不是功能而是素材追不回来源。手机页已经换了最新界面平板说明图还是上一轮截的运营在群里收到一批 PNG开发却不知道这些图到底对应哪个构建。这个问题单凭文件名很难解决因为home-final-2.png并没有可验证的版本含义。这次我把工作范围压到一件事在打开 AppGallery Connect 上传页面之前用本地 Node.js 对截图来源清单做一轮预审。ReleaseBinder 示例任务为RB-707目标版本2.7.0共有八条截图登记七条标记为目标版本一条TAB-03仍登记为2.6.4因此内部门禁输出HOLD。这是一套团队自定义的质量检查不是华为审核平台自动给出的结果也不意味着官方规定所有截图必须携带一致的版本字段。一、先弄清截图版本漂移到底指什么这里说的“漂移”不是 PNG 像素里的系统版本号也不是用户手机的 HarmonyOS 版本而是制图记录标注的应用构建版本和准备上架的目标构建不一致。截图可能仍然准确也可能展示已删除入口、过时文案或未经确认的开关具体影响必须由业务和审核人员复核。机器只负责指出需要复查的证据不能替代人工看图。我不建议脚本直接去扫描每张截图上的所有文字。一方面容易把隐私信息、日期和测试账号误判为版本另一方面那会变成 OCR 问题偏离本轮目标。最小可行办法是让素材导出动作同时写一条结构化登记记录截图 ID、文件相对路径、目标平台、制图来源构建版本和操作者核对标志。清单保存到 Git 或流水线工件里让后续人能找到是哪一次生成的图。华为开发者网站把 AppGallery Connect 列为应用分发与发布环节的入口具体是否需要哪种截图、分辨率或资质要以当前应用类型对应的官方控制台要求为准。本文并不杜撰截图数量、比例、文案限制更不会声称“这个脚本通过就能上架”。检查发生在上传之前仅对团队自定字段负责。二、固定最小数据契约避免报表自己制造歧义ReleaseBinder 项目结构很短release/build.json记当前构建release/captures.json记八条素材记录scripts/check-evidence.mjs运行预审reports/evidence-report.json保存结果。演示里的TAB-03是平板页面图登记版本仍是2.6.4其余七条为2.7.0。不把这八张业务截图作为真的图片文件交付本轮生成的三张是文章配图二者数量和用途必须分开。第一段代码说明输入形状。它是可以由脚本消费的标准 JavaScript 数据对象八条明细并不代表已经从 AppGallery Connect 实际读取任何记录。constbuild{app:ReleaseBinder,version:2.7.0,buildCode:270};constcaptures[{id:PH-01,platform:phone,version:2.7.0},{id:PH-02,platform:phone,version:2.7.0},{id:PH-03,platform:phone,version:2.7.0},{id:PH-04,platform:phone,version:2.7.0},{id:TAB-01,platform:tablet,version:2.7.0},{id:TAB-02,platform:tablet,version:2.7.0},{id:TAB-03,platform:tablet,version:2.6.4},{id:TAB-04,platform:tablet,version:2.7.0}];选这种结构而不是直接把版本塞进图片名是因为图片名会变ID 和登记记录可以单独维护。正式工程里还应该记录截图生成时间、构建流水线编号和负责复核的人。尤其在团队协作场景下一张截图被重新压缩或改名不代表画面内容变了反过来文件名没变也不代表内容没有变。三、判断要保守发现问题就先 HOLD真正的检查逻辑不需要调用任何尚未核实的华为审核 API。它是普通 Node.js 数组过滤与字段校验能够在开发机或 CI 环境执行和运行在 HarmonyOS 手机上的 ArkTS 页面也没有直接的系统调用关系。这里优先检查清单是否为空、ID 是否重复、版本字段是否存在再比较构建版本否则“全部无记录”也会被错误地算成全部一致。functionaudit(build,captures){if(!build.version||!Array.isArray(captures)||captures.length0){thrownewError(INVALID_MANIFEST);}constseennewSet();for(constrowofcaptures){if(!row.id||!row.version||seen.has(row.id)){thrownewError(INVALID_CAPTURE_ROW);}seen.add(row.id);}conststalecaptures.filter(rowrow.version!build.version);return{taskId:RB-707,target:build.version,captures:captures.length,matched:captures.length-stale.length,stale:stale.length,staleIds:stale.map(rowrow.id),gate:stale.length0?HOLD:READY};}HOLD与READY是团队内部门禁的名字不是 AppGallery Connect 的审核状态枚举。如果captures.json损坏、版本为空或有两个相同 ID函数会抛出异常外层命令应该退出非零状态而不是悄悄生成READY。脚本失败和截图版本漂移也不是一类问题前者意味着无法判断后者意味着已经拿到明确的待复核条目。模型计算结果为八条中的七条一致、一条不一致staleIds中只有TAB-03。为什么不自动把版本号改为2.7.0因为那会毁掉证据价值。发现旧图后应重新核对画面必要时重新用正确的构建截图若确认旧图仍适用也必须由负责人员写明理由而不是让算法替人证明正确。四、输出报表留存可复核的失败原因团队常常把“发现问题”当作最后一步但下一个同事接手时最重要的其实是复现路径。建议报表同时提供任务号、目标版本、数量、待复核 ID 和生成时间原始记录不在报表里被修改。磁盘写入失败时控制台应当明确打印失败原因不能在内存算出HOLD就告诉运营报告已经成功保存。import{readFile,writeFile,mkdir}fromnode:fs/promises;constbuildJSON.parse(awaitreadFile(release/build.json,utf8));constcapturesJSON.parse(awaitreadFile(release/captures.json,utf8));constreportaudit(build,captures);// audit 为上一段应用自定义函数awaitmkdir(reports,{recursive:true});awaitwriteFile(reports/evidence-report.json,JSON.stringify(report,null,2)\n,utf8);console.info([ReleaseBinder] RB-707 target${report.target}captures${report.captures});console.info([ReleaseBinder] RB-707 matched${report.matched}stale${report.stale});console.info([ReleaseBinder] RB-707 gate${report.gate}staleId${report.staleIds.join(,)});if(report.gate!READY)process.exitCode2;上面第三段是命令主体摘录运行时需要把第二段的audit放进同一模块或明确从自建模块导入不能把三个分开的文章代码块误认为复制即得完整工程。writeFile是覆盖写入更严格的 CI 还可先写临时文件再原子替换避免过程中被其他任务读取半份报告。对同一输出路径的并发写入必须串行化防止不同批次相互覆盖。五、示意页面应该展示异常而不是包装成绩界面演示名叫ReleaseBinder 证据预审预设任务号RB-707、目标2.7.0、截图8、同版本7、过期1、门禁HOLD。报表关注条目是TAB-03登记来源版本2.6.4。演示日志依次写15:45:10 target2.7.0 captures8、15:45:11 matched7 stale1、15:45:11 gateHOLD staleIdTAB-03。它们都是给文章与配图对照用的预设数据并非真实 HiLog 或上传记录。把 Node.js 脚本的源代码放在 DevEco 风格的开发环境截图里只是说明仓库可以同时存 ArkTS 工程和本地发布辅助脚本。不能因此推断 Node.js 脚本直接运行在 HarmonyOS 手机端也不能把模拟器里显示的HOLD说成应用市场审核失败。它表达的是“开发团队选择暂停本次素材流转”只有负责人核对后才能进入下一步。真实交付时还需要人工逐项打开手机、平板、多形态下的应用页面核实截图描述是否忠实、是否覆盖当前功能以及是否泄露测试账号、定位或第三方受保护信息。脚本只看登记元数据不读取 PNG 像素不具备识别页面真假和内容合规的能力。这一点比让界面出现醒目的绿色通过标记更重要。六、把失败拆开避免误把内部检查当审核结论异常可以分三层。输入层清单缺失、重复 ID、版本字段为空脚本标为不可判定并退出。内容层版本记录不一致产生HOLD和待复核条目不自动修订。平台层上传后实际 AppGallery Connect 的审核反馈是另一个系统的事实只能以后续官方控制台信息为准。这三层分清了CI 才不会因为报表生成就错误宣布应用发布成功。文件路径也要约束在工作空间内但该问题此前已单独讨论过目录逃逸本轮不重复展开。这里的新增工程重点是截图证据和应用构建版本之间的映射、预审状态的可追溯性以及把问题交还给人工复核。未来若要把这个流程接到 PR建议先输出机器可读的 JSON再生成供运营查看的摘要不要让开发与运营分别维护两套数据以免二次漂移。七、交付前我会实际确认哪些条件对这套模型最直接的验收是八条数据输出七条匹配、一条过期把TAB-03的版本改回2.7.0门禁才会由HOLD转成READY删掉必要字段或制造重复 ID 时应输出错误而非假通过。还要测试报表目录不可写、并发两次执行、文件格式损坏等环境条件。这里可以验证普通 Node.js 函数与序列化不等同于 AppGallery Connect 平台集成测试。我更看重它给发布团队提供了一条可核对的证据链而不是多了一个脚本。截图能从任务号找到来源构建能与截图逐项比对失败会留下具体 ID。至于华为应用市场最终的上架要求应以当前官方文档、开发者协议以及控制台即时提示为准。示意里的READY只说明本地规则放行不意味着任何真实应用通过审核。官方依据华为开发者《华为应用市场/应用测试与分发》可访问页面https://developer.huawei.com/consumer/cn/appgallery 华为官方《上架申请》2026-02-04https://developer.huawei.com/consumer/cn/doc/Media-Guides/app-release-0000001101428382 Node.js官方文件系统文档https://nodejs.org/api/fs.html 。上架流程资料用于确认平台背景不代表截图版本等于官方强制规定。