From 9e88c530529bedf659f21aea25d8a3b0ade36104 Mon Sep 17 00:00:00 2001 From: weicw <594098497@qq.com> Date: Mon, 26 Oct 2026 11:09:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=95=B0=E6=8D=AE=E6=BA=90=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E6=97=B6=E5=8C=BA=E5=BC=BA=E5=88=B6=20+08:00=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=9B=E5=BB=BA=E6=97=B6=E9=97=B4=E6=97=A9?= =?UTF-8?q?=208=20=E5=B0=8F=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 生产数据库容器(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 字面量与教师建号行的时间回填模板(默认注释未执行)。 --- sql/20261026_timezone_8h_check.sql | 56 +++++++++++++++++++++++++ src/main/resources/application-dev.yml | 3 +- src/main/resources/application-prod.yml | 5 ++- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 sql/20261026_timezone_8h_check.sql diff --git a/sql/20261026_timezone_8h_check.sql b/sql/20261026_timezone_8h_check.sql new file mode 100644 index 0000000..a66cb92 --- /dev/null +++ b/sql/20261026_timezone_8h_check.sql @@ -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; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3528085..354bbea 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -3,7 +3,8 @@ # 数据源配置 spring: 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 password: TytQZsSW5m4wGh38 driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 9d2e71c..392159b 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,7 +3,10 @@ # 数据源配置 spring: 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 password: TytQZsSW5m4wGh38 driver-class-name: com.mysql.cj.jdbc.Driver