20260915221013
This commit is contained in:
1 parent
1f56cc3efb
commit
dce29f250e
54 files changed
+3732
-574
No files matched your search
@@ -9,7 +9,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -51,10 +52,15 @@ public class LoginLogService {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
// 字符串业务键方案:主键为业务层生成的 UUIDv7
|
||||
row.put("b_id", UuidV7Generator.nextUuid().toString());
|
||||
// b_user_id 引用 b_user.b_id(账号字符串,即稳定用户标识);仅登录成功时存在
|
||||
row.put("b_user_id", success ? account : null);
|
||||
// b_user_id 引用 b_user.b_id(账号字符串,即稳定用户标识)。
|
||||
// 失败也照记:失败记录的账号恰恰是审计最需要的信息 —— 没有它就无法回答
|
||||
// 「哪个账号正在被爆破 / 被探测」;日志列表直接展示本列(v_s_log_login.v_user_name)。
|
||||
row.put("b_user_id", account);
|
||||
row.put("b_login_type", LOGIN_TYPE_PASSWORD);
|
||||
row.put("b_occurdatetime", Timestamp.from(java.time.Instant.now()));
|
||||
// datetime2 无时区语义:直接写「北京时间的墙钟值」,与库中既有数据、界面显示同一基准。
|
||||
// 不用 Timestamp.from(Instant.now()):它的落库含义随 JVM 默认时区浮动,
|
||||
// 读出后还会被当作瞬时时刻再转成 UTC(见 DbUtils.normalizeJdbcValue)。
|
||||
row.put("b_occurdatetime", LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
|
||||
row.put("b_ip", info.ip());
|
||||
row.put("b_ip_location", info.ipLocation());
|
||||
row.put("b_browser", info.browser());
|
||||
|
||||
@@ -13,6 +13,10 @@ import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -423,7 +427,7 @@ public class DbUtils {
|
||||
if (index != totalColumnIndex) {
|
||||
row.put(
|
||||
metadata.getColumnLabel(index),
|
||||
resultSet.getObject(index)
|
||||
normalizeJdbcValue(resultSet.getObject(index))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -646,7 +650,10 @@ public class DbUtils {
|
||||
while (resultSet.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>(columnCount);
|
||||
for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
|
||||
row.put(metadata.getColumnLabel(columnIndex), resultSet.getObject(columnIndex));
|
||||
row.put(
|
||||
metadata.getColumnLabel(columnIndex),
|
||||
normalizeJdbcValue(resultSet.getObject(columnIndex))
|
||||
);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
@@ -654,6 +661,35 @@ public class DbUtils {
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* datetime / datetime2 / date / time 在库里都是无时区的「墙钟时间」,读出即字面值。
|
||||
* 这里直接落成界面显示用的字符串(与旧项目读取 ResultSet 时 SimpleDateFormat 的做法一致):
|
||||
* datetime2 → "2026-09-15 21:16:47",date → "2026-09-15",time → "21:16:47"。
|
||||
* 三点理由:
|
||||
* ① 前端拿到即显示,不需要 T/小数秒的裁剪加工,各个消费点不必各写一遍;
|
||||
* ② 驱动默认返回的 java.sql.Timestamp 带「瞬时时刻」语义,Jackson 会按 UTC 序列化成
|
||||
* "2026-09-15T13:20:51.842Z",前端漏转就整体早 8 小时 —— 在出口落地成字面值可根治;
|
||||
* ③ 秒以下精度对业务无意义,统一截掉。
|
||||
*/
|
||||
private static Object normalizeJdbcValue(Object value) {
|
||||
if (value instanceof java.sql.Timestamp timestamp) {
|
||||
return timestamp.toLocalDateTime().format(DATE_TIME_FORMATTER);
|
||||
}
|
||||
if (value instanceof java.sql.Date date) {
|
||||
return date.toLocalDate().format(DATE_FORMATTER);
|
||||
}
|
||||
if (value instanceof java.sql.Time time) {
|
||||
return time.toLocalTime().format(TIME_FORMATTER);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/** 时间列的对外文本格式:与旧项目一致,也是浏览器直接展示的形态 */
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private boolean hasText(String value) {
|
||||
return value != null && !value.isBlank();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user