@infly/libs 2.0.36 → 2.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +29 -22
- package/build/build-dist/index.js +211 -46
- package/build/webpack5/webpack.base.js +10 -1
- package/build/webpack5/webpack.base.test.js +59 -0
- package/module/Permission.js +43 -36
- package/module/REST.js +109 -46
- package/module/Uts.js +90 -52
- package/module/cjs/deep-merge.cjs +38 -0
- package/module/cjs/page-config.cjs +119 -0
- package/module/cjs/request-url-rules.cjs +55 -0
- package/package.json +6 -7
- package/script/build/command.js +48 -0
- package/script/build/env.js +28 -0
- package/script/build/git.js +252 -0
- package/script/build/preview.js +75 -0
- package/script/build/webhook.js +118 -0
- package/script/git-automation/check-packages.js +11 -8
- package/script/git-automation/git-utils.js +67 -0
- package/script/git-automation/index.js +229 -104
- package/script/pts/cloud-scenes.mjs +65 -0
- package/script/pts/cloud.js +151 -0
- package/script/pts/generate-cloud-params.mjs +210 -0
- package/script/pts/generate-cloud-params.test.mjs +67 -0
- package/script/webhook/webhook.js +72 -2
- package/store/modules/user.js +63 -46
- package/tools/auto-export.js +56 -0
- package/tools/file-export.js +16 -12
- package/tools/project-preview.js +110 -97
- package/types/unused.index.d.ts +0 -71
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export var DEFAULT_BASE_URL = "https://api.sutpay.com";
|
|
2
|
+
|
|
3
|
+
export function createScenes(baseUrl) {
|
|
4
|
+
var resolvedBaseUrl = baseUrl || DEFAULT_BASE_URL;
|
|
5
|
+
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
name: "coupon_alipay",
|
|
9
|
+
generateUrl: resolvedBaseUrl + "/cpay/generate_api_coupon_request_data/",
|
|
10
|
+
issueUrl: resolvedBaseUrl + "/voucher/coupons/issue/test",
|
|
11
|
+
body: {
|
|
12
|
+
activity_id: "ACT735CCV04067449",
|
|
13
|
+
mobile: "17875733848",
|
|
14
|
+
notify_url: resolvedBaseUrl + "/cpay/test_callback_api_issue_coupons/",
|
|
15
|
+
user: "13688851059",
|
|
16
|
+
login_id: "17875733848"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "coupon_wx",
|
|
21
|
+
generateUrl: resolvedBaseUrl + "/cpay/generate_api_coupon_request_data/",
|
|
22
|
+
issueUrl: resolvedBaseUrl + "/voucher/coupons/issue/test",
|
|
23
|
+
body: {
|
|
24
|
+
activity_id: "21978842",
|
|
25
|
+
mobile: "17875733848",
|
|
26
|
+
notify_url: resolvedBaseUrl + "/cpay/test_callback_api_issue_coupons/",
|
|
27
|
+
user: "13688851059",
|
|
28
|
+
appid: "wxf68630951d36fd07",
|
|
29
|
+
openid: "oxSpZ5WBf_Qx5KN6AYXBgzwVf8mg"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "cash_alipay",
|
|
34
|
+
generateUrl: resolvedBaseUrl + "/cpay/generate_api_cash_request_data/",
|
|
35
|
+
issueUrl: resolvedBaseUrl + "/cpay/funds/transfer/test",
|
|
36
|
+
body: {
|
|
37
|
+
user: "13688851059",
|
|
38
|
+
mobile: "17875733848",
|
|
39
|
+
activity_id: "SUT00002382",
|
|
40
|
+
pay_type: "alipay",
|
|
41
|
+
amount: "1",
|
|
42
|
+
desc: "企业付款-开发调试支付宝",
|
|
43
|
+
notify_url: resolvedBaseUrl + "/cpay/test_callback_api_transfers/",
|
|
44
|
+
login_id: "1108@qq.com",
|
|
45
|
+
realname: "曾某某"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "cash_wx",
|
|
50
|
+
generateUrl: resolvedBaseUrl + "/cpay/generate_api_cash_request_data/",
|
|
51
|
+
issueUrl: resolvedBaseUrl + "/cpay/funds/transfer/test",
|
|
52
|
+
body: {
|
|
53
|
+
user: "13688851059",
|
|
54
|
+
mobile: "17875733848",
|
|
55
|
+
activity_id: "SUT00002382",
|
|
56
|
+
notify_url: resolvedBaseUrl + "/cpay/test_callback_api_transfers/",
|
|
57
|
+
appid: "wxf68630951d36fd07",
|
|
58
|
+
openid: "oxSpZ5WBf_Qx5KN6AYXBgzwVf8mg",
|
|
59
|
+
amount: "1",
|
|
60
|
+
pay_type: "wx",
|
|
61
|
+
desc: "企业付款-开发调试微信"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import http from "pts/http";
|
|
2
|
+
import { check } from "pts";
|
|
3
|
+
|
|
4
|
+
var PAYLOAD_FILE = "cloud-payloads.generated.json";
|
|
5
|
+
var CLOUD_PAYLOADS = JSON.parse(readTextFile(PAYLOAD_FILE));
|
|
6
|
+
|
|
7
|
+
var payloadIndex = 0;
|
|
8
|
+
|
|
9
|
+
function readTextFile(fileName) {
|
|
10
|
+
var content = open(fileName);
|
|
11
|
+
|
|
12
|
+
if (typeof content === "string") {
|
|
13
|
+
return content;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
var bytes = new Uint8Array(content);
|
|
17
|
+
var chunks = [];
|
|
18
|
+
var chunkSize = 8192;
|
|
19
|
+
|
|
20
|
+
for (var i = 0; i < bytes.length; i += chunkSize) {
|
|
21
|
+
var chunk = bytes.subarray(i, i + chunkSize);
|
|
22
|
+
chunks.push(String.fromCharCode.apply(null, chunk));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return chunks.join("");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getHttpStatus(resp) {
|
|
29
|
+
if (resp.statusCode !== undefined && resp.statusCode !== null) {
|
|
30
|
+
return Number(resp.statusCode);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return Number(resp.status);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isHttp200(resp) {
|
|
37
|
+
return getHttpStatus(resp) === 200;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function safeJson(resp) {
|
|
41
|
+
try {
|
|
42
|
+
return resp.json();
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getBusinessCode(json) {
|
|
49
|
+
if (!json) return undefined;
|
|
50
|
+
|
|
51
|
+
if (json.code !== undefined && json.code !== null) {
|
|
52
|
+
return Number(json.code);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (json.meta && json.meta.code !== undefined && json.meta.code !== null) {
|
|
56
|
+
return Number(json.meta.code);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (json.data && json.data.code !== undefined && json.data.code !== null) {
|
|
60
|
+
return Number(json.data.code);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isBusinessSuccess(json) {
|
|
67
|
+
var code = getBusinessCode(json);
|
|
68
|
+
return code === 0 || code === 200;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getBusinessMessage(json) {
|
|
72
|
+
if (!json) return "";
|
|
73
|
+
|
|
74
|
+
if (json.message !== undefined && json.message !== null) {
|
|
75
|
+
return String(json.message);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (json.msg !== undefined && json.msg !== null) {
|
|
79
|
+
return String(json.msg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (json.error !== undefined && json.error !== null) {
|
|
83
|
+
return String(json.error);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (json.meta && json.meta.message !== undefined && json.meta.message !== null) {
|
|
87
|
+
return String(json.meta.message);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return "";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── 使用本地预生成的签名参数调用统一派发接口 ────────────────────────────────
|
|
94
|
+
|
|
95
|
+
function runPayload(payload) {
|
|
96
|
+
payload = payload || { index: 0, sceneName: "", issueUrl: "", signedPayload: {} };
|
|
97
|
+
|
|
98
|
+
var headers = { "Content-Type": "application/json" };
|
|
99
|
+
var sceneName = payload.sceneName || "unknown";
|
|
100
|
+
var signedPayload = payload.signedPayload || {};
|
|
101
|
+
|
|
102
|
+
// Step 2:用签名参数调用统一派发接口
|
|
103
|
+
var t0 = Date.now();
|
|
104
|
+
var issueResp = http.post(payload.issueUrl, signedPayload, { headers: headers });
|
|
105
|
+
var duration = Date.now() - t0;
|
|
106
|
+
var issueJson = safeJson(issueResp);
|
|
107
|
+
|
|
108
|
+
check("[" + sceneName + "] issue: http status 200", function () {
|
|
109
|
+
return isHttp200(issueResp);
|
|
110
|
+
}, issueResp);
|
|
111
|
+
|
|
112
|
+
check("[" + sceneName + "] issue: has result", function () {
|
|
113
|
+
return !!issueJson;
|
|
114
|
+
}, issueResp);
|
|
115
|
+
|
|
116
|
+
check("[" + sceneName + "] issue: biz code 0 or 200", function () {
|
|
117
|
+
return isBusinessSuccess(issueJson);
|
|
118
|
+
}, issueResp);
|
|
119
|
+
|
|
120
|
+
console.log("[" + sceneName + "#" + payload.index + "] issue_resp:", JSON.stringify(issueJson));
|
|
121
|
+
if (!isBusinessSuccess(issueJson)) {
|
|
122
|
+
console.error(
|
|
123
|
+
"[" + sceneName + "#" + payload.index + "] issue_failed:",
|
|
124
|
+
"status=" + getHttpStatus(issueResp),
|
|
125
|
+
"code=" + getBusinessCode(issueJson),
|
|
126
|
+
"message=" + getBusinessMessage(issueJson),
|
|
127
|
+
"out_biz_no=" + (signedPayload.out_biz_no || "")
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
console.log("[" + sceneName + "#" + payload.index + "] issue_duration_ms:", duration);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── 主压测逻辑:每次迭代取一条本地预生成参数并派发 ──────────────────────────
|
|
134
|
+
|
|
135
|
+
export default function () {
|
|
136
|
+
check("[payloads] has local generated data", function () {
|
|
137
|
+
return CLOUD_PAYLOADS.length > 0;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (CLOUD_PAYLOADS.length === 0) {
|
|
141
|
+
console.error("No payloads found. Generate and upload request file: " + PAYLOAD_FILE);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var randomOffset = Math.floor(Math.random() * CLOUD_PAYLOADS.length);
|
|
146
|
+
var payloadArrayIndex = (payloadIndex + randomOffset) % CLOUD_PAYLOADS.length;
|
|
147
|
+
var payload = CLOUD_PAYLOADS[payloadArrayIndex];
|
|
148
|
+
payloadIndex++;
|
|
149
|
+
|
|
150
|
+
runPayload(payload);
|
|
151
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { createScenes, DEFAULT_BASE_URL } from "./cloud-scenes.mjs";
|
|
6
|
+
|
|
7
|
+
var DEFAULT_JSON_OUT = "cloud-payloads.generated.json";
|
|
8
|
+
var DEFAULT_COUNT = 3000;
|
|
9
|
+
var DEFAULT_CONCURRENCY = 10;
|
|
10
|
+
var SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
export function parseArgs(argv) {
|
|
13
|
+
var args = {
|
|
14
|
+
count: DEFAULT_COUNT,
|
|
15
|
+
concurrency: DEFAULT_CONCURRENCY,
|
|
16
|
+
baseUrl: process.env.CLOUD_BASE_URL || DEFAULT_BASE_URL,
|
|
17
|
+
jsonOut: DEFAULT_JSON_OUT,
|
|
18
|
+
scene: ""
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
for (var i = 0; i < argv.length; i++) {
|
|
22
|
+
var key = argv[i];
|
|
23
|
+
var value = argv[i + 1];
|
|
24
|
+
|
|
25
|
+
if (key === "--count") {
|
|
26
|
+
args.count = parsePositiveInt(value, "--count");
|
|
27
|
+
i++;
|
|
28
|
+
} else if (key === "--concurrency") {
|
|
29
|
+
args.concurrency = parsePositiveInt(value, "--concurrency");
|
|
30
|
+
i++;
|
|
31
|
+
} else if (key === "--base-url") {
|
|
32
|
+
args.baseUrl = requireValue(value, "--base-url");
|
|
33
|
+
i++;
|
|
34
|
+
} else if (key === "--json-out") {
|
|
35
|
+
args.jsonOut = requireValue(value, "--json-out");
|
|
36
|
+
i++;
|
|
37
|
+
} else if (key === "--scene") {
|
|
38
|
+
args.scene = requireValue(value, "--scene");
|
|
39
|
+
i++;
|
|
40
|
+
} else if (key === "--help" || key === "-h") {
|
|
41
|
+
args.help = true;
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error("Unknown argument: " + key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return args;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildGenerateJobs(scenes, count) {
|
|
51
|
+
var jobs = [];
|
|
52
|
+
|
|
53
|
+
for (var i = 0; i < count; i++) {
|
|
54
|
+
jobs.push({
|
|
55
|
+
index: i,
|
|
56
|
+
scene: scenes[i % scenes.length]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return jobs;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function createPayloadJsonSource(payloads) {
|
|
64
|
+
return JSON.stringify(payloads, null, 2) + "\n";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function main() {
|
|
68
|
+
var args = parseArgs(process.argv.slice(2));
|
|
69
|
+
|
|
70
|
+
if (args.help) {
|
|
71
|
+
printHelp();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
var scenes = createScenes(args.baseUrl);
|
|
76
|
+
if (args.scene) {
|
|
77
|
+
scenes = scenes.filter(function (scene) {
|
|
78
|
+
return scene.name === args.scene;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (scenes.length === 0) {
|
|
83
|
+
throw new Error("No scene matched: " + args.scene);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var jobs = buildGenerateJobs(scenes, args.count);
|
|
87
|
+
var payloads = await runPool(jobs, args.concurrency, generatePayload);
|
|
88
|
+
var jsonOutPath = resolveOutputPath(args.jsonOut);
|
|
89
|
+
|
|
90
|
+
await mkdir(path.dirname(jsonOutPath), { recursive: true });
|
|
91
|
+
await writeFile(jsonOutPath, createPayloadJsonSource(payloads), "utf8");
|
|
92
|
+
|
|
93
|
+
console.log("Generated request-file JSON -> " + jsonOutPath);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function generatePayload(job) {
|
|
97
|
+
var scene = job.scene;
|
|
98
|
+
var resp = await fetch(scene.generateUrl, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
body: JSON.stringify(scene.body)
|
|
102
|
+
});
|
|
103
|
+
var text = await resp.text();
|
|
104
|
+
var json = parseJson(text);
|
|
105
|
+
|
|
106
|
+
if (!resp.ok || !json || !json.data || !isBusinessSuccess(json)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
"[" + scene.name + "#" + job.index + "] generate failed: http_status=" +
|
|
109
|
+
resp.status +
|
|
110
|
+
", body=" +
|
|
111
|
+
text
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
index: job.index,
|
|
117
|
+
sceneName: scene.name,
|
|
118
|
+
issueUrl: scene.issueUrl,
|
|
119
|
+
signedPayload: json.data
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function runPool(items, concurrency, worker) {
|
|
124
|
+
var results = new Array(items.length);
|
|
125
|
+
var nextIndex = 0;
|
|
126
|
+
var workerCount = Math.min(concurrency, items.length);
|
|
127
|
+
var runners = [];
|
|
128
|
+
|
|
129
|
+
for (var i = 0; i < workerCount; i++) {
|
|
130
|
+
runners.push(
|
|
131
|
+
(async function () {
|
|
132
|
+
while (nextIndex < items.length) {
|
|
133
|
+
var currentIndex = nextIndex;
|
|
134
|
+
nextIndex++;
|
|
135
|
+
results[currentIndex] = await worker(items[currentIndex]);
|
|
136
|
+
|
|
137
|
+
if ((currentIndex + 1) % 100 === 0 || currentIndex + 1 === items.length) {
|
|
138
|
+
console.log("Generated " + (currentIndex + 1) + "/" + items.length);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
})()
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await Promise.all(runners);
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseJson(text) {
|
|
150
|
+
try {
|
|
151
|
+
return JSON.parse(text);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function getBusinessCode(json) {
|
|
158
|
+
if (!json) return undefined;
|
|
159
|
+
if (json.code !== undefined && json.code !== null) return Number(json.code);
|
|
160
|
+
if (json.meta && json.meta.code !== undefined && json.meta.code !== null) return Number(json.meta.code);
|
|
161
|
+
if (json.data && json.data.code !== undefined && json.data.code !== null) return Number(json.data.code);
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isBusinessSuccess(json) {
|
|
166
|
+
var code = getBusinessCode(json);
|
|
167
|
+
return code === 0 || code === 200;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parsePositiveInt(value, flag) {
|
|
171
|
+
var parsed = Number(requireValue(value, flag));
|
|
172
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
173
|
+
throw new Error(flag + " must be a positive integer");
|
|
174
|
+
}
|
|
175
|
+
return parsed;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function requireValue(value, flag) {
|
|
179
|
+
if (!value || value.indexOf("--") === 0) {
|
|
180
|
+
throw new Error(flag + " requires a value");
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function resolveOutputPath(value) {
|
|
186
|
+
if (path.isAbsolute(value)) {
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
return path.resolve(SCRIPT_DIR, value);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function printHelp() {
|
|
193
|
+
console.log([
|
|
194
|
+
"Usage: node packages/infly-libs/script/pts/generate-cloud-params.mjs [options]",
|
|
195
|
+
"",
|
|
196
|
+
"Options:",
|
|
197
|
+
" --count <n> Number of signed payloads to generate. Default: 3000",
|
|
198
|
+
" --concurrency <n> Concurrent generate API requests. Default: 10",
|
|
199
|
+
" --base-url <url> API base URL. Default: CLOUD_BASE_URL or " + DEFAULT_BASE_URL,
|
|
200
|
+
" --json-out <file> Output PTS request-file JSON in this folder. Default: " + DEFAULT_JSON_OUT,
|
|
201
|
+
" --scene <name> Generate only one scene, e.g. coupon_alipay"
|
|
202
|
+
].join("\n"));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
206
|
+
main().catch(function (error) {
|
|
207
|
+
console.error(error.message || error);
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildGenerateJobs,
|
|
6
|
+
createPayloadJsonSource,
|
|
7
|
+
parseArgs
|
|
8
|
+
} from "./generate-cloud-params.mjs";
|
|
9
|
+
|
|
10
|
+
const scenes = [
|
|
11
|
+
{ name: "coupon_alipay", generateUrl: "https://example.test/gen1", issueUrl: "https://example.test/issue1", body: { a: 1 } },
|
|
12
|
+
{ name: "coupon_wx", generateUrl: "https://example.test/gen2", issueUrl: "https://example.test/issue2", body: { b: 2 } }
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
test("buildGenerateJobs creates the requested count and cycles scenes", () => {
|
|
16
|
+
const jobs = buildGenerateJobs(scenes, 5);
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(jobs.map((job) => job.scene.name), [
|
|
19
|
+
"coupon_alipay",
|
|
20
|
+
"coupon_wx",
|
|
21
|
+
"coupon_alipay",
|
|
22
|
+
"coupon_wx",
|
|
23
|
+
"coupon_alipay"
|
|
24
|
+
]);
|
|
25
|
+
assert.deepEqual(jobs.map((job) => job.index), [0, 1, 2, 3, 4]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("parseArgs keeps defaults and accepts common overrides", () => {
|
|
29
|
+
const args = parseArgs([
|
|
30
|
+
"--count",
|
|
31
|
+
"3000",
|
|
32
|
+
"--concurrency",
|
|
33
|
+
"20",
|
|
34
|
+
"--base-url",
|
|
35
|
+
"https://local.test",
|
|
36
|
+
"--json-out",
|
|
37
|
+
"tmp/generated.json",
|
|
38
|
+
"--scene",
|
|
39
|
+
"coupon_wx"
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
assert.equal(args.count, 3000);
|
|
43
|
+
assert.equal(args.concurrency, 20);
|
|
44
|
+
assert.equal(args.baseUrl, "https://local.test");
|
|
45
|
+
assert.equal(args.jsonOut, "tmp/generated.json");
|
|
46
|
+
assert.equal(args.scene, "coupon_wx");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("createPayloadJsonSource writes request-file JSON data", () => {
|
|
50
|
+
const source = createPayloadJsonSource([
|
|
51
|
+
{
|
|
52
|
+
index: 0,
|
|
53
|
+
sceneName: "coupon_alipay",
|
|
54
|
+
issueUrl: "https://example.test/issue",
|
|
55
|
+
signedPayload: { sign: "abc" }
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
assert.deepEqual(JSON.parse(source), [
|
|
60
|
+
{
|
|
61
|
+
index: 0,
|
|
62
|
+
sceneName: "coupon_alipay",
|
|
63
|
+
issueUrl: "https://example.test/issue",
|
|
64
|
+
signedPayload: { sign: "abc" }
|
|
65
|
+
}
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
function shouldSkipWebhook() {
|
|
2
|
+
const args = process.argv || [];
|
|
3
|
+
const hasNoWebhookArg = args.some((arg = "") =>
|
|
4
|
+
["--nowebhook", "--noWebhook", "--no-webhook"].includes(arg)
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
const env = process.env || {};
|
|
8
|
+
const envValues = [
|
|
9
|
+
env.npm_config_nowebhook,
|
|
10
|
+
env.npm_config_noWebhook,
|
|
11
|
+
env.NO_WEBHOOK,
|
|
12
|
+
env.DISABLE_WEBHOOK
|
|
13
|
+
];
|
|
14
|
+
const hasNoWebhookEnv = envValues.some((value) => {
|
|
15
|
+
const normalized = String(value || "").toLowerCase();
|
|
16
|
+
return ["1", "true", "yes", "on"].includes(normalized);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return hasNoWebhookArg || hasNoWebhookEnv;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getWebhookMessage() {
|
|
23
|
+
const args = process.argv || [];
|
|
24
|
+
const env = process.env || {};
|
|
25
|
+
const messageEnv =
|
|
26
|
+
env.npm_config_m ||
|
|
27
|
+
env.npm_config_message ||
|
|
28
|
+
env.WEBHOOK_MESSAGE ||
|
|
29
|
+
env.BUILD_MESSAGE ||
|
|
30
|
+
"";
|
|
31
|
+
|
|
32
|
+
if (messageEnv) {
|
|
33
|
+
return String(messageEnv).trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
37
|
+
const arg = args[index] || "";
|
|
38
|
+
|
|
39
|
+
if (arg === "-m" || arg === "--message") {
|
|
40
|
+
return String(args[index + 1] || "").trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (arg.startsWith("-m=")) {
|
|
44
|
+
return arg.slice(3).trim();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (arg.startsWith("--message=")) {
|
|
48
|
+
return arg.slice(10).trim();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
|
|
1
55
|
function postVersionFileAndMsg(publishText, extraParams = {}) {
|
|
2
56
|
const {
|
|
3
57
|
projectName,
|
|
@@ -8,9 +62,11 @@ function postVersionFileAndMsg(publishText, extraParams = {}) {
|
|
|
8
62
|
webhookExtraText = "",
|
|
9
63
|
gitAutoPushRepos,
|
|
10
64
|
gitAutoPushReposBranchMap,
|
|
11
|
-
repos
|
|
65
|
+
repos,
|
|
66
|
+
deployUrl = "https://cicd.sutpay.com/"
|
|
12
67
|
} = extraParams || {};
|
|
13
68
|
const { branch, commitMsg, author } = gitInfo || {};
|
|
69
|
+
const webhookMessage = getWebhookMessage();
|
|
14
70
|
const GITPATH = `https://gitee.com/gdinfly_1/${projectName}/blob/${branch}/${newFileName}`;
|
|
15
71
|
const UPLOADURL = `https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=08ea37af-c6f5-47dc-8fab-0833707c70b8&type=file`;
|
|
16
72
|
/* const data = {
|
|
@@ -26,10 +82,18 @@ function postVersionFileAndMsg(publishText, extraParams = {}) {
|
|
|
26
82
|
} */
|
|
27
83
|
let content = publishText;
|
|
28
84
|
if (gitAutoPushReposBranchMap) {
|
|
29
|
-
content = `${publishText}\n构建文件:已推送对应仓库【${repos}】- 分支【${gitAutoPushReposBranchMap[branch]}】\n构建人:${author}`;
|
|
85
|
+
content = `${publishText}\n构建文件:已推送对应仓库【${repos}】- 分支【${gitAutoPushReposBranchMap[branch]}】\n构建人:${author}\n部署系统:${deployUrl}`;
|
|
30
86
|
} else {
|
|
31
87
|
content = `${publishText}\n构建文件下载地址:${GITPATH}\n${webhookExtraText}\n`;
|
|
32
88
|
}
|
|
89
|
+
|
|
90
|
+
if (webhookMessage) {
|
|
91
|
+
content = `${content}\n${webhookMessage}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 防御性过滤:防止运行时数据中的 %s 等格式化占位符泄露到推送内容
|
|
95
|
+
content = (content || "").replace(/%s/g, "");
|
|
96
|
+
|
|
33
97
|
const data = {
|
|
34
98
|
msgtype: "text",
|
|
35
99
|
text: {
|
|
@@ -37,6 +101,12 @@ function postVersionFileAndMsg(publishText, extraParams = {}) {
|
|
|
37
101
|
mentioned_mobile_list: Array.isArray(webhookAtUser) ? webhookAtUser : webhookAtUser[branch] // 支持按分支配置不同的提醒人
|
|
38
102
|
}
|
|
39
103
|
};
|
|
104
|
+
|
|
105
|
+
if (shouldSkipWebhook()) {
|
|
106
|
+
console.log(global.logColor?.info || "%s", "🔕 已跳过企业微信机器人推送(nowebhook)");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
40
110
|
try {
|
|
41
111
|
const axios = require("axios");
|
|
42
112
|
|