diff --git a/server/src/quota.js b/server/src/quota.js index cf5ca42..5e30778 100644 --- a/server/src/quota.js +++ b/server/src/quota.js @@ -38,12 +38,17 @@ function grant(openid, count = 1) { return getBalance(openid) } +// 消费:即便该 openid 从未 grant 过也要建行,否则 sentTotal 会静默丢失 +// (新建行 balance 直接落 0,等价于「从 0 扣减再取 MAX(0, ...)」) function consume(openid, count = 1) { const n = _normalize(count) db.prepare(` - UPDATE subscribe_quota - SET balance = MAX(0, balance - @n), sentTotal = sentTotal + @n, updateTime = @now - WHERE openid = @openid + INSERT INTO subscribe_quota (openid, balance, grantedTotal, sentTotal, updateTime) + VALUES (@openid, 0, 0, @n, @now) + ON CONFLICT(openid) DO UPDATE SET + balance = MAX(0, balance - @n), + sentTotal = sentTotal + @n, + updateTime = @now `).run({ openid, n, now: Date.now() }) return getBalance(openid) } diff --git a/server/test/quota.test.js b/server/test/quota.test.js index 8ab39a1..9a78d55 100644 --- a/server/test/quota.test.js +++ b/server/test/quota.test.js @@ -4,6 +4,7 @@ const { useTempDb } = require('./helper') useTempDb() // 必须在 require quota/db 之前 const quota = require('../src/quota') +const db = require('../src/db') test('未记录过的用户余额为 0', () => { assert.strictEqual(quota.getBalance('u_new'), 0) @@ -49,3 +50,14 @@ test('各用户额度互不影响', () => { assert.strictEqual(quota.getBalance('a'), 3) assert.strictEqual(quota.getBalance('b'), 1) }) + +test('consume 对从未 grant 过的 openid 也要建行,不丢失 sentTotal', () => { + quota.consume('u6', 3) + // 仅断言 getBalance 为 0 不足以证明建行了(缺行时本来就返回 0), + // 必须直接查表,确认行确实被创建且 sentTotal 记录了这次消费 + assert.strictEqual(quota.getBalance('u6'), 0) + const row = db.prepare('SELECT * FROM subscribe_quota WHERE openid = ?').get('u6') + assert.ok(row, 'u6 对应的记录行应当被创建') + assert.strictEqual(row.balance, 0) + assert.strictEqual(row.sentTotal, 3) +})