From cb2476aaed7607f18f5152f210ca367117f598c4 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 18 Aug 2026 19:58:32 +0800 Subject: [PATCH] =?utf8?q?fix:=20=E6=97=B6=E5=8C=BAbug-StudyMode/TestMode?= =?utf8?q?=E6=94=B9=E7=94=A8=E4=B8=AD=E5=9B=BD=E6=97=B6=E9=97=B4;=20?= =?utf8?q?=E4=BF=AE=E6=AD=A38/17=E8=AF=AF=E8=AE=B0=E5=88=B0=E5=89=8D?= =?utf8?q?=E4=B8=80=E5=A4=A9=E7=9A=84=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit - 新增 localTimestamp() 生成 Asia/Shanghai(UTC+8) 时间戳, 替代 toISOString()(UTC) 避免凌晨学习被记到前一天 - StudyMode/TestMode 完成学习时写入中国本地时间 - 修正 study_logs 中 id 395-398 因 UTC completed_at 被错误记到 2026-08-17 的问题 (实际为北京时间 8/18 早上 7点多, created_at UTC 23:39-23:55), 改为 2026-08-18 - 其他 100+ 历史误记记录按用户要求保持不变 --- backend/db/bcd.db | Bin 9461760 -> 9461760 bytes frontend/src/api.js | 10 ++++++++++ frontend/src/components/StudyMode.js | 4 ++-- frontend/src/components/TestMode.js | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/backend/db/bcd.db b/backend/db/bcd.db index ca3a2be4b0d82bf8187196f8c8869c7ee3ebc78f..da4027c10f43ed2e3d215d7291dfb237cddc3485 100644 GIT binary patch delta 1605 zcmZwD&2tlV9LMqge#vHA`haMg7)#p}MX^}j=O!CbYZad~P3~xnF*b z&Y{#Tl2^8mh~(P%+R=fnxCh&CFYZGpx)4VK z_ali1(2ecrK`;7{LO*ukK|F+=co>gh7aqj`cH=P&;&D6y4LS^%uwcW%5YouNMHWwD z51zu)cm~6G79)5L&torMz>9bZFXI)wir26Ym4CM%uj36Iz?(RTx9~Q`@D2{)T^z;{ zyoaM0$NM;j50FD1$MGR1Z~`as5l-P_e1cCgiPM~_XdAxel9A%lLrGs z_a~xqQVs;)>i)@DnUlRu8&7MrOURl0^o%1`+oA~}r)SRO&O2hIBib$GRQ~LV{5jng z#n7u^2|x5|nqs+jiY=BlPqD>fD5qOu;m&KKa;jm9LTGFnV!m+Q@XObY;-Ni}AH=n? zI$7)yJLILvZ;_wm?{Y#ak&BU}+$a8)H{|!?mULvF_*q>0W;k_tHm-=Se(6-I7@FY> zYUx2eEmjkXNbq^1Rp|RlQW4$#(!NtLG$~dmR&`>8CVq8dRwrg?vK&`tE8Fe5?T%Qg zZnx{VJ7TeJhh96$5es!YHn%&XP@Nc!+Z{1qsb07`Rm4j9>eOoP&&Y1&W}xmbjdZGi zsXwa6)q&VQv9;KA%vSzYZr&J9jVs&I-q!Xe(bd7kTiy0-0VTGneP-CaxD!Igz#{BCK@HSMg|R+qMd(??3l z&^0y=w$iSa&U&qNQ8T!8;I0Ro8P{~u-WJlE{p@N#r*>l}r5mn2M8%BPLUgmA9sJ*~ zF-=9T(Oh>8tJbeEt-F?y_0+nwS?$*lwJg`nWW88j)Y#~k;5Rp>b=Nk^sj<>+^*@xt B24esK delta 692 zcmWmA%Tp2o0Dy6Kmt3+Wvxg~QtCgmy0ll;YBg&q-SZQdc=A)2aM46S3P(;iYqiLMI zaPGj##=~Cq|gB_JP4Fy%G#u=Q&In;||(!7x!=<5AYC=(1A|u zzw@IDkMRUg(TyHFLjb+#!*lfG1qLvPA-u#eMlgyoyuxb)F^&-4;4R+aJw70ekBHzC zK4Su3Fp01DhVPidG-fc1ABbWO^N3*qaV#Q%CH%y)kyn4&bEbk_F{8^4ZA=e4v|&B# z&_;ESQw!=DrxyC_RsJP1F3rfel6`JzRZJUlIB6Fw(w4L)ZcDRLxwtN-*S$(eNTef4 atQhIY@8FJ9Wma_ApYqYNvY`Z0Ie8B>IZRRj diff --git a/frontend/src/api.js b/frontend/src/api.js index 6e735fd..2046886 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -12,6 +12,16 @@ export const http = axios.create({ timeout: 8000, }); +// 生成中国时间(Asia/Shanghai, UTC+8)时间戳 "YYYY-MM-DD HH:MM:SS"。 +// 注意: 必须用固定 +8h 偏移,不能用 toISOString()(那是 UTC),否则 +// 凌晨学习会被记到前一天(时区 bug)。与服务器 Asia/Shanghai 保持一致。 +export function localTimestamp() { + const sh = new Date(Date.now() + 8 * 3600 * 1000); + const p = (n) => String(n).padStart(2, '0'); + return `${sh.getUTCFullYear()}-${p(sh.getUTCMonth() + 1)}-${p(sh.getUTCDate())} ` + + `${p(sh.getUTCHours())}:${p(sh.getUTCMinutes())}:${p(sh.getUTCSeconds())}`; +} + // 统一 API 出口:所有组件应通过 api.xxx 调用,不再各自硬编码 API_BASE 或裸用 axios export const api = { getHome: () => http.get('/api/home'), diff --git a/frontend/src/components/StudyMode.js b/frontend/src/components/StudyMode.js index c03b78b..b4a501f 100644 --- a/frontend/src/components/StudyMode.js +++ b/frontend/src/components/StudyMode.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { api } from '../api'; +import { api, localTimestamp } from '../api'; import { useOnlineStatus } from '../hooks/useOnlineStatus'; import { getWordsByGroup, putProgress, getLearnableGroups } from '../db'; import { triggerAutoSync } from '../sync'; @@ -101,7 +101,7 @@ function StudyMode() { } else { if (submittedRef.current) return; // 防重入(按住Enter等场景) submittedRef.current = true; - const now = new Date().toISOString().slice(0, 19).replace('T', ' '); + const now = localTimestamp(); // 本地优先: 先写本地 dirty (必定保存, 不丢记录) await putProgress(groupId, 'study', now, true); // 在线则后台自动同步 (成功清 dirty, 失败保留) diff --git a/frontend/src/components/TestMode.js b/frontend/src/components/TestMode.js index 8f93e94..1ebd1fb 100644 --- a/frontend/src/components/TestMode.js +++ b/frontend/src/components/TestMode.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; -import { api } from '../api'; +import { api, localTimestamp } from '../api'; import { useOnlineStatus } from '../hooks/useOnlineStatus'; import { getWordsByGroup, putProgress, getLearnableGroups } from '../db'; import { triggerAutoSync } from '../sync'; @@ -143,7 +143,7 @@ function TestMode() { const finishTest = useCallback(async () => { if (submittedRef.current) return; // 防重入(按住Enter等场景) submittedRef.current = true; - const now = new Date().toISOString().slice(0, 19).replace('T', ' '); + const now = localTimestamp(); // 本地优先: 先写本地 dirty (必定保存, 不丢记录) await putProgress(groupId, activityType, now, true); // 在线则后台自动同步 (成功清 dirty, 失败保留) -- 2.43.0