fix(db): 修正存量「归还时间/报废时间」的 8 小时时区偏差(附自检)

问题
----
process_return 与 scrap_sources.deduct 原先用 datetime.now()(容器本地时间,
Docker 默认 UTC)写 return_time / operation_time,而同一行的 borrow_time 由
beijing_time() 写入 —— 同一行两个时钟,差 8 小时。

实测(开发库)trans_borrow 有 53 行呈现「归还时间早于借出时间」这种物理上
不可能的数据,例如 id=80 借出 09-09 11:04、归还 09-09 03:46。

代码侧已修(f7c49f4),新数据已是北京时间 —— 本次只处理存量。

★ 脚本刻意做了「先自检、后修正」的两段式
  服务器 API 容器的 TZ 未必是 UTC(若本就是 Asia/Shanghai,datetime.now()
  一直是北京时间,则**根本无需修正**)。若不做自检直接 +8h,会把正确的时间
  改错。故第 0 步先输出:异常行数、以及按分界切出的「UTC 段/空档/北京段」
  三段计数,由使用人据结果决定是否执行第 2 步。

★ 分界与幂等性都在脚本里写明
  · 分界 = 修复代码在该服务器**部署生效的时刻**(不是提交时刻),需按实际调整;
  · 脚本**不幂等** —— 重复执行会再加 8 小时,已显式警示并给出回滚写法。

开发库已按此逻辑修正:53 行 trans_borrow + 1 行 trans_scrap,
修正后「归还早于借出」为 0,抽样时间落在正常工作时段。
This commit is contained in:
yueli
2026-09-18 09:27:17 +08:00
parent 91c4ae85db
commit 1eaa203eba

View File

@ -0,0 +1,112 @@
-- =============================================================================
-- 修正存量「归还时间 / 报废时间」的 8 小时时区偏差
--
-- 问题
-- process_return 与 scrap_sources.deduct 原先用 datetime.now() 写 return_time /
-- operation_time取的是**容器本地时间**Docker 默认 UTC而同一行的
-- borrow_time 由 beijing_time() 写入。同一行两个时钟,差 8 小时。
-- 实测开发库53 行 trans_borrow 出现「归还时间早于借出时间」这种
-- 物理上不可能的数据,例如 id=80 借出 09-09 11:04、归还 09-09 03:46。
--
-- 代码侧已修
-- 提交 f7c49f42026-09-17 09:18 北京时间)起改用 beijing_time()
-- **新写入的数据已经是北京时间**(已验证 TransReturn / TransBorrowReturn /
-- TransScrap 三张表的模型默认值均为北京)。本脚本只处理存量。
--
-- ---------------------------------------------------------------------------
-- ★★ 执行前必读:请先跑「第 0 步 自检」,据其结果决定是否执行 ★★
--
-- 1) 若自检的「异常行数」为 0且 UTC 区间计数为 0 —— 说明**本服务器无此问题**
-- (例如 API 容器的 TZ 本就是 Asia/Shanghaidatetime.now() 一直是北京时间),
-- **不要执行第 2 步**,否则会把正确的时间又 +8 小时。
--
-- 2) 分界时间 @boundary 的含义:**修复代码在本服务器部署生效的时刻**。
-- · 该时刻之前写入的行 → UTC需 +8h
-- · 该时刻之后写入的行 → 北京时间,不能动。
-- 开发库用的是提交时间 2026-09-17 09:18北京= 01:18UTC
-- 两组数据之间通常有一段空档,请用自检查看空档是否存在。
-- 若你的部署时间不同,请替换下方两处 '2026-09-17 01:18:00'。
--
-- 幂等性
-- ⚠ 本脚本**不幂等** —— 重复执行会把已经修正过的时间再加 8 小时。
-- 请在执行前确认已备份,且只执行一次。
--
-- 回滚
-- 把两处 `+ interval '8 hours'` 改成 `- interval '8 hours'`,用同一条件再执行一次。
-- =============================================================================
-- =============================================================================
-- 第 0 步:自检(只读,先跑这一段看结果)
-- =============================================================================
SELECT '=== 0.1) 物理上不可能的「归还早于借出」行数 ===' AS "核对项";
SELECT count(*) AS
FROM trans_borrow
WHERE return_time IS NOT NULL AND borrow_time IS NOT NULL
AND return_time < borrow_time;
SELECT '=== 0.2) 按分界切分UTC 段 / 空档 / 北京段 ===' AS "核对项";
SELECT
count(*) FILTER (WHERE return_time < '2026-09-17 01:18:00') AS UTC段_需修正,
count(*) FILTER (WHERE return_time >= '2026-09-17 01:18:00'
AND return_time < '2026-09-17 09:18:00') AS _应为0,
count(*) FILTER (WHERE return_time >= '2026-09-17 09:18:00') AS _不可动
FROM trans_borrow
WHERE return_time IS NOT NULL;
SELECT '=== 0.3) trans_scrap 同样切分 ===' AS "核对项";
SELECT
count(*) FILTER (WHERE operation_time < '2026-09-17 01:18:00') AS UTC段_需修正,
count(*) FILTER (WHERE operation_time >= '2026-09-17 01:18:00'
AND operation_time < '2026-09-17 09:18:00') AS _应为0,
count(*) FILTER (WHERE operation_time >= '2026-09-17 09:18:00') AS _不可动
FROM trans_scrap;
-- 判读:
-- · 0.1 为 0 且 0.2/0.3 的 UTC 段也为 0 → 无需修正,停止。
-- · 0.1 大于 0 → 确有此问题,可继续第 2 步。
-- =============================================================================
-- 第 1 步:备份(强烈建议)
-- =============================================================================
-- CREATE TABLE trans_borrow_bak_20260918 AS
-- SELECT id, return_time, return_operator FROM trans_borrow;
-- CREATE TABLE trans_scrap_bak_20260918 AS
-- SELECT id, operation_time FROM trans_scrap;
-- =============================================================================
-- 第 2 步:修正(确认第 0 步结果后再执行)
-- =============================================================================
BEGIN;
UPDATE trans_borrow
SET return_time = return_time + interval '8 hours'
WHERE return_time IS NOT NULL
AND return_time < '2026-09-17 01:18:00'; -- ← 替换为你的部署时刻UTC 口径)
UPDATE trans_scrap
SET operation_time = operation_time + interval '8 hours'
WHERE operation_time < '2026-09-17 01:18:00'; -- ← 同上
COMMIT;
-- =============================================================================
-- 第 3 步:执行后核对
-- =============================================================================
SELECT '=== 3.1) 异常行数应为 0 ===' AS "核对项";
SELECT count(*) AS
FROM trans_borrow
WHERE return_time IS NOT NULL AND borrow_time IS NOT NULL
AND return_time < borrow_time;
SELECT '=== 3.2) 抽样:归还时间应晚于借出时间,且落在工作时段 ===' AS "核对项";
SELECT id,
to_char(borrow_time, 'MM-DD HH24:MI') AS ,
to_char(return_time, 'MM-DD HH24:MI') AS
FROM trans_borrow
WHERE return_time IS NOT NULL
ORDER BY return_time DESC
LIMIT 8;