fix: 数据源会话时区强制 +08:00,修复创建时间早 8 小时

生产数据库容器(1Panel-mariadb-ZOpD)的 system_time_zone 是 UTC,而数据源 URL 写着

serverTimezone=Asia/Shanghai:JDBC 会话时区是 UTC、驱动却按 +08 解析服务端返回的字面量,

于是 timestamp 列(分类管理、文章、系统日志、审核流、AI 对话等)读出来整体早 8 小时。

数据源 URL 增加 connectionTimeZone=%2B08:00&forceConnectionTimeZoneToSession=true,

连接建立时把会话时区设为 +08:00,与 JVM 默认时区、spring.jackson.time-zone 保持一致。

用偏移量而非命名时区,避免 MariaDB 容器未加载时区表时报 Unknown or incorrect time zone。

附 sql/20261026_timezone_8h_check.sql:部署后的时区自检、分类管理核对,

以及 datetime 列历史 UTC 字面量与教师建号行的时间回填模板(默认注释未执行)。
This commit is contained in:
2026-10-26 11:09:15 +08:00
parent 0252672997
commit 9e88c53052
3 changed files with 62 additions and 2 deletions
+56
View File
@@ -0,0 +1,56 @@
-- 时区 8 小时偏差:修复后核对 + 历史数据排查(默认只读,改数据的语句都已注释)
-- 背景:生产 DB 容器 1Panel-mariadb-ZOpD 系统时区是 UTC,应用容器/JVM 是 +08,
-- 驱动参数 serverTimezone=Asia/Shanghai ⇒ 会话时区(UTC) 与驱动连接时区(+08) 不一致。
-- 修复:application-prod.yml 的 url 加 connectionTimeZone=%2B08:00&forceConnectionTimeZoneToSession=true,
-- 连接建立时把会话时区设为 +08:00,与驱动一致。
-- 预期:
-- * timestamp 列(cms_* / ai_chat_* / sys_* / gxmu_review_* / shop_user_oauth)存的是真实时刻(epoch),
-- 修复后读出来自动回正:「知识库管理-分类管理」的创建时间会从 09:11 变成 17:11(真实时间)。
-- * datetime 列(gxmu_* 业务表)如果创建时间来自列默认值 CURRENT_TIMESTAMP,存的是 UTC 字面量,
-- 修复后仍然早 8 小时,需要按表核对后回填。
-- * 代码里用 java.util.Date 显式写 timestamp 列的行(TeacherAccountService 教师建号/授权/冻结),
-- 存的是 epoch 偏晚 8 小时,修复后会显示晚 8 小时,需要 -8h 回填。
-- 1) 会话时区自检(部署后执行,session 应为 +08:00 或 Asia/Shanghai)
SELECT @@global.time_zone AS global_tz, @@session.time_zone AS session_tz,
@@system_time_zone AS system_tz, NOW() AS db_now, UTC_TIMESTAMP() AS db_utc;
-- 2) 分类管理核对(修复后 create_time 应比修复前 +8 小时)
SELECT category_id, title, create_time, update_time
FROM cms_article_category ORDER BY category_id;
-- 3) 教师账号(TeacherAccountService 用 new Date() 显式写入)—— 修复后可能晚 8 小时
SELECT COUNT(*) AS teacher_user_cnt FROM sys_user WHERE user_code IS NOT NULL;
SELECT user_id, user_code, username, real_name, create_time, update_time
FROM sys_user WHERE user_code IS NOT NULL ORDER BY create_time DESC LIMIT 30;
-- 4) gxmu_* datetime 表排查:重点看 HOUR(create_time) < 8 的行(北京时间凌晨不可能大量建数据)
-- 逐表替换表名继续:gxmu_declare / gxmu_student / gxmu_teacher / gxmu_sync_record /
-- tzb_project_list / tzb_talent_list / wxxzx_* / qmgc_form / sjqn / ...
SELECT 'gxmu_declare' AS tbl, COUNT(*) AS total,
MIN(create_time) AS min_ct, MAX(create_time) AS max_ct,
SUM(HOUR(create_time) < 8) AS early_hour_rows
FROM gxmu_declare;
-- 5) 【谨慎】教师建号行 -8h 回填模板:先跑第 3 步确认数量与样本,备份后再执行
-- START TRANSACTION;
-- UPDATE sys_user
-- SET create_time = create_time - INTERVAL 8 HOUR,
-- update_time = update_time - INTERVAL 8 HOUR
-- WHERE user_code IS NOT NULL
-- AND create_time >= '<教师账号同步功能上线时间>';
-- UPDATE sys_user_role ur JOIN sys_user u ON u.user_id = ur.user_id
-- SET ur.create_time = ur.create_time - INTERVAL 8 HOUR,
-- ur.update_time = ur.update_time - INTERVAL 8 HOUR
-- WHERE u.user_code IS NOT NULL
-- AND ur.create_time >= '<教师账号同步功能上线时间>';
-- COMMIT;
-- 6) 【谨慎】gxmu_* datetime 列 +8h 回填模板:必须先按表确认这些行是列默认值写的 UTC 字面量。
-- 代码里 LocalDateTime.now() 写入的行在 JVM=+08 时已经是正确北京时间,回填会多 8 小时。
-- START TRANSACTION;
-- UPDATE gxmu_xxx
-- SET create_time = create_time + INTERVAL 8 HOUR,
-- update_time = update_time + INTERVAL 8 HOUR
-- WHERE create_time < '<切换时间点>';
-- COMMIT;
+2 -1
View File
@@ -3,7 +3,8 @@
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
url: jdbc:mysql://132.232.214.96:13306/tuanwei?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true # 与生产保持一致:会话时区强制 +08:00,避免 DB 容器时区不同导致 8 小时偏差
url: jdbc:mysql://132.232.214.96:13306/tuanwei?useUnicode=true&characterEncoding=utf8&useSSL=false&connectionTimeZone=%2B08:00&forceConnectionTimeZoneToSession=true&rewriteBatchedStatements=true
username: tuanwei username: tuanwei
password: TytQZsSW5m4wGh38 password: TytQZsSW5m4wGh38
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
+4 -1
View File
@@ -3,7 +3,10 @@
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
url: jdbc:mysql://1Panel-mariadb-ZOpD:3306/tuanwei?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # forceConnectionTimeZoneToSession:连接建立时把会话时区设为 +08:00。
# 生产 DB 容器(1Panel-mariadb-ZOpD)system_time_zone=UTC,若不强制,会话时区会是 UTC,
# 与驱动连接时区(+08) 不一致,timestamp/date 列读出会早 8 小时。
url: jdbc:mysql://1Panel-mariadb-ZOpD:3306/tuanwei?useUnicode=true&characterEncoding=utf8&useSSL=false&connectionTimeZone=%2B08:00&forceConnectionTimeZoneToSession=true&rewriteBatchedStatements=true
username: tuanwei username: tuanwei
password: TytQZsSW5m4wGh38 password: TytQZsSW5m4wGh38
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver