koishi-plugin-jscn-aaaquery 1.0.7 → 1.0.9

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.
Files changed (3) hide show
  1. package/lib/index.js +379 -144
  2. package/package.json +1 -1
  3. package/readme.md +40 -0
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 CACHE_EXPIRE_TIME = 5 * 60 * 1e3;
60
- const CACHE_CLEAN_INTERVAL = 60 * 1e3;
61
- const QUEUE_EXPIRE_TIME = 10 * 60 * 1e3;
62
- const QUEUE_CLEAN_INTERVAL = 5 * 60 * 1e3;
63
- const MAX_CONCURRENT_REQUESTS = 10;
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 MESSAGE_TIMEOUT = 2 * 60 * 1e3;
69
- const MESSAGE_CLEAN_INTERVAL = 60 * 1e3;
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
- throw error;
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 > MESSAGE_TIMEOUT) {
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 > MESSAGE_TIMEOUT) {
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,7 @@ function apply(ctx, config) {
796
875
  if (expiredCount > 0) {
797
876
  console.log(`清理了 ${expiredCount} 个超时的消息任务`);
798
877
  }
799
- }, MESSAGE_CLEAN_INTERVAL);
878
+ }, MESSAGE_CLEAN_INTERVAL2);
800
879
  async function queryTerminalInfo(mac, needDetailedInfo = false) {
801
880
  const formattedMac = formatMacAddress(mac);
802
881
  try {
@@ -915,80 +994,116 @@ function apply(ctx, config) {
915
994
  try {
916
995
  const radiusUserInfo = await queryRadiusUser(input);
917
996
  if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
918
- return "账号授权有误,请重新授权";
997
+ return `账号授权查询失败:${radiusUserInfo.message || "请确认账号是否正确"}`;
919
998
  }
920
999
  const { user, orders } = radiusUserInfo.data;
921
- const accountInfo = await queryAccount(input);
922
- const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
923
- const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
924
- const minutes = accountInfo.startTimeSum % 60;
925
- let durationText = "";
926
- if (days > 0) {
927
- durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
928
- } else if (hours > 0) {
929
- durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
930
- } else {
931
- durationText = `${minutes}分钟`;
932
- }
933
- const message = [
934
- "🌐 账号查询结果",
935
- "--------------------------------",
936
- `账号: ${accountInfo.accessUserName}`,
937
- // `RADIUS用户密码: ${user.password}`,
938
- `RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
939
- "",
940
- `状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
941
- `认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
942
- ];
943
- if (accountInfo.isPass === "上线失败") {
944
- message.push(
945
- `MAC地址: ${accountInfo.mac || "未知"}`,
946
- `失败时间: ${accountInfo.onlineFailTime || "未知"}`,
947
- `失败原因: ${accountInfo.onlineFailReason || "未知"}`,
948
- `回复消息: ${accountInfo.failReplyMessage || "未知"}`,
949
- `原因分析: ${accountInfo.reasonAnaly || "未知"}`
950
- );
951
- } else if (accountInfo.onlineStatus === "离线" || accountInfo.isPass === "原因未知") {
952
- message.push(
953
- `MAC地址: ${accountInfo.mac || "未知"}`,
954
- `最后IP: ${accountInfo.userIp || "未知"}`
955
- );
956
- if (accountInfo.startTime) {
957
- 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}分钟`;
958
1012
  }
959
- } else {
960
- message.push(
961
- `IP地址: ${accountInfo.userIp}`,
962
- `MAC地址: ${accountInfo.mac}`,
963
- `最大上行: ${accountInfo.maxUpSpeed}Mbps`,
964
- `最大下行: ${accountInfo.maxDownSpeed}Mbps`,
965
- `开始时间: ${accountInfo.startTime}`,
966
- `在线时长: ${durationText}`
967
- );
968
- }
969
- message.push("");
970
- if (orders && orders.length > 0) {
971
- message.push("🛒 订购信息");
972
- message.push("--------------------------------");
973
- orders.forEach((order, index) => {
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 === "原因未知") {
974
1032
  message.push(
975
- `订购 #${index + 1}:`,
976
- ` 订购状态: ${translateOrderStatus(order.order_status)}`,
977
- ` 服务名称: ${order.service_name}`,
978
- ` 生效时间: ${formatRadiusTime(order.valid_date)}`,
979
- ` 失效时间: ${formatRadiusTime(order.expire_date)}`
1033
+ `MAC地址: ${accountInfo.mac || "未知"}`
980
1034
  );
981
- if (index < orders.length - 1) {
982
- message.push("");
1035
+ if (accountInfo.startTime) {
1036
+ message.push(`最后在线: ${accountInfo.startTime || "未知"}`);
983
1037
  }
984
- });
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");
985
1103
  }
986
- message.push("--------------------------------");
987
- return message.join("\n");
988
1104
  } catch (error) {
989
1105
  console.error("查询账号过程出错:", error);
990
- resolve(next());
991
- return null;
1106
+ return `查询账号${input}失败:${error.message || "服务暂时不可用,请稍后再试"}`;
992
1107
  }
993
1108
  }
994
1109
  } catch (error) {
@@ -1006,79 +1121,116 @@ function apply(ctx, config) {
1006
1121
  try {
1007
1122
  const radiusUserInfo = await queryRadiusUser(account);
1008
1123
  if (radiusUserInfo.code !== "000000" || !radiusUserInfo.data) {
1009
- return "账号授权有误,请重新授权";
1124
+ return `账号授权查询失败:${radiusUserInfo.message || "请确认账号是否正确"}`;
1010
1125
  }
1011
1126
  const { user, orders } = radiusUserInfo.data;
1012
- const accountInfo = await queryAccount(account);
1013
- const days = Math.floor(accountInfo.startTimeSum / (60 * 24));
1014
- const hours = Math.floor(accountInfo.startTimeSum % (60 * 24) / 60);
1015
- const minutes = accountInfo.startTimeSum % 60;
1016
- let durationText = "";
1017
- if (days > 0) {
1018
- durationText = `${days}天${hours > 0 ? hours + "小时" : ""}${minutes > 0 ? minutes + "分钟" : ""}`;
1019
- } else if (hours > 0) {
1020
- durationText = `${hours}小时${minutes > 0 ? minutes + "分钟" : ""}`;
1021
- } else {
1022
- durationText = `${minutes}分钟`;
1023
- }
1024
- const message = [
1025
- "🌐 账号查询结果",
1026
- "--------------------------------",
1027
- `账号: ${accountInfo.accessUserName}`,
1028
- // `RADIUS用户密码: ${user.password}`,
1029
- `RADIUS状态: ${translateUserStatus(user.user_status)} ${user.user_status === "active" ? "✅" : user.user_status === "suspend" ? "⚠️" : "❌"}`,
1030
- "",
1031
- `状态: ${accountInfo.onlineStatus} ${accountInfo.onlineStatus === "在线" ? "✅" : "❌"}`,
1032
- `认证: ${accountInfo.isPass} ${accountInfo.isPass.includes("成功") ? "✅" : "❌"}`
1033
- ];
1034
- if (accountInfo.isPass === "上线失败") {
1035
- message.push(
1036
- `MAC地址: ${accountInfo.mac || "未知"}`,
1037
- `失败时间: ${accountInfo.onlineFailTime || "未知"}`,
1038
- `失败原因: ${accountInfo.onlineFailReason || "未知"}`,
1039
- `回复消息: ${accountInfo.failReplyMessage || "未知"}`,
1040
- `原因分析: ${accountInfo.reasonAnaly || "未知"}`
1041
- );
1042
- } else if (accountInfo.onlineStatus === "离线" || accountInfo.isPass === "原因未知") {
1043
- message.push(
1044
- `MAC地址: ${accountInfo.mac || "未知"}`,
1045
- `最后IP: ${accountInfo.userIp || "未知"}`
1046
- );
1047
- if (accountInfo.startTime) {
1048
- 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}分钟`;
1049
1139
  }
1050
- } else {
1051
- message.push(
1052
- `IP地址: ${accountInfo.userIp}`,
1053
- `MAC地址: ${accountInfo.mac}`,
1054
- `最大上行: ${accountInfo.maxUpSpeed}Mbps`,
1055
- `最大下行: ${accountInfo.maxDownSpeed}Mbps`,
1056
- `开始时间: ${accountInfo.startTime}`,
1057
- `在线时长: ${durationText}`
1058
- );
1059
- }
1060
- message.push("");
1061
- if (orders && orders.length > 0) {
1062
- message.push("🛒 订购信息");
1063
- message.push("--------------------------------");
1064
- 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 === "上线失败") {
1065
1151
  message.push(
1066
- `订购 #${index + 1}:`,
1067
- ` 订购状态: ${translateOrderStatus(order.order_status)}`,
1068
- ` 服务名称: ${order.service_name}`,
1069
- ` 生效时间: ${formatRadiusTime(order.valid_date)}`,
1070
- ` 失效时间: ${formatRadiusTime(order.expire_date)}`
1152
+ `MAC地址: ${accountInfo.mac || "未知"}`,
1153
+ `失败时间: ${accountInfo.onlineFailTime || "未知"}`,
1154
+ `失败原因: ${accountInfo.onlineFailReason || "未知"}`,
1155
+ `回复消息: ${accountInfo.failReplyMessage || "未知"}`,
1156
+ `原因分析: ${accountInfo.reasonAnaly || "未知"}`
1071
1157
  );
1072
- if (index < orders.length - 1) {
1073
- message.push("");
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 || "未知"}`);
1074
1164
  }
1075
- });
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");
1076
1230
  }
1077
- message.push("--------------------------------");
1078
- return message.join("\n");
1079
1231
  } catch (error) {
1080
1232
  console.error("查询过程出错:", error);
1081
- return error.message || "查询失败,请检查账号是否正确";
1233
+ return `账号 ${account} 查询失败: ${error.message || "服务暂时不可用,请稍后再试"}`;
1082
1234
  }
1083
1235
  });
1084
1236
  resolve();
@@ -1336,6 +1488,89 @@ function apply(ctx, config) {
1336
1488
  resolve();
1337
1489
  });
1338
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
+ });
1527
+ ctx.command("使用说明", "显示插件的使用方法和命令说明").alias("/使用说明").action(async ({ session }) => {
1528
+ const helpText = [
1529
+ "📋 JSCN小帮手使用说明",
1530
+ "--------------------------------",
1531
+ "命令格式:",
1532
+ " @JSCN小助手+命令+空格+带查询参数",
1533
+ "示例:",
1534
+ " @JSCN小帮手 账号查询 GDF8510013564887",
1535
+ " @JSCN小帮手 E0568934EF95",
1536
+ "",
1537
+ "⚠️ 注意:",
1538
+ " 1. 所有命令只有在@机器人之后输入才会触发!",
1539
+ " 2. @机器人之后提示的4个快捷指令与下列功能无关,实现查询功能需要@后手动输入指令!",
1540
+ " 3. 命令与参数之间必须需要加空格!",
1541
+ "",
1542
+ "🔍 核心功能与命令(@JSCN小帮手之后再输入命令)",
1543
+ "--------------------------------",
1544
+ "1. 账号查询",
1545
+ " 命令:账号查询 [账号]",
1546
+ " 示例:账号查询 GDC8510019239048(账号不区分大小写)",
1547
+ " 功能:查询账号的在线状态、IP地址、订购信息等。离线状态显示最后在线时间,失败状态提供详细原因。",
1548
+ "",
1549
+ "2. 账号详情",
1550
+ " 命令:账号详情 [账号]",
1551
+ " 示例:账号详情 GDC8510019239048",
1552
+ " 功能:查询宽带账号的详细记录,包括登录时间、登出时间等。",
1553
+ "",
1554
+ "3. 终端查询",
1555
+ " 命令:终端查询 [MAC地址]",
1556
+ " 示例:终端查询 00:11:22:33:44:55",
1557
+ " 补充:MAC地址支持任意分隔符,如 00-11-22-33-44-55 或 00:11。22-33|44:55 或 1122334455",
1558
+ " 功能:查询终端的基本信息,包括光功率和业务配置信息。",
1559
+ "",
1560
+ "4. 终端详细查询",
1561
+ " 命令:终端详细查询 [MAC地址]",
1562
+ " 示例:终端详细查询 00:11:22:33:44:55",
1563
+ " 功能:查询终端的详细信息,包括工单信息等。",
1564
+ "",
1565
+ "5. 智能查询",
1566
+ " @JSCN小帮手之后直接输入账号或MAC地址,自动识别类型并返回结果。",
1567
+ " 示例:",
1568
+ " 输入 GDC8510019239048 → 触发账号查询",
1569
+ " 输入 00:11:22:33:44:55 → 触发终端查询",
1570
+ "--------------------------------"
1571
+ ].join("\n");
1572
+ return formatOutputMessage(helpText);
1573
+ });
1339
1574
  }
1340
1575
  __name(apply, "apply");
1341
1576
  // Annotate the CommonJS export names for ESM import in node:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-jscn-aaaquery",
3
3
  "description": "江苏有线无锡分公司宽带信息查询插件",
4
- "version": "1.0.7",
4
+ "version": "1.0.9",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
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
  ### 必需配置项
@@ -106,6 +133,9 @@
106
133
  9. **错误处理**:完善的错误处理机制,确保查询失败时有清晰的提示
107
134
  10. **并行查询**:终端查询采用并行请求策略,在保持接口依赖关系的同时大幅提升查询速度
108
135
  11. **智能合并**:具有相同VLAN配置的多个LAN端口会自动合并显示,提高信息的可读性
136
+ 12. **缓存监控**:内置缓存统计和监控功能,可随时查看缓存状态并手动清理
137
+ 13. **系统状态可视化**:提供系统状态的实时监控,包括直观的进度条显示各项指标
138
+ 14. **自动资源清理**:定期自动清理过期的缓存项、请求队列和消息队列,优化内存使用
109
139
 
110
140
  ## 显示优化
111
141
 
@@ -133,6 +163,10 @@
133
163
  - 工单VLAN值为0时正确显示为"0"
134
164
  - 相同VLAN配置的多个LAN端口合并显示,如"LAN2,LAN3,LAN4"
135
165
 
166
+ 6. **系统状态可视化**:
167
+ - 使用进度条直观显示缓存命中率、并发请求使用情况和消息队列状态
168
+ - 系统关键指标一目了然,方便管理员快速评估系统性能
169
+
136
170
  ## 注意事项
137
171
  1. 所有查询都需要正确的权限和配置。
138
172
  2. MAC地址不区分大小写,支持多种分隔符格式,包括中文符号。
@@ -144,10 +178,16 @@
144
178
  8. 光功率刷新功能将无论设备是否在线都会尝试刷新,确保获取最新数据。
145
179
  9. 针对上线失败或离线状态的账号,系统也能提供有用的信息,不会简单报错。
146
180
  10. 所有命令支持带斜杠(如`/账号查询`)和不带斜杠(如`账号查询`)两种触发方式,提高命令输入的灵活性。
181
+ 11. 系统管理命令可用于监控和优化系统性能,建议定期查看系统状态,必要时手动清理缓存。
147
182
 
148
183
  ## 更新日志
149
184
 
150
185
  ### 最新更新
186
+ - 增加了系统管理命令,包括缓存统计、清理缓存和系统状态监控
187
+ - 新增缓存监控功能,支持查看缓存命中率、大小、项目数等统计信息
188
+ - 增加系统状态可视化展示,使用进度条直观呈现系统各项指标
189
+ - 优化了缓存管理机制,包括定期统计重置和性能数据收集
190
+ - 完善了资源自动清理,减少内存占用和提高系统稳定性
151
191
  - 优化终端查询性能,实现并行请求,大幅减少查询响应时间
152
192
  - 智能合并显示具有相同VLAN配置的LAN端口,使得信息展示更加清晰
153
193
  - 优化VLAN值显示:端口VLAN为"-"时显示为"未配置",工单VLAN为0时正确显示为"0"