koishi-plugin-jscn-aaaquery 1.0.3 → 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.js +617 -370
- package/package.json +1 -1
- package/readme.md +93 -9
package/lib/index.js
CHANGED
|
@@ -34,6 +34,23 @@ var Config = import_koishi.Schema.object({
|
|
|
34
34
|
// 添加RADIUS API的配置项
|
|
35
35
|
radiusApiUrl: import_koishi.Schema.string().description("RADIUS API基础URL").default("http://111.208.114.248:28210/api/radius")
|
|
36
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");
|
|
37
54
|
function apply(ctx, config) {
|
|
38
55
|
let currentToken = null;
|
|
39
56
|
let tokenExpireTime = 0;
|
|
@@ -46,6 +63,11 @@ function apply(ctx, config) {
|
|
|
46
63
|
const MAX_CONCURRENT_REQUESTS = 10;
|
|
47
64
|
let currentConcurrentRequests = 0;
|
|
48
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;
|
|
49
71
|
function getCacheKey(type, params) {
|
|
50
72
|
return `${type}:${JSON.stringify(params)}`;
|
|
51
73
|
}
|
|
@@ -129,7 +151,7 @@ function apply(ctx, config) {
|
|
|
129
151
|
}
|
|
130
152
|
__name(addToQueue, "addToQueue");
|
|
131
153
|
function formatMacAddress(mac) {
|
|
132
|
-
return mac.replace(/[:-\s
|
|
154
|
+
return mac.replace(/[:-\s.:。-|]/g, "").toLowerCase();
|
|
133
155
|
}
|
|
134
156
|
__name(formatMacAddress, "formatMacAddress");
|
|
135
157
|
function isValidMacAddress(mac) {
|
|
@@ -310,32 +332,42 @@ function apply(ctx, config) {
|
|
|
310
332
|
});
|
|
311
333
|
console.log("查询响应:", JSON.stringify(response, null, 2));
|
|
312
334
|
if (response.status === 200 && response.data) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
"Content-Type": "application/json"
|
|
328
|
-
}
|
|
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"
|
|
329
349
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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 {
|
|
333
361
|
response.data.onuReceivePower = powerResponse.data.onuReceivePower;
|
|
334
362
|
response.data.onuSendPower = powerResponse.data.onuSendPower;
|
|
335
|
-
response.data.statusHealth = powerResponse.data.statusHealth;
|
|
336
363
|
}
|
|
337
|
-
|
|
338
|
-
|
|
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 = "设备不在线";
|
|
339
371
|
}
|
|
340
372
|
}
|
|
341
373
|
setCache("onuDetail", { onuId }, response.data);
|
|
@@ -453,7 +485,7 @@ function apply(ctx, config) {
|
|
|
453
485
|
});
|
|
454
486
|
}
|
|
455
487
|
__name(queryOnuDetailVlanInfo, "queryOnuDetailVlanInfo");
|
|
456
|
-
async function queryOrderLog(mac, startTime, endTime) {
|
|
488
|
+
async function queryOrderLog(mac, startTime, endTime, token) {
|
|
457
489
|
const cacheKey = { mac, startTime, endTime };
|
|
458
490
|
const cachedData = getFromCache("orderLog", cacheKey);
|
|
459
491
|
if (cachedData) {
|
|
@@ -463,7 +495,6 @@ function apply(ctx, config) {
|
|
|
463
495
|
return addToQueue("orderLog", cacheKey, async () => {
|
|
464
496
|
try {
|
|
465
497
|
console.log("开始查询工单日志...");
|
|
466
|
-
const token = await getToken();
|
|
467
498
|
const formattedMac = formatMacAddress(mac);
|
|
468
499
|
console.log("处理后的MAC地址:", formattedMac);
|
|
469
500
|
const response = await ctx.http.get(`${config.baseUrl}/fttx/orderAnalyBlock/selectorderAnalyBlockOneList`, {
|
|
@@ -563,43 +594,488 @@ function apply(ctx, config) {
|
|
|
563
594
|
}
|
|
564
595
|
}
|
|
565
596
|
__name(translateUserStatus, "translateUserStatus");
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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;
|
|
570
677
|
try {
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
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);
|
|
574
693
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
694
|
+
} catch (error) {
|
|
695
|
+
throw new Error("日期格式错误,支持的格式:\n1. 2025.4.4 或 2025/4/4\n2. 4.7 或 4/7(自动补全当前年份)\n3. 四月五号\n4. 四月五日");
|
|
696
|
+
}
|
|
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;
|
|
717
|
+
try {
|
|
718
|
+
const task = messageQueue.shift();
|
|
719
|
+
if (!task) {
|
|
720
|
+
isProcessingMessage = false;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
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("消息处理完成");
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error("处理消息队列任务出错:", error);
|
|
738
|
+
} finally {
|
|
739
|
+
isProcessingMessage = false;
|
|
740
|
+
setTimeout(processMessageQueue, 100);
|
|
741
|
+
}
|
|
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));
|
|
749
|
+
}
|
|
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
|
+
});
|
|
909
|
+
});
|
|
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 "账号授权有误,请重新授权";
|
|
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 || "查询失败,请检查账号是否正确";
|
|
970
|
+
}
|
|
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 || "查询失败,请稍后重试";
|
|
1002
|
+
}
|
|
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 "查询失败,请稍后重试";
|
|
1044
|
+
}
|
|
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 () => {
|
|
579
1053
|
try {
|
|
580
|
-
const
|
|
1054
|
+
const formattedMac = formatMacAddress(mac);
|
|
1055
|
+
const onuList = await queryOnuList(formattedMac);
|
|
581
1056
|
if (!onuList) {
|
|
582
1057
|
return "未找到该MAC地址的设备信息";
|
|
583
1058
|
}
|
|
584
1059
|
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
585
|
-
const ccname = await queryCCName(
|
|
1060
|
+
const ccname = await queryCCName(formattedMac);
|
|
586
1061
|
const order = await queryOnuOrder(onuDetail);
|
|
587
1062
|
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
588
1063
|
const messages = [
|
|
589
|
-
"
|
|
1064
|
+
"🖥️ 终端基本信息",
|
|
590
1065
|
"--------------------------------",
|
|
591
|
-
`MAC地址: ${
|
|
592
|
-
`状态:${onuDetail.status === 1 ? "在线" : "不在线"}`,
|
|
1066
|
+
`MAC地址: ${formattedMac}`,
|
|
1067
|
+
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
593
1068
|
`机房: ${onuDetail.roomName}`,
|
|
594
|
-
`设备名称:
|
|
1069
|
+
`设备名称: `,
|
|
1070
|
+
`${onuDetail.deviceName}`,
|
|
595
1071
|
`OLT IP: ${onuDetail.oltIp}`,
|
|
596
1072
|
`PON端口: ${onuDetail.portName}`,
|
|
597
1073
|
`端口逻辑号: ${onuDetail.logicId}`,
|
|
598
|
-
`发射光功率: ${onuDetail.onuSendPower}
|
|
599
|
-
`接收光功率: ${onuDetail.onuReceivePower}
|
|
1074
|
+
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
1075
|
+
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
600
1076
|
`CCName: ${ccname}`,
|
|
601
1077
|
"",
|
|
602
|
-
"
|
|
1078
|
+
"📃 工单信息",
|
|
603
1079
|
"--------------------------------",
|
|
604
1080
|
order ? [
|
|
605
1081
|
`工单编号: ${order.orderId}`,
|
|
@@ -607,361 +1083,132 @@ function apply(ctx, config) {
|
|
|
607
1083
|
`更新时间: ${order.updateDate}`
|
|
608
1084
|
].join("\n") : "无工单信息",
|
|
609
1085
|
"",
|
|
610
|
-
"
|
|
1086
|
+
"⚙️ 业务配置信息",
|
|
611
1087
|
"--------------------------------"
|
|
612
1088
|
];
|
|
613
1089
|
vlanInfo.forEach((info) => {
|
|
614
1090
|
messages.push(
|
|
615
1091
|
`端口 ${info.LAN}:`,
|
|
616
|
-
` VLAN: ${info.
|
|
1092
|
+
` 端口VLAN: ${info.VLAN || "未配置"}`,
|
|
1093
|
+
` 工单VLAN: ${info.actualVlan || "未配置"}`
|
|
617
1094
|
);
|
|
618
1095
|
});
|
|
1096
|
+
messages.push("--------------------------------");
|
|
619
1097
|
return messages.join("\n");
|
|
620
1098
|
} catch (error) {
|
|
621
|
-
console.error("
|
|
1099
|
+
console.error("查询过程出错:", error);
|
|
622
1100
|
return "查询失败,请稍后重试";
|
|
623
1101
|
}
|
|
624
|
-
} else {
|
|
625
|
-
console.log("内容不是MAC地址,尝试作为账号查询");
|
|
626
|
-
try {
|
|
627
|
-
const accountInfo = await queryAccount(input);
|
|
628
|
-
const message = [
|
|
629
|
-
"📊 账号查询结果",
|
|
630
|
-
"--------------------------------",
|
|
631
|
-
`账号: ${accountInfo.accessUserName}`,
|
|
632
|
-
`状态: ${accountInfo.onlineStatus}`,
|
|
633
|
-
`认证: ${accountInfo.isPass}`,
|
|
634
|
-
`IP地址: ${accountInfo.userIp}`,
|
|
635
|
-
`MAC地址: ${accountInfo.mac}`,
|
|
636
|
-
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
637
|
-
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
638
|
-
`接入域: ${accountInfo.accessDomain}`,
|
|
639
|
-
`开始时间: ${accountInfo.startTime}`,
|
|
640
|
-
`在线时长: ${accountInfo.startTimeSum}分钟`,
|
|
641
|
-
`设备名称: ${accountInfo.nickName}`
|
|
642
|
-
].join("\n");
|
|
643
|
-
return message;
|
|
644
|
-
} catch (error) {
|
|
645
|
-
console.error("查询账号过程出错:", error);
|
|
646
|
-
console.log("账号查询失败,交给其他中间件处理");
|
|
647
|
-
return next();
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
} catch (error) {
|
|
651
|
-
console.error("智能查询过程出错:", error);
|
|
652
|
-
return next();
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
ctx.command("账号查询 <account:string>", "查询宽带账号状态").action(async (_, account) => {
|
|
656
|
-
if (!account) return "请输入要查询的账号";
|
|
657
|
-
try {
|
|
658
|
-
const accountInfo = await queryAccount(account);
|
|
659
|
-
const message = [
|
|
660
|
-
"📊 账号查询结果",
|
|
661
|
-
"--------------------------------",
|
|
662
|
-
`账号: ${accountInfo.accessUserName}`,
|
|
663
|
-
`状态: ${accountInfo.onlineStatus}`,
|
|
664
|
-
`认证: ${accountInfo.isPass}`,
|
|
665
|
-
`IP地址: ${accountInfo.userIp}`,
|
|
666
|
-
`MAC地址: ${accountInfo.mac}`,
|
|
667
|
-
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
668
|
-
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
669
|
-
`接入域: ${accountInfo.accessDomain}`,
|
|
670
|
-
`开始时间: ${accountInfo.startTime}`,
|
|
671
|
-
`在线时长: ${accountInfo.startTimeSum}分钟`,
|
|
672
|
-
`设备名称: ${accountInfo.nickName}`
|
|
673
|
-
].join("\n");
|
|
674
|
-
return message;
|
|
675
|
-
} catch (error) {
|
|
676
|
-
console.error("查询过程出错:", error);
|
|
677
|
-
return error.message || "查询失败,请检查账号是否正确";
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
ctx.command("账号详情 <account:string>", "查询宽带账号详细记录").action(async (_, account) => {
|
|
681
|
-
if (!account) return "请输入要查询的账号";
|
|
682
|
-
try {
|
|
683
|
-
const records = await queryAccountDetail(account);
|
|
684
|
-
if (records.length === 0) {
|
|
685
|
-
return "未找到该账号的详细记录";
|
|
686
|
-
}
|
|
687
|
-
const messages = records.map((record, index) => [
|
|
688
|
-
`📝 记录 #${index + 1}`,
|
|
689
|
-
"--------------------------------",
|
|
690
|
-
`账号: ${record.accessUserName}`,
|
|
691
|
-
`记录类型: ${record.recordType}`,
|
|
692
|
-
`设备名称: ${record.nickName}`,
|
|
693
|
-
`IP地址: ${record.ip}`,
|
|
694
|
-
`MAC地址: ${record.mac}`,
|
|
695
|
-
`登录时间: ${record.userLoginInTime}`,
|
|
696
|
-
`登出时间: ${record.userLoginOutTime}`,
|
|
697
|
-
`原因: ${record.reason || "无"}`
|
|
698
|
-
].join("\n"));
|
|
699
|
-
return messages.join("\n\n");
|
|
700
|
-
} catch (error) {
|
|
701
|
-
console.error("查询过程出错:", error);
|
|
702
|
-
return error.message || "查询失败,请稍后重试";
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
ctx.command("终端查询 <mac:string>", "查询终端基本信息").action(async (_, mac) => {
|
|
706
|
-
if (!mac) return "请输入要查询的MAC地址";
|
|
707
|
-
try {
|
|
708
|
-
const formattedMac = formatMacAddress(mac);
|
|
709
|
-
const onuList = await queryOnuList(formattedMac);
|
|
710
|
-
if (!onuList) {
|
|
711
|
-
return "未找到该MAC地址的设备信息";
|
|
712
|
-
}
|
|
713
|
-
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
714
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
715
|
-
const messages = [
|
|
716
|
-
"📊 终端基本信息",
|
|
717
|
-
"--------------------------------",
|
|
718
|
-
`MAC地址: ${formattedMac}`,
|
|
719
|
-
`状态:${onuDetail.status === 1 ? "在线" : "不在线"}`,
|
|
720
|
-
`发射光功率: ${onuDetail.onuSendPower}dBm`,
|
|
721
|
-
`接收光功率: ${onuDetail.onuReceivePower}dBm`,
|
|
722
|
-
"",
|
|
723
|
-
"🔧 业务配置信息",
|
|
724
|
-
"--------------------------------"
|
|
725
|
-
];
|
|
726
|
-
vlanInfo.forEach((info) => {
|
|
727
|
-
messages.push(
|
|
728
|
-
`端口 ${info.LAN}:`,
|
|
729
|
-
` VLAN: ${info.actualVlan}`
|
|
730
|
-
);
|
|
731
1102
|
});
|
|
732
|
-
|
|
733
|
-
}
|
|
734
|
-
console.error("查询过程出错:", error);
|
|
735
|
-
return "查询失败,请稍后重试";
|
|
736
|
-
}
|
|
1103
|
+
resolve();
|
|
1104
|
+
});
|
|
737
1105
|
});
|
|
738
|
-
ctx.command("
|
|
1106
|
+
ctx.command("日志查询 <mac:string> [startTime:string] [endTime:string]", "查询终端工单日志").action(async ({ session }, mac, startTime, endTime) => {
|
|
739
1107
|
if (!mac) return "请输入要查询的MAC地址";
|
|
740
1108
|
try {
|
|
1109
|
+
await session?.send("正在查询日志,请稍候...");
|
|
741
1110
|
const formattedMac = formatMacAddress(mac);
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const ccname = await queryCCName(formattedMac);
|
|
748
|
-
const order = await queryOnuOrder(onuDetail);
|
|
749
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
750
|
-
const messages = [
|
|
751
|
-
"📊 终端基本信息",
|
|
752
|
-
"--------------------------------",
|
|
753
|
-
`MAC地址: ${formattedMac}`,
|
|
754
|
-
`状态:${onuDetail.status === 1 ? "在线" : "不在线"}`,
|
|
755
|
-
`机房: ${onuDetail.roomName}`,
|
|
756
|
-
`设备名称: ${onuDetail.deviceName}`,
|
|
757
|
-
`OLT IP: ${onuDetail.oltIp}`,
|
|
758
|
-
`PON端口: ${onuDetail.portName}`,
|
|
759
|
-
`端口逻辑号: ${onuDetail.logicId}`,
|
|
760
|
-
`发射光功率: ${onuDetail.onuSendPower}dBm`,
|
|
761
|
-
`接收光功率: ${onuDetail.onuReceivePower}dBm`,
|
|
762
|
-
`CCName: ${ccname}`,
|
|
763
|
-
"",
|
|
764
|
-
"📝 工单信息",
|
|
765
|
-
"--------------------------------",
|
|
766
|
-
order ? [
|
|
767
|
-
`工单编号: ${order.orderId}`,
|
|
768
|
-
`创建时间: ${order.createDate}`,
|
|
769
|
-
`更新时间: ${order.updateDate}`
|
|
770
|
-
].join("\n") : "无工单信息",
|
|
771
|
-
"",
|
|
772
|
-
"🔧 业务配置信息",
|
|
773
|
-
"--------------------------------"
|
|
774
|
-
];
|
|
775
|
-
vlanInfo.forEach((info) => {
|
|
776
|
-
messages.push(
|
|
777
|
-
`端口 ${info.LAN}:`,
|
|
778
|
-
` VLAN: ${info.actualVlan}`
|
|
779
|
-
);
|
|
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);
|
|
780
1116
|
});
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
try {
|
|
790
|
-
const formatDate = /* @__PURE__ */ __name((date) => {
|
|
791
|
-
const year = date.getFullYear();
|
|
792
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
793
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
794
|
-
return `${year}-${month}-${day}`;
|
|
795
|
-
}, "formatDate");
|
|
796
|
-
const parseDate = /* @__PURE__ */ __name((dateStr) => {
|
|
797
|
-
if (dateStr.includes(".") || dateStr.includes("/")) {
|
|
798
|
-
const parts = dateStr.split(/[./]/);
|
|
799
|
-
if (parts.length === 3) {
|
|
800
|
-
const [year, month, day] = parts.map(Number);
|
|
801
|
-
return new Date(year, month - 1, day);
|
|
802
|
-
} else if (parts.length === 2) {
|
|
803
|
-
const [month, day] = parts.map(Number);
|
|
804
|
-
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
805
|
-
return new Date(year, month - 1, day);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
const chineseMonthMap = {
|
|
809
|
-
"一": 1,
|
|
810
|
-
"二": 2,
|
|
811
|
-
"三": 3,
|
|
812
|
-
"四": 4,
|
|
813
|
-
"五": 5,
|
|
814
|
-
"六": 6,
|
|
815
|
-
"七": 7,
|
|
816
|
-
"八": 8,
|
|
817
|
-
"九": 9,
|
|
818
|
-
"十": 10,
|
|
819
|
-
"十一": 11,
|
|
820
|
-
"十二": 12
|
|
821
|
-
};
|
|
822
|
-
const chineseDayMap = {
|
|
823
|
-
"一": 1,
|
|
824
|
-
"二": 2,
|
|
825
|
-
"三": 3,
|
|
826
|
-
"四": 4,
|
|
827
|
-
"五": 5,
|
|
828
|
-
"六": 6,
|
|
829
|
-
"七": 7,
|
|
830
|
-
"八": 8,
|
|
831
|
-
"九": 9,
|
|
832
|
-
"十": 10,
|
|
833
|
-
"十一": 11,
|
|
834
|
-
"十二": 12,
|
|
835
|
-
"十三": 13,
|
|
836
|
-
"十四": 14,
|
|
837
|
-
"十五": 15,
|
|
838
|
-
"十六": 16,
|
|
839
|
-
"十七": 17,
|
|
840
|
-
"十八": 18,
|
|
841
|
-
"十九": 19,
|
|
842
|
-
"二十": 20,
|
|
843
|
-
"二十一": 21,
|
|
844
|
-
"二十二": 22,
|
|
845
|
-
"二十三": 23,
|
|
846
|
-
"二十四": 24,
|
|
847
|
-
"二十五": 25,
|
|
848
|
-
"二十六": 26,
|
|
849
|
-
"二十七": 27,
|
|
850
|
-
"二十八": 28,
|
|
851
|
-
"二十九": 29,
|
|
852
|
-
"三十": 30,
|
|
853
|
-
"三十一": 31
|
|
854
|
-
};
|
|
855
|
-
const monthMatch = dateStr.match(/[一二三四五六七八九十]+月/);
|
|
856
|
-
const dayMatch = dateStr.match(/[一二三四五六七八九十]+[号日]/);
|
|
857
|
-
if (monthMatch && dayMatch) {
|
|
858
|
-
const monthStr = monthMatch[0].replace("月", "");
|
|
859
|
-
const dayStr = dayMatch[0].replace(/[号日]/, "");
|
|
860
|
-
const year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
861
|
-
const month = chineseMonthMap[monthStr];
|
|
862
|
-
const day = chineseDayMap[dayStr];
|
|
863
|
-
return new Date(year, month - 1, day);
|
|
864
|
-
}
|
|
865
|
-
throw new Error("不支持的日期格式");
|
|
866
|
-
}, "parseDate");
|
|
867
|
-
let queryStartTime;
|
|
868
|
-
let queryEndTime;
|
|
869
|
-
try {
|
|
870
|
-
if (startTime && endTime) {
|
|
871
|
-
queryStartTime = formatDate(parseDate(startTime));
|
|
872
|
-
queryEndTime = formatDate(parseDate(endTime));
|
|
873
|
-
} else if (startTime) {
|
|
874
|
-
const endDate = parseDate(startTime);
|
|
875
|
-
const startDate = new Date(endDate);
|
|
876
|
-
startDate.setDate(startDate.getDate() - 1);
|
|
877
|
-
queryStartTime = formatDate(startDate);
|
|
878
|
-
queryEndTime = formatDate(endDate);
|
|
879
|
-
} else {
|
|
880
|
-
const today = /* @__PURE__ */ new Date();
|
|
881
|
-
const yesterday = new Date(today);
|
|
882
|
-
yesterday.setDate(yesterday.getDate() - 1);
|
|
883
|
-
queryStartTime = formatDate(yesterday);
|
|
884
|
-
queryEndTime = formatDate(today);
|
|
885
|
-
}
|
|
886
|
-
} catch (error) {
|
|
887
|
-
return "日期格式错误,支持的格式:\n1. 2025.4.4 或 2025/4/4\n2. 4.7 或 4/7(自动补全当前年份)\n3. 四月五号\n4. 四月五日";
|
|
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地址的上线分析日志";
|
|
888
1125
|
}
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
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}批发送`));
|
|
892
1130
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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) => {
|
|
899
1140
|
messages.push(
|
|
900
|
-
|
|
901
|
-
`标题: ${log.title}
|
|
1141
|
+
`📄 日志 #${startIdx + index + 1}`,
|
|
1142
|
+
`标题: ${log.title || "无标题"}`,
|
|
1143
|
+
`描述: ${log.description || "无描述"}`,
|
|
1144
|
+
`创建时间: ${log.createTime || "未知"}`,
|
|
1145
|
+
`更新时间: ${log.updateTime || "未知"}`,
|
|
1146
|
+
"--------------------------------"
|
|
902
1147
|
);
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (logLine.startsWith("1.") && logLine.includes("Pon端TrunkAll检查结果是:") || logLine.startsWith("2.") && logLine.includes("查询ccname=")) {
|
|
906
|
-
messages.push(` ${logLine}`);
|
|
907
|
-
}
|
|
908
|
-
});
|
|
909
|
-
});
|
|
1148
|
+
});
|
|
1149
|
+
if (!messages[messages.length - 1].includes("----")) {
|
|
910
1150
|
messages.push("--------------------------------");
|
|
911
1151
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1152
|
+
await session?.send(formatOutputMessage(messages.join("\n")));
|
|
1153
|
+
if (i < totalBatches - 1) {
|
|
1154
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1155
|
+
}
|
|
915
1156
|
}
|
|
916
|
-
return
|
|
1157
|
+
return;
|
|
917
1158
|
} catch (error) {
|
|
918
1159
|
console.error("查询过程出错:", error);
|
|
919
1160
|
return "查询失败,请稍后重试";
|
|
920
1161
|
}
|
|
921
1162
|
});
|
|
922
|
-
ctx.command("用户查询 <account:string>", "查询RADIUS用户信息").action(async (
|
|
1163
|
+
ctx.command("用户查询 <account:string>", "查询RADIUS用户信息").action(async ({ session }, account) => {
|
|
923
1164
|
if (!account) return "请输入要查询的账号";
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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
|
+
});
|
|
965
1212
|
});
|
|
966
1213
|
}
|
|
967
1214
|
__name(apply, "apply");
|
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
|
+
- 增强了高并发下的消息处理能力
|