Jacob
2026-10-12 61274af2f456188fa611dd871bc2bcc1a3a85db0
环境切换方案
7个文件已添加
5个文件已修改
416 ■■■■■ 已修改文件
.gitignore 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/app.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/index.js 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/prod.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env/sit.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
project.config.json 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
publish-prod.bat 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
publish-sit.bat 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/publish.js 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/set-env.js 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
....@@ -2,10 +2,13 @@
22 node_modules
33 /dist
44
5
-# local env files
5
+# 本地环境文件
66 .env.local
77 .env.*.local
88
9
+# 微信小程序上传密钥(机密,切勿提交)
10
+keys/
11
+
912 # Log files
1013 npm-debug.log*
1114 yarn-debug.log*
config/app.js
....@@ -7,31 +7,12 @@
77 // +----------------------------------------------------------------------
88 // | Author: CRMEB Team <admin@crmeb.com>
99 // +----------------------------------------------------------------------
10
-//移动端商城API
11
-let domain = 'https://acmall.bluedeer.com.cn/crmeb/front'
1210
13
-module.exports = {
14
- // 请求域名 格式: https://您的域名
15
- HTTP_REQUEST_URL:domain,
16
-
17
- // #ifdef MP
18
- HTTP_REQUEST_URL: domain,
19
- // #endif
20
- HTTP_REQUEST_URL:'https://acmall.bluedeer.com.cn/crmeb/front',
21
- HTTP_ADMIN_URL:'https://acmall.bluedeer.com.cn/crmeb/admin',
22
- APPID:'wx15d029cdf2e92a9f',
23
- GDP_ACCOUNT_ID:'8672f93377575ace',
24
- GDP_DATASOURCE_ID:'b01182b2d4f73e45',
25
- GDP_SERVER_URL:'https://giocollect.aviva-cofco.com.cn',
26
- GDP_DEBUG:true,
27
- THIRD_APPID:'wxa83daa550324ead8',
28
- // #ifdef H5
29
- //H5接口是浏览器地址
30
- // HTTP_REQUEST_URL: window.location.protocol+"//"+window.location.host,
31
- // http://api.java.crmeb.net:20001
32
- HTTP_REQUEST_URL:domain,
33
- // #endif
34
-
11
+// 环境相关参数(HTTP_REQUEST_URL/HTTP_ADMIN_URL/APPID/THIRD_APPID/GDP_* 等)
12
+// 统一维护在 config/env/ 下,本文件只做合并,对外导出名保持不变
13
+const env = require('./env');
14
+
15
+module.exports = Object.assign({}, env, {
3516 HEADER:{
3617 'content-type': 'application/json'
3718 },
....@@ -44,4 +25,4 @@
4425 EXPIRE:0,
4526 //分页最多显示条数
4627 LIMIT: 10
47
-};
28
+});
config/env/index.js
....@@ -0,0 +1,49 @@
1
+// +----------------------------------------------------------------------
2
+// | 环境选择入口:一套代码跑多环境
3
+// | 识别优先级:手动指定 > 小程序 appid 自动识别 > H5 域名识别 > 默认 prod
4
+// | 说明:小程序 appid 由 manifest.json -> mp-weixin.appid 决定,
5
+// | 所以“用哪个 appid 跑”就等于“用哪套接口”,不会出现接口与环境错配
6
+// +----------------------------------------------------------------------
7
+const envs = {
8
+ prod: require('./prod.js'),
9
+ sit: require('./sit.js')
10
+};
11
+
12
+// 手动强制指定环境:'' = 自动识别(推荐);也可填 'prod' 或 'sit'
13
+const OVERRIDE_ENV = '';
14
+
15
+// appid -> 环境名 映射
16
+const APPID_ENV_MAP = {};
17
+Object.keys(envs).forEach(function(key) {
18
+ APPID_ENV_MAP[envs[key].APPID] = key;
19
+});
20
+
21
+function detectEnv() {
22
+ if (OVERRIDE_ENV && envs[OVERRIDE_ENV]) return OVERRIDE_ENV;
23
+
24
+ // #ifdef MP
25
+ try {
26
+ const info = uni.getAccountInfoSync();
27
+ const appId = info && info.miniProgram && info.miniProgram.appId;
28
+ if (appId && APPID_ENV_MAP[appId]) return APPID_ENV_MAP[appId];
29
+ } catch (e) {}
30
+ // #endif
31
+
32
+ // #ifdef H5
33
+ if (typeof location !== 'undefined' && location.hostname) {
34
+ const host = location.hostname;
35
+ if (/sit|tohours|localhost|127\.0\.0\.1/.test(host)) return 'sit';
36
+ }
37
+ // #endif
38
+
39
+ return 'prod';
40
+}
41
+
42
+const ENV_NAME = detectEnv();
43
+const current = envs[ENV_NAME] || envs.prod;
44
+
45
+module.exports = Object.assign({}, current, {
46
+ ENV_NAME: ENV_NAME,
47
+ IS_PROD: ENV_NAME === 'prod',
48
+ IS_SIT: ENV_NAME === 'sit'
49
+});
config/env/prod.js
....@@ -0,0 +1,23 @@
1
+// +----------------------------------------------------------------------
2
+// | 生产环境(PROD)参数
3
+// | 注意:新增环境参数时,请同步维护 prod.js 与 sit.js,保持键名完全一致
4
+// +----------------------------------------------------------------------
5
+module.exports = {
6
+ ENV_NAME: 'prod',
7
+ // 请求域名
8
+ HTTP_REQUEST_URL: 'https://acmall.bluedeer.com.cn/crmeb/front',
9
+ HTTP_ADMIN_URL: 'https://acmall.bluedeer.com.cn/crmeb/admin',
10
+ // 微信小程序 appid(需与 manifest.json -> mp-weixin.appid 保持一致)
11
+ APPID: 'wx15d029cdf2e92a9f',
12
+ // 跳转关联小程序 appid
13
+ THIRD_APPID: 'wxa83daa550324ead8',
14
+ // GrowingIO 埋点
15
+ GDP_ACCOUNT_ID: '8672f93377575ace',
16
+ GDP_DATASOURCE_ID: 'b01182b2d4f73e45',
17
+ GDP_SERVER_URL: 'https://giocollect.aviva-cofco.com.cn',
18
+ GDP_DEBUG: true,
19
+ // 活动页/内嵌 web 资源地址(替代代码里按域名判断环境)
20
+ WEB_ACTIVITY_BASE: 'https://ossacmall.bluedeer.com.cn',
21
+ // DCloud 应用标识(需与 manifest.json -> appid 保持一致)
22
+ UNI_APPID: '__UNI__CE0C605'
23
+};
config/env/sit.js
....@@ -0,0 +1,23 @@
1
+// +----------------------------------------------------------------------
2
+// | 测试环境(SIT)参数
3
+// | 注意:新增环境参数时,请同步维护 prod.js 与 sit.js,保持键名完全一致
4
+// +----------------------------------------------------------------------
5
+module.exports = {
6
+ ENV_NAME: 'sit',
7
+ // 请求域名
8
+ HTTP_REQUEST_URL: 'https://sit.tohours.com/crmeb/front',
9
+ HTTP_ADMIN_URL: 'https://sit.tohours.com/crmeb/admin',
10
+ // 微信小程序 appid(需与 manifest.json -> mp-weixin.appid 保持一致)
11
+ APPID: 'wx7e443c60d08449de',
12
+ // 跳转关联小程序 appid
13
+ THIRD_APPID: 'wx64d2ecbbeb3eae79',
14
+ // GrowingIO 埋点
15
+ GDP_ACCOUNT_ID: '83b2522b4d9c972a',
16
+ GDP_DATASOURCE_ID: 'a1616a9a748d2c7f',
17
+ GDP_SERVER_URL: 'https://collecttest.aviva-cofco.com.cn',
18
+ GDP_DEBUG: true,
19
+ // 活动页/内嵌 web 资源地址(替代代码里按域名判断环境)
20
+ WEB_ACTIVITY_BASE: 'https://sit.tohours.com/crmeb_web',
21
+ // DCloud 应用标识(需与 manifest.json -> appid 保持一致)
22
+ UNI_APPID: '__UNI__3D0C5FE'
23
+};
manifest.json
....@@ -1,6 +1,6 @@
11 {
22 "name" : "CRMEB Java 商城",
3
- "appid" : "__UNI__CE0C605",
3
+ "appid" : "__UNI__3D0C5FE",
44 "description" : "CRMEB Java 商城",
55 "versionName" : "1.0.0",
66 "versionCode" : 1001,
....@@ -123,7 +123,7 @@
123123 "quickapp" : {},
124124 /* 小程序特有相关 */
125125 "mp-weixin" : {
126
- "appid" : "wx15d029cdf2e92a9f",
126
+ "appid" : "wx7e443c60d08449de",
127127 "setting" : {
128128 "urlCheck" : true,
129129 "minified" : true,
package.json
....@@ -1,4 +1,10 @@
11 {
2
+ "scripts": {
3
+ "env:prod": "node scripts/set-env.js prod",
4
+ "env:sit": "node scripts/set-env.js sit",
5
+ "release:sit": "node scripts/publish.js sit",
6
+ "release:prod": "node scripts/publish.js prod"
7
+ },
28 "dependencies": {
39 "bignumber.js": "^9.3.1",
410 "vue-inset-loader": "^1.2.6"
project.config.json
....@@ -1,5 +1,5 @@
11 {
2
- "appid": "wx15d029cdf2e92a9f",
2
+ "appid": "wx7e443c60d08449de",
33 "compileType": "miniprogram",
44 "libVersion": "3.1.4",
55 "packOptions": {
publish-prod.bat
....@@ -0,0 +1,16 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+echo ============================================
5
+echo 发布微信小程序 - 生产环境 PROD
6
+echo 接口: https://acmall.bluedeer.com.cn/crmeb/front
7
+echo ============================================
8
+echo.
9
+node "scripts\publish.js" prod %*
10
+echo.
11
+if errorlevel 1 (
12
+ echo [发布失败] 请查看上方输出
13
+) else (
14
+ echo [发布完成]
15
+)
16
+pause
publish-sit.bat
....@@ -0,0 +1,16 @@
1
+@echo off
2
+chcp 65001 >nul
3
+cd /d "%~dp0"
4
+echo ============================================
5
+echo 发布微信小程序 - 测试环境 SIT
6
+echo 接口: https://sit.tohours.com/crmeb/front
7
+echo ============================================
8
+echo.
9
+node "scripts\publish.js" sit %*
10
+echo.
11
+if errorlevel 1 (
12
+ echo [发布失败] 请查看上方输出
13
+) else (
14
+ echo [发布完成]
15
+)
16
+pause
scripts/publish.js
....@@ -0,0 +1,133 @@
1
+/**
2
+ * HBuilderX CLI 命令行发布:按环境发布微信小程序
3
+ *
4
+ * 用法:
5
+ * node scripts/publish.js sit # 编译 + 上传到 SIT 小程序(微信 CI,无需打开微信开发者工具)
6
+ * node scripts/publish.js prod # 编译 + 上传到 PROD 小程序
7
+ * node scripts/publish.js sit --local # 只本地编译,不上传
8
+ * node scripts/publish.js sit --dry-run # 只打印将要执行的命令
9
+ * node scripts/publish.js sit --version 2.0.0 --robot 3 # 额外参数原样透传给 cli
10
+ *
11
+ * 环境相关的值统一取自 config/env/<env>.js(唯一数据源):
12
+ * - 接口/埋点等运行时参数:包内自动按小程序 appid 识别,无需在此处理
13
+ * - APPID:作为 --appid 传给微信 CI 上传
14
+ *
15
+ * 密钥文件:keys/private.<env>.key
16
+ * 获取方式:微信公众平台 → 开发管理 → 开发设置 → 小程序代码上传 → 生成上传密钥
17
+ * 注意:密钥属于机密,已通过 .gitignore 排除,切勿提交
18
+ */
19
+const { spawnSync } = require('child_process');
20
+const fs = require('fs');
21
+const path = require('path');
22
+
23
+const ROOT = path.resolve(__dirname, '..');
24
+const ENV_DIR = path.join(ROOT, 'config', 'env');
25
+
26
+// HBuilderX cli.exe 路径:优先读环境变量,便于换机器 / 换安装位置
27
+const HX_CLI = process.env.HBUILDERX_CLI || 'C:\\Users\\Administrator\\Desktop\\HBuilderX\\cli.exe';
28
+
29
+const argv = process.argv.slice(2);
30
+const envName = (argv.find(function(a) { return a.charAt(0) !== '-'; }) || '').trim();
31
+const localOnly = argv.indexOf('--local') > -1;
32
+const dryRun = argv.indexOf('--dry-run') > -1;
33
+// 默认不改 manifest.json:上传用 --appid 指定目标小程序,编译产物里的 appid 不参与校验。
34
+// 若遇到上传失败/appid 不一致,加 --sync-manifest 先同步 manifest 再发布。
35
+const syncManifest = argv.indexOf('--sync-manifest') > -1;
36
+const OWN_ARGS = ['--local', '--dry-run', '--sync-manifest'];
37
+// 透传给 cli 的额外参数(保留 "参数 值" 的成对关系,如 --robot 2)
38
+const passthrough = [];
39
+let envNameSkipped = false;
40
+argv.forEach(function(a) {
41
+ if (OWN_ARGS.indexOf(a) > -1) return;
42
+ if (!envNameSkipped && a === envName) {
43
+ envNameSkipped = true;
44
+ return;
45
+ }
46
+ passthrough.push(a);
47
+});
48
+
49
+function fail(msg) {
50
+ console.error('[publish] ' + msg);
51
+ process.exit(1);
52
+}
53
+
54
+function gitShortHash() {
55
+ const res = spawnSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ROOT, encoding: 'utf8' });
56
+ return res.status === 0 ? String(res.stdout).trim() : '';
57
+}
58
+
59
+function readManifestVersion() {
60
+ try {
61
+ const txt = fs.readFileSync(path.join(ROOT, 'manifest.json'), 'utf8');
62
+ const m = txt.match(/"versionName"\s*:\s*"([^"]*)"/);
63
+ return m ? m[1] : '1.0.0';
64
+ } catch (e) {
65
+ return '1.0.0';
66
+ }
67
+}
68
+
69
+function main() {
70
+ const available = fs.existsSync(ENV_DIR)
71
+ ? fs.readdirSync(ENV_DIR).filter(f => f.endsWith('.js') && f !== 'index.js').map(f => f.replace(/\.js$/, ''))
72
+ : [];
73
+
74
+ if (!envName) fail('请指定环境名,可选:' + available.join(' / ') + '\n 例:node scripts/publish.js sit');
75
+ if (available.indexOf(envName) < 0) fail('未找到 config/env/' + envName + '.js,可选:' + available.join(' / '));
76
+
77
+ const env = require(path.join(ENV_DIR, envName + '.js'));
78
+
79
+ // 1) 校验 cli.exe
80
+ if (!fs.existsSync(HX_CLI)) {
81
+ fail('未找到 HBuilderX cli.exe:' + HX_CLI +
82
+ '\n 请确认 HBuilderX 安装路径,或设置环境变量 HBUILDERX_CLI 指向 cli.exe');
83
+ }
84
+
85
+ // 2) 组装 cli 参数
86
+ const cliArgs = ['publish', '--platform', 'mp-weixin', '--project', ROOT];
87
+
88
+ if (!localOnly) {
89
+ const keyFile = path.join(ROOT, 'keys', 'private.' + envName + '.key');
90
+ if (!fs.existsSync(keyFile)) {
91
+ fail('未找到上传密钥文件:' + keyFile +
92
+ '\n 获取方式:微信公众平台 → 开发管理 → 开发设置 → 小程序代码上传 → 生成上传密钥' +
93
+ '\n 若只想本地编译不上传,请加 --local');
94
+ }
95
+ const stamp = new Date().toISOString().slice(0, 16).replace('T', ' ');
96
+ const hash = gitShortHash();
97
+ cliArgs.push('--upload', 'true');
98
+ cliArgs.push('--appid', env.APPID);
99
+ cliArgs.push('--privatekey', keyFile);
100
+ cliArgs.push('--version', readManifestVersion());
101
+ cliArgs.push('--description', '[' + env.ENV_NAME + '] ' + stamp + (hash ? ' ' + hash : ''));
102
+ }
103
+
104
+ console.log('[publish] 环境 : ' + envName + '(' + env.ENV_NAME + ')');
105
+ console.log('[publish] 小程序appid: ' + env.APPID);
106
+ console.log('[publish] 接口地址 : ' + env.HTTP_REQUEST_URL);
107
+ console.log('[publish] 产物目录 : ' + path.join('unpackage', 'dist', 'build', 'mp-weixin'));
108
+ console.log('[publish] 模式 : ' + (localOnly ? '仅本地编译' : '编译 + 上传到微信平台'));
109
+ console.log('[publish] DCloud标识 : ' + env.UNI_APPID + '(manifest.json 里的值,仅影响非小程序端与云服务识别)');
110
+ console.log('[publish] 命令 : "' + HX_CLI + '" ' + cliArgs.concat(passthrough).join(' '));
111
+
112
+ if (dryRun) {
113
+ console.log('[publish] --dry-run,未执行');
114
+ return;
115
+ }
116
+
117
+ // 3) 可选:先把 appid 同步进 manifest.json(走 set-env 的同一套逻辑)
118
+ if (syncManifest) {
119
+ console.log('[publish] --sync-manifest:先同步 manifest.json 的 appid');
120
+ const sync = spawnSync(process.execPath, [path.join(__dirname, 'set-env.js'), envName], { cwd: ROOT, stdio: 'inherit' });
121
+ if (sync.status !== 0) fail('同步 manifest.json 失败');
122
+ }
123
+
124
+ // 4) 执行(HBuilderX 会在其控制台输出编译与上传日志)
125
+ const res = spawnSync(HX_CLI, cliArgs.concat(passthrough), { cwd: ROOT, stdio: 'inherit' });
126
+
127
+ if (res.error) fail('调用 cli.exe 失败:' + res.error.message);
128
+ if (res.status !== 0) fail('cli.exe 退出码 ' + res.status + ',请查看 HBuilderX 控制台输出');
129
+
130
+ console.log('[publish] 完成。若上传成功,可在微信公众平台 → 版本管理 中看到对应版本');
131
+}
132
+
133
+main();
scripts/set-env.js
....@@ -0,0 +1,108 @@
1
+/**
2
+ * 环境切换脚本:把 config/env/<env>.js 里的 appid 同步到编译期配置文件
3
+ *
4
+ * 用法:
5
+ * npm run env:sit 等价于 node scripts/set-env.js sit
6
+ * npm run env:prod 等价于 node scripts/set-env.js prod
7
+ *
8
+ * 说明:
9
+ * appid 是编译期值(manifest.json 为 HBuilderX 固定输入、微信开发者工具只认
10
+ * project.config.json 的 appid),无法像接口地址那样运行时切换,所以这里做
11
+ * “一次命令写回”。改完必须重新编译一次,dist 下的 project.config.json 才会更新。
12
+ *
13
+ * 唯一数据源:config/env/<env>.js 中的 UNI_APPID(DCloud 标识)、APPID(小程序 appid)
14
+ */
15
+const fs = require('fs');
16
+const path = require('path');
17
+
18
+const ROOT = path.resolve(__dirname, '..');
19
+const ENV_DIR = path.join(ROOT, 'config', 'env');
20
+
21
+const files = {
22
+ manifest: path.join(ROOT, 'manifest.json'),
23
+ projectConfig: path.join(ROOT, 'project.config.json')
24
+};
25
+
26
+function fail(msg) {
27
+ console.error('[set-env] ' + msg);
28
+ process.exit(1);
29
+}
30
+
31
+/** 只替换第一处 "appid" : "xxx",保留原有空格/缩进风格 */
32
+function replaceAppId(text, value) {
33
+ let done = false;
34
+ const result = text.replace(/"appid"(\s*:\s*)"[^"]*"/, function(match, sep) {
35
+ done = true;
36
+ return '"appid"' + sep + '"' + value + '"';
37
+ });
38
+ return { text: result, done: done };
39
+}
40
+
41
+/** 替换 manifest.json 顶层 appid(第一处出现即为顶层) */
42
+function patchManifest(text, env) {
43
+ // 1) 顶层 appid(DCloud 应用标识)
44
+ const top = replaceAppId(text, env.UNI_APPID);
45
+ if (!top.done) fail('manifest.json 中未找到顶层 "appid" 字段');
46
+ text = top.text;
47
+
48
+ // 2) mp-weixin 下的 appid(微信小程序 appid),只在该区块内替换第一处
49
+ const idx = text.indexOf('"mp-weixin"');
50
+ if (idx < 0) fail('manifest.json 中未找到 "mp-weixin" 配置块');
51
+ const scoped = replaceAppId(text.slice(idx), env.APPID);
52
+ if (!scoped.done) fail('manifest.json 的 "mp-weixin" 中未找到 "appid" 字段');
53
+ text = text.slice(0, idx) + scoped.text;
54
+
55
+ return text;
56
+}
57
+
58
+/** 替换 project.config.json 的 appid(保持原格式,避免整文件重排) */
59
+function patchProjectConfig(text, env) {
60
+ const res = replaceAppId(text, env.APPID);
61
+ if (!res.done) fail('project.config.json 中未找到 "appid" 字段');
62
+ return res.text;
63
+}
64
+
65
+function writeIfChanged(file, next) {
66
+ const prev = fs.readFileSync(file, 'utf8');
67
+ if (prev === next) {
68
+ console.log(' - ' + path.relative(ROOT, file) + ' 已是目标值,无需改动');
69
+ return false;
70
+ }
71
+ fs.writeFileSync(file, next, 'utf8');
72
+ console.log(' √ ' + path.relative(ROOT, file) + ' 已更新');
73
+ return true;
74
+}
75
+
76
+function main() {
77
+ const envName = (process.argv[2] || '').trim();
78
+ const available = fs.existsSync(ENV_DIR)
79
+ ? fs.readdirSync(ENV_DIR).filter(f => f.endsWith('.js') && f !== 'index.js').map(f => f.replace(/\.js$/, ''))
80
+ : [];
81
+
82
+ if (!envName) {
83
+ fail('请指定环境名,可选:' + available.join(' / ') + '\n 例:npm run env:sit');
84
+ }
85
+ if (available.indexOf(envName) < 0) {
86
+ fail('未找到 config/env/' + envName + '.js,可选:' + available.join(' / '));
87
+ }
88
+
89
+ const env = require(path.join(ENV_DIR, envName + '.js'));
90
+ if (!env.UNI_APPID || !env.APPID) {
91
+ fail('config/env/' + envName + '.js 缺少 UNI_APPID 或 APPID');
92
+ }
93
+
94
+ console.log('[set-env] 切换到 ' + envName + '(' + env.ENV_NAME + ')');
95
+ console.log(' DCloud appid : ' + env.UNI_APPID);
96
+ console.log(' 小程序 appid : ' + env.APPID);
97
+ console.log(' 接口地址 : ' + env.HTTP_REQUEST_URL);
98
+
99
+ const manifestChanged = writeIfChanged(files.manifest, patchManifest(fs.readFileSync(files.manifest, 'utf8'), env));
100
+ const projectChanged = writeIfChanged(files.projectConfig, patchProjectConfig(fs.readFileSync(files.projectConfig, 'utf8'), env));
101
+ const changed = manifestChanged || projectChanged;
102
+
103
+ console.log(changed
104
+ ? '[set-env] 完成,请在 HBuilderX 中重新编译一次,并在微信开发者工具中重新打开项目'
105
+ : '[set-env] 无需改动,当前已是 ' + envName + ' 环境配置');
106
+}
107
+
108
+main();