☰
HarmonyOS 7 + Node.js + AppGallery Connect:多设备截图素材矩阵的缺口扫描与发布门禁【鸿蒙心迹】
2026/10/3 7:16:44 网站建设 项目流程

上架前最尴尬的素材问题,往往不是“完全没有截图”,而是中文手机页齐全、英文 PC/2in1 少两张;运营表格看着已经打勾,真正切到 AppGallery Connect 的另一个语言和设备页签才发现空位。下面不写审核经验故事,也不虚构某次驳回,而是把公开配置要求翻译成一个可执行的本地门禁:AssetGate。

一、把素材清单看成矩阵,不看成文件夹

AppGallery Connect 的公开说明指出,应用可按支持语言配置介绍、功能说明、截图和视频;如果支持多种设备类型,需要切换对应设备页签配置素材。手机和平板共用一个素材页签,手表类共用一个页签,智慧屏则单独上传。官方提交入口也把应用图标、截图和视频列为上架前需要配置的基本信息。

这意味着“目录里有 9 张 PNG”并不能证明素材完整。完整性至少有三个轴:语言、设备组、截图槽位。AssetGate 的演示范围刻意缩小为两个语言zh-CN、en-US,两个设备组phone_tablet、pc_2in1,每组 3 个槽位01、02、03。因此期望项是 12 个。

演示运行编号为AGC-20261001-17,页面叫“素材矩阵诊断”。扫描发现 9 项,缺失 3 项:

  • zh-CN / pc_2in1 / 03.png
  • en-US / pc_2in1 / 02.png
  • en-US / pc_2in1 / 03.png

状态固定为READY → SCANNING → BLOCKED。这些是本文 Demo 数据,不代表 AppGallery Connect 对所有应用强制要求每个页签恰好三张。槽位数量、文件尺寸、语言范围和设备范围都应由项目根据当前官方素材规范与自身发布配置维护,不能把示例数字冒充平台政策。

我更倾向把规则写进版本库,而不是把要求藏在群消息里。原因很实际:代码评审能看到新增设备形态后有没有补素材;流水线能阻止残缺目录进入候选发布;运营同学也能拿到明确的缺口路径,而不是一句“英文截图不全”。

二、先让配置文件说清楚发布意图

这段代码解决什么问题:用一份强类型配置声明语言、设备组和槽位,避免扫描器把目录结构写死。

// tools/asset-gate/config.tsexporttypeLocale='zh-CN'|'en-US';exporttypeDeviceGroup='phone_tablet'|'pc_2in1';exporttypeSlot='01'|'02'|'03';exportinterfaceAssetGateConfig{runId:string;rootDir:string;locales:Locale[];deviceGroups:DeviceGroup[];slots:Slot[];extensions:string[];}exportconstconfig:AssetGateConfig={runId:'AGC-20261001-17',rootDir:'release-assets/screenshots',locales:['zh-CN','en-US'],deviceGroups:['phone_tablet','pc_2in1'],slots:['01','02','03'],extensions:['.png']};

强类型的价值不是“写起来更高级”,而是把发布意图变成可评审的代码。新增de-DE时,变更会出现在 diff 中;新增智慧屏时,设备组也会显式增加。扫描器不应偷偷从现有目录推断语言,因为一个目录不存在时,推断恰好会把缺口一起忽略。

phone_tablet是根据官方说明对共享素材页签做的项目内命名,不是平台 API 枚举;pc_2in1同样只是本地规则标识。真正上传时仍以控制台当期显示和官方文档为准。这里没有调用不存在的“自动提交截图”接口,也没有假设控制台导出格式。

目录约定为release-assets/screenshots/{locale}/{deviceGroup}/{slot}.png。把语言放在第一层,方便本地化负责人一次查看某语言下的所有形态;如果团队按设备负责人分工,也可以交换两层顺序,只要配置和报告一致。

三、扫描器只做事实判断,不替团队猜政策

这段代码解决什么问题:展开完整笛卡尔积,逐项检查文件,并用非零退出码阻断缺素材的候选发布。

