From 4352a351242143d4f8de590929ffd368bb9dd64a Mon Sep 17 00:00:00 2001 From: youpaishidifu <1@ypshidifu.cn> Date: Tue, 13 Oct 2026 14:55:29 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E5=8D=81=E4=B8=80=E6=89=B9=EF=BC=9A?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A7=92/=E6=AF=AB=E7=A7=92=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3=E6=B7=B7=E5=AD=98=E5=AF=BC=E8=87=B41970?= =?UTF-8?q?=E5=B9=B4=E6=98=BE=E7=A4=BAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根因:register_time/last_login_time/invited_at 两种单位混存——Java 本地注册写毫秒(System.currentTimeMillis)、PHP同步拉回写秒(time()), 而所有显示端都按毫秒解释 → 秒值~1.78e9显示为1970-01-21 - DurationParser 新增 toMs() 归一化辅助(<10^12视为秒,×1000,幂等) - whois/Main /account/GUI多账号/邀请GUI绑定时间/临时密码邮件:读取端统一归一化 - InviteManager.reconcileInviteData:秒级存量invited_at原地×1000迁移, 回填兜底链register_time/last_login_time先归一化;月度计数与明细比较前归一化 - ChatFilterManager.isNewPlayerUnder1Hour:旧代码无条件×1000,对毫秒行 算出巨大负elapsed恒判新玩家 → 改用toMs - AreaProtection/WebManager 领地转让5分钟防秒退检查:同样归一化 - 不动同步层(applyPulledRow保持原值),避免与PHP对账拉锯 --- pom.xml | 2 +- src/main/java/Sdf1_login/AreaProtection.java | 8 ++++-- .../java/Sdf1_login/ChatFilterManager.java | 15 ++++++----- src/main/java/Sdf1_login/DurationParser.java | 9 +++++++ src/main/java/Sdf1_login/GUIManager.java | 7 +++-- src/main/java/Sdf1_login/InviteManager.java | 26 +++++++++++++++---- src/main/java/Sdf1_login/Main.java | 13 +++++++--- src/main/java/Sdf1_login/WebManager.java | 8 ++++-- 8 files changed, 66 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 1540546..fd87061 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ jar - 25 + 27 UTF-8 diff --git a/src/main/java/Sdf1_login/AreaProtection.java b/src/main/java/Sdf1_login/AreaProtection.java index cc6c16b..1e76030 100644 --- a/src/main/java/Sdf1_login/AreaProtection.java +++ b/src/main/java/Sdf1_login/AreaProtection.java @@ -9829,7 +9829,9 @@ public class AreaProtection implements Listener { try { Object regTimeObj = dbMgr.getField(newOwner, "register_time"); if (regTimeObj instanceof Number) { - registerTime = ((Number) regTimeObj).longValue(); + // ★ 第十一批:秒/毫秒混存归一化 + registerTime = DurationParser.toMs( + ((Number) regTimeObj).longValue()); } } catch (Exception e) { // 忽略异常 @@ -11982,7 +11984,9 @@ public class AreaProtection implements Listener { try { Object regTimeObj = dbMgr.getField(newOwner, "register_time"); if (regTimeObj instanceof Number) { - registerTime = ((Number) regTimeObj).longValue(); + // ★ 第十一批:秒/毫秒混存归一化 + registerTime = DurationParser.toMs( + ((Number) regTimeObj).longValue()); } } catch (Exception e) { // 忽略异常 diff --git a/src/main/java/Sdf1_login/ChatFilterManager.java b/src/main/java/Sdf1_login/ChatFilterManager.java index 6814775..e9fade6 100644 --- a/src/main/java/Sdf1_login/ChatFilterManager.java +++ b/src/main/java/Sdf1_login/ChatFilterManager.java @@ -631,14 +631,17 @@ public class ChatFilterManager { DatabaseManager db = mainPlugin.getDb(); if (db == null) return false; // 查不到就不拦截,直接放行 - // register_time 在数据库存的是秒级时间戳 + // ★ 第十一批:register_time 秒/毫秒混存(PHP 同步行为秒、 + // 本地注册为毫秒)→ 统一归一化为毫秒。 + // 旧代码无条件 ×1000:对已是毫秒的行会算出巨大负数 + // elapsed,恒判「新玩家」。 Object regTimeObj = db.getField(p.getName(), "register_time"); if (regTimeObj == null) return false; // 没注册记录 → 放行 - - long regTimeSeconds = ((Number) regTimeObj).longValue(); - if (regTimeSeconds == 0) return false; // 注册时间为0 → 放行 - - long regTimeMs = regTimeSeconds * 1000; + + long regTimeMs = DurationParser.toMs( + ((Number) regTimeObj).longValue()); + if (regTimeMs == 0) return false; // 注册时间为0 → 放行 + long elapsed = System.currentTimeMillis() - regTimeMs; boolean isNew = elapsed < 3600000; diff --git a/src/main/java/Sdf1_login/DurationParser.java b/src/main/java/Sdf1_login/DurationParser.java index 5271395..ee3b976 100644 --- a/src/main/java/Sdf1_login/DurationParser.java +++ b/src/main/java/Sdf1_login/DurationParser.java @@ -315,6 +315,15 @@ public class DurationParser { return section + (hasNum ? num : 0); } + /** ★ 第十一批(2026-10-12):秒/毫秒混合时间戳归一化为毫秒。 + * login.db 的 register_time/last_login_time/invited_at 两种单位 + * 混存(Java 本地注册写毫秒、PHP 同步拉回写秒),凡显示、比较、 + * 回填前一律先过这里。幂等:已是毫秒的原样返回。 + * 阈值 10^12:秒级时间戳到 33658 年才到 10^12,安全。 */ + public static long toMs(long ts) { + return (ts > 0 && ts < 1000000000000L) ? ts * 1000L : ts; + } + /** 格式化绝对到期时间戳为「yyyy年M月d日 HH:mm」 */ public static String formatExpire(long ms) { Calendar c = Calendar.getInstance(); diff --git a/src/main/java/Sdf1_login/GUIManager.java b/src/main/java/Sdf1_login/GUIManager.java index 3f76978..f32b6fe 100644 --- a/src/main/java/Sdf1_login/GUIManager.java +++ b/src/main/java/Sdf1_login/GUIManager.java @@ -2219,8 +2219,9 @@ public class GUIManager implements Listener { if (slot >= 45) break; String accName = (String) acc .get("player_name"); - long regTime = ((Number) acc - .get("register_time")).longValue(); + // ★ 第十一批:秒/毫秒混存归一化(PHP 同步行为秒) + long regTime = DurationParser.toMs(((Number) acc + .get("register_time")).longValue()); String dateStr = regTime > 0 ? sdf.format( new java.util.Date(regTime)) @@ -2444,6 +2445,8 @@ public class GUIManager implements Listener { p.getName(), "invited_at"); long ts = (tsObj instanceof Number) ? ((Number) tsObj).longValue() : 0L; + // ★ 第十一批:秒/毫秒混存归一化(回填来源 register_time 可能为秒) + ts = DurationParser.toMs(ts); String timeStr = ts > 0 ? new java.text.SimpleDateFormat( "yyyy-MM-dd HH:mm") diff --git a/src/main/java/Sdf1_login/InviteManager.java b/src/main/java/Sdf1_login/InviteManager.java index f4a9a40..251d3fb 100644 --- a/src/main/java/Sdf1_login/InviteManager.java +++ b/src/main/java/Sdf1_login/InviteManager.java @@ -142,6 +142,15 @@ public class InviteManager { String name = str(u.get("player_name")); if (name.isEmpty()) continue; if (str(u.get("invited_by")).isEmpty()) continue; + long invAt = lng(u.get("invited_at")); + if (invAt > 0 && invAt < 1000000000000L) { + // ★ 第十一批:秒级存量(回填时继承了秒级 + // register_time)→ 原地转毫秒 + plugin.getDb().setField(name, "invited_at", + invAt * 1000L); + u.put("invited_at", invAt * 1000L); + fixedAt++; + } if (lng(u.get("invited_at")) > 0) continue; long ts = lookupBondTx( "player_name", name, @@ -149,8 +158,10 @@ public class InviteManager { if (ts <= 0) ts = lookupBondTx( "target_player", name, "invite_reward"); - if (ts <= 0) ts = lng(u.get("register_time")); - if (ts <= 0) ts = lng(u.get("last_login_time")); + if (ts <= 0) ts = DurationParser.toMs( + lng(u.get("register_time"))); + if (ts <= 0) ts = DurationParser.toMs( + lng(u.get("last_login_time"))); if (ts <= 0) ts = now; plugin.getDb().setField( name, "invited_at", ts); @@ -163,7 +174,8 @@ public class InviteManager { for (Map u : users) { String inviter = str(u.get("invited_by")); if (inviter.isEmpty()) continue; - if (lng(u.get("invited_at")) >= monthStart) { + if (DurationParser.toMs( + lng(u.get("invited_at"))) >= monthStart) { counter.merge(inviter, 1, Integer::sum); } } @@ -244,7 +256,9 @@ public class InviteManager { : plugin.getDb().getAllUsers()) { if (!inviter.equals( str(u.get("invited_by")))) continue; - if (lng(u.get("invited_at")) >= monthStart) n++; + // ★ 第十一批:秒/毫秒归一化后再比较 + if (DurationParser.toMs( + lng(u.get("invited_at"))) >= monthStart) n++; } return n; } @@ -265,7 +279,9 @@ public class InviteManager { : plugin.getDb().getAllUsers()) { if (!inviter.equals( str(u.get("invited_by")))) continue; - long ts = lng(u.get("invited_at")); + // ★ 第十一批:秒/毫秒归一化后再比较与展示 + long ts = DurationParser.toMs( + lng(u.get("invited_at"))); if (ts < monthStart) continue; Map row = new LinkedHashMap<>(); row.put("name", str(u.get("player_name"))); diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index 7125d1c..4b8b8aa 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -1805,8 +1805,11 @@ public class Main extends JavaPlugin sender.sendMessage("§c未找到玩家: " + target); return; } - long reg = asLong(db.getField(target, "register_time")); - long lastLogin = asLong(db.getField(target, "last_login_time")); + // ★ 第十一批:秒/毫秒混存归一化(PHP 同步行为秒、本地注册为毫秒) + long reg = DurationParser.toMs( + asLong(db.getField(target, "register_time"))); + long lastLogin = DurationParser.toMs( + asLong(db.getField(target, "last_login_time"))); Object ipObj = db.getField(target, "last_login_ip"); String lastIp = (ipObj == null || String.valueOf(ipObj).isEmpty()) @@ -7954,7 +7957,7 @@ public class Main extends JavaPlugin // 获取最近登录信息 Object lastLoginObj = db.getField(tgt, "last_login_time"); long lastLogin = lastLoginObj != null - ? ((Number) lastLoginObj).longValue() : 0; + ? DurationParser.toMs(((Number) lastLoginObj).longValue()) : 0; String lastIP = (String) db.getField(tgt, "last_ip"); if (lastIP == null || lastIP.isEmpty()) lastIP = "未知"; @@ -9926,7 +9929,9 @@ public class Main extends JavaPlugin sender.sendMessage("§7邮箱: §e" + user.getOrDefault("email", "无")); Object regTime = user.get("register_time"); if (regTime instanceof Number) { - long rt = ((Number) regTime).longValue(); + // ★ 第十一批:秒/毫秒混存归一化 + long rt = DurationParser.toMs( + ((Number) regTime).longValue()); if (rt > 0) { String date = new java.text.SimpleDateFormat( "yyyy-MM-dd HH:mm") diff --git a/src/main/java/Sdf1_login/WebManager.java b/src/main/java/Sdf1_login/WebManager.java index 988f3f8..4d44ba2 100644 --- a/src/main/java/Sdf1_login/WebManager.java +++ b/src/main/java/Sdf1_login/WebManager.java @@ -6100,7 +6100,9 @@ public class WebManager { try { Object regTimeObj = plugin.getDb().getField(newOwner, "register_time"); if (regTimeObj instanceof Number) { - registerTime = ((Number) regTimeObj).longValue(); + // ★ 第十一批:秒/毫秒混存归一化 + registerTime = DurationParser.toMs( + ((Number) regTimeObj).longValue()); } } catch (Exception e) { // 忽略异常 @@ -6489,7 +6491,9 @@ public class WebManager { try { Object regTimeObj = dbMgr.getField(newOwner, "register_time"); if (regTimeObj instanceof Number) { - registerTime = ((Number) regTimeObj).longValue(); + // ★ 第十一批:秒/毫秒混存归一化 + registerTime = DurationParser.toMs( + ((Number) regTimeObj).longValue()); } } catch (Exception e) { // 忽略异常