koishi-plugin-jscn-aaaquery 1.0.2 → 1.0.4
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/lib/index.d.ts +1 -0
- package/lib/index.js +720 -341
- package/package.json +1 -1
- package/readme.md +93 -9
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -28,10 +28,29 @@ module.exports = __toCommonJS(src_exports);
|
|
|
28
28
|
var import_koishi = require("koishi");
|
|
29
29
|
var name = "jscn-aaaquery";
|
|
30
30
|
var Config = import_koishi.Schema.object({
|
|
31
|
-
loginName: import_koishi.Schema.string().required().description("运维系统登录账号"),
|
|
32
|
-
password: import_koishi.Schema.string().required().description("运维系统登录密码"),
|
|
33
|
-
baseUrl: import_koishi.Schema.string().default("http://172.16.251.75:8090/nms/api").description("运维系统API地址")
|
|
31
|
+
loginName: import_koishi.Schema.string().required().description("运维系统登录账号").default("admin"),
|
|
32
|
+
password: import_koishi.Schema.string().required().description("运维系统登录密码").default("admin"),
|
|
33
|
+
baseUrl: import_koishi.Schema.string().default("http://172.16.251.75:8090/nms/api").description("运维系统API地址"),
|
|
34
|
+
// 添加RADIUS API的配置项
|
|
35
|
+
radiusApiUrl: import_koishi.Schema.string().description("RADIUS API基础URL").default("http://111.208.114.248:28210/api/radius")
|
|
34
36
|
});
|
|
37
|
+
function evaluateOpticalPower(power) {
|
|
38
|
+
if (power === "设备不在线" || power === void 0 || power === null) {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
const powerValue = parseFloat(power);
|
|
42
|
+
if (isNaN(powerValue)) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
if (powerValue >= -24 && powerValue <= -8) {
|
|
46
|
+
return "🟢正常";
|
|
47
|
+
} else if (powerValue > -8) {
|
|
48
|
+
return "🔴过高";
|
|
49
|
+
} else {
|
|
50
|
+
return "🔴过低";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
__name(evaluateOpticalPower, "evaluateOpticalPower");
|
|
35
54
|
function apply(ctx, config) {
|
|
36
55
|
let currentToken = null;
|
|
37
56
|
let tokenExpireTime = 0;
|
|
@@ -44,6 +63,11 @@ function apply(ctx, config) {
|
|
|
44
63
|
const MAX_CONCURRENT_REQUESTS = 10;
|
|
45
64
|
let currentConcurrentRequests = 0;
|
|
46
65
|
const requestWaitQueue = [];
|
|
66
|
+
const messageQueue = [];
|
|
67
|
+
let isProcessingMessage = false;
|
|
68
|
+
const MESSAGE_TIMEOUT = 2 * 60 * 1e3;
|
|
69
|
+
const MESSAGE_CLEAN_INTERVAL = 60 * 1e3;
|
|
70
|
+
const MAX_QUEUE_LENGTH = 50;
|
|
47
71
|
function getCacheKey(type, params) {
|
|
48
72
|
return `${type}:${JSON.stringify(params)}`;
|
|
49
73
|
}
|
|
@@ -127,7 +151,7 @@ function apply(ctx, config) {
|
|
|
127
151
|
}
|
|
128
152
|
__name(addToQueue, "addToQueue");
|
|
129
153
|
function formatMacAddress(mac) {
|
|
130
|
-
return mac.replace(/[:-\s
|
|
154
|
+
return mac.replace(/[:-\s.:。-|]/g, "").toLowerCase();
|
|
131
155
|
}
|
|
132
156
|
__name(formatMacAddress, "formatMacAddress");
|
|
133
157
|
function isValidMacAddress(mac) {
|
|
@@ -203,7 +227,7 @@ function apply(ctx, config) {
|
|
|
203
227
|
console.log("查询响应:", JSON.stringify(response, null, 2));
|
|
204
228
|
if (response.status === 200 && response.data) {
|
|
205
229
|
if (!isAccountResponseComplete(response.data)) {
|
|
206
|
-
throw new Error("
|
|
230
|
+
throw new Error("账号信息有误,请检查账号是否正确");
|
|
207
231
|
}
|
|
208
232
|
setCache("account", { account: formattedAccount }, response.data);
|
|
209
233
|
return response.data;
|
|
@@ -308,32 +332,42 @@ function apply(ctx, config) {
|
|
|
308
332
|
});
|
|
309
333
|
console.log("查询响应:", JSON.stringify(response, null, 2));
|
|
310
334
|
if (response.status === 200 && response.data) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
{
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
"Content-Type": "application/json"
|
|
326
|
-
}
|
|
335
|
+
console.log("尝试刷新设备光功率...");
|
|
336
|
+
try {
|
|
337
|
+
const powerResponse = await ctx.http.get(
|
|
338
|
+
`${config.baseUrl}/fttx/onu/refreshOnuPower`,
|
|
339
|
+
{
|
|
340
|
+
params: {
|
|
341
|
+
cid: response.data.cid,
|
|
342
|
+
gponIndex: response.data.gponIndex,
|
|
343
|
+
onuIndex: response.data.onuIndex
|
|
344
|
+
},
|
|
345
|
+
headers: {
|
|
346
|
+
"Authorization": token,
|
|
347
|
+
"Accept": "application/json",
|
|
348
|
+
"Content-Type": "application/json"
|
|
327
349
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
if (powerResponse.status === 200 && powerResponse.data) {
|
|
353
|
+
console.log("光功率刷新结果:", powerResponse.data);
|
|
354
|
+
if (powerResponse.data.status !== void 0) {
|
|
355
|
+
response.data.status = powerResponse.data.status;
|
|
356
|
+
}
|
|
357
|
+
if (response.data.status !== 1 && (powerResponse.data.onuReceivePower === void 0 || powerResponse.data.onuSendPower === void 0)) {
|
|
358
|
+
response.data.onuReceivePower = "设备不在线";
|
|
359
|
+
response.data.onuSendPower = "设备不在线";
|
|
360
|
+
} else {
|
|
331
361
|
response.data.onuReceivePower = powerResponse.data.onuReceivePower;
|
|
332
362
|
response.data.onuSendPower = powerResponse.data.onuSendPower;
|
|
333
|
-
response.data.statusHealth = powerResponse.data.statusHealth;
|
|
334
363
|
}
|
|
335
|
-
|
|
336
|
-
|
|
364
|
+
response.data.statusHealth = powerResponse.data.statusHealth;
|
|
365
|
+
}
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error("刷新光功率过程中出错:", error);
|
|
368
|
+
if (response.data.status !== 1 && (response.data.onuReceivePower === void 0 || response.data.onuReceivePower === null || response.data.onuSendPower === void 0 || response.data.onuSendPower === null)) {
|
|
369
|
+
response.data.onuReceivePower = "设备不在线";
|
|
370
|
+
response.data.onuSendPower = "设备不在线";
|
|
337
371
|
}
|
|
338
372
|
}
|
|
339
373
|
setCache("onuDetail", { onuId }, response.data);
|
|
@@ -451,7 +485,7 @@ function apply(ctx, config) {
|
|
|
451
485
|
});
|
|
452
486
|
}
|
|
453
487
|
__name(queryOnuDetailVlanInfo, "queryOnuDetailVlanInfo");
|
|
454
|
-
async function queryOrderLog(mac, startTime, endTime) {
|
|
488
|
+
async function queryOrderLog(mac, startTime, endTime, token) {
|
|
455
489
|
const cacheKey = { mac, startTime, endTime };
|
|
456
490
|
const cachedData = getFromCache("orderLog", cacheKey);
|
|
457
491
|
if (cachedData) {
|
|
@@ -461,7 +495,6 @@ function apply(ctx, config) {
|
|
|
461
495
|
return addToQueue("orderLog", cacheKey, async () => {
|
|
462
496
|
try {
|
|
463
497
|
console.log("开始查询工单日志...");
|
|
464
|
-
const token = await getToken();
|
|
465
498
|
const formattedMac = formatMacAddress(mac);
|
|
466
499
|
console.log("处理后的MAC地址:", formattedMac);
|
|
467
500
|
const response = await ctx.http.get(`${config.baseUrl}/fttx/orderAnalyBlock/selectorderAnalyBlockOneList`, {
|
|
@@ -486,351 +519,697 @@ function apply(ctx, config) {
|
|
|
486
519
|
});
|
|
487
520
|
}
|
|
488
521
|
__name(queryOrderLog, "queryOrderLog");
|
|
489
|
-
|
|
490
|
-
const
|
|
491
|
-
if (
|
|
492
|
-
|
|
522
|
+
async function queryRadiusUser(account) {
|
|
523
|
+
const cachedData = getFromCache("radiusUser", { account });
|
|
524
|
+
if (cachedData) {
|
|
525
|
+
console.log("使用缓存的RADIUS用户信息");
|
|
526
|
+
return cachedData;
|
|
493
527
|
}
|
|
494
|
-
|
|
495
|
-
console.log("处理后的内容:", cleanInput);
|
|
496
|
-
if (isValidMacAddress(cleanInput)) {
|
|
528
|
+
return addToQueue("radiusUser", { account }, async () => {
|
|
497
529
|
try {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
`CCName: ${ccname}`,
|
|
519
|
-
"",
|
|
520
|
-
"📝 工单信息",
|
|
521
|
-
"--------------------------------",
|
|
522
|
-
order ? [
|
|
523
|
-
`工单编号: ${order.orderId}`,
|
|
524
|
-
`创建时间: ${order.createDate}`,
|
|
525
|
-
`更新时间: ${order.updateDate}`
|
|
526
|
-
].join("\n") : "无工单信息",
|
|
527
|
-
"",
|
|
528
|
-
"🔧 业务配置信息",
|
|
529
|
-
"--------------------------------"
|
|
530
|
-
];
|
|
531
|
-
vlanInfo.forEach((info) => {
|
|
532
|
-
messages.push(
|
|
533
|
-
`端口 ${info.LAN}:`,
|
|
534
|
-
` VLAN: ${info.actualVlan}`
|
|
535
|
-
);
|
|
536
|
-
});
|
|
537
|
-
return messages.join("\n");
|
|
538
|
-
} catch (error) {
|
|
539
|
-
console.error("查询过程出错:", error);
|
|
540
|
-
return "查询失败,请稍后重试";
|
|
541
|
-
}
|
|
542
|
-
} else {
|
|
543
|
-
try {
|
|
544
|
-
const accountInfo = await queryAccount(input);
|
|
545
|
-
const message = [
|
|
546
|
-
"📊 账号查询结果",
|
|
547
|
-
"--------------------------------",
|
|
548
|
-
`账号: ${accountInfo.accessUserName}`,
|
|
549
|
-
`状态: ${accountInfo.onlineStatus}`,
|
|
550
|
-
`认证: ${accountInfo.isPass}`,
|
|
551
|
-
`IP地址: ${accountInfo.userIp}`,
|
|
552
|
-
`MAC地址: ${accountInfo.mac}`,
|
|
553
|
-
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
554
|
-
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
555
|
-
`接入域: ${accountInfo.accessDomain}`,
|
|
556
|
-
`开始时间: ${accountInfo.startTime}`,
|
|
557
|
-
`在线时长: ${accountInfo.startTimeSum}分钟`,
|
|
558
|
-
`设备名称: ${accountInfo.nickName}`
|
|
559
|
-
].join("\n");
|
|
560
|
-
return message;
|
|
530
|
+
console.log("开始查询RADIUS用户信息...");
|
|
531
|
+
const response = await ctx.http.post(
|
|
532
|
+
`${config.radiusApiUrl}/userquery`,
|
|
533
|
+
{
|
|
534
|
+
login_name: account,
|
|
535
|
+
org_code: "403",
|
|
536
|
+
// 固定值
|
|
537
|
+
channel: "ALL"
|
|
538
|
+
// 固定值
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
headers: {
|
|
542
|
+
"Content-Type": "application/json",
|
|
543
|
+
"Accept": "application/json"
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
);
|
|
547
|
+
console.log("查询响应:", JSON.stringify(response, null, 2));
|
|
548
|
+
setCache("radiusUser", { account }, response);
|
|
549
|
+
return response;
|
|
561
550
|
} catch (error) {
|
|
562
|
-
console.error("
|
|
563
|
-
|
|
551
|
+
console.error("查询RADIUS用户错误详情:", error);
|
|
552
|
+
if (error.response) {
|
|
553
|
+
console.error("错误响应:", error.response.data);
|
|
554
|
+
}
|
|
555
|
+
throw error;
|
|
564
556
|
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
__name(queryRadiusUser, "queryRadiusUser");
|
|
560
|
+
function formatRadiusTime(timeString) {
|
|
561
|
+
if (!timeString || timeString.length < 14) return "未知时间";
|
|
562
|
+
const year = timeString.substring(0, 4);
|
|
563
|
+
const month = timeString.substring(4, 6);
|
|
564
|
+
const day = timeString.substring(6, 8);
|
|
565
|
+
const hour = timeString.substring(8, 10);
|
|
566
|
+
const minute = timeString.substring(10, 12);
|
|
567
|
+
const second = timeString.substring(12, 14);
|
|
568
|
+
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
569
|
+
}
|
|
570
|
+
__name(formatRadiusTime, "formatRadiusTime");
|
|
571
|
+
function translateOrderStatus(status) {
|
|
572
|
+
switch (status) {
|
|
573
|
+
case "active":
|
|
574
|
+
return "有效";
|
|
575
|
+
case "inactive":
|
|
576
|
+
return "无效";
|
|
577
|
+
case "suspend":
|
|
578
|
+
return "暂停";
|
|
579
|
+
default:
|
|
580
|
+
return status;
|
|
565
581
|
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
"
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
`MAC地址: ${accountInfo.mac}`,
|
|
579
|
-
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
580
|
-
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
581
|
-
`接入域: ${accountInfo.accessDomain}`,
|
|
582
|
-
`开始时间: ${accountInfo.startTime}`,
|
|
583
|
-
`在线时长: ${accountInfo.startTimeSum}分钟`,
|
|
584
|
-
`设备名称: ${accountInfo.nickName}`
|
|
585
|
-
].join("\n");
|
|
586
|
-
return message;
|
|
587
|
-
} catch (error) {
|
|
588
|
-
console.error("查询过程出错:", error);
|
|
589
|
-
return error.message || "查询失败,请检查账号是否正确";
|
|
582
|
+
}
|
|
583
|
+
__name(translateOrderStatus, "translateOrderStatus");
|
|
584
|
+
function translateUserStatus(status) {
|
|
585
|
+
switch (status) {
|
|
586
|
+
case "active":
|
|
587
|
+
return "正常";
|
|
588
|
+
case "inactive":
|
|
589
|
+
return "停用";
|
|
590
|
+
case "suspend":
|
|
591
|
+
return "暂停";
|
|
592
|
+
default:
|
|
593
|
+
return status;
|
|
590
594
|
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
|
|
595
|
+
}
|
|
596
|
+
__name(translateUserStatus, "translateUserStatus");
|
|
597
|
+
function processTimeRange(startTime, endTime) {
|
|
598
|
+
const formatDate = /* @__PURE__ */ __name((date) => {
|
|
599
|
+
const year = date.getFullYear();
|
|
600
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
601
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
602
|
+
return `${year}-${month}-${day}`;
|
|
603
|
+
}, "formatDate");
|
|
604
|
+
const parseDate = /* @__PURE__ */ __name((dateStr) => {
|
|
605
|
+
if (dateStr.includes(".") || dateStr.includes("/")) {
|
|
606
|
+
const parts = dateStr.split(/[./]/);
|
|
607
|
+
if (parts.length === 3) {
|
|
608
|
+
const [year, month, day] = parts.map(Number);
|
|
609
|
+
return new Date(year, month - 1, day);
|
|
610
|
+
} else if (parts.length === 2) {
|
|
611
|
+
const [month, day] = parts.map(Number);
|
|
612
|
+
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
613
|
+
return new Date(year, month - 1, day);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const chineseMonthMap = {
|
|
617
|
+
"一": 1,
|
|
618
|
+
"二": 2,
|
|
619
|
+
"三": 3,
|
|
620
|
+
"四": 4,
|
|
621
|
+
"五": 5,
|
|
622
|
+
"六": 6,
|
|
623
|
+
"七": 7,
|
|
624
|
+
"八": 8,
|
|
625
|
+
"九": 9,
|
|
626
|
+
"十": 10,
|
|
627
|
+
"十一": 11,
|
|
628
|
+
"十二": 12
|
|
629
|
+
};
|
|
630
|
+
const chineseDayMap = {
|
|
631
|
+
"一": 1,
|
|
632
|
+
"二": 2,
|
|
633
|
+
"三": 3,
|
|
634
|
+
"四": 4,
|
|
635
|
+
"五": 5,
|
|
636
|
+
"六": 6,
|
|
637
|
+
"七": 7,
|
|
638
|
+
"八": 8,
|
|
639
|
+
"九": 9,
|
|
640
|
+
"十": 10,
|
|
641
|
+
"十一": 11,
|
|
642
|
+
"十二": 12,
|
|
643
|
+
"十三": 13,
|
|
644
|
+
"十四": 14,
|
|
645
|
+
"十五": 15,
|
|
646
|
+
"十六": 16,
|
|
647
|
+
"十七": 17,
|
|
648
|
+
"十八": 18,
|
|
649
|
+
"十九": 19,
|
|
650
|
+
"二十": 20,
|
|
651
|
+
"二十一": 21,
|
|
652
|
+
"二十二": 22,
|
|
653
|
+
"二十三": 23,
|
|
654
|
+
"二十四": 24,
|
|
655
|
+
"二十五": 25,
|
|
656
|
+
"二十六": 26,
|
|
657
|
+
"二十七": 27,
|
|
658
|
+
"二十八": 28,
|
|
659
|
+
"二十九": 29,
|
|
660
|
+
"三十": 30,
|
|
661
|
+
"三十一": 31
|
|
662
|
+
};
|
|
663
|
+
const monthMatch = dateStr.match(/[一二三四五六七八九十]+月/);
|
|
664
|
+
const dayMatch = dateStr.match(/[一二三四五六七八九十]+[号日]/);
|
|
665
|
+
if (monthMatch && dayMatch) {
|
|
666
|
+
const monthStr = monthMatch[0].replace("月", "");
|
|
667
|
+
const dayStr = dayMatch[0].replace(/[号日]/, "");
|
|
668
|
+
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
669
|
+
const month = chineseMonthMap[monthStr];
|
|
670
|
+
const day = chineseDayMap[dayStr];
|
|
671
|
+
return new Date(year, month - 1, day);
|
|
672
|
+
}
|
|
673
|
+
throw new Error("不支持的日期格式");
|
|
674
|
+
}, "parseDate");
|
|
675
|
+
let formattedStartTime;
|
|
676
|
+
let formattedEndTime;
|
|
594
677
|
try {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
678
|
+
if (startTime && endTime) {
|
|
679
|
+
formattedStartTime = formatDate(parseDate(startTime));
|
|
680
|
+
formattedEndTime = formatDate(parseDate(endTime));
|
|
681
|
+
} else if (startTime) {
|
|
682
|
+
const endDate = parseDate(startTime);
|
|
683
|
+
const startDate = new Date(endDate);
|
|
684
|
+
startDate.setDate(startDate.getDate() - 1);
|
|
685
|
+
formattedStartTime = formatDate(startDate);
|
|
686
|
+
formattedEndTime = formatDate(endDate);
|
|
687
|
+
} else {
|
|
688
|
+
const today = /* @__PURE__ */ new Date();
|
|
689
|
+
const yesterday = new Date(today);
|
|
690
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
691
|
+
formattedStartTime = formatDate(yesterday);
|
|
692
|
+
formattedEndTime = formatDate(today);
|
|
598
693
|
}
|
|
599
|
-
const messages = records.map((record, index) => [
|
|
600
|
-
`📝 记录 #${index + 1}`,
|
|
601
|
-
"--------------------------------",
|
|
602
|
-
`账号: ${record.accessUserName}`,
|
|
603
|
-
`记录类型: ${record.recordType}`,
|
|
604
|
-
`设备名称: ${record.nickName}`,
|
|
605
|
-
`IP地址: ${record.ip}`,
|
|
606
|
-
`MAC地址: ${record.mac}`,
|
|
607
|
-
`登录时间: ${record.userLoginInTime}`,
|
|
608
|
-
`登出时间: ${record.userLoginOutTime}`,
|
|
609
|
-
`原因: ${record.reason || "无"}`
|
|
610
|
-
].join("\n"));
|
|
611
|
-
return messages.join("\n\n");
|
|
612
694
|
} catch (error) {
|
|
613
|
-
|
|
614
|
-
return error.message || "查询失败,请稍后重试";
|
|
695
|
+
throw new Error("日期格式错误,支持的格式:\n1. 2025.4.4 或 2025/4/4\n2. 4.7 或 4/7(自动补全当前年份)\n3. 四月五号\n4. 四月五日");
|
|
615
696
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
697
|
+
return { formattedStartTime, formattedEndTime };
|
|
698
|
+
}
|
|
699
|
+
__name(processTimeRange, "processTimeRange");
|
|
700
|
+
function isShortPrompt(message) {
|
|
701
|
+
return !message.includes("\n") && message.length < 30;
|
|
702
|
+
}
|
|
703
|
+
__name(isShortPrompt, "isShortPrompt");
|
|
704
|
+
function formatOutputMessage(message) {
|
|
705
|
+
if (isShortPrompt(message)) {
|
|
706
|
+
return message;
|
|
707
|
+
}
|
|
708
|
+
if (!message.trim().endsWith("--------------------------------")) {
|
|
709
|
+
message = message + "\n--------------------------------";
|
|
710
|
+
}
|
|
711
|
+
return "\n" + message;
|
|
712
|
+
}
|
|
713
|
+
__name(formatOutputMessage, "formatOutputMessage");
|
|
714
|
+
async function processMessageQueue() {
|
|
715
|
+
if (isProcessingMessage || messageQueue.length === 0) return;
|
|
716
|
+
isProcessingMessage = true;
|
|
619
717
|
try {
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
return
|
|
718
|
+
const task = messageQueue.shift();
|
|
719
|
+
if (!task) {
|
|
720
|
+
isProcessingMessage = false;
|
|
721
|
+
return;
|
|
624
722
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
vlanInfo.forEach((info) => {
|
|
639
|
-
messages.push(
|
|
640
|
-
`端口 ${info.LAN}:`,
|
|
641
|
-
` VLAN: ${info.actualVlan}`
|
|
642
|
-
);
|
|
643
|
-
});
|
|
644
|
-
return messages.join("\n");
|
|
723
|
+
if (Date.now() - task.timestamp > MESSAGE_TIMEOUT) {
|
|
724
|
+
console.log("消息处理任务超时,跳过处理");
|
|
725
|
+
isProcessingMessage = false;
|
|
726
|
+
setTimeout(processMessageQueue, 0);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
console.log("开始处理消息队列中的任务");
|
|
730
|
+
const result = await task.handler();
|
|
731
|
+
if (result && task.session?.send) {
|
|
732
|
+
const formattedResult = formatOutputMessage(result);
|
|
733
|
+
await task.session.send(formattedResult);
|
|
734
|
+
}
|
|
735
|
+
console.log("消息处理完成");
|
|
645
736
|
} catch (error) {
|
|
646
|
-
console.error("
|
|
647
|
-
|
|
737
|
+
console.error("处理消息队列任务出错:", error);
|
|
738
|
+
} finally {
|
|
739
|
+
isProcessingMessage = false;
|
|
740
|
+
setTimeout(processMessageQueue, 100);
|
|
648
741
|
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
return "未找到该MAC地址的设备信息";
|
|
742
|
+
}
|
|
743
|
+
__name(processMessageQueue, "processMessageQueue");
|
|
744
|
+
function addToMessageQueue(session, handler) {
|
|
745
|
+
if (messageQueue.length >= MAX_QUEUE_LENGTH) {
|
|
746
|
+
const oldestTask = messageQueue.shift();
|
|
747
|
+
if (oldestTask?.session?.send) {
|
|
748
|
+
oldestTask.session.send("系统繁忙,请稍后再试").catch((error) => console.error("发送繁忙提示失败:", error));
|
|
657
749
|
}
|
|
658
|
-
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
659
|
-
const ccname = await queryCCName(formattedMac);
|
|
660
|
-
const order = await queryOnuOrder(onuDetail);
|
|
661
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
662
|
-
const messages = [
|
|
663
|
-
"📊 终端基本信息",
|
|
664
|
-
"--------------------------------",
|
|
665
|
-
`MAC地址: ${formattedMac}`,
|
|
666
|
-
`状态:${onuDetail.status === 1 ? "在线" : "不在线"}`,
|
|
667
|
-
`机房: ${onuDetail.roomName}`,
|
|
668
|
-
`设备名称: ${onuDetail.deviceName}`,
|
|
669
|
-
`OLT IP: ${onuDetail.oltIp}`,
|
|
670
|
-
`PON端口: ${onuDetail.portName}`,
|
|
671
|
-
`端口逻辑号: ${onuDetail.logicId}`,
|
|
672
|
-
`发射光功率: ${onuDetail.onuSendPower}dBm`,
|
|
673
|
-
`接收光功率: ${onuDetail.onuReceivePower}dBm`,
|
|
674
|
-
`CCName: ${ccname}`,
|
|
675
|
-
"",
|
|
676
|
-
"📝 工单信息",
|
|
677
|
-
"--------------------------------",
|
|
678
|
-
order ? [
|
|
679
|
-
`工单编号: ${order.orderId}`,
|
|
680
|
-
`创建时间: ${order.createDate}`,
|
|
681
|
-
`更新时间: ${order.updateDate}`
|
|
682
|
-
].join("\n") : "无工单信息",
|
|
683
|
-
"",
|
|
684
|
-
"🔧 业务配置信息",
|
|
685
|
-
"--------------------------------"
|
|
686
|
-
];
|
|
687
|
-
vlanInfo.forEach((info) => {
|
|
688
|
-
messages.push(
|
|
689
|
-
`端口 ${info.LAN}:`,
|
|
690
|
-
` VLAN: ${info.actualVlan}`
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
return messages.join("\n");
|
|
694
|
-
} catch (error) {
|
|
695
|
-
console.error("查询过程出错:", error);
|
|
696
|
-
return "查询失败,请稍后重试";
|
|
697
750
|
}
|
|
751
|
+
messageQueue.push({
|
|
752
|
+
session,
|
|
753
|
+
handler,
|
|
754
|
+
timestamp: Date.now()
|
|
755
|
+
});
|
|
756
|
+
setTimeout(processMessageQueue, 0);
|
|
757
|
+
}
|
|
758
|
+
__name(addToMessageQueue, "addToMessageQueue");
|
|
759
|
+
setInterval(() => {
|
|
760
|
+
const now = Date.now();
|
|
761
|
+
let expiredCount = 0;
|
|
762
|
+
while (messageQueue.length > 0 && now - messageQueue[0].timestamp > MESSAGE_TIMEOUT) {
|
|
763
|
+
const expiredTask = messageQueue.shift();
|
|
764
|
+
if (expiredTask?.session?.send) {
|
|
765
|
+
expiredTask.session.send("处理超时,请稍后再试").catch((error) => console.error("发送超时提示失败:", error));
|
|
766
|
+
}
|
|
767
|
+
expiredCount++;
|
|
768
|
+
}
|
|
769
|
+
if (expiredCount > 0) {
|
|
770
|
+
console.log(`清理了 ${expiredCount} 个超时的消息任务`);
|
|
771
|
+
}
|
|
772
|
+
}, MESSAGE_CLEAN_INTERVAL);
|
|
773
|
+
ctx.middleware(async (session, next) => {
|
|
774
|
+
console.log("原始消息:", session.content);
|
|
775
|
+
const originalInput = session.stripped.content.trim();
|
|
776
|
+
let input = originalInput;
|
|
777
|
+
console.log("处理后的消息:", input);
|
|
778
|
+
if (!input) {
|
|
779
|
+
console.log("处理后的消息为空,跳过智能查询");
|
|
780
|
+
return next();
|
|
781
|
+
}
|
|
782
|
+
if (/[\u4e00-\u9fa5]/.test(input)) {
|
|
783
|
+
console.log("消息包含中文,跳过智能查询");
|
|
784
|
+
return next();
|
|
785
|
+
}
|
|
786
|
+
return new Promise((resolve) => {
|
|
787
|
+
addToMessageQueue(session, async () => {
|
|
788
|
+
try {
|
|
789
|
+
const cleanInput = formatMacAddress(input);
|
|
790
|
+
console.log("处理后的内容:", cleanInput);
|
|
791
|
+
if (isValidMacAddress(cleanInput)) {
|
|
792
|
+
console.log("内容是合法的MAC地址,开始查询");
|
|
793
|
+
try {
|
|
794
|
+
const onuList = await queryOnuList(cleanInput);
|
|
795
|
+
if (!onuList) {
|
|
796
|
+
return "未找到该MAC地址的设备信息";
|
|
797
|
+
}
|
|
798
|
+
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
799
|
+
const ccname = await queryCCName(cleanInput);
|
|
800
|
+
const order = await queryOnuOrder(onuDetail);
|
|
801
|
+
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
802
|
+
const messages = [
|
|
803
|
+
"🖥️ 终端基本信息",
|
|
804
|
+
"--------------------------------",
|
|
805
|
+
`MAC地址: ${cleanInput}`,
|
|
806
|
+
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
807
|
+
`机房: ${onuDetail.roomName}`,
|
|
808
|
+
`设备名称: `,
|
|
809
|
+
`${onuDetail.deviceName}`,
|
|
810
|
+
`OLT IP: ${onuDetail.oltIp}`,
|
|
811
|
+
`PON端口: ${onuDetail.portName}`,
|
|
812
|
+
`端口逻辑号: ${onuDetail.logicId}`,
|
|
813
|
+
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
814
|
+
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
815
|
+
`CCName: ${ccname}`,
|
|
816
|
+
"",
|
|
817
|
+
"📃 工单信息",
|
|
818
|
+
"--------------------------------",
|
|
819
|
+
order ? [
|
|
820
|
+
`工单编号: ${order.orderId}`,
|
|
821
|
+
`创建时间: ${order.createDate}`,
|
|
822
|
+
`更新时间: ${order.updateDate}`
|
|
823
|
+
].join("\n") : "无工单信息",
|
|
824
|
+
"",
|
|
825
|
+
"⚙️ 业务配置信息",
|
|
826
|
+
"--------------------------------"
|
|
827
|
+
];
|
|
828
|
+
vlanInfo.forEach((info) => {
|
|
829
|
+
messages.push(
|
|
830
|
+
`端口 ${info.LAN}:`,
|
|
831
|
+
` 端口VLAN: ${info.VLAN || "未配置"}`,
|
|
832
|
+
` 工单VLAN: ${info.actualVlan || "未配置"}`
|
|
833
|
+
);
|
|
834
|
+
});
|
|
835
|
+
messages.push("--------------------------------");
|
|
836
|
+
return messages.join("\n");
|
|
837
|
+
} catch (error) {
|
|
838
|
+
console.error("查询MAC地址过程出错:", error);
|
|
839
|
+
return "查询失败,请稍后重试";
|
|
840
|
+
}
|
|
841
|
+
} else {
|
|
842
|
+
console.log("内容不是MAC地址,尝试作为账号查询");
|
|
843
|
+
try {
|
|
844
|
+
const radiusUserInfo = await queryRadiusUser(input);
|
|
845
|
+
if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
|
|
846
|
+
return "账号授权有误,请重新授权";
|
|
847
|
+
}
|
|
848
|
+
const { user, orders } = radiusUserInfo.data;
|
|
849
|
+
const accountInfo = await queryAccount(input);
|
|
850
|
+
const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
|
|
851
|
+
const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
|
|
852
|
+
const minutes = accountInfo.startTimeSum % 60;
|
|
853
|
+
let durationText = "";
|
|
854
|
+
if (days > 0) {
|
|
855
|
+
durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
856
|
+
} else if (hours > 0) {
|
|
857
|
+
durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
858
|
+
} else {
|
|
859
|
+
durationText = `${minutes}分钟`;
|
|
860
|
+
}
|
|
861
|
+
const message = [
|
|
862
|
+
"🌐 账号查询结果",
|
|
863
|
+
"--------------------------------",
|
|
864
|
+
`账号: ${accountInfo.accessUserName}`,
|
|
865
|
+
// `RADIUS用户密码: ${user.password}`,
|
|
866
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
867
|
+
"",
|
|
868
|
+
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus.includes("在线") ? "✅" : "❌"}`,
|
|
869
|
+
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`,
|
|
870
|
+
`IP地址: ${accountInfo.userIp}`,
|
|
871
|
+
`MAC地址: ${accountInfo.mac}`,
|
|
872
|
+
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
873
|
+
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
874
|
+
`开始时间: ${accountInfo.startTime}`,
|
|
875
|
+
`在线时长: ${durationText}`,
|
|
876
|
+
""
|
|
877
|
+
];
|
|
878
|
+
if (orders && orders.length > 0) {
|
|
879
|
+
message.push("🛒 订购信息");
|
|
880
|
+
message.push("--------------------------------");
|
|
881
|
+
orders.forEach((order, index) => {
|
|
882
|
+
message.push(
|
|
883
|
+
`订购 #${index + 1}:`,
|
|
884
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
885
|
+
` 服务名称: ${order.service_name}`,
|
|
886
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
887
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
888
|
+
);
|
|
889
|
+
if (index < orders.length - 1) {
|
|
890
|
+
message.push("");
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
message.push("--------------------------------");
|
|
895
|
+
return message.join("\n");
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("查询账号过程出错:", error);
|
|
898
|
+
resolve(next());
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
} catch (error) {
|
|
903
|
+
console.error("智能查询过程出错:", error);
|
|
904
|
+
resolve(next());
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
});
|
|
698
909
|
});
|
|
699
|
-
ctx.command("
|
|
700
|
-
if (!
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}, "formatDate");
|
|
708
|
-
const parseDate = /* @__PURE__ */ __name((dateStr) => {
|
|
709
|
-
if (dateStr.includes(".") || dateStr.includes("/")) {
|
|
710
|
-
const parts = dateStr.split(/[./]/);
|
|
711
|
-
if (parts.length === 3) {
|
|
712
|
-
const [year, month, day] = parts.map(Number);
|
|
713
|
-
return new Date(year, month - 1, day);
|
|
714
|
-
} else if (parts.length === 2) {
|
|
715
|
-
const [month, day] = parts.map(Number);
|
|
716
|
-
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
717
|
-
return new Date(year, month - 1, day);
|
|
910
|
+
ctx.command("账号查询 <account:string>", "查询宽带账号状态").action(async ({ session }, account) => {
|
|
911
|
+
if (!account) return "请输入要查询的账号";
|
|
912
|
+
return new Promise((resolve) => {
|
|
913
|
+
addToMessageQueue(session, async () => {
|
|
914
|
+
try {
|
|
915
|
+
const radiusUserInfo = await queryRadiusUser(account);
|
|
916
|
+
if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
|
|
917
|
+
return "账号授权有误,请重新授权";
|
|
718
918
|
}
|
|
919
|
+
const { user, orders } = radiusUserInfo.data;
|
|
920
|
+
const accountInfo = await queryAccount(account);
|
|
921
|
+
const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
|
|
922
|
+
const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
|
|
923
|
+
const minutes = accountInfo.startTimeSum % 60;
|
|
924
|
+
let durationText = "";
|
|
925
|
+
if (days > 0) {
|
|
926
|
+
durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
927
|
+
} else if (hours > 0) {
|
|
928
|
+
durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
929
|
+
} else {
|
|
930
|
+
durationText = `${minutes}分钟`;
|
|
931
|
+
}
|
|
932
|
+
const message = [
|
|
933
|
+
"🌐 账号查询结果",
|
|
934
|
+
"--------------------------------",
|
|
935
|
+
`账号: ${accountInfo.accessUserName}`,
|
|
936
|
+
// `RADIUS用户密码: ${user.password}`,
|
|
937
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
938
|
+
"",
|
|
939
|
+
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus.includes("在线") ? "✅" : "❌"}`,
|
|
940
|
+
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`,
|
|
941
|
+
`IP地址: ${accountInfo.userIp}`,
|
|
942
|
+
`MAC地址: ${accountInfo.mac}`,
|
|
943
|
+
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
944
|
+
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
945
|
+
`开始时间: ${accountInfo.startTime}`,
|
|
946
|
+
`在线时长: ${durationText}`,
|
|
947
|
+
""
|
|
948
|
+
];
|
|
949
|
+
if (orders && orders.length > 0) {
|
|
950
|
+
message.push("🛒 订购信息");
|
|
951
|
+
message.push("--------------------------------");
|
|
952
|
+
orders.forEach((order, index) => {
|
|
953
|
+
message.push(
|
|
954
|
+
`订购 #${index + 1}:`,
|
|
955
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
956
|
+
` 服务名称: ${order.service_name}`,
|
|
957
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
958
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
959
|
+
);
|
|
960
|
+
if (index < orders.length - 1) {
|
|
961
|
+
message.push("");
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
message.push("--------------------------------");
|
|
966
|
+
return message.join("\n");
|
|
967
|
+
} catch (error) {
|
|
968
|
+
console.error("查询过程出错:", error);
|
|
969
|
+
return error.message || "查询失败,请检查账号是否正确";
|
|
719
970
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
"
|
|
745
|
-
"
|
|
746
|
-
"
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
"
|
|
750
|
-
"
|
|
751
|
-
"十七": 17,
|
|
752
|
-
"十八": 18,
|
|
753
|
-
"十九": 19,
|
|
754
|
-
"二十": 20,
|
|
755
|
-
"二十一": 21,
|
|
756
|
-
"二十二": 22,
|
|
757
|
-
"二十三": 23,
|
|
758
|
-
"二十四": 24,
|
|
759
|
-
"二十五": 25,
|
|
760
|
-
"二十六": 26,
|
|
761
|
-
"二十七": 27,
|
|
762
|
-
"二十八": 28,
|
|
763
|
-
"二十九": 29,
|
|
764
|
-
"三十": 30,
|
|
765
|
-
"三十一": 31
|
|
766
|
-
};
|
|
767
|
-
const monthMatch = dateStr.match(/[一二三四五六七八九十]+月/);
|
|
768
|
-
const dayMatch = dateStr.match(/[一二三四五六七八九十]+[号日]/);
|
|
769
|
-
if (monthMatch && dayMatch) {
|
|
770
|
-
const monthStr = monthMatch[0].replace("月", "");
|
|
771
|
-
const dayStr = dayMatch[0].replace(/[号日]/, "");
|
|
772
|
-
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
773
|
-
const month = chineseMonthMap[monthStr];
|
|
774
|
-
const day = chineseDayMap[dayStr];
|
|
775
|
-
return new Date(year, month - 1, day);
|
|
971
|
+
});
|
|
972
|
+
resolve();
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
ctx.command("账号详情 <account:string>", "查询宽带账号详细记录").action(async ({ session }, account) => {
|
|
976
|
+
if (!account) return "请输入要查询的账号";
|
|
977
|
+
return new Promise((resolve) => {
|
|
978
|
+
addToMessageQueue(session, async () => {
|
|
979
|
+
try {
|
|
980
|
+
const records = await queryAccountDetail(account);
|
|
981
|
+
if (records.length === 0) {
|
|
982
|
+
return "未找到该账号的详细记录";
|
|
983
|
+
}
|
|
984
|
+
const messages = records.map((record, index) => [
|
|
985
|
+
`📋 记录 #${index + 1}`,
|
|
986
|
+
"--------------------------------",
|
|
987
|
+
`账号: ${record.accessUserName}`,
|
|
988
|
+
`记录类型: ${record.recordType}`,
|
|
989
|
+
`设备名称: ${record.nickName}`,
|
|
990
|
+
`IP地址: ${record.ip}`,
|
|
991
|
+
`MAC地址: ${record.mac}`,
|
|
992
|
+
`登录时间: ${record.userLoginInTime}`,
|
|
993
|
+
`登出时间: ${record.userLoginOutTime}`,
|
|
994
|
+
`原因: ${record.reason || "无"}`
|
|
995
|
+
].join("\n"));
|
|
996
|
+
let result = messages.join("\n\n");
|
|
997
|
+
result += "\n--------------------------------";
|
|
998
|
+
return result;
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
console.error("查询过程出错:", error);
|
|
1001
|
+
return error.message || "查询失败,请稍后重试";
|
|
776
1002
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
const
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1003
|
+
});
|
|
1004
|
+
resolve();
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
ctx.command("终端查询 <mac:string>", "查询终端基本信息").action(async ({ session }, mac) => {
|
|
1008
|
+
if (!mac) return "请输入要查询的MAC地址";
|
|
1009
|
+
return new Promise((resolve) => {
|
|
1010
|
+
addToMessageQueue(session, async () => {
|
|
1011
|
+
try {
|
|
1012
|
+
const formattedMac = formatMacAddress(mac);
|
|
1013
|
+
const onuList = await queryOnuList(formattedMac);
|
|
1014
|
+
if (!onuList) {
|
|
1015
|
+
return "未找到该MAC地址的设备信息";
|
|
1016
|
+
}
|
|
1017
|
+
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
1018
|
+
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
1019
|
+
const messages = [
|
|
1020
|
+
"🖥️ 终端基本信息",
|
|
1021
|
+
"--------------------------------",
|
|
1022
|
+
`MAC地址: ${formattedMac}`,
|
|
1023
|
+
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
1024
|
+
`设备名称: `,
|
|
1025
|
+
`${onuDetail.deviceName}`,
|
|
1026
|
+
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
1027
|
+
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
1028
|
+
"",
|
|
1029
|
+
"⚙️ 业务配置信息",
|
|
1030
|
+
"--------------------------------"
|
|
1031
|
+
];
|
|
1032
|
+
vlanInfo.forEach((info) => {
|
|
1033
|
+
messages.push(
|
|
1034
|
+
`端口 ${info.LAN}:`,
|
|
1035
|
+
` 端口VLAN: ${info.VLAN || "未配置"}`,
|
|
1036
|
+
` 工单VLAN: ${info.actualVlan || "未配置"}`
|
|
1037
|
+
);
|
|
1038
|
+
});
|
|
1039
|
+
messages.push("--------------------------------");
|
|
1040
|
+
return messages.join("\n");
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
console.error("查询过程出错:", error);
|
|
1043
|
+
return "查询失败,请稍后重试";
|
|
797
1044
|
}
|
|
798
|
-
}
|
|
799
|
-
|
|
1045
|
+
});
|
|
1046
|
+
resolve();
|
|
1047
|
+
});
|
|
1048
|
+
});
|
|
1049
|
+
ctx.command("终端详细查询 <mac:string>", "查询终端详细信息").action(async ({ session }, mac) => {
|
|
1050
|
+
if (!mac) return "请输入要查询的MAC地址";
|
|
1051
|
+
return new Promise((resolve) => {
|
|
1052
|
+
addToMessageQueue(session, async () => {
|
|
1053
|
+
try {
|
|
1054
|
+
const formattedMac = formatMacAddress(mac);
|
|
1055
|
+
const onuList = await queryOnuList(formattedMac);
|
|
1056
|
+
if (!onuList) {
|
|
1057
|
+
return "未找到该MAC地址的设备信息";
|
|
1058
|
+
}
|
|
1059
|
+
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
1060
|
+
const ccname = await queryCCName(formattedMac);
|
|
1061
|
+
const order = await queryOnuOrder(onuDetail);
|
|
1062
|
+
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
1063
|
+
const messages = [
|
|
1064
|
+
"🖥️ 终端基本信息",
|
|
1065
|
+
"--------------------------------",
|
|
1066
|
+
`MAC地址: ${formattedMac}`,
|
|
1067
|
+
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
1068
|
+
`机房: ${onuDetail.roomName}`,
|
|
1069
|
+
`设备名称: `,
|
|
1070
|
+
`${onuDetail.deviceName}`,
|
|
1071
|
+
`OLT IP: ${onuDetail.oltIp}`,
|
|
1072
|
+
`PON端口: ${onuDetail.portName}`,
|
|
1073
|
+
`端口逻辑号: ${onuDetail.logicId}`,
|
|
1074
|
+
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
1075
|
+
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
1076
|
+
`CCName: ${ccname}`,
|
|
1077
|
+
"",
|
|
1078
|
+
"📃 工单信息",
|
|
1079
|
+
"--------------------------------",
|
|
1080
|
+
order ? [
|
|
1081
|
+
`工单编号: ${order.orderId}`,
|
|
1082
|
+
`创建时间: ${order.createDate}`,
|
|
1083
|
+
`更新时间: ${order.updateDate}`
|
|
1084
|
+
].join("\n") : "无工单信息",
|
|
1085
|
+
"",
|
|
1086
|
+
"⚙️ 业务配置信息",
|
|
1087
|
+
"--------------------------------"
|
|
1088
|
+
];
|
|
1089
|
+
vlanInfo.forEach((info) => {
|
|
1090
|
+
messages.push(
|
|
1091
|
+
`端口 ${info.LAN}:`,
|
|
1092
|
+
` 端口VLAN: ${info.VLAN || "未配置"}`,
|
|
1093
|
+
` 工单VLAN: ${info.actualVlan || "未配置"}`
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
messages.push("--------------------------------");
|
|
1097
|
+
return messages.join("\n");
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
console.error("查询过程出错:", error);
|
|
1100
|
+
return "查询失败,请稍后重试";
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
resolve();
|
|
1104
|
+
});
|
|
1105
|
+
});
|
|
1106
|
+
ctx.command("日志查询 <mac:string> [startTime:string] [endTime:string]", "查询终端工单日志").action(async ({ session }, mac, startTime, endTime) => {
|
|
1107
|
+
if (!mac) return "请输入要查询的MAC地址";
|
|
1108
|
+
try {
|
|
1109
|
+
await session?.send("正在查询日志,请稍候...");
|
|
1110
|
+
const formattedMac = formatMacAddress(mac);
|
|
1111
|
+
const { formattedStartTime, formattedEndTime } = processTimeRange(startTime, endTime);
|
|
1112
|
+
const token = await getToken();
|
|
1113
|
+
const fetchLogsPromise = queryOrderLog(formattedMac, formattedStartTime, formattedEndTime, token);
|
|
1114
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1115
|
+
setTimeout(() => reject(new Error("查询超时,请稍后重试或缩短查询时间范围")), 3e4);
|
|
1116
|
+
});
|
|
1117
|
+
const logs = await Promise.race([fetchLogsPromise, timeoutPromise]).catch((error) => {
|
|
1118
|
+
console.error("日志查询超时或出错:", error);
|
|
1119
|
+
session?.send("日志查询耗时较长,结果将稍后发送。如长时间未收到,请重试或缩短查询范围");
|
|
1120
|
+
return fetchLogsPromise.catch(() => []);
|
|
1121
|
+
});
|
|
1122
|
+
const filteredLogs = Array.isArray(logs) ? logs.filter((log) => log.title && log.title.includes("上线分析")) : [];
|
|
1123
|
+
if (!filteredLogs || filteredLogs.length === 0) {
|
|
1124
|
+
return "未找到该MAC地址的上线分析日志";
|
|
800
1125
|
}
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
1126
|
+
const MAX_LOGS_PER_MESSAGE = 5;
|
|
1127
|
+
const totalBatches = Math.ceil(filteredLogs.length / MAX_LOGS_PER_MESSAGE);
|
|
1128
|
+
if (filteredLogs.length > MAX_LOGS_PER_MESSAGE) {
|
|
1129
|
+
await session?.send(formatOutputMessage(`共找到 ${filteredLogs.length} 条上线分析日志,将分${totalBatches}批发送`));
|
|
804
1130
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1131
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
1132
|
+
const startIdx = i * MAX_LOGS_PER_MESSAGE;
|
|
1133
|
+
const endIdx = Math.min(startIdx + MAX_LOGS_PER_MESSAGE, filteredLogs.length);
|
|
1134
|
+
const batchLogs = filteredLogs.slice(startIdx, endIdx);
|
|
1135
|
+
const messages = [
|
|
1136
|
+
`📝 上线分析日志 (第 ${i + 1}/${totalBatches} 批)`,
|
|
1137
|
+
"--------------------------------"
|
|
1138
|
+
];
|
|
1139
|
+
batchLogs.forEach((log, index) => {
|
|
811
1140
|
messages.push(
|
|
812
|
-
|
|
813
|
-
`标题: ${log.title}
|
|
1141
|
+
`📄 日志 #${startIdx + index + 1}`,
|
|
1142
|
+
`标题: ${log.title || "无标题"}`,
|
|
1143
|
+
`描述: ${log.description || "无描述"}`,
|
|
1144
|
+
`创建时间: ${log.createTime || "未知"}`,
|
|
1145
|
+
`更新时间: ${log.updateTime || "未知"}`,
|
|
1146
|
+
"--------------------------------"
|
|
814
1147
|
);
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
if (logLine.startsWith("1.") && logLine.includes("Pon端TrunkAll检查结果是:") || logLine.startsWith("2.") && logLine.includes("查询ccname=")) {
|
|
818
|
-
messages.push(` ${logLine}`);
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
});
|
|
1148
|
+
});
|
|
1149
|
+
if (!messages[messages.length - 1].includes("----")) {
|
|
822
1150
|
messages.push("--------------------------------");
|
|
823
1151
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1152
|
+
await session?.send(formatOutputMessage(messages.join("\n")));
|
|
1153
|
+
if (i < totalBatches - 1) {
|
|
1154
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1155
|
+
}
|
|
827
1156
|
}
|
|
828
|
-
return
|
|
1157
|
+
return;
|
|
829
1158
|
} catch (error) {
|
|
830
1159
|
console.error("查询过程出错:", error);
|
|
831
1160
|
return "查询失败,请稍后重试";
|
|
832
1161
|
}
|
|
833
1162
|
});
|
|
1163
|
+
ctx.command("用户查询 <account:string>", "查询RADIUS用户信息").action(async ({ session }, account) => {
|
|
1164
|
+
if (!account) return "请输入要查询的账号";
|
|
1165
|
+
return new Promise((resolve) => {
|
|
1166
|
+
addToMessageQueue(session, async () => {
|
|
1167
|
+
try {
|
|
1168
|
+
const radiusUserInfo = await queryRadiusUser(account);
|
|
1169
|
+
if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
|
|
1170
|
+
return `查询失败: ${radiusUserInfo.message || "未知错误"}`;
|
|
1171
|
+
}
|
|
1172
|
+
const { user, orders } = radiusUserInfo.data;
|
|
1173
|
+
const messages = [
|
|
1174
|
+
"🔐 RADIUS用户信息",
|
|
1175
|
+
"--------------------------------",
|
|
1176
|
+
`账号: ${user.login_name}`,
|
|
1177
|
+
`密码: ${user.password}`,
|
|
1178
|
+
`状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
1179
|
+
`漫游状态: ${user.roam_status === "0" ? "允许漫游" : "禁止漫游"}`,
|
|
1180
|
+
`分公司标识: ${user.org_code}`,
|
|
1181
|
+
`BOSS分公司标识: ${user.boss_org_code}`,
|
|
1182
|
+
`用户VLAN: ${user.user_vlan}`,
|
|
1183
|
+
"",
|
|
1184
|
+
"🛒 订购信息",
|
|
1185
|
+
"--------------------------------"
|
|
1186
|
+
];
|
|
1187
|
+
if (orders && orders.length > 0) {
|
|
1188
|
+
orders.forEach((order, index) => {
|
|
1189
|
+
messages.push(
|
|
1190
|
+
`订购 #${index + 1}:`,
|
|
1191
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
1192
|
+
` 服务名称: ${order.service_name}`,
|
|
1193
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
1194
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1195
|
+
);
|
|
1196
|
+
if (index < orders.length - 1) {
|
|
1197
|
+
messages.push("");
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
} else {
|
|
1201
|
+
messages.push("无订购信息");
|
|
1202
|
+
}
|
|
1203
|
+
messages.push("--------------------------------");
|
|
1204
|
+
return messages.join("\n");
|
|
1205
|
+
} catch (error) {
|
|
1206
|
+
console.error("查询过程出错:", error);
|
|
1207
|
+
return error.message || "查询失败,请稍后重试";
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
resolve();
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
834
1213
|
}
|
|
835
1214
|
__name(apply, "apply");
|
|
836
1215
|
// Annotate the CommonJS export names for ESM import in node:
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -2,38 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/koishi-plugin-jscn-aaaquery)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
江苏有线运维系统和RADIUS系统查询插件
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 功能概述
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
本插件提供了江苏有线运维系统和RADIUS系统的查询功能,包括账号查询、终端查询、日志查询和用户查询等功能。支持智能识别输入内容自动选择查询类型。
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 命令列表
|
|
12
12
|
|
|
13
13
|
### 1. 账号查询
|
|
14
14
|
- 命令格式:`账号查询 <账号>`
|
|
15
15
|
- 示例:`账号查询 GDC8510019239048`
|
|
16
16
|
- 功能:查询宽带账号的基本信息,包括在线状态、IP地址、MAC地址等。
|
|
17
|
+
- 返回信息:
|
|
18
|
+
- RADIUS用户信息:状态、密码等
|
|
19
|
+
- 账号基本信息:在线状态、认证状态、IP地址、MAC地址等
|
|
20
|
+
- 订购信息:订购状态、服务名称、生效时间、失效时间等
|
|
21
|
+
- 备注:账号会自动转换为大写,在线时长会以"天小时分钟"的形式显示。
|
|
17
22
|
|
|
18
23
|
### 2. 账号详情
|
|
19
24
|
- 命令格式:`账号详情 <账号>`
|
|
20
25
|
- 示例:`账号详情 GDC8510019239048`
|
|
21
26
|
- 功能:查询宽带账号的详细记录,包括登录时间、登出时间等。
|
|
27
|
+
- 备注:账号会自动转换为大写。
|
|
22
28
|
|
|
23
29
|
### 3. 终端查询
|
|
24
30
|
- 命令格式:`终端查询 <MAC地址>`
|
|
25
31
|
- 示例:`终端查询 00:11:22:33:44:55`
|
|
26
32
|
- 功能:查询终端的基本信息,包括光功率和业务配置信息。
|
|
33
|
+
- 返回信息:
|
|
34
|
+
- 设备基本信息:状态、设备名称等
|
|
35
|
+
- 光功率信息:发射光功率、接收光功率(包含正常/过高/过低评估)
|
|
36
|
+
- 业务配置信息:端口VLAN和工单VLAN
|
|
27
37
|
- MAC地址支持多种格式:
|
|
28
38
|
- 纯字符串:`001122334455`
|
|
29
39
|
- 冒号分隔:`00:11:22:33:44:55`
|
|
30
40
|
- 短横线分隔:`00-11-22-33-44-55`
|
|
31
41
|
- 点号分隔:`00.11.22.33.44.55`
|
|
42
|
+
- 中文符号分隔:`00:11。22-33|44:55`
|
|
32
43
|
|
|
33
44
|
### 4. 终端详细查询
|
|
34
45
|
- 命令格式:`终端详细查询 <MAC地址>`
|
|
35
46
|
- 示例:`终端详细查询 00:11:22:33:44:55`
|
|
36
47
|
- 功能:查询终端的详细信息,包括机房、设备名称、OLT IP、工单信息等。
|
|
48
|
+
- 返回信息:
|
|
49
|
+
- 设备详细信息:状态、机房、设备名称、OLT IP、PON端口、逻辑号等
|
|
50
|
+
- 光功率信息:发射光功率、接收光功率(包含正常/过高/过低评估)
|
|
51
|
+
- 工单信息:工单编号、创建时间、更新时间
|
|
52
|
+
- 业务配置信息:端口VLAN和工单VLAN配置详情
|
|
37
53
|
|
|
38
54
|
### 5. 日志查询
|
|
39
55
|
- 命令格式:`日志查询 <MAC地址> [开始时间] [结束时间]`
|
|
@@ -41,20 +57,88 @@
|
|
|
41
57
|
- `日志查询 00:11:22:33:44:55`
|
|
42
58
|
- `日志查询 00:11:22:33:44:55 2024.3.1 2024.3.10`
|
|
43
59
|
- `日志查询 00:11:22:33:44:55 三月一日`
|
|
44
|
-
-
|
|
60
|
+
- 功能:查询终端的上线分析日志。
|
|
45
61
|
- 时间格式支持:
|
|
46
62
|
- 完整日期:`2024.3.1` 或 `2024/3/1`
|
|
47
63
|
- 简写日期:`3.1` 或 `3/1`(自动补全当前年份)
|
|
48
64
|
- 中文日期:`三月一日` 或 `三月一号`
|
|
49
65
|
|
|
50
|
-
### 6.
|
|
66
|
+
### 6. 用户查询
|
|
67
|
+
- 命令格式:`用户查询 <账号>`
|
|
68
|
+
- 示例:`用户查询 GDC8510013590471`
|
|
69
|
+
- 功能:查询RADIUS系统中的用户信息和订购状态。
|
|
70
|
+
- 返回信息:
|
|
71
|
+
- 用户基本信息:账号、密码、状态、漫游设置等
|
|
72
|
+
- 订购信息:订购状态、服务名称、生效时间、失效时间等
|
|
73
|
+
|
|
74
|
+
### 7. 智能查询
|
|
51
75
|
- 直接输入MAC地址或账号即可查询,无需输入命令。
|
|
52
76
|
- 示例:
|
|
53
77
|
- `00:11:22:33:44:55`(自动识别为MAC地址并查询终端详情)
|
|
54
78
|
- `GDC8510019239048`(自动识别为账号并查询账号信息)
|
|
79
|
+
- 支持@机器人后发送查询内容
|
|
80
|
+
|
|
81
|
+
## 插件配置
|
|
82
|
+
|
|
83
|
+
### 必需配置项
|
|
84
|
+
- `loginName`:运维系统登录账号
|
|
85
|
+
- `password`:运维系统登录密码
|
|
86
|
+
- `baseUrl`:运维系统API地址(默认:`http://172.16.251.75:8090/nms/api`)
|
|
87
|
+
|
|
88
|
+
### 可选配置项
|
|
89
|
+
- `radiusApiUrl`:RADIUS API基础URL(默认:`http://111.208.114.248:28210/api/radius`)
|
|
90
|
+
|
|
91
|
+
## 性能优化
|
|
92
|
+
|
|
93
|
+
插件内置了以下优化机制:
|
|
94
|
+
1. **缓存机制**:缓存查询结果,减少重复请求(缓存时间默认5分钟)
|
|
95
|
+
2. **令牌管理**:自动处理登录令牌的获取和刷新
|
|
96
|
+
3. **请求队列**:防止相同请求重复发送,降低服务器负载
|
|
97
|
+
4. **并发控制**:限制最大并发请求数,防止请求过多导致系统不稳定
|
|
98
|
+
5. **消息队列**:使用消息处理队列,确保在高并发下消息不丢失
|
|
99
|
+
6. **光功率评估**:自动评估接收光功率是否在正常范围内(-24dBm ~ -8dBm)
|
|
100
|
+
7. **超时处理**:设置请求超时,自动处理长时间未响应的请求
|
|
101
|
+
8. **格式优化**:优化消息显示格式,使用直观的图标显示状态信息
|
|
102
|
+
9. **错误处理**:完善的错误处理机制,确保查询失败时有清晰的提示
|
|
103
|
+
|
|
104
|
+
## 显示优化
|
|
105
|
+
|
|
106
|
+
1. **状态指示**:
|
|
107
|
+
- 在线/正常状态:✅
|
|
108
|
+
- 离线/错误状态:❌
|
|
109
|
+
- 警告状态:⚠️
|
|
110
|
+
|
|
111
|
+
2. **光功率评估**:
|
|
112
|
+
- 正常范围(-24dBm ~ -8dBm):🟢正常
|
|
113
|
+
- 光功率过高(> -8dBm):🔴过高
|
|
114
|
+
- 光功率过低(< -24dBm):🔴过低
|
|
115
|
+
|
|
116
|
+
3. **时间格式**:
|
|
117
|
+
- 在线时长显示为"天小时分钟"形式,例如"2天3小时15分钟"
|
|
118
|
+
|
|
119
|
+
4. **消息格式**:
|
|
120
|
+
- 丰富的图标使信息更加直观
|
|
121
|
+
- 分级显示使信息层次清晰
|
|
122
|
+
- 内容丰富的查询结果使用新行显示
|
|
123
|
+
- 简短提示直接显示在同一行
|
|
55
124
|
|
|
56
125
|
## 注意事项
|
|
57
126
|
1. 所有查询都需要正确的权限和配置。
|
|
58
|
-
2. MAC
|
|
59
|
-
3.
|
|
60
|
-
4.
|
|
127
|
+
2. MAC地址不区分大小写,支持多种分隔符格式,包括中文符号。
|
|
128
|
+
3. 账号查询时会自动转换为大写。
|
|
129
|
+
4. 日期格式支持多种写法,但建议使用标准格式。
|
|
130
|
+
5. 如果查询失败,请检查输入格式是否正确。
|
|
131
|
+
6. 确保运维系统和RADIUS系统API能够正常访问。
|
|
132
|
+
7. 账号查询时会先检查RADIUS授权,如授权有误将不继续查询账号信息。
|
|
133
|
+
|
|
134
|
+
## 更新日志
|
|
135
|
+
|
|
136
|
+
### 最新更新
|
|
137
|
+
- 增加了光功率评估功能,直观显示接收光功率是否在正常范围内
|
|
138
|
+
- 优化了在线时长显示,支持天/小时/分钟格式
|
|
139
|
+
- 增强了MAC地址格式处理,支持中文符号分隔
|
|
140
|
+
- 添加了状态指示图标,使查询结果更加直观
|
|
141
|
+
- 优化了消息格式,内容丰富的查询使用新行显示
|
|
142
|
+
- 增加了RADIUS用户查询前置验证
|
|
143
|
+
- 无论设备在线状态如何,都会尝试刷新光功率信息
|
|
144
|
+
- 增强了高并发下的消息处理能力
|