// tools/asset-gate/scan.tsimportfsfrom'node:fs';importpathfrom'node:path';import{config,Locale,DeviceGroup,Slot}from'./config';interfaceMissingAsset{locale:Locale;deviceGroup:DeviceGroup;slot:Slot;expectedPath:string;}constmissing:MissingAsset[]=[];letfound=0;for(constlocaleofconfig.locales){for(constdeviceGroupofconfig.deviceGroups){for(constslotofconfig.slots){constexpectedPath=path.join(config.rootDir,locale,deviceGroup,`${slot}.png`);if(fs.existsSync(expectedPath)&&fs.statSync(expectedPath).size>0){found+=1;}else{missing.push({locale,deviceGroup,slot,expectedPath});}}}}constexpected=config.locales.length*config.deviceGroups.length*config.slots.length;constreport={runId:config.runId,state:missing.length===0?'PASSED':'BLOCKED',expected,found,missingCount:missing.length,missing};fs.mkdirSync('build/reports',{recursive:true});fs.writeFileSync('build/reports/asset-gate.json',JSON.stringify(report,null,2));console.log(`[AssetGate]${report.state}${found}/${expected}`);process.exitCode=missing.length===0?0:2;

扫描器最重要的细节是先构造“应该存在什么”,再去文件系统验证;不能先枚举已有文件再统计。演示结果是BLOCKED 9/12,退出码 2。0 表示通过,2 表示素材缺口,其他异常可以由 Node 进程的错误码表示。把业务失败和脚本崩溃区分开,流水线报告才不会把“缺三张图”写成“未知执行错误”。

这里只检查存在性与非空。像素尺寸、宽高比、格式、大小上限和内容合规不应凭记忆硬编码;应以当前素材规范为准,再作为配置加入校验。尤其平台规则可能更新,脚本需要有规则版本和复核日期。本文最后核对的公开说明更新于 2026 年,正式发布前仍应再次查看控制台和官方文档。

扫描也不等于审核。它只能证明团队声明的矩阵没有文件缺口,不能证明截图与真实功能一致、没有误导性文案、没有测试数据,也不能替代兼容性、稳定性、隐私或资质检查。

图 02 是演示构图,不是真实 DevEco Studio 截图或实际审核证据。中间代码展开矩阵,右侧模拟器显示AGC-20261001-17,底部日志严格对应BLOCKED 9/12 · missing 3。

四、报告先服务修复,再服务归档

一份只能给流水线看的 JSON 还不够。素材负责人需要知道哪三张缺失、每张应该放在哪里;开发负责人需要知道本次门禁规则是否覆盖当前发布形态;审核前的交接人需要知道报告是哪次运行生成的。

这段代码解决什么问题:把扫描报告映射成 ArkUI 诊断页面,按语言与设备组展示缺口,同时避免把演示结果说成平台审核结果。

interfaceAssetRow{key:string;label:string;state:'FOUND'|'MISSING';}@Entry@Componentstruct AssetGatePage{@StaterunState:string='BLOCKED';@Statefound:number=9;@Stateexpected:number=12;privaterows:AssetRow[]=[{key:'zh-CN/pc_2in1/03',label:'中文 · PC/2in1 · 03',state:'MISSING'},{key:'en-US/pc_2in1/02',label:'English · PC/2in1 · 02',state:'MISSING'},{key:'en-US/pc_2in1/03',label:'English · PC/2in1 · 03',state:'MISSING'}];build(){Column({space:16}){Text('素材矩阵诊断').fontSize(24).fontWeight(FontWeight.Bold)Text('AGC-20261001-17').fontColor('#667085')Text(`${this.runState}${this.found}/${this.expected}`).fontSize(30).fontColor('#C62828')Text('语言 2 × 设备组 2 × 槽位 3')List(){ForEach(this.rows,(row:AssetRow)=>{ListItem(){Row(){Text(row.label).layoutWeight(1)Text(row.state).fontColor('#C62828')}.padding(12)}},(row:AssetRow)=>row.key)}.layoutWeight(1)Text('演示扫描结果 · 非平台审核结论').fontSize(12).fontColor('#667085')}.padding(24).width('100%').height('100%')}}

这个页面不是发布必需能力,而是让报告更容易在团队里被消费。它明确写出“演示扫描结果”,避免截图被二次传播后误解为平台审核页。ForEach的 key 使用完整矩阵路径,补图后单行状态可以稳定更新,不会因为列表重排串位。

图 03 展示扫描总览:时间17:38,运行编号AGC-20261001-17,状态BLOCKED,完成度9/12。状态栏中的 Wi-Fi、5G、信号和电量仅用于满足纯手机运行图的完整视觉语境。

五、缺口页必须给出可执行路径

总览告诉你“少三张”,但修复者真正需要的是路径。AssetGate 的详情页按严重度排序,先列不存在的文件,再列后续可选的尺寸或内容校验。三个路径与正文数据完全一致:

release-assets/screenshots/zh-CN/pc_2in1/03.png release-assets/screenshots/en-US/pc_2in1/02.png release-assets/screenshots/en-US/pc_2in1/03.png

