lightshortcuts 1.0.8 → 1.1.0

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.
@@ -0,0 +1,1134 @@
1
+ "use strict";
2
+ const axios = require("axios").default;
3
+ const qs = require("qs");
4
+ const { createReadStream, access, statSync, constants } = require("fs");
5
+ const FormData = require("form-data");
6
+ const { getPkgInfo, getCookieObj, fmtTime } = require("../utils");
7
+
8
+ const log = (message, ...args) => {
9
+ console.log(`[${fmtTime()}] ${message}`, ...args);
10
+ };
11
+
12
+ const error = (message, ...args) => {
13
+ console.error(`[${fmtTime()}] ${message}`, ...args);
14
+ };
15
+
16
+ const warn = (message, ...args) => {
17
+ console.warn(`[${fmtTime()}] ${message}`, ...args);
18
+ };
19
+
20
+ const info = (message, ...args) => {
21
+ console.info(`[${fmtTime()}] ${message}`, ...args);
22
+ };
23
+ const { ApiList } = require("./apiUrl");
24
+
25
+ // 存储APP的离线包
26
+ let allOfflinePkgLists = {};
27
+
28
+ const initAxiosConfig = async function (lightBaseURL, token) {
29
+ axios.defaults.baseURL = lightBaseURL;
30
+ axios.defaults.headers["cookie"] = `token=${token}`;
31
+ axios.defaults.headers["User-Agent"] =
32
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.8.8.8";
33
+ axios.defaults.timeout = 30000;
34
+ };
35
+
36
+ const retryWrapper = async function (fn, options = {}) {
37
+ const { maxRetries = 3, delay = 4000, retryMessage = "请求" } = options;
38
+
39
+ let lastError;
40
+
41
+ for (let i = 0; i < maxRetries; i++) {
42
+ try {
43
+ const result = await fn();
44
+ return result;
45
+ } catch (error) {
46
+ lastError = error;
47
+ log(
48
+ `第 ${i + 1} 次${retryMessage}失败,${i < maxRetries - 1 ? `${delay / 1000}秒后重试...` : "已达到最大重试次数"}`,
49
+ );
50
+
51
+ if (i < maxRetries - 1) {
52
+ await new Promise((resolve) => setTimeout(resolve, delay));
53
+ }
54
+ }
55
+ }
56
+
57
+ throw lastError;
58
+ };
59
+
60
+ // 添加请求拦截器
61
+ axios.interceptors.request.use(
62
+ function (config) {
63
+ // 在发送请求之前做些什么
64
+ // log(config)
65
+ return config;
66
+ },
67
+ function (error) {
68
+ // 对请求错误做些什么
69
+ return Promise.reject(error);
70
+ },
71
+ );
72
+
73
+ // 添加响应拦截器
74
+ axios.interceptors.response.use(
75
+ function (response) {
76
+ // 2xx 范围内的状态码都会触发该函数。
77
+ // log(response.data);
78
+ return response;
79
+ },
80
+ function (error) {
81
+ // 超出 2xx 范围的状态码都会触发该函数。
82
+ return Promise.reject(error);
83
+ },
84
+ );
85
+
86
+ // 通用的axios配置
87
+ const getAxiosConfig = function (config = {}) {
88
+ return {
89
+ timeout: 30000,
90
+ headers: {
91
+ "User-Agent": axios.defaults.headers["User-Agent"],
92
+ Connection: "keep-alive",
93
+ ...config.headers,
94
+ },
95
+ ...config,
96
+ };
97
+ };
98
+
99
+ // 通用的错误处理函数
100
+ const handleAxiosError = function (error, errorMessage = "请求失败") {
101
+ log(
102
+ `${errorMessage}:${error.response?.data?.data?.[0]?.error_info || error.message}`,
103
+ );
104
+ return Promise.reject(error);
105
+ };
106
+
107
+ // 通用的响应处理函数
108
+ const handleAxiosResponse = function (
109
+ response,
110
+ successCallback,
111
+ errorCallback,
112
+ ) {
113
+ const { data } = response;
114
+ if (data.err_no === 0) {
115
+ return successCallback ? successCallback(data) : data;
116
+ } else {
117
+ return errorCallback ? errorCallback(data) : data;
118
+ }
119
+ };
120
+
121
+ //获取图形验证码,返回base64编码和其响应头cookie里面的captcha_id
122
+ const getCaptcha = async function () {
123
+ return retryWrapper(
124
+ async () => {
125
+ try {
126
+ // 发送请求获取验证码图片
127
+ const res = await axios({
128
+ url: ApiList.getCaptcha,
129
+ method: "get",
130
+ responseType: "arraybuffer",
131
+ accept: "image/*",
132
+ ...getAxiosConfig(),
133
+ });
134
+
135
+ // 处理响应数据
136
+ const base64 = Buffer.from(res.data).toString("base64");
137
+ const captchaId = getCookieObj(res.headers["set-cookie"][0]).captcha_id;
138
+
139
+ // 调用ocrCaptcha识别验证码
140
+ const captchaText = await ocrCaptcha(base64);
141
+ log("识别验证码成功:", captchaText);
142
+
143
+ // 返回结果
144
+ return { captchaId, captchaText };
145
+ } catch (error) {
146
+ log("获取验证码失败:", error);
147
+ throw error;
148
+ }
149
+ },
150
+ {
151
+ maxRetries: 3,
152
+ delay: 4000,
153
+ retryMessage: "获取验证码",
154
+ },
155
+ );
156
+ };
157
+ //通过传入的图像验证码图片,经过识别之后返回文字验证码
158
+ const ocrCaptcha = async function (base64) {
159
+ return retryWrapper(
160
+ async () => {
161
+ try {
162
+ // 发送OCR识别请求
163
+ const res = await axios({
164
+ url: `https://vv5s.com:18080/predict/base64`,
165
+ method: "post",
166
+ data: {
167
+ data: base64,
168
+ },
169
+ ...getAxiosConfig({
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ },
173
+ }),
174
+ });
175
+
176
+ // 处理响应数据
177
+ const responseData = res?.data;
178
+ log(`OCR识别响应: ${JSON.stringify(responseData)}`);
179
+ const captcha = responseData?.result;
180
+
181
+ // 返回识别结果
182
+ return captcha;
183
+ } catch (error) {
184
+ log("OCR识别失败:", error);
185
+ throw error;
186
+ }
187
+ },
188
+ {
189
+ maxRetries: 3,
190
+ delay: 1000,
191
+ retryMessage: "OCR识别",
192
+ },
193
+ );
194
+ };
195
+ //表单登录接受传入登录账号密码,返回token
196
+ const submitLogin = async function (loginData = { account: "", password: "" }) {
197
+ return retryWrapper(
198
+ async () => {
199
+ // 校验登录账号密码是否为空
200
+ if (!loginData.account || !loginData.password) {
201
+ throw new Error("登录账号或密码不能为空");
202
+ }
203
+
204
+ // 获取验证码和captcha_id
205
+ const { captchaId, captchaText } = await getCaptcha();
206
+ warn("captchaId:", { captchaId, captchaText });
207
+
208
+ try {
209
+ // 发送登录请求
210
+ const res = await axios({
211
+ url: ApiList.submitLogin,
212
+ method: "post",
213
+ data: qs.stringify({
214
+ account: loginData.account,
215
+ password: loginData.password,
216
+ captcha: captchaText,
217
+ }),
218
+ ...getAxiosConfig({
219
+ headers: {
220
+ "Content-Type":
221
+ "application/x-www-form-urlencoded; charset=UTF-8",
222
+ Cookie: `captcha_id=${captchaId}`,
223
+ Authorization: "Light undefined",
224
+ DNT: "1",
225
+ },
226
+ }),
227
+ });
228
+
229
+ // 处理响应数据
230
+ const { data } = res;
231
+
232
+ if (data.user_token) {
233
+ // 登录成功,返回用户数据
234
+ // log("登录响应:", data.user_token, data.user?.identity_name);
235
+ return data;
236
+ } else {
237
+ // 登录失败,抛出错误
238
+ throw new Error(`登录失败: ${JSON.stringify(data)}`);
239
+ }
240
+ } catch (error) {
241
+ log(`登录失败,原因是:${error}`);
242
+ throw error;
243
+ }
244
+ },
245
+ {
246
+ maxRetries: 6,
247
+ delay: 3000,
248
+ retryMessage: "登录",
249
+ },
250
+ );
251
+ };
252
+
253
+ //校验会话有效性
254
+ const checkSession = async function (token) {
255
+ return retryWrapper(
256
+ async () => {
257
+ try {
258
+ // 发送会话检查请求
259
+ const res = await axios.get(ApiList.checkSession, {
260
+ ...getAxiosConfig(),
261
+ });
262
+
263
+ // 处理响应数据
264
+ let { data } = res;
265
+
266
+ if (data.err_no === 0) {
267
+ log(`欢迎${data?.data?.user?.identity_name},会话有效。`);
268
+ return true;
269
+ } else {
270
+ return false;
271
+ }
272
+ } catch (error) {
273
+ warn(`检查会话报错!${error}`);
274
+ throw error;
275
+ }
276
+ },
277
+ {
278
+ maxRetries: 3,
279
+ delay: 4000,
280
+ retryMessage: "检查会话",
281
+ },
282
+ );
283
+ };
284
+
285
+ // 注销会话
286
+ const logout = async function (token) {
287
+ return retryWrapper(
288
+ async () => {
289
+ try {
290
+ // 发送注销请求
291
+ const res = await axios.get(ApiList.logout, {
292
+ ...getAxiosConfig(),
293
+ });
294
+
295
+ // 处理响应数据
296
+ let { data } = res;
297
+
298
+ if (data.err_no === 0) {
299
+ return true;
300
+ } else {
301
+ return false;
302
+ }
303
+ } catch (error) {
304
+ warn(
305
+ `注销会话报错,原因是:${error.response?.data?.data?.[0]?.error_info || error.message}`,
306
+ );
307
+ throw error;
308
+ }
309
+ },
310
+ {
311
+ maxRetries: 3,
312
+ delay: 4000,
313
+ retryMessage: "注销会话",
314
+ },
315
+ );
316
+ };
317
+
318
+ // 上传文件,__filePath:绝对路径
319
+ const fileUpload = async function (__filePath, bizType = "h5_offline_src") {
320
+ return retryWrapper(
321
+ async () => {
322
+ try {
323
+ // 检查文件是否存在
324
+ await new Promise((resolve, reject) => {
325
+ access(__filePath, constants.F_OK, (err) => {
326
+ if (err) {
327
+ log(`文件${__filePath}不存在`);
328
+ reject(`文件${__filePath}不存在`);
329
+ } else {
330
+ resolve();
331
+ }
332
+ });
333
+ });
334
+
335
+ // 创建表单数据
336
+ const formData = new FormData();
337
+ formData.append("file", createReadStream(__filePath));
338
+
339
+ // 发送上传请求
340
+ const res = await axios.post(
341
+ `${ApiList.fileUpload}?bizType=${bizType}`,
342
+ formData,
343
+ {
344
+ ...getAxiosConfig({
345
+ headers: {
346
+ ...formData.getHeaders(),
347
+ },
348
+ }),
349
+ },
350
+ );
351
+
352
+ log("上传文件:", res.data);
353
+
354
+ // 处理响应数据
355
+ let { err_no, data, err_msg } = res.data;
356
+
357
+ if (err_no == 0) {
358
+ return data;
359
+ } else {
360
+ throw new Error(`上传失败: ${err_msg}`);
361
+ }
362
+ } catch (error) {
363
+ log(`上传文件失败: ${error}`);
364
+ throw error;
365
+ }
366
+ },
367
+ {
368
+ maxRetries: 3,
369
+ delay: 4000,
370
+ retryMessage: "上传文件",
371
+ },
372
+ );
373
+ };
374
+
375
+ // 查询APP下的离线包列表 √
376
+ const queryOfflinePkgList = async function (ids = ["3542", "3543"]) {
377
+ // 获取单个APP的离线包列表
378
+ const getPkgListOf = async function (appid) {
379
+ return retryWrapper(
380
+ async () => {
381
+ try {
382
+ // 发送请求获取离线包列表
383
+ const res = await axios.get(
384
+ `${ApiList.queryOfflinePkgList}?app_id=${appid}`,
385
+ {
386
+ ...getAxiosConfig(),
387
+ },
388
+ );
389
+
390
+ // 处理响应数据
391
+ let { err_no, err_info, data } = res.data;
392
+
393
+ if (err_no == 0) {
394
+ allOfflinePkgLists[appid] = data;
395
+ } else {
396
+ log(`${appid}报错:${err_info}`);
397
+ }
398
+
399
+ return true;
400
+ } catch (error) {
401
+ log(`${appid}采集报错:${error}`);
402
+ throw error;
403
+ }
404
+ },
405
+ {
406
+ maxRetries: 6,
407
+ delay: 4000,
408
+ retryMessage: `获取${appid}离线包列表`,
409
+ },
410
+ );
411
+ };
412
+
413
+ // 批量查询
414
+ try {
415
+ await Promise.all(ids.map((appid) => getPkgListOf(appid)));
416
+
417
+ // 返回所有离线包列表
418
+ return allOfflinePkgLists;
419
+ } catch (error) {
420
+ log(`批量查询离线包列表失败:${error}`);
421
+ throw error;
422
+ }
423
+ };
424
+
425
+ // 同步APP的离线包列表,如果没有创建,则进行创建,以拉平两端离线包
426
+ async function syncPkgList(ids = [], h5app_id) {
427
+ try {
428
+ // 获取所有APP的离线包列表
429
+ await queryOfflinePkgList(ids);
430
+
431
+ // 分析需要创建离线包的APP列表
432
+ let keys = Object.keys(allOfflinePkgLists);
433
+ let newAppPkgList = keys.reduce(
434
+ (acc, cur) => {
435
+ let a = allOfflinePkgLists[cur];
436
+ let b = a.filter((val) => val.h5app_id == h5app_id);
437
+
438
+ if (b.length > 0) {
439
+ acc.name = b[0].name;
440
+ } else {
441
+ acc.none.push(cur);
442
+ }
443
+
444
+ return acc;
445
+ },
446
+ { none: [], name: "" },
447
+ );
448
+
449
+ // 如果有需要创建离线包的APP,并且获取到了包名,则批量创建
450
+ if (newAppPkgList["none"].length > 0 && newAppPkgList["name"] != "") {
451
+ const results = await Promise.all(
452
+ newAppPkgList["none"].map((appId) => {
453
+ return addOfflineH5pkg(appId, h5app_id, newAppPkgList["name"]);
454
+ }),
455
+ );
456
+
457
+ log("同步离线包信息:", results);
458
+ }
459
+
460
+ return true;
461
+ } catch (error) {
462
+ log(`同步离线包列表失败:${error}`);
463
+ throw error;
464
+ }
465
+ }
466
+
467
+ //查询版本信息√
468
+ const queryOfflineVersionlist = async function (app_id, h5app_id) {
469
+ try {
470
+ // 获取包信息
471
+ let t = await getPkgInfo(allOfflinePkgLists, app_id, h5app_id);
472
+ let { id } = t;
473
+
474
+ // 发送请求获取版本列表
475
+ let qovRes = await retryWrapper(
476
+ async () => {
477
+ return await axios.get(
478
+ `${ApiList.queryOfflineVersionlist}?app_id=${app_id}&pkg_id=${id}&page_no=1&page_size=10`,
479
+ {
480
+ ...getAxiosConfig(),
481
+ },
482
+ );
483
+ },
484
+ {
485
+ maxRetries: 3,
486
+ delay: 4000,
487
+ retryMessage: "查询版本信息",
488
+ },
489
+ );
490
+
491
+ return qovRes.data;
492
+ } catch (error) {
493
+ log(`查询版本信息失败:${error}`);
494
+ throw error;
495
+ }
496
+ };
497
+
498
+ // 如果未创建离线包,则创建之√
499
+ const addOfflineH5pkg = async function (app_id, h5app_id, name) {
500
+ try {
501
+ // 检查是否已存在相同h5app_id的离线包
502
+ let copRes = await retryWrapper(
503
+ async () => {
504
+ return await axios.get(
505
+ `${ApiList.checkOfflinePkgid}?app_id=${app_id}&h5app_id=${h5app_id}`,
506
+ {
507
+ ...getAxiosConfig(),
508
+ },
509
+ );
510
+ },
511
+ {
512
+ maxRetries: 3,
513
+ delay: 4000,
514
+ retryMessage: "检查离线包ID",
515
+ },
516
+ );
517
+ let copFlag = copRes.data.data;
518
+
519
+ // 检查是否已存在相同名称的离线包
520
+ let conRes = await retryWrapper(
521
+ async () => {
522
+ return await axios.get(
523
+ `${ApiList.checkOfflinePkgname}?app_id=${app_id}&name=${encodeURI(name)}`,
524
+ {
525
+ ...getAxiosConfig(),
526
+ },
527
+ );
528
+ },
529
+ {
530
+ maxRetries: 3,
531
+ delay: 4000,
532
+ retryMessage: "检查离线包名称",
533
+ },
534
+ );
535
+ let conFlag = conRes.data.data;
536
+
537
+ // 如果已存在,则直接返回
538
+ if (copFlag || conFlag) {
539
+ log(`离线包已存在:${h5app_id}-${name}`);
540
+ return false;
541
+ } else {
542
+ // 创建新的离线包
543
+ let aofRes = await retryWrapper(
544
+ async () => {
545
+ return await axios({
546
+ method: "post",
547
+ url: ApiList.addOfflineH5pkg,
548
+ ...getAxiosConfig({
549
+ data: qs.stringify({
550
+ app_id,
551
+ h5app_id,
552
+ name,
553
+ pkg_type: 1,
554
+ }),
555
+ }),
556
+ });
557
+ },
558
+ {
559
+ maxRetries: 3,
560
+ delay: 4000,
561
+ retryMessage: "添加离线包",
562
+ },
563
+ );
564
+
565
+ let { err_no, data } = aofRes.data;
566
+ log(`添加离线包:${h5app_id}-${name}`, aofRes.data);
567
+
568
+ if (err_no == 0 && data == 1) {
569
+ // 创建成功,更新离线包列表
570
+ await queryOfflinePkgList([app_id]);
571
+ return true;
572
+ } else {
573
+ return false;
574
+ }
575
+ }
576
+ } catch (error) {
577
+ log(`添加离线包失败:${error}`);
578
+ throw error;
579
+ }
580
+ };
581
+
582
+ //创建离线发布版本 √
583
+ const addOfflineVersion = async function (args) {
584
+ return retryWrapper(
585
+ async () => {
586
+ try {
587
+ // 发送创建版本请求
588
+ const res = await axios({
589
+ method: "post",
590
+ url: ApiList.addOfflineVersion,
591
+ ...getAxiosConfig({
592
+ data: qs.stringify(args),
593
+ }),
594
+ });
595
+
596
+ // 处理响应数据
597
+ // {"err_no":0,"err_info":"成功执行","data":1}
598
+ log("创建离线发布版本:", res.data);
599
+ const { err_no } = res.data;
600
+
601
+ if (err_no == 0) {
602
+ return true;
603
+ } else {
604
+ return false;
605
+ }
606
+ } catch (error) {
607
+ log(`创建离线发布版本报错:${error}`);
608
+ throw error;
609
+ }
610
+ },
611
+ {
612
+ maxRetries: 3,
613
+ delay: 3000,
614
+ retryMessage: "创建离线发布版本",
615
+ },
616
+ );
617
+ };
618
+
619
+ // 添加发布任务 √
620
+ const addOfflineTask = async function (args) {
621
+ return retryWrapper(
622
+ async () => {
623
+ try {
624
+ // 发送添加任务请求
625
+ const res = await axios({
626
+ method: "post",
627
+ url: ApiList.addOfflineTask,
628
+ ...getAxiosConfig({
629
+ data: qs.stringify(args),
630
+ }),
631
+ });
632
+
633
+ // 处理响应数据
634
+ // {"err_no":0,"err_info":"成功执行","data":24303}
635
+ log("添加发布任务:", res.data);
636
+ const { err_no } = res.data;
637
+
638
+ if (err_no == 0) {
639
+ return true;
640
+ } else {
641
+ return false;
642
+ }
643
+ } catch (error) {
644
+ log(`添加发布任务报错:${error}`);
645
+ throw error;
646
+ }
647
+ },
648
+ {
649
+ maxRetries: 3,
650
+ delay: 3000,
651
+ retryMessage: "添加发布任务",
652
+ },
653
+ );
654
+ };
655
+
656
+ //查询某个版本的发布任务 √
657
+ const queryOfflineTask = async function (args) {
658
+ return retryWrapper(
659
+ async () => {
660
+ try {
661
+ // 发送查询任务请求
662
+ const res = await axios.get(
663
+ `${ApiList.queryOfflineTask}?${qs.stringify(
664
+ args,
665
+ )}&page_no=1&page_size=5`,
666
+ {
667
+ ...getAxiosConfig(),
668
+ },
669
+ );
670
+
671
+ // 处理响应数据
672
+ const { err_no, data } = res.data;
673
+
674
+ if (err_no == 0) {
675
+ return data.list;
676
+ } else {
677
+ return [];
678
+ }
679
+ } catch (error) {
680
+ log(`获取指定版本下的发布任务列表报错:${error}`);
681
+ throw error;
682
+ }
683
+ },
684
+ {
685
+ maxRetries: 3,
686
+ delay: 5000,
687
+ retryMessage: "查询发布任务",
688
+ },
689
+ );
690
+ };
691
+
692
+ //更新发布任务状态√
693
+ const updateOfflineTask = async function (uotArgs = {}) {
694
+ return retryWrapper(
695
+ async () => {
696
+ try {
697
+ // 发送更新任务状态请求
698
+ const res = await axios({
699
+ method: "post",
700
+ url: ApiList.updateOfflineTask,
701
+ ...getAxiosConfig({
702
+ data: qs.stringify(uotArgs),
703
+ }),
704
+ });
705
+
706
+ // 处理响应数据
707
+ const { err_no } = res.data;
708
+
709
+ if (err_no == 0) {
710
+ return true;
711
+ } else {
712
+ return false;
713
+ }
714
+ } catch (error) {
715
+ log(`更新发布任务状态报错:${error}`);
716
+ throw error;
717
+ }
718
+ },
719
+ {
720
+ maxRetries: 3,
721
+ delay: 4000,
722
+ retryMessage: "更新发布任务状态",
723
+ },
724
+ );
725
+ };
726
+
727
+ //查询执行发布任务状态成功与否接口,返回true或false
728
+ const queryOfflineVersionDetail = async function ({ id, app_id }) {
729
+ try {
730
+ const res = await retryWrapper(
731
+ async () => {
732
+ return await axios.get(
733
+ `${ApiList.queryOfflineVersionDetail}?id=${id}&app_id=${app_id}`,
734
+ {
735
+ ...getAxiosConfig(),
736
+ },
737
+ );
738
+ },
739
+ {
740
+ maxRetries: 5,
741
+ delay: 3000,
742
+ retryMessage: "查询执行发布任务状态",
743
+ },
744
+ );
745
+
746
+ // 处理响应数据
747
+ const { err_no, data } = res.data;
748
+
749
+ if (err_no == 0) {
750
+ const { status } = data;
751
+ if (status == 4) {
752
+ // 状态4 表示任务执行成功
753
+ return true;
754
+ } else {
755
+ // 其他状态都认为是失败,则继续重试查询,直到状态为4或重试次数耗尽
756
+ log(`查询执行发布任务状态详情是:${status}`);
757
+ throw new Error(`查询执行发布任务状态详情是:${status}`);
758
+ }
759
+ } else {
760
+ return false;
761
+ }
762
+ } catch (error) {
763
+ log(`查询执行发布任务状态报错:${error}`);
764
+ throw error;
765
+ }
766
+ };
767
+
768
+ // 处理版本范围相互覆盖的离线包
769
+ const dealVersionList = async function ({ app_id, pkg_id, status = 2, name }) {
770
+ if (pkg_id == null) {
771
+ return false;
772
+ }
773
+
774
+ try {
775
+ // 查询版本列表
776
+ let qovRes = await retryWrapper(
777
+ async () => {
778
+ return await axios.get(
779
+ `${ApiList.queryOfflineVersionlist}?app_id=${app_id}&pkg_id=${pkg_id}&page_no=1&page_size=30`,
780
+ {
781
+ ...getAxiosConfig(),
782
+ },
783
+ );
784
+ },
785
+ {
786
+ maxRetries: 3,
787
+ delay: 4000,
788
+ retryMessage: "查询版本列表",
789
+ },
790
+ );
791
+
792
+ let { list } = qovRes.data.data;
793
+ let versionList = [];
794
+
795
+ // 获取要下架的版本号ID
796
+ let toDoId = list.reduce((acc, cur) => {
797
+ let {
798
+ id,
799
+ ios_version_scope, //iOS端离线包兼容版本
800
+ android_version_scope, //Android端离线包兼容版本
801
+ harmony_version_scope, //Harmony端离线包兼容版本
802
+ status,
803
+ version,
804
+ release_type,
805
+ } = cur;
806
+
807
+ if (status == 1 && release_type == 0) {
808
+ const versionScope =
809
+ ios_version_scope || android_version_scope || harmony_version_scope;
810
+
811
+ if (versionList.includes(versionScope)) {
812
+ let o = acc[`${versionScope}`];
813
+ acc[`${versionScope}`] = [...o, { id, version }];
814
+ } else {
815
+ acc[`${versionScope}`] = [];
816
+ versionList.push(versionScope);
817
+ }
818
+ }
819
+
820
+ return acc;
821
+ }, {});
822
+
823
+ // 处理需要下架的版本
824
+ for (const key in toDoId) {
825
+ if (Object.hasOwnProperty.call(toDoId, key)) {
826
+ const vl = toDoId[key];
827
+
828
+ if (vl.length > 0) {
829
+ for (let index = 0; index < vl.length; index++) {
830
+ const val = vl[index];
831
+
832
+ try {
833
+ let t = await todoVersion(app_id, val.id);
834
+ let sm = { 0: "发布", 1: "暂停", 2: "下架" },
835
+ tips = sm[`${status}`];
836
+
837
+ log(
838
+ `${app_id}${name ? name : ""}版本范围:${key},版本号:${
839
+ val.version
840
+ },版本ID:${val.id},处理情况:${tips}${t ? "成功" : "失败"}`,
841
+ );
842
+ } catch (error) {
843
+ log(`处理版本${val.id}失败: ${error}`);
844
+ }
845
+ }
846
+ }
847
+ }
848
+ }
849
+ } catch (error) {
850
+ log(`处理版本列表失败: ${error}`);
851
+ return false;
852
+ }
853
+
854
+ // 内部函数:处理单个版本的下架操作
855
+ async function todoVersion(app_id, version_id) {
856
+ try {
857
+ // 查询版本对应的发布任务
858
+ const qot = await queryOfflineTask({ app_id, version_id });
859
+
860
+ // 筛选状态为0的任务ID
861
+ let tmp = qot.reduce((acc, cur) => {
862
+ if (cur.status == 0) {
863
+ acc = [...acc, cur.id];
864
+ }
865
+ return acc;
866
+ }, []);
867
+
868
+ let taskId = tmp[0];
869
+
870
+ // 执行下架操作
871
+ return await updateOfflineTask({
872
+ id: taskId,
873
+ status: 2,
874
+ app_id,
875
+ });
876
+ } catch (error) {
877
+ log(`处理单个版本失败: ${error}`);
878
+ return false;
879
+ }
880
+ }
881
+ };
882
+
883
+ // 批处理版本范围相互覆盖的离线包
884
+ const batchDealVersionList = async function (ids, h5app_id, status = 2) {
885
+ try {
886
+ // 获取所有APP的离线包列表
887
+ const pkgLists = await queryOfflinePkgList(ids);
888
+
889
+ // 遍历每个APP的离线包列表
890
+ for (const key in pkgLists) {
891
+ if (Object.hasOwnProperty.call(pkgLists, key)) {
892
+ const ele = pkgLists[key];
893
+
894
+ // 遍历每个离线包
895
+ for (let index = 0; index < ele.length; index++) {
896
+ const f = ele[index];
897
+ let { app_id, pkg_id, name } = f;
898
+
899
+ // 如果有pkg_id,则处理版本列表
900
+ if (pkg_id != null) {
901
+ await dealVersionList({ app_id, pkg_id, status: 2, name });
902
+ }
903
+ }
904
+ }
905
+ }
906
+ } catch (error) {
907
+ log(`批量处理版本列表失败: ${error}`);
908
+ }
909
+ };
910
+
911
+ const sendWechatWebhook = async function (url, content) {
912
+ return retryWrapper(
913
+ async () => {
914
+ try {
915
+ // 发送企业微信消息
916
+ const res = await axios({
917
+ url: url,
918
+ method: "post",
919
+ data: {
920
+ msgtype: "text",
921
+ text: {
922
+ content: content,
923
+ mentioned_mobile_list: ["15019253060"],
924
+ },
925
+ },
926
+ ...getAxiosConfig({
927
+ headers: {
928
+ "Content-Type": "application/json",
929
+ },
930
+ }),
931
+ });
932
+
933
+ // 处理响应数据
934
+ const { data } = res;
935
+ log("企业微信消息发送响应:", data);
936
+
937
+ if (data.errcode === 0) {
938
+ return data;
939
+ } else {
940
+ throw new Error(`企业微信消息发送失败: ${data.errmsg}`);
941
+ }
942
+ } catch (error) {
943
+ log("企业微信消息发送失败:", error);
944
+ throw error;
945
+ }
946
+ },
947
+ {
948
+ maxRetries: 3,
949
+ delay: 4000,
950
+ retryMessage: "发送企业微信消息",
951
+ },
952
+ );
953
+ };
954
+
955
+ // 添加H5web包
956
+ const addH5webPkg = async function (pkgData = {}) {
957
+ return retryWrapper(
958
+ async () => {
959
+ try {
960
+ // 发送添加H5web包请求
961
+ const res = await axios({
962
+ method: "post",
963
+ url: ApiList.addH5webPkg,
964
+ ...getAxiosConfig({
965
+ data: qs.stringify(pkgData),
966
+ }),
967
+ });
968
+
969
+ // 处理响应数据
970
+ log("添加H5web包:", res.data);
971
+ const { err_no, err_info, data } = res.data;
972
+
973
+ if (err_no == 0) {
974
+ return data;
975
+ } else {
976
+ throw new Error(`添加H5web包失败: ${err_info}`);
977
+ }
978
+ } catch (error) {
979
+ log(`添加H5web包报错:${error}`);
980
+ throw error;
981
+ }
982
+ },
983
+ {
984
+ maxRetries: 3,
985
+ delay: 3000,
986
+ retryMessage: "添加H5web包",
987
+ },
988
+ );
989
+ };
990
+
991
+ //以快捷发布方式发布H5应用包到对应产品下
992
+ const releaseMuchVersion = async function (
993
+ comp_id,
994
+ comp_pkg_id,
995
+ version_desc = "此版本为快捷发布",
996
+ ) {
997
+ return retryWrapper(
998
+ async () => {
999
+ try {
1000
+ // 准备请求数据
1001
+ const reqData = {
1002
+ comp_id,
1003
+ comp_pkg_id,
1004
+ version_desc,
1005
+ };
1006
+
1007
+ // 发送发布H5web版本请求
1008
+ const res = await axios({
1009
+ method: "post",
1010
+ url: ApiList.releaseMuchVersion,
1011
+ ...getAxiosConfig({
1012
+ data: qs.stringify(reqData),
1013
+ }),
1014
+ });
1015
+
1016
+ // 处理响应数据
1017
+ // log("发布H5web版本:", res.data);
1018
+ const { err_no, err_info, data } = res.data;
1019
+
1020
+ if (err_no == 0) {
1021
+ log("发布H5web版本:", err_info);
1022
+ return data;
1023
+ } else {
1024
+ throw new Error(`发布H5web版本: ${err_info}`);
1025
+ }
1026
+ } catch (error) {
1027
+ log(`发布H5web版本报错:${error}`);
1028
+ throw error;
1029
+ }
1030
+ },
1031
+ {
1032
+ maxRetries: 3,
1033
+ delay: 3000,
1034
+ retryMessage: "发布H5web版本",
1035
+ },
1036
+ );
1037
+ };
1038
+ //查询H5应用ID列表接口
1039
+ const queryCompList = async function (params = {}) {
1040
+ try {
1041
+ const { is_online = 1, page_size = 1000, page_no = 1, group = '', sort_prop = 'create_time', sort_order = 1 } = params;
1042
+
1043
+ const res = await retryWrapper(
1044
+ async () => {
1045
+ return await axios.get(
1046
+ `${ApiList.queryCompList}?is_online=${is_online}&page_size=${page_size}&page_no=${page_no}&group=${encodeURIComponent(group)}&sort_prop=${sort_prop}&sort_order=${sort_order}`,
1047
+ {
1048
+ ...getAxiosConfig(),
1049
+ },
1050
+ );
1051
+ },
1052
+ {
1053
+ maxRetries: 3,
1054
+ delay: 4000,
1055
+ retryMessage: "查询H5应用ID列表",
1056
+ },
1057
+ );
1058
+
1059
+ // 处理响应数据
1060
+ const { err_no, data } = res.data;
1061
+
1062
+ if (err_no == 0) {
1063
+ return data.data;
1064
+ } else {
1065
+ return [];
1066
+ }
1067
+ } catch (error) {
1068
+ log(`查询H5应用ID列表报错:${error}`);
1069
+ throw error;
1070
+ }
1071
+ };
1072
+
1073
+ //获取APP列表接口
1074
+ const queryApp = async function (params = {}) {
1075
+ try {
1076
+ const { keywords = '' } = params;
1077
+
1078
+ const res = await retryWrapper(
1079
+ async () => {
1080
+ return await axios.post(
1081
+ ApiList.queryApp,
1082
+ qs.stringify({ keywords }),
1083
+ {
1084
+ ...getAxiosConfig(),
1085
+ },
1086
+ );
1087
+ },
1088
+ {
1089
+ maxRetries: 6,
1090
+ delay: 3000,
1091
+ retryMessage: "获取APP列表",
1092
+ },
1093
+ );
1094
+ // 处理响应数据
1095
+ const { err_no, data } = res.data;
1096
+
1097
+ if (err_no == 0) {
1098
+ return data.app || [];
1099
+ } else {
1100
+ return [];
1101
+ }
1102
+ } catch (error) {
1103
+ log(`获取APP列表报错:${error}`);
1104
+ throw error;
1105
+ }
1106
+ };
1107
+
1108
+ module.exports = {
1109
+ submitLogin,
1110
+ ocrCaptcha,
1111
+ getCaptcha,
1112
+ allOfflinePkgLists,
1113
+ logout,
1114
+ checkSession,
1115
+ fileUpload,
1116
+ queryOfflinePkgList,
1117
+ getPkgInfo,
1118
+ queryOfflineVersionlist,
1119
+ addOfflineH5pkg,
1120
+ addOfflineVersion,
1121
+ addOfflineTask,
1122
+ queryOfflineTask,
1123
+ updateOfflineTask,
1124
+ syncPkgList,
1125
+ initAxiosConfig,
1126
+ dealVersionList,
1127
+ batchDealVersionList,
1128
+ sendWechatWebhook,
1129
+ addH5webPkg,
1130
+ releaseMuchVersion,
1131
+ queryOfflineVersionDetail,
1132
+ queryCompList,
1133
+ queryApp,
1134
+ };