From 12b146c2c9b4e420e04220a7e4acb54730709005 Mon Sep 17 00:00:00 2001 From: yuming Date: Sun, 16 Aug 2026 10:19:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=A4=E5=A4=84=E6=8F=90=E9=86=92=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=A1=A5=20ORDER=20BY=20id=EF=BC=8C=E4=BF=9D=E8=AF=81?= =?UTF-8?q?=E5=B9=B3=E5=B1=80=E5=8F=96=E8=88=8D=E5=8F=AF=E5=A4=8D=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reminder.js 扫全表和 index.js 的 getQuotaStatus 都没有 ORDER BY, 两边的 sort 又都是稳定排序,同一天、同 kind、同 importance 的条目 先后完全由 SQLite 返回顺序决定。一个走全表扫、一个可能走索引, 顺序一致纯属巧合——一旦不一致,首页预告的人名就和实际被跳过的人对不上。 Co-Authored-By: Claude Opus 5 (1M context) --- server/src/index.js | 4 +++- server/src/reminder.js | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/index.js b/server/src/index.js index b802fbf..ea81541 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -234,7 +234,9 @@ function grantQuota(openid, data) { // 首页查询:返回余额和「哪几个人的提醒有风险」 function getQuotaStatus(openid) { const balance = quota.getBalance(openid) - const rows = db.prepare('SELECT * FROM anniversaries WHERE openid = ? AND remindEnabled = 1').all(openid) + // ORDER BY id 必须与 reminder.js 的取数保持一致:平局(同日、同 kind、同 importance)时 + // 先后顺序完全由这里决定,两边不一致就会出现「预告的人名不是真正被跳过的人」 + const rows = db.prepare('SELECT * FROM anniversaries WHERE openid = ? AND remindEnabled = 1 ORDER BY id').all(openid) const { atRiskCount, atRiskNames } = atRisk.computeAtRisk(rows.map(normalize), balance) return { success: true, balance, atRiskCount, atRiskNames } } diff --git a/server/src/reminder.js b/server/src/reminder.js index b72d219..57e2570 100644 --- a/server/src/reminder.js +++ b/server/src/reminder.js @@ -144,7 +144,11 @@ async function runForUser(openid, items, today = new Date()) { async function runOnce() { console.log('[reminder] 开始扫描纪念日...') - const list = db.prepare('SELECT * FROM anniversaries WHERE remindEnabled = 1').all() + // ORDER BY id 不是为了排序好看,而是为了「平局可复现」: + // 同一天、同 kind、同 importance 的条目在 sort 里比不出先后(JS sort 是稳定的), + // 最终取舍就取决于 SQLite 的返回顺序。这里和 index.js 的 getQuotaStatus 必须用同一个 + // 兜底顺序,否则首页预告「谁有风险」的人名会和实际被跳过的人对不上。 + const list = db.prepare('SELECT * FROM anniversaries WHERE remindEnabled = 1 ORDER BY id').all() console.log(`[reminder] 启用提醒的纪念日 ${list.length} 条`) // 额度是按用户算的,所以必须分组处理