修复动作也应有边界。补图后重新运行扫描,让状态从BLOCKED回到SCANNING,全部 12 项存在才进入PASSED。不要允许用户在诊断页直接点“忽略并发布”,否则门禁会退化成提醒。如果某个设备组本次确实不发布,应修改版本化配置并经过评审,而不是在运行时临时跳过。

图 04 是详情/调试页,时间17:39,列出三条缺失路径和退出码 2。红圈标注的是en-US / pc_2in1连续两个槽位缺失,红色箭头指向“补齐后重新扫描”,承担解释技术问题的作用,而不只是展示 UI。

六、从“文件存在”继续走到“素材可信”

第一版扫描器只判断路径存在与文件非空,这是有意收窄范围。发布工具最危险的做法,是一开始就塞进几十条模糊规则,最后团队不知道哪条来自官方要求,哪条只是历史习惯。更稳的演进顺序是:先把矩阵缺口查准,再逐项增加能够解释、能够追溯来源的验证。

图像可解码是第二层。后缀名为.png不代表内容一定是 PNG,零字节检查也发现不了损坏文件。可以使用团队已经审计过的图像库读取宽高与格式;解析失败时报告UNREADABLE,不要和MISSING混在一起。两者的修复动作不同:前者要重新导出,后者要补充文件。

尺寸与宽高比是第三层,但参数必须来自版本化规范。建议配置项同时记录ruleId、reviewedAt和官方链接。平台更新素材规格时,提交者能看到规则依据和上次复核日期。不要在代码里留下一个没有来源的1080×1920常量,然后几年后把它当成不可变政策。图 03 为了演示槽位含义展示了尺寸文案,但正文并不据此宣称平台强制值。

重复内容是第四层。同一语言同一设备组的三张截图如果哈希完全一致,形式上满足 3 个文件,实际上没有提供三份不同信息。可以对像素内容做哈希,在组内发现重复时标记DUPLICATE。跨语言完全相同则不能直接判错:界面本身可能没有文字,也可能语言替换失败。工具只能提示复核,不能自动阻断所有跨语言重复。

文案语言是第五层,也是最容易误判的一层。OCR 可以提示英文素材里出现大段中文,但模型识别错误、品牌名和代码片段都会带来噪声。比较合适的策略是生成REVIEW项,让人工确认;除非团队已经用自己的样本验证过准确率,否则不要把 OCR 单独作为发布失败条件。

敏感信息检查同样适合“提示优先”。测试账号、手机号、定位、订单号或内部环境标记可能出现在截图中。可以用正则和 OCR 做初筛,却不能保证覆盖所有风险。最终素材仍需要人工走读,确认画面来自公开版本、文案与实际功能一致、没有调试开关,也没有夸大能力。

因此报告状态最好不只有通过与失败。PASSED表示强制结构规则通过;BLOCKED表示明确缺口或不可解码;REVIEW表示存在需要人工判断的提示。流水线对BLOCKED返回非零退出码,对REVIEW生成可见报告但是否阻断由发布策略决定。把确定性和推测性分开,工具才不会因为误报被团队整体绕过。

七、把配置变更也纳入评审

门禁规则保存在仓库后,一个新的风险随之出现:有人为了让流水线变绿,直接从配置中删掉缺失语言或设备组。单看扫描结果会变成PASSED,但发布意图已经被悄悄缩小。因此配置变更应和应用能力变更一样进入代码评审,并由发布负责人确认。

可以在报告里输出本次矩阵摘要:语言 2、设备组 2、槽位 3、期望 12。流水线再与上一候选版本对比,如果期望项突然从 12 降到 6,就给出醒目的MATRIX_SHRUNK提示。缩小矩阵不一定错误,例如本次确实停止 PC/2in1 分发;但它需要理由,而不是静默发生。

同样,新增语言时不应依赖默认回退悄悄过关。默认语言内容能够兜底显示,是平台行为;项目是否接受回退,则是产品决策。AssetGate 可以为特定字段设置回退白名单,例如版本更新说明临时复用默认语言,但截图素材默认要求显式存在。白名单也要带截止版本,避免临时例外长期留存。

设备组映射应与当前控制台页签保持一致。官方文档说明手机和平板共享素材页签、手表类共享页签,这适合映射成本地设备组;如果平台后续调整,项目配置也应跟着复核。不要直接用工程deviceTypes数组机械生成素材页签,因为应用支持的运行设备与控制台素材分组不是同一层概念。

分支策略也要考虑素材体积。大量截图放进 Git 可能增加仓库负担,有的团队会存对象存储,只在仓库保留清单。无论文件放哪里,扫描器都应先读取同一份矩阵,再由适配器验证本地路径或远端对象。不要让本地模式和流水线模式各维护一套语言列表。

