fix: 用户组PHP→Java拉取JSON解析bug + owner_change重复打印

1. pullUserGroupsFromPHP: 数组结束查找逻辑修复
   - 旧代码用[]计数找数组边界,但去掉开头[后内容是{},遇到]时depth=-1永远不匹配
   - 改为用{}计数+objDepth==0时匹配],正确解析[{...},{...}]格式
2. setLandOwnerFromWeb: 幂等检查
   - 如果所有者已相同(equalsIgnoreCase),跳过DB更新和日志打印
   - 防止ack失败后同一条owner_change重复打印
3. processedChangeIds清理优化
   - 超500时不再粗暴清空全部,先移除已过游标的旧ID(id < lastPollAdminChangesId)
   - 只在清理后仍然过大时才全部清空
4. pullUserGroupsFromPHP: 增加调试日志
   - 空响应/success!=true/无groups数组/解析失败都记录fine级别日志
This commit is contained in:
2026-06-28 09:57:52 +08:00
parent 06f8bd68cd
commit b26f979924
2 changed files with 25 additions and 16 deletions
+6 -1
View File
@@ -7545,6 +7545,12 @@ public class AreaProtection implements Listener {
public void setLandOwnerFromWeb(String landName, String newOwner) {
if (dbConnection == null) return;
try {
// ★ 幂等检查:如果所有者已相同则跳过,避免ack失败后重复打印
AreaConfig ac = getLand(landName);
if (ac != null && newOwner.equalsIgnoreCase(ac.owner)) {
return;
}
// 更新数据库
PreparedStatement ps = dbConnection.prepareStatement("UPDATE area_lands SET owner = ? WHERE name = ?");
ps.setString(1, newOwner);
@@ -7553,7 +7559,6 @@ public class AreaProtection implements Listener {
ps.close();
// 更新内存中的AreaConfig
AreaConfig ac = getLand(landName);
if (ac != null) {
ac.owner = newOwner;
}
+19 -15
View File
@@ -3077,35 +3077,37 @@ public class WebManager {
params.put("action", "list_user_groups");
params.put("secret", secretKey);
String resp = httpGet("api/land_api.php", params);
if (resp == null || resp.isEmpty()) return;
if (resp == null || resp.isEmpty()) { plugin.getLogger().fine("[防护-sync] 从PHP拉取用户组: 响应为空"); return; }
// 简单解析JSON: {"success":true,"groups":[{...},{...}]}
if (!resp.contains("\"success\":true")) return;
if (!resp.contains("\"success\":true")) { plugin.getLogger().fine("[防护-sync] 从PHP拉取用户组: success!=true, resp=" + resp.substring(0, Math.min(200, resp.length()))); return; }
int groupsStart = resp.indexOf("\"groups\":[");
if (groupsStart < 0) return;
String arrStr = resp.substring(groupsStart + 10);
// 找到数组的结束 ]
int depth = 0;
if (groupsStart < 0) { plugin.getLogger().fine("[防护-sync] 从PHP拉取用户组: 未找到groups数组"); return; }
String arrStr = resp.substring(groupsStart + 10); // 跳过 "groups":[
// ★ 修复:用{}计数来找数组的结束 ](去掉开头[后内容是{...},{...}],不含嵌套[])
int objDepth = 0;
int arrEnd = -1;
for (int i = 0; i < arrStr.length(); i++) {
char c = arrStr.charAt(i);
if (c == '[') depth++;
else if (c == ']') {
depth--;
if (depth == 0) { arrEnd = i; break; }
if (c == '{') objDepth++;
else if (c == '}') objDepth--;
else if (c == ']' && objDepth == 0) {
arrEnd = i;
break;
}
}
if (arrEnd < 0) return;
arrStr = arrStr.substring(1, arrEnd); // 去掉 [ ]
if (arrEnd < 0) { plugin.getLogger().fine("[防护-sync] 从PHP拉取用户组: 数组解析失败"); return; }
arrStr = arrStr.substring(1, arrEnd); // 去掉开头 [ 和结尾 ]
if (arrStr.trim().isEmpty()) return; // 空数组
UserGroupManager ugm = plugin.getUserGroup();
if (ugm == null) return;
plugin.getLogger().fine("[防护-sync] 从PHP拉取用户组: 解析到 " + arrStr.length() + " 字符的数组内容");
// 拆分每个JSON对象
int imported = 0;
depth = 0;
int depth = 0;
int objStart = -1;
for (int i = 0; i < arrStr.length(); i++) {
char c = arrStr.charAt(i);
@@ -3309,9 +3311,11 @@ public class WebManager {
doGet(ackUrl);
}
// ★ 清理去重集合,防止内存泄漏
// ★ 清理去重集合:只清理已过游标的旧ID,防止ACK失败后重复打印
if (processedChangeIds.size() > 500) {
processedChangeIds.clear();
processedChangeIds.removeIf(id -> id < lastPollAdminChangesId);
// 如果清理后仍然过大,才全部清空(最后手段)
if (processedChangeIds.size() > 500) processedChangeIds.clear();
}
} catch (Exception e) {
plugin.getLogger().warning("[Web通信] 轮询PHP管理员变更异常: " + e.getMessage());