质疑"某个目录有没有被打进 zip"时, 原先只能人工逐目录核对。现在把这个检查做进 脚本, 每次导出都跑: copyDir 记录每个被复制文件在 zip 中应有的条目名, verifyZipFile 把它与实际 zip 条目做**逐个双向比对** —— 少一个报"漏拷"并列出文件名, 多一个报"多拷"。这样 漏拷会当场失败, 而不是等在别的电脑上解压缩时才发现。 自检输出新增一行, 例如: 自检: 零漏拷 ✅(1381/1381) / CRC 逐条 ✅ / 偏移一致 ✅ / ... 同时把 copyDir 的条目名拼接由"从 nameOfParent 反推"改为显式传 relBase, 去掉 原先那段绕的 split/map 逻辑。 新增 3 条测试(漏拷拒绝 / 多拷拒绝 / 清单吻合时放行且 README.txt 豁免), 本 测试文件 19/19 通过, npm test 全量 17 个测试文件通过。
小程序及快应用前端源码
1. 📋 项目说明
该项目基于 uni-app 开发构建,请使用 HBuilderX 进行代码开发及构建发布。
2. 🛠️ 开发说明
- 源码基于 Vue2 版本
- SCSS 采用 dart-sass 进行编译输出
3. 🔧 开发调试说明
3.1 注意事项
- 应用访问等关键参数的配置来源于
./common/js/config.js
3.2 本地调试
.local.config.js.example 是本地调试配置示例。
拷贝 .local.config.js.example 为 .local.config.js 文件,默认开发调试模式会自动加载该文件
4. 📦 发布说明
4.1 小程序发布
4.1.1 基本操作步骤(通用版/定制化版)
##### ##### ##### 【前置准备】
- 在项目根目录打开终端安装依赖:
已有依赖可跳过此步骤
npm install
##### ##### ##### 【发布构建】
- 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
- 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成 ⚠️ 注意:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
- 构建完成后,自动执行补丁脚本(拷贝 site.js / config 文件 + 生成 ZIP 包),无需手动操作
- 找到项目根目录
/unpackage/dist/build下生成的 mp-weixin 压缩包 💡 示例:mp-weixin-2026-05-11-3bff91e.zip压缩包文件名末尾为 Git 提交 ID 同时该目录下会生成未压缩的mp-weixin目录
##### ##### ##### 【验证与重命名】
-
打开未压缩的
mp-weixin目录,找到site.js文件,将文件内的uniacid值改为当前客户编号(如:2812)并保存 -
打开微信开发者工具,导入「mp-weixin」目录,点击「编译」,在开发者工具控制台验证有无报错,以及能否返回对应客户的业务数据
-
确保无误后将生成的 mp-weixin 压缩包重命名
- 定制化版格式:
定制化-客户编号-域名-mp-weixin-当前日期-Git提交ID.zip - 通用版无需重命名
💡 示例:
custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-05-11-3bff91e.zip🚫 禁止:压缩包命名禁止包含
/ \ : * ? " < > |等特殊字符 - 定制化版格式:
##### ##### ##### 【交付与最终发布】🔍
- 将重命名后的文件发送给技术人员 📌通用版直接将生成的 mp-weixin 压缩包发送给技术人员
- 技术人员操作流程:
- 解压压缩包
- 确认
site.js中的uniacid为客户编号 - 用微信开发者工具导入解压后的代码目录
- 编译验证无误后,上传发布
4.2 快应用发布
4.2.1 基本操作(通用版/定制化版)
4.2.1.1 【发布构建】
-
使用 HBuilderX 打开项目
-
打开项目根目录的
manifest.json文件,切换至可视化配置界面:- 依次点击「Web 配置」→「运行的基础配置」
- 根据情况查看是否修改路径中/hwappx/????/的客户编号
📌 通用版保留原有
/hwappx/common/📌 测试版保留原有/hwappx/test/💡 个性化定制示例:/hwappx/2811/,其中 2811 为定制化版客户编号
-
选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本 💡 例如:网址是
5g.aigc-quickapp.com选H5-5g.aigc-quickapp.com -
修改以下配置:
- 网站标题:快应用
- 网站域名:当前客户域名
- 确认后点击「发行」等待构建完成
⚠️ 注意:构建完成后自动输出到
unpackage/dist/build/h5/,格式为{域名}-{base路径}.zip💡 示例:xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip
4.2.1.2 【交付与最终发布】🔍
- 将生成的
.zip文件发送给运维人员 - 运维人员操作流程:
- 解压压缩包
- 打开快应用开发者工具,导入解压后的代码目录
- 验证代码无报错后,执行上传发布操作
4.2.1.3 【测试流程】
H5:打开自己的浏览器输入网址(https://5g.aigc-quickapp.com/hwappx/common/?uniacid=75)改站点 如果是test就把hwappx/common/改为hwappx/test/----------改站点
4.3 鸿蒙元服务发布(mp-harmony)
4.3.1 【发布构建】
- 使用 HBuilderX 打开项目
- 选择菜单栏「发行」 → 「HarmonyOS」→ 「发行」,等待构建完成
- 构建完成后,自动执行
harmony.patch.js补丁脚本(配置拷贝 + ZIP 打包) - 找到项目根目录
/unpackage/dist/build下生成的 mp-harmony 压缩包
4.3.2 【DevEco Studio 编译与产物】
在 DevEco Studio 中导入 mp-harmony 工程后,点击「Build」→「Build App(s)/Project(s)」,实际执行的任务是 assembleApp:
# 由 DevEco Studio 自动执行,无需手动敲
hvigorw --mode project -p product=default assembleApp
这个任务是两步串联的 —— 先逐个 module 编译出包,再把这些包打成 APP:
| 产物 | 路径 | 说明 |
|---|---|---|
| 分包(中间产物) | {module}/build/default/outputs/default/{module}-default-signed.{hap|hsp} |
每个 module 一个,编译+签名的最小单位 |
| APP(最终产物) | build/outputs/default/mp-harmony-default-signed.app |
把全部分包收拢成的分发单元 |
📌 元服务上架 AppGallery Connect 上传的是 .app,不是 .hap。pack.info 中 "bundleType": "atomicService" 即元服务包标记。
📌 本工程共 1 个 HAP + 5 个 HSP(不是 6 个 HAP)—— module.json5 里 type: "entry" 编成 HAP,type: "shared" 编成 HSP(共享包,动态下发)。详见 build/outputs/default/pack.info:
| 包名 | 扩展名 | moduleType | 说明 |
|---|---|---|---|
entry-default |
.hap |
entry |
主包,EntryAbility + 服务卡片 + uniapp 主 bundle |
components_diy-default |
.hsp |
shared |
ascf 动态化分包 |
pages_goods-default |
.hsp |
shared |
ascf 动态化分包 |
pages_order-default |
.hsp |
shared |
ascf 动态化分包 |
pages_promotion-default |
.hsp |
shared |
ascf 动态化分包 |
pages_tool-default |
.hsp |
shared |
ascf 动态化分包 |
⚠️ 主包体积:元服务官方要求单个包 ≤ 2MB(按压缩后算,含其直接依赖的 HSP),同一设备类型下全部分包总和 ≤ 10MB。实测本工程主包 entry-default.hap 约 2.37 MiB,已超单包 2MB 限制;总包约 6.39 MiB,在 10MB 以内。
若只想单模块调试,可在 DevEco 中用「Build Hap(s)/Module(s)」(即
assembleHap),只产出分包、不打 APP。
4.3.3 【工程包导出(换机编译)】
需要在一台没有本仓库 / 没有 HBuilderX 的电脑上编译鸿蒙工程时,用脚本把 stub-app-projects/mp-harmony 里编译所需的文件导成一个 zip:
npm run pack:harmony
输出到 unpackage/dist/harmony-export/mp-harmony-project-v<日期>-<提交ID>.zip,解压后即得 mp-harmony/ 目录。
| 参数 | 用途 |
|---|---|
| (无参数) | 默认行为:含签名证书,不含 node_modules |
--no-certs |
不含 cert-profile/(见下方安全提示) |
--with-node-modules |
额外含 hvigor/node_modules(约 320 MB),供离线目标机 |
--out <目录> |
自定义输出目录 |
脚本全程只读源目录,产物先复制到暂存目录再压缩,不修改 stub-app-projects 下任何文件。
导出内容
| 类别 | 说明 |
|---|---|
| 工程源码 | AppScope/、entry/src/、ascf/ascf_build/*/src/、各类 *.json5 / hvigorfile.ts |
ascf/ascf_src/ |
uniapp 编译产物(页面/组件代码),带上它目标机就不需要本仓库和 HBuilderX |
cert-profile/ |
签名证书(--no-certs 可排除) |
已排除(可由目标机 DevEco 重新生成)
node_modules/(约 320 MB)、oh_modules/、**/build/、.preview/(约 22 MB)、.idea/、.hvigor/、local.properties、.cxx/、.test/
⚠️ 证书安全:默认导出包含签名私钥
cert-profile/qq/qq.p12,且 p12 密码在build-profile.json5中本就是明文。请仅在可信渠道传递,用完及时销毁或归档。对外发给不可信对象时务必加--no-certs。📌 缺证书 hvigor 无法签名,产不出可上传的
.app。用--no-certs时需自行把cert-profile/放到目标工程对应位置。
目标机操作
- 安装 DevEco Studio 5.0.x(本工程
compatibleSdkVersion5.0.0(12)) - 解压 zip,得到
mp-harmony/ - DevEco Studio「打开」
mp-harmony目录本身 - 首次打开自动执行依赖同步(
ohpm install拉取hvigor/node_modules),需联网;离线机请用--with-node-modules导出 - 「Build」→「Build App(s)/Project(s)」,产物
build/outputs/default/mp-harmony-default-signed.app
⚠️
ascf/ascf_src/是导出时刻的快照。若前端代码有更新,必须回源机器重新执行导出,否则编译出的包内容是旧的。
📌 zip 由脚本内纯 Node 实现(zlib + 手写 ZIP 头)生成,条目名统一正斜杠并置 UTF-8 标志位,可跨 Windows/macOS/Linux 使用。
产出后自检(每次导出都会跑)
脚本写完 zip 后会把文件重新解析一遍,任何一项不符直接判失败、不输出「完成」:
| 校验项 | 内容 |
|---|---|
| 零漏拷 / 零多拷 | 把本次实际复制的文件清单与 zip 条目逐个双向比对,有任何文件没打进去、或出现清单外的文件,当场失败 |
| EOCD / 中央目录 | 签名、条目数、中央目录偏移与长度边界一致 |
localOffset |
每条都必须真的指向本地文件头,且文件名与中央目录一致 |
| CRC | 逐条解压后重算 CRC32 比对 |
| 条目名 | 必须正斜杠、必须置 UTF-8 标志位、必须可逆解码为合法 UTF-8 |
| 内容断言 | 构建必需文件齐全;node_modules/build/.preview 等排除项不得混入 |
npm test # 跑全量测试, 含 pack-harmony-project.test.js 的 19 条用例
正常输出示例:
条目: 1382 个文件 + 327 个目录 (自检通过)
自检: 零漏拷 ✅(1381/1381) / CRC 逐条 ✅ / 偏移一致 ✅ / 正斜杠 ✅ / UTF-8 ✅ / 骨架齐全 ✅
📌 不必再人工核对"某个目录有没有打进去"——上面的「零漏拷」会逐文件双向比对并打印数量。怀疑漏拷时看这一行即可;若真的漏了,脚本会直接失败并列出文件名。
📌
EntryCard/不在build-profile.json5的模块列表中,不是编译模块,其内容不参与构建;但导出脚本按「全量复制 − 排除清单」处理,同样会把它带进 zip。
📌 测试分正向(写出来能还原)与反向(故意篡改 zip,断言自检能抓到)两类。反向用例锁定了开发期真实踩过的三个 bug:中央目录偏移漏算文件数据长度、
localOffset记成数据区起点、.test子串匹配误伤entry/src/ohosTest/ets/test/源码。反向用例如果失效,自检就形同虚设,改动脚本时务必保持它们通过。
4.3.4 【常见错误】
❌ hvigor ERROR: 00303218 Configuration Error
Error Message: The reason and usedScene attributes are mandatory for user_grant permissions.
at entry\src\main\module.json5
原因:user_grant 类型权限必须同时声明 reason 和 usedScene 两个字段,只写 usedScene 会导致 :entry:default@PreBuild 失败。
解决:给对应权限补上 reason 字段,并在 entry/src/main/resources/base/element/string.json 中新增对应的理由文案。
// entry/src/main/module.json5
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:permission_microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
// entry/src/main/resources/base/element/string.json
{
"name": "permission_microphone_reason",
"value": "用于语音搜索与语音输入功能,录制语音以识别用户需求"
}
📌 权限类型速查 —— 只有 user_grant 才需要 reason:
| 权限 | 类型 | 需要 reason |
|---|---|---|
ohos.permission.INTERNET |
system_grant | 否 |
ohos.permission.GET_NETWORK_INFO |
system_grant | 否 |
ohos.permission.VIBRATE |
system_grant | 否 |
ohos.permission.MICROPHONE |
user_grant | 是 |
📌 reason 文案必须是用户可读的中文说明,不能为空或写 xxx description 之类的占位文本,否则上架审核会被驳回。
📌 改完 reason 还不够 —— user_grant 权限编译通过后,仍需在运行时弹窗申请,否则录音等功能不可用。本项目已在 EntryAbility.ets 的 onCreate 中接入:
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 必须先调父类: ascf runtime 的初始化在 AscfUIAbility.onCreate 里, 漏掉会导致元服务白屏
super.onCreate(want, launchParam);
this.requestMicrophonePermission();
}
⚠️ 坑:
AscfUIAbility父类已定义onCreate,重写时必须调super.onCreate(want, launchParam),否则 ascf runtime 初始化被跳过,元服务白屏。
⚠️ 坑:
Permissions在@ohos.permissions里是字符串联合类型(permissions.d.ts),不是枚举,只能用在类型位置,当值引用会报:'Permissions' only refers to a type, but is being used as a value here.所以正确写法是标注 + 字面量,且权限名必须与
module.json5中声明的一致:import { abilityAccessCtrl, AbilityConstant, Permissions, Want } from '@kit.AbilityKit'; const PERMISSION_MICROPHONE: Permissions = 'ohos.permission.MICROPHONE'; // ❌ Permissions.MICROPHONE —— 编译不过 // ⚠️ abilityAccessCtrl.SwitchType.MICROPHONE(=1) 是"开关类型",不是权限名,别搞混
📌 为什么在 onCreate 申请而不是"用户点录音时才申请":
- 权限状态在 JS 侧(
uni.getRecorderManager)读不到,项目里也没有现成的 JS ↔ ArkTS 通道,做不了按需触发 requestPermissionsFromUser对已授权的应用直接 resolve 且不弹窗,所以实际只有首次启动打扰用户一次
📌 用户拒绝后的降级:不影响浏览/下单/卡片等其他功能,仅语音输入不可用;ai-chat-voice-panel.vue 会 catch 到 recorder 错误并 showToast 提示用户。
4.3.5 【待完善】⚠️
- release 签名仍指向 debug 证书:
build-profile.json5中release签名的profile仍是./cert-profile/qq/qqDebug.p7b,与default(debug)共用同一份证书。本地调试无碍,但提交 AppGallery 前必须换成发布证书 + release 类型 provisioning profile,否则 AGC 校验会直接拒绝。
4.3.6 【交付与最终发布】🔍
- 将生成的
.zip文件发送给运维人员 - 运维人员操作流程:
- 解压压缩包
- 打开鸿蒙开发者工具,导入解压后的代码目录
- 验证代码无报错后,执行上传发布操作
4.3.7 【本地设置】
5. 📝 构建与版本信息
5.1 自动注入机制
生产构建时,vue.config.js 中的 VersionInjectPlugin 会自动执行以下操作:
| 操作 | 适用平台 | 说明 |
|---|---|---|
__app_version 全局变量注入 |
所有平台 | 构建产物的 vendor.js(小程序)或 index.html(H5)中自动内联版本数据 |
| 构建版本信息查看 | 所有平台 | 首页右下角连击 5 次弹出模态框显示 commitId 和 buildTime |
| 极限压缩优化 | 小程序(mp-weixin / mp-harmony) | 生产构建自动应用多层次压缩配置,显著减小产物包体积 |
版本数据包含:
- commitId:当前构建的 Git 短提交 ID
- buildTime:构建时间
开发模式:HMR 热更新时由 webpack emit 钩子自动处理,不影响开发体验。
5.2 自动 ZIP 打包
H5 自定义发行构建完成后自动执行:
| 步骤 | 说明 |
|---|---|
| 重命名输出目录 | {域名}-{base路径用.连接}(如 xcx.aigc-quickapp.com-hwappx.common) |
| 创建 ZIP 文件 | 文件名格式:{目录名}-{Git提交ID}-{上海时区时间戳}.zip |
| 清理 | ZIP 创建完成后自动删除源目录 |
💡 示例 ZIP:xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip
微信小程序生产构建时自动执行:
- 拷贝
site.js/project.config.json/project.private.config.json文件到构建目录 - 创建 ZIP 包(文件名包含 Git 提交 ID)
鸿蒙小程序生产构建时自动执行:
- 拷贝必要配置文件到构建目录
- 创建 ZIP 包(文件名包含 Git 提交 ID)
5.3 环境配置
域名配置统一通过 package.json 中各脚本的 env.UNI_APP_DOMAIN 传入:
"h5-xcx.aigc-quickapp.com": {
"title": "...",
"env": {
"UNI_PLATFORM": "h5",
"UNI_APP_DOMAIN": "https://xcx.aigc-quickapp.com/"
},
"define": { "PRODUCTION": true }
}
config.js 和 vue.config.js 均通过 process.env.UNI_APP_DOMAIN 读取,新增/删除脚本无需改动代码。
6. 🔄 版本历史
6.1 v1.5.1
VersionInjectPlugin:webpack emit 钩子自动注入__app_version到所有平台产物- 首页右下角连击 5 次显示构建版本信息(commitId + buildTime)
mp-weixin.patch.js精简为仅文件拷贝和 ZIP 打包- 新增
harmony.patch.js:鸿蒙小程序生产构建补丁脚本(配置拷贝 + ZIP 打包) open-directory.js:构建完成后自动打开输出目录- 极限压缩优化:集成
zion-uniapp-mp-load-package插件 + TerserWebpackPlugin 多层次压缩配置 - 修复
openWxWorkServiceChat方法中 fail 回调语法错误 - ZIP 文件名末尾使用 Git 提交 ID 替换时间戳
- 构建脚本幂等性修复,支持多次运行不产生重复注入
6.2 v1.3
- 修复微信小程序构建脚本,支持复制
project.config.json和project.private.config.json文件 - 增强构建脚本功能,添加命令行参数支持
- 在
package.json中添加相关 npm 脚本命令 - 优化脚本执行逻辑,提高可靠性和灵活性