如果素材来自设计系统导出,可以要求每个文件旁带一个轻量元数据条目,记录页面、语言、设备组、导出时间和源设计版本。扫描器不需要相信这些字段就通过,但可以把它们写入报告,帮助定位“文件存在却内容过期”的问题。这里仍然只做工程追踪,不把源设计版本冒充审核要求。

八、让失败报告能被不同角色读懂

开发者关心退出码和路径,设计师关心缺哪张画面,运营关心哪个语言页签不能提交,发布负责人关心是否允许继续。把所有人都丢进一份原始 JSON,沟通成本并不会消失,只是换了位置。

可以从同一报告派生三种视图。终端视图保持短:BLOCKED 9/12 missing=3,接着打印三条路径。合并请求视图按语言和设备组折叠,附带修复说明。诊断页面则像图 03、图 04 一样先给总览,再给可执行缺口。三个视图必须来自同一 JSON,不能各自重新计算。

失败信息应避免含糊的“素材不完整”。更好的写法是“期望 12,发现 9,缺失 3;运行 AGC-20261001-17;退出码 2”。路径统一使用仓库相对路径,避免把构建机用户名和绝对目录带进日志。对于远端对象,用逻辑键而不是带签名的临时下载链接。

修复后再次扫描要生成新的运行记录,不要原地覆盖唯一报告。候选发布可以保留最近一次失败和最终通过两个摘要,便于交接时说明发生了什么。但这仍是团队内部的工程记录,不应宣称“已经通过平台审核”。真正的审核结果只能来自平台流程。

九、门禁接入流水线时,别制造第二份真相

接入构建流程有三种常见做法。第一种是开发者本地手动运行,成本最低,但最容易忘记。第二种是合并请求检查,每次素材或配置变更都执行,反馈及时。第三种是候选发布流水线的强制步骤,最接近最终状态,但发现问题较晚。比较稳妥的组合是合并请求做快速存在性扫描,候选发布再做完整规则校验。

无论采用哪种方式,报告都应来自同一份配置。不要让运营表格写两个语言、脚本写三个语言、流水线变量又排除其中一个。配置文件可以包含负责人、规则复核日期和官方规范链接,但敏感凭据不能进入仓库。

如果未来接入图像元数据解析,可以增加以下检查:PNG 是否可解码、宽高是否满足本批素材规范、是否带不允许的透明通道、文件是否重复、截图是否包含明显占位文案。不过内容是否准确仍需要人工核对。自动化擅长发现结构缺口,不擅长判断截图是否真实反映功能。

另一个边界是默认语言回退。官方说明提到,特定语言未配置信息时可能显示默认语言内容。这不等于项目就应该接受所有回退。面向英语地区发布却展示中文截图,用户体验和审核沟通都可能受影响。AssetGate 可以把“允许回退”做成显式白名单,但默认应要求发布矩阵完整。

十、核对清单要能解释失败,而不只是报红

发布前先核对当前版本支持的设备类型,再核对 AppGallery Connect 中配置的语言,然后生成矩阵。确认手机/平板是否共享同一组素材,确认 PC/2in1 是否属于本次发布范围。扫描通过后,再人工检查截图内容、文案语言、功能一致性与敏感信息。

当结果为BLOCKED 9/12时,报告必须直接给出三条路径;当脚本异常退出时,要与“缺素材”使用不同的错误信息;当规则更新时,要同时更新配置版本和复核日期。只有这样,门禁才是修复工具,而不是红灯装饰。

候选发布前最好再做一次“反向抽查”:从控制台计划发布的语言和设备页签出发,逐一回到仓库矩阵确认,而不是只相信脚本配置。这样能发现配置本身漏写了某个发布范围。自动化验证事实,人工确认意图,两者互相校验,才不会把一份写错的清单执行得非常准确。

素材补齐也应保留责任边界。开发脚本可以指出路径,设计与产品需要确认画面内容,发布人员负责核对控制台页签,最终审核由平台完成。任何一方都不该把PASSED 12/12解读为“审核一定通过”。它只表示这一条结构门禁已经满足,且报告能够复现。

官方参考:

  • 提交 HarmonyOS 应用:配置图标、截图和视频等基本信息
  • AppGallery Connect:按语言和设备类型配置介绍及素材
  • AppGallery 审核指南概览
  • AppGallery Connect 发布前检查

这类脚本真正省下的不是三张图的补传时间,而是临近发布时在多个页签之间来回确认的认知成本。把语言、设备组和槽位做成矩阵,把缺口做成路径,把失败做成非零退出码,审核准备就从记忆任务变成了可复现的工程步骤。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询