koishi-plugin-jscn-aaaquery 1.0.6 → 1.0.8
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 +459 -212
- package/package.json +1 -1
- package/readme.md +50 -18
package/lib/index.js
CHANGED
|
@@ -51,22 +51,34 @@ function evaluateOpticalPower(power) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
__name(evaluateOpticalPower, "evaluateOpticalPower");
|
|
54
|
+
var CACHE_EXPIRE_TIME = 5 * 60 * 1e3;
|
|
55
|
+
var CACHE_CLEAN_INTERVAL = 60 * 1e3;
|
|
56
|
+
var CACHE_STATS_RESET_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
57
|
+
var QUEUE_EXPIRE_TIME = 2 * 60 * 1e3;
|
|
58
|
+
var QUEUE_CLEAN_INTERVAL = 30 * 1e3;
|
|
59
|
+
var MAX_CONCURRENT_REQUESTS = 5;
|
|
60
|
+
var MESSAGE_TIMEOUT = 2 * 60 * 1e3;
|
|
61
|
+
var MESSAGE_CLEAN_INTERVAL = 60 * 1e3;
|
|
54
62
|
function apply(ctx, config) {
|
|
55
63
|
let currentToken = null;
|
|
56
64
|
let tokenExpireTime = 0;
|
|
57
65
|
const requestQueue = {};
|
|
58
66
|
const cache = {};
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const cacheStats = {
|
|
68
|
+
hits: 0,
|
|
69
|
+
misses: 0,
|
|
70
|
+
totalItems: 0,
|
|
71
|
+
totalSize: 0,
|
|
72
|
+
oldestItemAge: 0,
|
|
73
|
+
cleanups: 0,
|
|
74
|
+
itemsRemoved: 0
|
|
75
|
+
};
|
|
64
76
|
let currentConcurrentRequests = 0;
|
|
65
77
|
const requestWaitQueue = [];
|
|
66
78
|
const messageQueue = [];
|
|
67
79
|
let isProcessingMessage = false;
|
|
68
|
-
const
|
|
69
|
-
const
|
|
80
|
+
const MESSAGE_TIMEOUT2 = 2 * 60 * 1e3;
|
|
81
|
+
const MESSAGE_CLEAN_INTERVAL2 = 60 * 1e3;
|
|
70
82
|
const MAX_QUEUE_LENGTH = 50;
|
|
71
83
|
function getCacheKey(type, params) {
|
|
72
84
|
return `${type}:${JSON.stringify(params)}`;
|
|
@@ -76,8 +88,10 @@ function apply(ctx, config) {
|
|
|
76
88
|
const key = getCacheKey(type, params);
|
|
77
89
|
const item = cache[key];
|
|
78
90
|
if (item && Date.now() - item.timestamp <= CACHE_EXPIRE_TIME) {
|
|
91
|
+
cacheStats.hits++;
|
|
79
92
|
return item.data;
|
|
80
93
|
}
|
|
94
|
+
cacheStats.misses++;
|
|
81
95
|
return null;
|
|
82
96
|
}
|
|
83
97
|
__name(getFromCache, "getFromCache");
|
|
@@ -87,16 +101,71 @@ function apply(ctx, config) {
|
|
|
87
101
|
data,
|
|
88
102
|
timestamp: Date.now()
|
|
89
103
|
};
|
|
104
|
+
updateCacheStats();
|
|
90
105
|
}
|
|
91
106
|
__name(setCache, "setCache");
|
|
107
|
+
function updateCacheStats() {
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
cacheStats.totalItems = Object.keys(cache).length;
|
|
110
|
+
let oldestTimestamp = now;
|
|
111
|
+
let totalSizeEstimate = 0;
|
|
112
|
+
for (const key in cache) {
|
|
113
|
+
const itemSize = JSON.stringify(cache[key].data).length / 1024;
|
|
114
|
+
totalSizeEstimate += itemSize;
|
|
115
|
+
if (cache[key].timestamp < oldestTimestamp) {
|
|
116
|
+
oldestTimestamp = cache[key].timestamp;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
cacheStats.totalSize = Math.round(totalSizeEstimate * 100) / 100;
|
|
120
|
+
cacheStats.oldestItemAge = Math.round((now - oldestTimestamp) / 1e3);
|
|
121
|
+
}
|
|
122
|
+
__name(updateCacheStats, "updateCacheStats");
|
|
92
123
|
setInterval(() => {
|
|
93
124
|
const now = Date.now();
|
|
125
|
+
let removedCount = 0;
|
|
94
126
|
for (const key in cache) {
|
|
95
127
|
if (now - cache[key].timestamp > CACHE_EXPIRE_TIME) {
|
|
96
128
|
delete cache[key];
|
|
129
|
+
removedCount++;
|
|
97
130
|
}
|
|
98
131
|
}
|
|
132
|
+
if (removedCount > 0) {
|
|
133
|
+
cacheStats.cleanups++;
|
|
134
|
+
cacheStats.itemsRemoved += removedCount;
|
|
135
|
+
updateCacheStats();
|
|
136
|
+
}
|
|
99
137
|
}, CACHE_CLEAN_INTERVAL);
|
|
138
|
+
setInterval(() => {
|
|
139
|
+
cacheStats.hits = 0;
|
|
140
|
+
cacheStats.misses = 0;
|
|
141
|
+
cacheStats.cleanups = 0;
|
|
142
|
+
cacheStats.itemsRemoved = 0;
|
|
143
|
+
}, CACHE_STATS_RESET_INTERVAL);
|
|
144
|
+
function getCacheStats() {
|
|
145
|
+
updateCacheStats();
|
|
146
|
+
const hitRate = cacheStats.hits + cacheStats.misses > 0 ? Math.round(cacheStats.hits / (cacheStats.hits + cacheStats.misses) * 100) : 0;
|
|
147
|
+
return [
|
|
148
|
+
"📊 缓存统计信息",
|
|
149
|
+
"--------------------------------",
|
|
150
|
+
`总缓存项: ${cacheStats.totalItems}`,
|
|
151
|
+
`缓存大小: 约 ${cacheStats.totalSize} KB`,
|
|
152
|
+
`命中率: ${hitRate}%(${cacheStats.hits}命中/${cacheStats.misses}未命中)`,
|
|
153
|
+
`最老缓存项: ${Math.floor(cacheStats.oldestItemAge / 60)}分${cacheStats.oldestItemAge % 60}秒`,
|
|
154
|
+
`清理次数: ${cacheStats.cleanups}`,
|
|
155
|
+
`已清理项目: ${cacheStats.itemsRemoved}`,
|
|
156
|
+
"--------------------------------"
|
|
157
|
+
].join("\n");
|
|
158
|
+
}
|
|
159
|
+
__name(getCacheStats, "getCacheStats");
|
|
160
|
+
function clearCache() {
|
|
161
|
+
const itemCount = Object.keys(cache).length;
|
|
162
|
+
for (const key in cache) {
|
|
163
|
+
delete cache[key];
|
|
164
|
+
}
|
|
165
|
+
updateCacheStats();
|
|
166
|
+
return `已清理 ${itemCount} 个缓存项`;
|
|
167
|
+
}
|
|
168
|
+
__name(clearCache, "clearCache");
|
|
100
169
|
setInterval(() => {
|
|
101
170
|
const now = Date.now();
|
|
102
171
|
for (const key in requestQueue) {
|
|
@@ -165,9 +234,12 @@ function apply(ctx, config) {
|
|
|
165
234
|
__name(formatAccount, "formatAccount");
|
|
166
235
|
function isAccountResponseComplete(data) {
|
|
167
236
|
const hasBasicFields = !!(data.accessUserName && data.onlineStatus && data.isPass);
|
|
168
|
-
if (data.onlineStatus === "离线" || data.isPass === "原因未知") {
|
|
237
|
+
if (data.onlineStatus === "离线" || data.onlineStatus === "不在线" || data.isPass === "原因未知") {
|
|
169
238
|
return hasBasicFields;
|
|
170
239
|
}
|
|
240
|
+
if (data.isPass === "上线失败") {
|
|
241
|
+
return hasBasicFields && !!data.mac;
|
|
242
|
+
}
|
|
171
243
|
return hasBasicFields && !!(data.userIp && data.mac && data.maxUpSpeed !== void 0 && data.maxDownSpeed !== void 0 && data.accessDomain && data.startTime && data.startTimeSum !== void 0 && data.nickName);
|
|
172
244
|
}
|
|
173
245
|
__name(isAccountResponseComplete, "isAccountResponseComplete");
|
|
@@ -240,12 +312,12 @@ function apply(ctx, config) {
|
|
|
240
312
|
setCache("account", { account: formattedAccount }, response.data);
|
|
241
313
|
return response.data;
|
|
242
314
|
}
|
|
243
|
-
if (response.data.onlineStatus === "离线" || response.data.isPass === "原因未知") {
|
|
315
|
+
if (response.data.onlineStatus === "离线" || response.data.onlineStatus === "不在线" || response.data.isPass === "原因未知") {
|
|
244
316
|
console.log("账号离线或状态异常,将使用有限信息");
|
|
245
317
|
if (!response.data.startTimeSum) response.data.startTimeSum = 0;
|
|
246
318
|
if (!response.data.maxUpSpeed) response.data.maxUpSpeed = 0;
|
|
247
319
|
if (!response.data.maxDownSpeed) response.data.maxDownSpeed = 0;
|
|
248
|
-
if (!response.data.startTime) response.data.startTime = "未知";
|
|
320
|
+
if (!response.data.startTime) response.data.startTime = response.data.userLoginOutTime || "未知";
|
|
249
321
|
if (!response.data.nickName) response.data.nickName = "未知";
|
|
250
322
|
if (!response.data.mac) response.data.mac = "未知";
|
|
251
323
|
if (!response.data.userIp) response.data.userIp = "未知";
|
|
@@ -568,7 +640,9 @@ function apply(ctx, config) {
|
|
|
568
640
|
headers: {
|
|
569
641
|
"Content-Type": "application/json",
|
|
570
642
|
"Accept": "application/json"
|
|
571
|
-
}
|
|
643
|
+
},
|
|
644
|
+
timeout: 2e4
|
|
645
|
+
// 设置20秒超时,避免长时间无响应
|
|
572
646
|
}
|
|
573
647
|
);
|
|
574
648
|
console.log("查询响应:", JSON.stringify(response, null, 2));
|
|
@@ -579,7 +653,12 @@ function apply(ctx, config) {
|
|
|
579
653
|
if (error.response) {
|
|
580
654
|
console.error("错误响应:", error.response.data);
|
|
581
655
|
}
|
|
582
|
-
|
|
656
|
+
const fallbackResponse = {
|
|
657
|
+
code: "ERROR",
|
|
658
|
+
message: `查询RADIUS系统失败: ${error.message || "未知错误,可能是网络连接问题"}`,
|
|
659
|
+
data: null
|
|
660
|
+
};
|
|
661
|
+
return fallbackResponse;
|
|
583
662
|
}
|
|
584
663
|
});
|
|
585
664
|
}
|
|
@@ -747,7 +826,7 @@ function apply(ctx, config) {
|
|
|
747
826
|
isProcessingMessage = false;
|
|
748
827
|
return;
|
|
749
828
|
}
|
|
750
|
-
if (Date.now() - task.timestamp >
|
|
829
|
+
if (Date.now() - task.timestamp > MESSAGE_TIMEOUT2) {
|
|
751
830
|
console.log("消息处理任务超时,跳过处理");
|
|
752
831
|
isProcessingMessage = false;
|
|
753
832
|
setTimeout(processMessageQueue, 0);
|
|
@@ -786,7 +865,7 @@ function apply(ctx, config) {
|
|
|
786
865
|
setInterval(() => {
|
|
787
866
|
const now = Date.now();
|
|
788
867
|
let expiredCount = 0;
|
|
789
|
-
while (messageQueue.length > 0 && now - messageQueue[0].timestamp >
|
|
868
|
+
while (messageQueue.length > 0 && now - messageQueue[0].timestamp > MESSAGE_TIMEOUT2) {
|
|
790
869
|
const expiredTask = messageQueue.shift();
|
|
791
870
|
if (expiredTask?.session?.send) {
|
|
792
871
|
expiredTask.session.send("处理超时,请稍后再试").catch((error) => console.error("发送超时提示失败:", error));
|
|
@@ -796,7 +875,99 @@ function apply(ctx, config) {
|
|
|
796
875
|
if (expiredCount > 0) {
|
|
797
876
|
console.log(`清理了 ${expiredCount} 个超时的消息任务`);
|
|
798
877
|
}
|
|
799
|
-
},
|
|
878
|
+
}, MESSAGE_CLEAN_INTERVAL2);
|
|
879
|
+
async function queryTerminalInfo(mac, needDetailedInfo = false) {
|
|
880
|
+
const formattedMac = formatMacAddress(mac);
|
|
881
|
+
try {
|
|
882
|
+
const onuList = await queryOnuList(formattedMac);
|
|
883
|
+
if (!onuList) {
|
|
884
|
+
throw new Error("未找到该MAC地址的设备信息");
|
|
885
|
+
}
|
|
886
|
+
const [onuDetail, vlanInfo] = await Promise.all([
|
|
887
|
+
queryOnuDetailInfo(onuList.id),
|
|
888
|
+
queryOnuDetailVlanInfo(onuList.id)
|
|
889
|
+
]);
|
|
890
|
+
if (needDetailedInfo) {
|
|
891
|
+
const [ccname, order] = await Promise.all([
|
|
892
|
+
queryCCName(formattedMac),
|
|
893
|
+
queryOnuOrder(onuDetail)
|
|
894
|
+
]);
|
|
895
|
+
return {
|
|
896
|
+
onuList,
|
|
897
|
+
onuDetail,
|
|
898
|
+
ccname,
|
|
899
|
+
order,
|
|
900
|
+
vlanInfo
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
onuList,
|
|
905
|
+
onuDetail,
|
|
906
|
+
vlanInfo
|
|
907
|
+
};
|
|
908
|
+
} catch (error) {
|
|
909
|
+
console.error("终端查询过程出错:", error);
|
|
910
|
+
throw error;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
__name(queryTerminalInfo, "queryTerminalInfo");
|
|
914
|
+
async function smartQueryMacAddress(cleanInput) {
|
|
915
|
+
try {
|
|
916
|
+
const { onuDetail, ccname, order, vlanInfo } = await queryTerminalInfo(cleanInput, true);
|
|
917
|
+
const messages = [
|
|
918
|
+
"🖥️ 终端基本信息",
|
|
919
|
+
"--------------------------------",
|
|
920
|
+
`MAC地址: ${cleanInput}`,
|
|
921
|
+
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
922
|
+
`机房: ${onuDetail.roomName}`,
|
|
923
|
+
`设备名称: `,
|
|
924
|
+
`${onuDetail.deviceName}`,
|
|
925
|
+
`OLT IP: ${onuDetail.oltIp}`,
|
|
926
|
+
`PON端口: ${onuDetail.portName}`,
|
|
927
|
+
`端口逻辑号: ${onuDetail.logicId}`,
|
|
928
|
+
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
929
|
+
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
930
|
+
`CCName: ${ccname}`,
|
|
931
|
+
"",
|
|
932
|
+
"📃 工单信息",
|
|
933
|
+
"--------------------------------",
|
|
934
|
+
order ? [
|
|
935
|
+
`工单编号: ${order.orderId}`,
|
|
936
|
+
`创建时间: ${order.createDate}`,
|
|
937
|
+
`更新时间: ${order.updateDate}`
|
|
938
|
+
].join("\n") : "无工单信息",
|
|
939
|
+
"",
|
|
940
|
+
"⚙️ 业务配置信息",
|
|
941
|
+
"--------------------------------"
|
|
942
|
+
];
|
|
943
|
+
const vlanGroups = {};
|
|
944
|
+
vlanInfo.forEach((info) => {
|
|
945
|
+
const key = `${info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置"}-${info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置"}`;
|
|
946
|
+
if (!vlanGroups[key]) {
|
|
947
|
+
vlanGroups[key] = {
|
|
948
|
+
VLAN: info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置",
|
|
949
|
+
actualVlan: info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置",
|
|
950
|
+
ports: []
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
vlanGroups[key].ports.push(info.LAN);
|
|
954
|
+
});
|
|
955
|
+
Object.values(vlanGroups).forEach((group) => {
|
|
956
|
+
const portsText = group.ports.join(",");
|
|
957
|
+
messages.push(
|
|
958
|
+
`端口 ${portsText}:`,
|
|
959
|
+
` 端口VLAN: ${group.VLAN}`,
|
|
960
|
+
` 工单VLAN: ${group.actualVlan}`
|
|
961
|
+
);
|
|
962
|
+
});
|
|
963
|
+
messages.push("--------------------------------");
|
|
964
|
+
return messages.join("\n");
|
|
965
|
+
} catch (error) {
|
|
966
|
+
console.error("查询MAC地址过程出错:", error);
|
|
967
|
+
return "查询失败,请稍后重试";
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
__name(smartQueryMacAddress, "smartQueryMacAddress");
|
|
800
971
|
ctx.middleware(async (session, next) => {
|
|
801
972
|
console.log("原始消息:", session.content);
|
|
802
973
|
const originalInput = session.stripped.content.trim();
|
|
@@ -817,133 +988,122 @@ function apply(ctx, config) {
|
|
|
817
988
|
console.log("处理后的内容:", cleanInput);
|
|
818
989
|
if (isValidMacAddress(cleanInput)) {
|
|
819
990
|
console.log("内容是合法的MAC地址,开始查询");
|
|
820
|
-
|
|
821
|
-
const onuList = await queryOnuList(cleanInput);
|
|
822
|
-
if (!onuList) {
|
|
823
|
-
return "未找到该MAC地址的设备信息";
|
|
824
|
-
}
|
|
825
|
-
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
826
|
-
const ccname = await queryCCName(cleanInput);
|
|
827
|
-
const order = await queryOnuOrder(onuDetail);
|
|
828
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
829
|
-
const messages = [
|
|
830
|
-
"🖥️ 终端基本信息",
|
|
831
|
-
"--------------------------------",
|
|
832
|
-
`MAC地址: ${cleanInput}`,
|
|
833
|
-
`状态:${onuDetail.status === 1 ? "在线 ✅" : "不在线 ❌"}`,
|
|
834
|
-
`机房: ${onuDetail.roomName}`,
|
|
835
|
-
`设备名称: `,
|
|
836
|
-
`${onuDetail.deviceName}`,
|
|
837
|
-
`OLT IP: ${onuDetail.oltIp}`,
|
|
838
|
-
`PON端口: ${onuDetail.portName}`,
|
|
839
|
-
`端口逻辑号: ${onuDetail.logicId}`,
|
|
840
|
-
`发射光功率: ${onuDetail.onuSendPower === "设备不在线" ? "设备不在线" : onuDetail.onuSendPower + "dBm"}`,
|
|
841
|
-
`接收光功率: ${onuDetail.onuReceivePower === "设备不在线" ? "设备不在线" : onuDetail.onuReceivePower + "dBm"}${onuDetail.onuReceivePower !== "设备不在线" ? " " + evaluateOpticalPower(onuDetail.onuReceivePower) : ""}`,
|
|
842
|
-
`CCName: ${ccname}`,
|
|
843
|
-
"",
|
|
844
|
-
"📃 工单信息",
|
|
845
|
-
"--------------------------------",
|
|
846
|
-
order ? [
|
|
847
|
-
`工单编号: ${order.orderId}`,
|
|
848
|
-
`创建时间: ${order.createDate}`,
|
|
849
|
-
`更新时间: ${order.updateDate}`
|
|
850
|
-
].join("\n") : "无工单信息",
|
|
851
|
-
"",
|
|
852
|
-
"⚙️ 业务配置信息",
|
|
853
|
-
"--------------------------------"
|
|
854
|
-
];
|
|
855
|
-
vlanInfo.forEach((info) => {
|
|
856
|
-
messages.push(
|
|
857
|
-
`端口 ${info.LAN}:`,
|
|
858
|
-
` 端口VLAN: ${info.VLAN || "未配置"}`,
|
|
859
|
-
` 工单VLAN: ${info.actualVlan || "未配置"}`
|
|
860
|
-
);
|
|
861
|
-
});
|
|
862
|
-
messages.push("--------------------------------");
|
|
863
|
-
return messages.join("\n");
|
|
864
|
-
} catch (error) {
|
|
865
|
-
console.error("查询MAC地址过程出错:", error);
|
|
866
|
-
return "查询失败,请稍后重试";
|
|
867
|
-
}
|
|
991
|
+
return await smartQueryMacAddress(cleanInput);
|
|
868
992
|
} else {
|
|
869
993
|
console.log("内容不是MAC地址,尝试作为账号查询");
|
|
870
994
|
try {
|
|
871
995
|
const radiusUserInfo = await queryRadiusUser(input);
|
|
872
996
|
if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
|
|
873
|
-
return "
|
|
997
|
+
return `账号授权查询失败:${radiusUserInfo.message || "请确认账号是否正确"}`;
|
|
874
998
|
}
|
|
875
999
|
const { user, orders } = radiusUserInfo.data;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
const message = [
|
|
889
|
-
"🌐 账号查询结果",
|
|
890
|
-
"--------------------------------",
|
|
891
|
-
`账号: ${accountInfo.accessUserName}`,
|
|
892
|
-
// `RADIUS用户密码: ${user.password}`,
|
|
893
|
-
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
894
|
-
"",
|
|
895
|
-
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
|
|
896
|
-
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
|
|
897
|
-
];
|
|
898
|
-
if (accountInfo.isPass === "上线失败") {
|
|
899
|
-
message.push(
|
|
900
|
-
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
901
|
-
`失败时间: ${accountInfo.onlineFailTime || "未知"}`,
|
|
902
|
-
`失败原因: ${accountInfo.onlineFailReason || "未知"}`,
|
|
903
|
-
`回复消息: ${accountInfo.failReplyMessage || "未知"}`,
|
|
904
|
-
`原因分析: ${accountInfo.reasonAnaly || "未知"}`
|
|
905
|
-
);
|
|
906
|
-
} else if (accountInfo.onlineStatus === "离线" || accountInfo.isPass === "原因未知") {
|
|
907
|
-
message.push(
|
|
908
|
-
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
909
|
-
`最后IP: ${accountInfo.userIp || "未知"}`
|
|
910
|
-
);
|
|
911
|
-
if (accountInfo.startTime) {
|
|
912
|
-
message.push(`最后在线: ${accountInfo.startTime || "未知"}`);
|
|
1000
|
+
try {
|
|
1001
|
+
const accountInfo = await queryAccount(input);
|
|
1002
|
+
const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
|
|
1003
|
+
const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
|
|
1004
|
+
const minutes = accountInfo.startTimeSum % 60;
|
|
1005
|
+
let durationText = "";
|
|
1006
|
+
if (days > 0) {
|
|
1007
|
+
durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
1008
|
+
} else if (hours > 0) {
|
|
1009
|
+
durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
1010
|
+
} else {
|
|
1011
|
+
durationText = `${minutes}分钟`;
|
|
913
1012
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1013
|
+
const message = [
|
|
1014
|
+
"🌐 账号查询结果",
|
|
1015
|
+
"--------------------------------",
|
|
1016
|
+
`账号: ${accountInfo.accessUserName}`,
|
|
1017
|
+
// `RADIUS用户密码: ${user.password}`,
|
|
1018
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
1019
|
+
"",
|
|
1020
|
+
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
|
|
1021
|
+
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
|
|
1022
|
+
];
|
|
1023
|
+
if (accountInfo.isPass === "上线失败") {
|
|
1024
|
+
message.push(
|
|
1025
|
+
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
1026
|
+
`失败时间: ${accountInfo.onlineFailTime || "未知"}`,
|
|
1027
|
+
`失败原因: ${accountInfo.onlineFailReason || "未知"}`,
|
|
1028
|
+
`回复消息: ${accountInfo.failReplyMessage || "未知"}`,
|
|
1029
|
+
`原因分析: ${accountInfo.reasonAnaly || "未知"}`
|
|
1030
|
+
);
|
|
1031
|
+
} else if (accountInfo.onlineStatus === "离线" || accountInfo.onlineStatus === "不在线" || accountInfo.isPass === "原因未知") {
|
|
929
1032
|
message.push(
|
|
930
|
-
|
|
931
|
-
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
932
|
-
` 服务名称: ${order.service_name}`,
|
|
933
|
-
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
934
|
-
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1033
|
+
`MAC地址: ${accountInfo.mac || "未知"}`
|
|
935
1034
|
);
|
|
936
|
-
if (
|
|
937
|
-
message.push("");
|
|
1035
|
+
if (accountInfo.startTime) {
|
|
1036
|
+
message.push(`最后在线: ${accountInfo.startTime || "未知"}`);
|
|
938
1037
|
}
|
|
939
|
-
|
|
1038
|
+
if (accountInfo.offlineReason) {
|
|
1039
|
+
message.push(`离线原因: ${accountInfo.offlineReason || "未知"}`);
|
|
1040
|
+
}
|
|
1041
|
+
if (accountInfo.reasonAnaly) {
|
|
1042
|
+
message.push(`原因分析: ${accountInfo.reasonAnaly || "未知"}`);
|
|
1043
|
+
}
|
|
1044
|
+
} else {
|
|
1045
|
+
message.push(
|
|
1046
|
+
`IP地址: ${accountInfo.userIp}`,
|
|
1047
|
+
`MAC地址: ${accountInfo.mac}`,
|
|
1048
|
+
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
1049
|
+
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
1050
|
+
`开始时间: ${accountInfo.startTime}`,
|
|
1051
|
+
`在线时长: ${durationText}`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
message.push("");
|
|
1055
|
+
if (orders && orders.length > 0) {
|
|
1056
|
+
message.push("🛒 订购信息");
|
|
1057
|
+
message.push("--------------------------------");
|
|
1058
|
+
orders.forEach((order, index) => {
|
|
1059
|
+
message.push(
|
|
1060
|
+
`订购 #${index + 1}:`,
|
|
1061
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
1062
|
+
` 服务名称: ${order.service_name}`,
|
|
1063
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
1064
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1065
|
+
);
|
|
1066
|
+
if (index < orders.length - 1) {
|
|
1067
|
+
message.push("");
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
message.push("--------------------------------");
|
|
1072
|
+
return message.join("\n");
|
|
1073
|
+
} catch (accountError) {
|
|
1074
|
+
console.error("智能查询中账号详细信息出错:", accountError);
|
|
1075
|
+
const message = [
|
|
1076
|
+
"🌐 账号查询结果(部分信息)",
|
|
1077
|
+
"--------------------------------",
|
|
1078
|
+
`账号: ${input}`,
|
|
1079
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
1080
|
+
"",
|
|
1081
|
+
"⚠️ 账号详细信息查询失败,只显示基本信息",
|
|
1082
|
+
`错误信息: ${accountError.message || "未知错误"}`,
|
|
1083
|
+
""
|
|
1084
|
+
];
|
|
1085
|
+
if (orders && orders.length > 0) {
|
|
1086
|
+
message.push("🛒 订购信息");
|
|
1087
|
+
message.push("--------------------------------");
|
|
1088
|
+
orders.forEach((order, index) => {
|
|
1089
|
+
message.push(
|
|
1090
|
+
`订购 #${index + 1}:`,
|
|
1091
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
1092
|
+
` 服务名称: ${order.service_name}`,
|
|
1093
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
1094
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1095
|
+
);
|
|
1096
|
+
if (index < orders.length - 1) {
|
|
1097
|
+
message.push("");
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
message.push("--------------------------------");
|
|
1102
|
+
return message.join("\n");
|
|
940
1103
|
}
|
|
941
|
-
message.push("--------------------------------");
|
|
942
|
-
return message.join("\n");
|
|
943
1104
|
} catch (error) {
|
|
944
1105
|
console.error("查询账号过程出错:", error);
|
|
945
|
-
|
|
946
|
-
return null;
|
|
1106
|
+
return `查询账号${input}失败:${error.message || "服务暂时不可用,请稍后再试"}`;
|
|
947
1107
|
}
|
|
948
1108
|
}
|
|
949
1109
|
} catch (error) {
|
|
@@ -961,79 +1121,116 @@ function apply(ctx, config) {
|
|
|
961
1121
|
try {
|
|
962
1122
|
const radiusUserInfo = await queryRadiusUser(account);
|
|
963
1123
|
if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
|
|
964
|
-
return "
|
|
1124
|
+
return `账号授权查询失败:${radiusUserInfo.message || "请确认账号是否正确"}`;
|
|
965
1125
|
}
|
|
966
1126
|
const { user, orders } = radiusUserInfo.data;
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
const message = [
|
|
980
|
-
"🌐 账号查询结果",
|
|
981
|
-
"--------------------------------",
|
|
982
|
-
`账号: ${accountInfo.accessUserName}`,
|
|
983
|
-
// `RADIUS用户密码: ${user.password}`,
|
|
984
|
-
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
985
|
-
"",
|
|
986
|
-
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
|
|
987
|
-
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
|
|
988
|
-
];
|
|
989
|
-
if (accountInfo.isPass === "上线失败") {
|
|
990
|
-
message.push(
|
|
991
|
-
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
992
|
-
`失败时间: ${accountInfo.onlineFailTime || "未知"}`,
|
|
993
|
-
`失败原因: ${accountInfo.onlineFailReason || "未知"}`,
|
|
994
|
-
`回复消息: ${accountInfo.failReplyMessage || "未知"}`,
|
|
995
|
-
`原因分析: ${accountInfo.reasonAnaly || "未知"}`
|
|
996
|
-
);
|
|
997
|
-
} else if (accountInfo.onlineStatus === "离线" || accountInfo.isPass === "原因未知") {
|
|
998
|
-
message.push(
|
|
999
|
-
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
1000
|
-
`最后IP: ${accountInfo.userIp || "未知"}`
|
|
1001
|
-
);
|
|
1002
|
-
if (accountInfo.startTime) {
|
|
1003
|
-
message.push(`最后在线: ${accountInfo.startTime || "未知"}`);
|
|
1127
|
+
try {
|
|
1128
|
+
const accountInfo = await queryAccount(account);
|
|
1129
|
+
const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
|
|
1130
|
+
const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
|
|
1131
|
+
const minutes = accountInfo.startTimeSum % 60;
|
|
1132
|
+
let durationText = "";
|
|
1133
|
+
if (days > 0) {
|
|
1134
|
+
durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
1135
|
+
} else if (hours > 0) {
|
|
1136
|
+
durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
|
|
1137
|
+
} else {
|
|
1138
|
+
durationText = `${minutes}分钟`;
|
|
1004
1139
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
if (orders && orders.length > 0) {
|
|
1017
|
-
message.push("🛒 订购信息");
|
|
1018
|
-
message.push("--------------------------------");
|
|
1019
|
-
orders.forEach((order, index) => {
|
|
1140
|
+
const message = [
|
|
1141
|
+
"🌐 账号查询结果",
|
|
1142
|
+
"--------------------------------",
|
|
1143
|
+
`账号: ${accountInfo.accessUserName}`,
|
|
1144
|
+
// `RADIUS用户密码: ${user.password}`,
|
|
1145
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
1146
|
+
"",
|
|
1147
|
+
`状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
|
|
1148
|
+
`认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
|
|
1149
|
+
];
|
|
1150
|
+
if (accountInfo.isPass === "上线失败") {
|
|
1020
1151
|
message.push(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1152
|
+
`MAC地址: ${accountInfo.mac || "未知"}`,
|
|
1153
|
+
`失败时间: ${accountInfo.onlineFailTime || "未知"}`,
|
|
1154
|
+
`失败原因: ${accountInfo.onlineFailReason || "未知"}`,
|
|
1155
|
+
`回复消息: ${accountInfo.failReplyMessage || "未知"}`,
|
|
1156
|
+
`原因分析: ${accountInfo.reasonAnaly || "未知"}`
|
|
1026
1157
|
);
|
|
1027
|
-
|
|
1028
|
-
|
|
1158
|
+
} else if (accountInfo.onlineStatus === "离线" || accountInfo.onlineStatus === "不在线" || accountInfo.isPass === "原因未知") {
|
|
1159
|
+
message.push(
|
|
1160
|
+
`MAC地址: ${accountInfo.mac || "未知"}`
|
|
1161
|
+
);
|
|
1162
|
+
if (accountInfo.startTime) {
|
|
1163
|
+
message.push(`最后在线: ${accountInfo.startTime || "未知"}`);
|
|
1029
1164
|
}
|
|
1030
|
-
|
|
1165
|
+
if (accountInfo.offlineReason) {
|
|
1166
|
+
message.push(`离线原因: ${accountInfo.offlineReason || "未知"}`);
|
|
1167
|
+
}
|
|
1168
|
+
if (accountInfo.reasonAnaly) {
|
|
1169
|
+
message.push(`原因分析: ${accountInfo.reasonAnaly || "未知"}`);
|
|
1170
|
+
}
|
|
1171
|
+
} else {
|
|
1172
|
+
message.push(
|
|
1173
|
+
`IP地址: ${accountInfo.userIp}`,
|
|
1174
|
+
`MAC地址: ${accountInfo.mac}`,
|
|
1175
|
+
`最大上行: ${accountInfo.maxUpSpeed}Mbps`,
|
|
1176
|
+
`最大下行: ${accountInfo.maxDownSpeed}Mbps`,
|
|
1177
|
+
`开始时间: ${accountInfo.startTime}`,
|
|
1178
|
+
`在线时长: ${durationText}`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
message.push("");
|
|
1182
|
+
if (orders && orders.length > 0) {
|
|
1183
|
+
message.push("🛒 订购信息");
|
|
1184
|
+
message.push("--------------------------------");
|
|
1185
|
+
orders.forEach((order, index) => {
|
|
1186
|
+
message.push(
|
|
1187
|
+
`订购 #${index + 1}:`,
|
|
1188
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
1189
|
+
` 服务名称: ${order.service_name}`,
|
|
1190
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
1191
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1192
|
+
);
|
|
1193
|
+
if (index < orders.length - 1) {
|
|
1194
|
+
message.push("");
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
message.push("--------------------------------");
|
|
1199
|
+
return message.join("\n");
|
|
1200
|
+
} catch (accountError) {
|
|
1201
|
+
console.error("查询账号详细信息出错:", accountError);
|
|
1202
|
+
const message = [
|
|
1203
|
+
"🌐 账号查询结果(部分信息)",
|
|
1204
|
+
"--------------------------------",
|
|
1205
|
+
`账号: ${account}`,
|
|
1206
|
+
`RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
|
|
1207
|
+
"",
|
|
1208
|
+
"⚠️ 账号详细信息查询失败,只显示基本信息",
|
|
1209
|
+
`错误信息: ${accountError.message || "未知错误"}`,
|
|
1210
|
+
""
|
|
1211
|
+
];
|
|
1212
|
+
if (orders && orders.length > 0) {
|
|
1213
|
+
message.push("🛒 订购信息");
|
|
1214
|
+
message.push("--------------------------------");
|
|
1215
|
+
orders.forEach((order, index) => {
|
|
1216
|
+
message.push(
|
|
1217
|
+
`订购 #${index + 1}:`,
|
|
1218
|
+
` 订购状态: ${translateOrderStatus(order.order_status)}`,
|
|
1219
|
+
` 服务名称: ${order.service_name}`,
|
|
1220
|
+
` 生效时间: ${formatRadiusTime(order.valid_date)}`,
|
|
1221
|
+
` 失效时间: ${formatRadiusTime(order.expire_date)}`
|
|
1222
|
+
);
|
|
1223
|
+
if (index < orders.length - 1) {
|
|
1224
|
+
message.push("");
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
message.push("--------------------------------");
|
|
1229
|
+
return message.join("\n");
|
|
1031
1230
|
}
|
|
1032
|
-
message.push("--------------------------------");
|
|
1033
|
-
return message.join("\n");
|
|
1034
1231
|
} catch (error) {
|
|
1035
1232
|
console.error("查询过程出错:", error);
|
|
1036
|
-
return error.message || "
|
|
1233
|
+
return `账号 ${account} 查询失败: ${error.message || "服务暂时不可用,请稍后再试"}`;
|
|
1037
1234
|
}
|
|
1038
1235
|
});
|
|
1039
1236
|
resolve();
|
|
@@ -1077,12 +1274,7 @@ function apply(ctx, config) {
|
|
|
1077
1274
|
addToMessageQueue(session, async () => {
|
|
1078
1275
|
try {
|
|
1079
1276
|
const formattedMac = formatMacAddress(mac);
|
|
1080
|
-
const
|
|
1081
|
-
if (!onuList) {
|
|
1082
|
-
return "未找到该MAC地址的设备信息";
|
|
1083
|
-
}
|
|
1084
|
-
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
1085
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
1277
|
+
const { onuDetail, vlanInfo } = await queryTerminalInfo(formattedMac, false);
|
|
1086
1278
|
const messages = [
|
|
1087
1279
|
"🖥️ 终端基本信息",
|
|
1088
1280
|
"--------------------------------",
|
|
@@ -1096,11 +1288,24 @@ function apply(ctx, config) {
|
|
|
1096
1288
|
"⚙️ 业务配置信息",
|
|
1097
1289
|
"--------------------------------"
|
|
1098
1290
|
];
|
|
1291
|
+
const vlanGroups = {};
|
|
1099
1292
|
vlanInfo.forEach((info) => {
|
|
1293
|
+
const key = `${info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置"}-${info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置"}`;
|
|
1294
|
+
if (!vlanGroups[key]) {
|
|
1295
|
+
vlanGroups[key] = {
|
|
1296
|
+
VLAN: info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置",
|
|
1297
|
+
actualVlan: info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置",
|
|
1298
|
+
ports: []
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
vlanGroups[key].ports.push(info.LAN);
|
|
1302
|
+
});
|
|
1303
|
+
Object.values(vlanGroups).forEach((group) => {
|
|
1304
|
+
const portsText = group.ports.join(",");
|
|
1100
1305
|
messages.push(
|
|
1101
|
-
`端口 ${
|
|
1102
|
-
` 端口VLAN: ${
|
|
1103
|
-
` 工单VLAN: ${
|
|
1306
|
+
`端口 ${portsText}:`,
|
|
1307
|
+
` 端口VLAN: ${group.VLAN}`,
|
|
1308
|
+
` 工单VLAN: ${group.actualVlan}`
|
|
1104
1309
|
);
|
|
1105
1310
|
});
|
|
1106
1311
|
messages.push("--------------------------------");
|
|
@@ -1119,14 +1324,7 @@ function apply(ctx, config) {
|
|
|
1119
1324
|
addToMessageQueue(session, async () => {
|
|
1120
1325
|
try {
|
|
1121
1326
|
const formattedMac = formatMacAddress(mac);
|
|
1122
|
-
const
|
|
1123
|
-
if (!onuList) {
|
|
1124
|
-
return "未找到该MAC地址的设备信息";
|
|
1125
|
-
}
|
|
1126
|
-
const onuDetail = await queryOnuDetailInfo(onuList.id);
|
|
1127
|
-
const ccname = await queryCCName(formattedMac);
|
|
1128
|
-
const order = await queryOnuOrder(onuDetail);
|
|
1129
|
-
const vlanInfo = await queryOnuDetailVlanInfo(onuList.id);
|
|
1327
|
+
const { onuDetail, ccname, order, vlanInfo } = await queryTerminalInfo(formattedMac, true);
|
|
1130
1328
|
const messages = [
|
|
1131
1329
|
"🖥️ 终端基本信息",
|
|
1132
1330
|
"--------------------------------",
|
|
@@ -1153,11 +1351,24 @@ function apply(ctx, config) {
|
|
|
1153
1351
|
"⚙️ 业务配置信息",
|
|
1154
1352
|
"--------------------------------"
|
|
1155
1353
|
];
|
|
1354
|
+
const vlanGroups = {};
|
|
1156
1355
|
vlanInfo.forEach((info) => {
|
|
1356
|
+
const key = `${info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置"}-${info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置"}`;
|
|
1357
|
+
if (!vlanGroups[key]) {
|
|
1358
|
+
vlanGroups[key] = {
|
|
1359
|
+
VLAN: info.VLAN !== void 0 && info.VLAN !== null && info.VLAN !== "-" ? info.VLAN : "未配置",
|
|
1360
|
+
actualVlan: info.actualVlan !== void 0 && info.actualVlan !== null ? info.actualVlan : "未配置",
|
|
1361
|
+
ports: []
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
vlanGroups[key].ports.push(info.LAN);
|
|
1365
|
+
});
|
|
1366
|
+
Object.values(vlanGroups).forEach((group) => {
|
|
1367
|
+
const portsText = group.ports.join(",");
|
|
1157
1368
|
messages.push(
|
|
1158
|
-
`端口 ${
|
|
1159
|
-
` 端口VLAN: ${
|
|
1160
|
-
` 工单VLAN: ${
|
|
1369
|
+
`端口 ${portsText}:`,
|
|
1370
|
+
` 端口VLAN: ${group.VLAN}`,
|
|
1371
|
+
` 工单VLAN: ${group.actualVlan}`
|
|
1161
1372
|
);
|
|
1162
1373
|
});
|
|
1163
1374
|
messages.push("--------------------------------");
|
|
@@ -1277,6 +1488,42 @@ function apply(ctx, config) {
|
|
|
1277
1488
|
resolve();
|
|
1278
1489
|
});
|
|
1279
1490
|
});
|
|
1491
|
+
ctx.command("缓存统计").action(async ({ session }) => {
|
|
1492
|
+
return formatOutputMessage(getCacheStats());
|
|
1493
|
+
});
|
|
1494
|
+
ctx.command("清理缓存").action(async ({ session }) => {
|
|
1495
|
+
const result = clearCache();
|
|
1496
|
+
return formatOutputMessage(`✅ ${result}`);
|
|
1497
|
+
});
|
|
1498
|
+
function getSystemStatus() {
|
|
1499
|
+
updateCacheStats();
|
|
1500
|
+
const hitRate = cacheStats.hits + cacheStats.misses > 0 ? Math.round(cacheStats.hits / (cacheStats.hits + cacheStats.misses) * 100) : 0;
|
|
1501
|
+
const queueUtilization = MAX_CONCURRENT_REQUESTS > 0 ? Math.round(currentConcurrentRequests / MAX_CONCURRENT_REQUESTS * 100) : 0;
|
|
1502
|
+
const messageQueueUtilization = MAX_QUEUE_LENGTH > 0 ? Math.round(messageQueue.length / MAX_QUEUE_LENGTH * 100) : 0;
|
|
1503
|
+
const waitingRequests = requestWaitQueue.length;
|
|
1504
|
+
const buildBar = /* @__PURE__ */ __name((percent) => {
|
|
1505
|
+
const barLength = 10;
|
|
1506
|
+
const filledLength = Math.round(barLength * percent / 100);
|
|
1507
|
+
const emptyLength = barLength - filledLength;
|
|
1508
|
+
return "▓".repeat(filledLength) + "░".repeat(emptyLength);
|
|
1509
|
+
}, "buildBar");
|
|
1510
|
+
return [
|
|
1511
|
+
"📈 系统状态监控",
|
|
1512
|
+
"--------------------------------",
|
|
1513
|
+
`缓存命中率: ${hitRate}% ${buildBar(hitRate)}`,
|
|
1514
|
+
`缓存项数量: ${cacheStats.totalItems}项`,
|
|
1515
|
+
`缓存占用空间: 约 ${cacheStats.totalSize} KB`,
|
|
1516
|
+
`并发请求: ${currentConcurrentRequests}/${MAX_CONCURRENT_REQUESTS} ${buildBar(queueUtilization)}`,
|
|
1517
|
+
`等待请求: ${waitingRequests}`,
|
|
1518
|
+
`消息队列: ${messageQueue.length}/${MAX_QUEUE_LENGTH} ${buildBar(messageQueueUtilization)}`,
|
|
1519
|
+
`处理状态: ${isProcessingMessage ? "正在处理" : "空闲"}`,
|
|
1520
|
+
"--------------------------------"
|
|
1521
|
+
].join("\n");
|
|
1522
|
+
}
|
|
1523
|
+
__name(getSystemStatus, "getSystemStatus");
|
|
1524
|
+
ctx.command("系统状态").alias("/系统状态").action(async ({ session }) => {
|
|
1525
|
+
return formatOutputMessage(getSystemStatus());
|
|
1526
|
+
});
|
|
1280
1527
|
}
|
|
1281
1528
|
__name(apply, "apply");
|
|
1282
1529
|
// Annotate the CommonJS export names for ESM import in node:
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -82,6 +82,33 @@
|
|
|
82
82
|
- `GDC8510019239048`(自动识别为账号并查询账号信息)
|
|
83
83
|
- 支持@机器人后发送查询内容
|
|
84
84
|
|
|
85
|
+
### 8. 系统管理命令
|
|
86
|
+
- **缓存统计**
|
|
87
|
+
- 命令格式:`缓存统计` 或 `/缓存统计`
|
|
88
|
+
- 功能:显示当前缓存的统计信息,包括总缓存项数量、缓存大小、命中率等。
|
|
89
|
+
- 返回信息:
|
|
90
|
+
- 总缓存项数量
|
|
91
|
+
- 估计的缓存大小(KB)
|
|
92
|
+
- 缓存命中率及命中/未命中次数
|
|
93
|
+
- 最老缓存项的年龄
|
|
94
|
+
- 缓存清理次数和已清理项目数
|
|
95
|
+
|
|
96
|
+
- **清理缓存**
|
|
97
|
+
- 命令格式:`清理缓存` 或 `/清理缓存`
|
|
98
|
+
- 功能:手动清理所有缓存项,释放内存并强制下次查询从服务器获取最新数据。
|
|
99
|
+
- 返回信息:清理的缓存项数量
|
|
100
|
+
|
|
101
|
+
- **系统状态**
|
|
102
|
+
- 命令格式:`系统状态` 或 `/系统状态`
|
|
103
|
+
- 功能:显示当前系统运行状态,包括缓存、并发请求和消息队列的实时状态。
|
|
104
|
+
- 返回信息:
|
|
105
|
+
- 缓存命中率(带可视化进度条)
|
|
106
|
+
- 缓存项数量和占用空间
|
|
107
|
+
- 当前并发请求数与最大限制(带可视化进度条)
|
|
108
|
+
- 等待中的请求数量
|
|
109
|
+
- 消息队列状态(带可视化进度条)
|
|
110
|
+
- 当前处理状态(空闲/处理中)
|
|
111
|
+
|
|
85
112
|
## 插件配置
|
|
86
113
|
|
|
87
114
|
### 必需配置项
|
|
@@ -104,6 +131,11 @@
|
|
|
104
131
|
7. **超时处理**:设置请求超时,自动处理长时间未响应的请求
|
|
105
132
|
8. **格式优化**:优化消息显示格式,使用直观的图标显示状态信息
|
|
106
133
|
9. **错误处理**:完善的错误处理机制,确保查询失败时有清晰的提示
|
|
134
|
+
10. **并行查询**:终端查询采用并行请求策略,在保持接口依赖关系的同时大幅提升查询速度
|
|
135
|
+
11. **智能合并**:具有相同VLAN配置的多个LAN端口会自动合并显示,提高信息的可读性
|
|
136
|
+
12. **缓存监控**:内置缓存统计和监控功能,可随时查看缓存状态并手动清理
|
|
137
|
+
13. **系统状态可视化**:提供系统状态的实时监控,包括直观的进度条显示各项指标
|
|
138
|
+
14. **自动资源清理**:定期自动清理过期的缓存项、请求队列和消息队列,优化内存使用
|
|
107
139
|
|
|
108
140
|
## 显示优化
|
|
109
141
|
|
|
@@ -126,6 +158,15 @@
|
|
|
126
158
|
- 内容丰富的查询结果使用新行显示
|
|
127
159
|
- 简短提示直接显示在同一行
|
|
128
160
|
|
|
161
|
+
5. **VLAN显示**:
|
|
162
|
+
- 端口VLAN值为"-"时显示为"未配置"
|
|
163
|
+
- 工单VLAN值为0时正确显示为"0"
|
|
164
|
+
- 相同VLAN配置的多个LAN端口合并显示,如"LAN2,LAN3,LAN4"
|
|
165
|
+
|
|
166
|
+
6. **系统状态可视化**:
|
|
167
|
+
- 使用进度条直观显示缓存命中率、并发请求使用情况和消息队列状态
|
|
168
|
+
- 系统关键指标一目了然,方便管理员快速评估系统性能
|
|
169
|
+
|
|
129
170
|
## 注意事项
|
|
130
171
|
1. 所有查询都需要正确的权限和配置。
|
|
131
172
|
2. MAC地址不区分大小写,支持多种分隔符格式,包括中文符号。
|
|
@@ -137,28 +178,19 @@
|
|
|
137
178
|
8. 光功率刷新功能将无论设备是否在线都会尝试刷新,确保获取最新数据。
|
|
138
179
|
9. 针对上线失败或离线状态的账号,系统也能提供有用的信息,不会简单报错。
|
|
139
180
|
10. 所有命令支持带斜杠(如`/账号查询`)和不带斜杠(如`账号查询`)两种触发方式,提高命令输入的灵活性。
|
|
140
|
-
|
|
141
|
-
## 开发相关
|
|
142
|
-
|
|
143
|
-
为了支持开发过程中的热重载功能,需要确保:
|
|
144
|
-
1. 在`koishi.yml`中正确配置hmr插件
|
|
145
|
-
2. 安装并启用loader服务
|
|
146
|
-
3. 在开发环境下运行Koishi时设置`NODE_ENV=development`
|
|
147
|
-
|
|
148
|
-
```yaml
|
|
149
|
-
# 开发相关配置示例
|
|
150
|
-
group:develop:
|
|
151
|
-
$if: env.NODE_ENV === 'development'
|
|
152
|
-
hmr:
|
|
153
|
-
root:
|
|
154
|
-
- .
|
|
155
|
-
loader:
|
|
156
|
-
watch: true
|
|
157
|
-
```
|
|
181
|
+
11. 系统管理命令可用于监控和优化系统性能,建议定期查看系统状态,必要时手动清理缓存。
|
|
158
182
|
|
|
159
183
|
## 更新日志
|
|
160
184
|
|
|
161
185
|
### 最新更新
|
|
186
|
+
- 增加了系统管理命令,包括缓存统计、清理缓存和系统状态监控
|
|
187
|
+
- 新增缓存监控功能,支持查看缓存命中率、大小、项目数等统计信息
|
|
188
|
+
- 增加系统状态可视化展示,使用进度条直观呈现系统各项指标
|
|
189
|
+
- 优化了缓存管理机制,包括定期统计重置和性能数据收集
|
|
190
|
+
- 完善了资源自动清理,减少内存占用和提高系统稳定性
|
|
191
|
+
- 优化终端查询性能,实现并行请求,大幅减少查询响应时间
|
|
192
|
+
- 智能合并显示具有相同VLAN配置的LAN端口,使得信息展示更加清晰
|
|
193
|
+
- 优化VLAN值显示:端口VLAN为"-"时显示为"未配置",工单VLAN为0时正确显示为"0"
|
|
162
194
|
- 增加了对双触发词的支持,同时兼容带斜杠(如`/账号查询`)和不带斜杠(如`账号查询`)的命令格式
|
|
163
195
|
- 增加了离线状态和上线失败状态的特殊处理,提供更多有用信息
|
|
164
196
|
- 修复了状态图标显示问题,确保"不在线"状态正确显示❌图标
|