feat(M01-B): 严格转换旧库订单金额

This commit is contained in:
Codex
2026-06-18 09:40:40 +08:00
parent 2715405c7d
commit b8b384ed67
5 changed files with 273 additions and 5 deletions
+45
View File
@@ -0,0 +1,45 @@
import assert from 'node:assert/strict';
import {
legacyDecimalToCents,
MYSQL_UNSIGNED_INT_MAX
} from '../dist/db/legacy-money.js';
assert.equal(legacyDecimalToCents('0'), 0);
assert.equal(legacyDecimalToCents('0.01'), 1);
assert.equal(legacyDecimalToCents('12.3'), 1230);
assert.equal(legacyDecimalToCents('12.34'), 1234);
assert.equal(legacyDecimalToCents(12.34), 1234);
assert.equal(legacyDecimalToCents('42949672.95'), MYSQL_UNSIGNED_INT_MAX);
assert.equal(legacyDecimalToCents(null, { nullable: true }), null);
for (const invalidValue of [
'',
' 1.00',
'1.00 ',
'-0.01',
'+1.00',
'1.001',
'1e2',
'NaN',
Number.NaN,
Number.POSITIVE_INFINITY,
{},
null
]) {
assert.throws(
() => legacyDecimalToCents(invalidValue),
/Legacy money/
);
}
assert.throws(
() => legacyDecimalToCents('42949672.96', { field: 'member_order_info.price' }),
/member_order_info\.price.*exceeds/
);
assert.throws(
() => legacyDecimalToCents('1.00', { maxCents: Number.MAX_SAFE_INTEGER + 1 }),
/maxCents/
);
console.log('PASS: legacy DECIMAL money conversion is exact and bounded.');
+72 -1
View File
@@ -5,6 +5,7 @@ const calls = [];
const fakePool = {
async query(sql, parameters) {
calls.push({ sql, parameters });
const isOrderQuery = /FROM `member_order_info`/.test(sql);
return [[{
legacyId: '7',
tenantId: '2',
@@ -13,7 +14,12 @@ const fakePool = {
code: 'A01',
status: 'OPEN',
startAt: null,
endAt: null
endAt: null,
totalAmountDecimal: isOrderQuery ? '25.80' : null,
paidAmountDecimal: isOrderQuery ? '20.00' : null,
renewalAmountDecimal: isOrderQuery ? '5.8' : null,
groupAmountDecimal: isOrderQuery ? '0.00' : null,
refundAmountDecimal: null
}], []];
}
};
@@ -22,6 +28,11 @@ const repository = new LegacyReadRepository(fakePool);
const rooms = await repository.listRooms({ tenantId: 2, parentId: 3, limit: 25 });
assert.equal(rooms.length, 1);
assert.equal(rooms[0].totalAmountCents, null);
assert.equal(rooms[0].paidAmountCents, null);
assert.equal(rooms[0].renewalAmountCents, null);
assert.equal(rooms[0].groupAmountCents, null);
assert.equal(rooms[0].refundAmountCents, null);
assert.equal(calls.length, 1);
assert.match(calls[0].sql, /FROM `member_room_info`/);
assert.match(calls[0].sql, /`tenant_id` = \?/);
@@ -30,6 +41,16 @@ assert.match(calls[0].sql, /ORDER BY `id` ASC LIMIT \?/);
assert.deepEqual(calls[0].parameters, [2, 3, 25]);
assert.doesNotMatch(calls[0].sql, /\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i);
const orders = await repository.listOrders({ tenantId: 2, parentId: 3, limit: 10 });
assert.equal(orders[0].totalAmountCents, 2580);
assert.equal(orders[0].paidAmountCents, 2000);
assert.equal(orders[0].renewalAmountCents, 580);
assert.equal(orders[0].groupAmountCents, 0);
assert.equal(orders[0].refundAmountCents, null);
assert.match(calls[1].sql, /`price` AS `totalAmountDecimal`/);
assert.match(calls[1].sql, /`pay_price` AS `paidAmountDecimal`/);
assert.match(calls[1].sql, /`refund_price` AS `refundAmountDecimal`/);
await assert.rejects(
() => repository.listStores({ tenantId: 2, limit: 501 }),
/between 1 and 500/
@@ -63,4 +84,54 @@ await assert.rejects(
/Unsafe legacy SQL identifier/
);
const invalidMoneyRepository = new LegacyReadRepository({
async query() {
return [[{
legacyId: '8',
tenantId: '2',
parentId: '3',
name: null,
code: 'ORDER-8',
status: 'PAID',
startAt: null,
endAt: null,
totalAmountDecimal: '1.001',
paidAmountDecimal: '1.00',
renewalAmountDecimal: '0.00',
groupAmountDecimal: '0.00',
refundAmountDecimal: '0.00'
}], []];
}
});
await assert.rejects(
() => invalidMoneyRepository.listOrders({ tenantId: 2 }),
/member_order_info\.price/
);
const missingRequiredMoneyRepository = new LegacyReadRepository({
async query() {
return [[{
legacyId: '9',
tenantId: '2',
parentId: '3',
name: null,
code: 'ORDER-9',
status: 'PAID',
startAt: null,
endAt: null,
totalAmountDecimal: null,
paidAmountDecimal: null,
renewalAmountDecimal: null,
groupAmountDecimal: null,
refundAmountDecimal: null
}], []];
}
});
await assert.rejects(
() => missingRequiredMoneyRepository.listOrders({ tenantId: 2 }),
/member_order_info\.price.*cannot be null/
);
console.log('PASS: legacy read-only repository contracts are present.');