Files
lucky_shop/readme.md
ZF sun 373db1fb73 docs:修正鸿蒙产物构成描述(1 HAP + 5 HSP)
原描述「共 6 个 HAP」不准确。module.json5 中 type: "entry" 编成 HAP,
type: "shared" 编成 HSP(共享包, 动态下发), 本工程实际是 1 个 HAP + 5 个 HSP。

同步修正:
- 两步流程描述改为「逐个 module 编译出包, 再打成 APP」, 产物路径扩展名
  改为 {hap|hsp}
- 包列表新增扩展名列, 标明哪些是 .hap、哪些是 .hsp
- 单模块调试说明改为「只产出分包、不打 APP」

另补充实测体积: 主包 entry-default.hap 约 2.37 MiB(超官方单包 2MB 限制),
总包约 6.39 MiB(在 10MB 以内)。
2026-10-17 13:55:54 +08:00

314 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 小程序及快应用前端源码
## 1. 📋 项目说明
该项目基于 **uni-app** 开发构建,请使用 [HBuilderX](https://www.dcloud.io/hbuilderx.html) 进行代码开发及构建发布。
## 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 基本操作步骤(通用版/定制化版)
##### ##### ##### ##### 【前置准备】
1. 在项目根目录打开终端安装依赖:
```bash
npm install
```
*已有依赖可跳过此步骤*
##### ##### ##### ##### 【发布构建】
1. 使用 HBuilderX 打开项目(不用改manifest.json的web,只有H5要改 )
2. 选择菜单栏「发行」 → 「小程序-微信」→ 「发行」,等待构建完成
⚠️ **注意**:底部控制台弹出"请在微信小程序开发者工具中点击上传"后再执行下一步
3. 构建完成后,**自动执行**补丁脚本(拷贝 site.js / config 文件 + 生成 ZIP 包),无需手动操作
4. 找到项目根目录 `/unpackage/dist/build` 下生成的 mp-weixin 压缩包
💡 **示例**:`mp-weixin-2026-05-11-3bff91e.zip`
*压缩包文件名末尾为 Git 提交 ID*
*同时该目录下会生成未压缩的 `mp-weixin` 目录*
##### ##### ##### ##### 【验证与重命名】
1. 打开未压缩的 `mp-weixin` 目录,找到 `site.js` 文件,将文件内的 `uniacid` 值改为当前客户编号(如:2812)并保存
2. 打开微信开发者工具,导入「mp-weixin」目录,点击「编译」,在开发者工具控制台验证有无报错,以及能否返回对应客户的业务数据
3. 确保无误后将生成的 mp-weixin 压缩包重命名
- **定制化版**格式:`定制化-客户编号-域名-mp-weixin-当前日期-Git提交ID.zip`
- **通用版**无需重命名
💡 **示例**:`custom-2812-xcx.aigc-quickapp.com-mp-weixin-2026-05-11-3bff91e.zip`
🚫 **禁止**:压缩包命名禁止包含 `/ \ : * ? " < > |` 等特殊字符
##### ##### ##### ##### 【交付与最终发布】🔍
1. 将重命名后的文件发送给技术人员
*📌通用版直接将生成的 mp-weixin 压缩包发送给技术人员*
2. **技术人员操作流程**:
- 解压压缩包
- 确认 `site.js` 中的 `uniacid` 为客户编号
- 用微信开发者工具导入解压后的代码目录
- 编译验证无误后,上传发布
### 4.2 快应用发布
#### 4.2.1 基本操作(通用版/定制化版)
##### 4.2.1.1 【发布构建】
1. 使用 HBuilderX 打开项目
2. 打开项目根目录的 `manifest.json` 文件,切换至可视化配置界面:
1. 依次点击「Web 配置」→「运行的基础配置」
2. 根据情况查看是否修改路径中/hwappx/????/的客户编号
📌 **通用版**保留原有 `/hwappx/common/`
📌 **测试版**保留原有 `/hwappx/test/`
💡 **个性化定制示例**:`/hwappx/2811/`,其中 2811 为定制化版客户编号
3. 选择菜单栏「发行」 → 「自定义发行」 → 选择对应的站点脚本
💡 例如:网址是 `5g.aigc-quickapp.com` 选 `H5-5g.aigc-quickapp.com`
4. 修改以下配置:
1. **网站标题**:快应用
2. **网站域名**:当前客户域名
3. 确认后点击「发行」等待构建完成
⚠️ **注意**:构建完成后自动输出到 `unpackage/dist/build/h5/`,格式为 `{域名}-{base路径}.zip`
💡 **示例**:`xcx.aigc-quickapp.com-hwappx.common-3bff91e-20260511_1058.zip`
##### 4.2.1.2 【交付与最终发布】🔍
1. 将生成的 `.zip` 文件发送给运维人员
2. **运维人员操作流程**:
1. 解压压缩包
2. 打开快应用开发者工具,导入解压后的代码目录
3. 验证代码无报错后,执行上传发布操作
##### 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 【发布构建】
1. 使用 HBuilderX 打开项目
2. 选择菜单栏「发行」 → 「HarmonyOS」→ 「发行」,等待构建完成
3. 构建完成后,**自动执行** `harmony.patch.js` 补丁脚本(配置拷贝 + ZIP 打包)
4. 找到项目根目录 `/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 【常见错误】
**❌ `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` 中新增对应的理由文案。
```json5
// entry/src/main/module.json5
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:permission_microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
```
```json
// 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](stub-app-projects/mp-harmony/entry/src/main/ets/entryability/EntryAbility.ets) 的 `onCreate` 中接入:
```ts
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](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis-ability-kit/js-apis-permissions.md)),不是枚举,**只能用在类型位置**,当值引用会报:
> ```
> 'Permissions' only refers to a type, but is being used as a value here.
> ```
> 所以正确写法是标注 + 字面量,且权限名必须与 `module.json5` 中声明的一致:
> ```ts
> 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](pages_tool/ai-chat/components/ai-chat-voice-panel.vue) 会 catch 到 recorder 错误并 `showToast` 提示用户。
#### 4.3.4 【待完善】⚠️
- **release 签名仍指向 debug 证书**:`build-profile.json5` 中 `release` 签名的 `profile` 仍是 `./cert-profile/qq/qqDebug.p7b`,与 `default`(debug)共用同一份证书。本地调试无碍,但提交 AppGallery 前必须换成**发布证书 + release 类型 provisioning profile**,否则 AGC 校验会直接拒绝。
#### 4.3.5 【交付与最终发布】🔍
1. 将生成的 `.zip` 文件发送给运维人员
2. **运维人员操作流程**:
- 解压压缩包
- 打开鸿蒙开发者工具,导入解压后的代码目录
- 验证代码无报错后,执行上传发布操作
#### 4.3.6 【本地设置】
![](readme_files/1.jpg)
---
## 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` 传入:
```json
"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 脚本命令
- 优化脚本执行逻辑,提高可靠性和灵活性