feat(M07-A): 建立双余额账本
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { WalletLedgerError, WalletLedgerService } from '../dist/wallets/wallet-ledger-service.js';
|
||||
|
||||
const state = {
|
||||
account: null,
|
||||
ledger: [],
|
||||
nextLedgerId: 100
|
||||
};
|
||||
const pool = {
|
||||
async getConnection() {
|
||||
return connection;
|
||||
}
|
||||
};
|
||||
const connection = {
|
||||
async beginTransaction() {},
|
||||
async commit() {},
|
||||
async rollback() {},
|
||||
release() {},
|
||||
async execute(sql, params) {
|
||||
if (sql.includes('INSERT IGNORE INTO qipai_wallet_accounts')) {
|
||||
if (!state.account) {
|
||||
state.account = {
|
||||
id: '51',
|
||||
tenantId: String(params[0]),
|
||||
userId: String(params[1]),
|
||||
scopeType: params[2],
|
||||
storeId: params[3] === null ? null : String(params[3]),
|
||||
cashBalanceCents: 0,
|
||||
giftBalanceCents: 0,
|
||||
status: 'ACTIVE'
|
||||
};
|
||||
}
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
if (sql.includes('FROM qipai_wallet_accounts')) {
|
||||
return [[state.account], []];
|
||||
}
|
||||
if (sql.includes('FROM qipai_wallet_ledger_entries')) {
|
||||
const duplicate = state.ledger.find((item) =>
|
||||
item.businessType === params[2] && item.businessId === params[3]
|
||||
);
|
||||
return [duplicate ? [duplicate] : [], []];
|
||||
}
|
||||
if (sql.includes('UPDATE qipai_wallet_accounts')) {
|
||||
state.account.cashBalanceCents = params[0];
|
||||
state.account.giftBalanceCents = params[1];
|
||||
return [{ affectedRows: 1 }, []];
|
||||
}
|
||||
if (sql.includes('INSERT INTO qipai_wallet_ledger_entries')) {
|
||||
const item = {
|
||||
id: String(state.nextLedgerId++),
|
||||
businessType: params[4],
|
||||
businessId: params[5],
|
||||
cashDeltaCents: params[7],
|
||||
giftDeltaCents: params[8],
|
||||
cashBalanceAfterCents: params[9],
|
||||
giftBalanceAfterCents: params[10]
|
||||
};
|
||||
state.ledger.push(item);
|
||||
return [{ insertId: item.id, affectedRows: 1 }, []];
|
||||
}
|
||||
throw new Error(`Unexpected SQL: ${sql}`);
|
||||
}
|
||||
};
|
||||
|
||||
const service = new WalletLedgerService(pool);
|
||||
const base = {
|
||||
tenantId: '7',
|
||||
userId: '21',
|
||||
scopeType: 'STORE',
|
||||
storeId: '11',
|
||||
operatorId: '22',
|
||||
traceId: 'wallet-test'
|
||||
};
|
||||
|
||||
const credited = await service.credit({
|
||||
...base,
|
||||
businessType: 'RECHARGE',
|
||||
businessId: 'pay-001',
|
||||
entryType: 'RECHARGE',
|
||||
cashDeltaCents: 1000,
|
||||
giftDeltaCents: 300
|
||||
});
|
||||
assert.equal(credited.cashBalanceCents, 1000);
|
||||
assert.equal(credited.giftBalanceCents, 300);
|
||||
|
||||
const duplicate = await service.credit({
|
||||
...base,
|
||||
businessType: 'RECHARGE',
|
||||
businessId: 'pay-001',
|
||||
entryType: 'RECHARGE',
|
||||
cashDeltaCents: 1000,
|
||||
giftDeltaCents: 300
|
||||
});
|
||||
assert.equal(duplicate.idempotent, true);
|
||||
assert.equal(state.ledger.length, 1);
|
||||
|
||||
const debited = await service.debit({
|
||||
...base,
|
||||
businessType: 'ORDER',
|
||||
businessId: 'order-001',
|
||||
entryType: 'CONSUME',
|
||||
amountCents: 500
|
||||
});
|
||||
assert.equal(debited.giftDeltaCents, -300);
|
||||
assert.equal(debited.cashDeltaCents, -200);
|
||||
assert.equal(debited.cashBalanceCents, 800);
|
||||
assert.equal(debited.giftBalanceCents, 0);
|
||||
|
||||
await assert.rejects(
|
||||
() => service.debit({
|
||||
...base,
|
||||
businessType: 'ORDER',
|
||||
businessId: 'order-002',
|
||||
entryType: 'CONSUME',
|
||||
amountCents: 9999
|
||||
}),
|
||||
(error) => error instanceof WalletLedgerError
|
||||
&& error.code === 'WALLET_BALANCE_INSUFFICIENT'
|
||||
);
|
||||
|
||||
const adjusted = await service.adjust({
|
||||
...base,
|
||||
businessType: 'ADMIN_ADJUST',
|
||||
businessId: 'adjust-001',
|
||||
entryType: 'ADJUST',
|
||||
cashDeltaCents: -100,
|
||||
giftDeltaCents: 50,
|
||||
note: 'sanitized adjustment'
|
||||
});
|
||||
assert.equal(adjusted.cashBalanceCents, 700);
|
||||
assert.equal(adjusted.giftBalanceCents, 50);
|
||||
|
||||
console.log('PASS: M07-A wallet ledger credits, gift-first debit, idempotency and adjustments work.');
|
||||
Reference in New Issue
Block a user