第十一批:修复秒/毫秒时间戳混存导致1970年显示bug
- 根因: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对账拉锯
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.version>25</java.version>
|
||||
<java.version>27</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
// 忽略异常
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<String, Object> 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<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("name", str(u.get("player_name")));
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
// 忽略异常
|
||||
|
||||
Reference in New Issue
Block a user