优化
This commit is contained in:
@@ -6,6 +6,10 @@ const response = (data, http = 200, code = '200') => ({ http, body: { code, data
|
||||
|
||||
function fakeContext(overrides = {}) {
|
||||
const balances = new Map([['101', 100], ['102', 100], ['103', 100], ['201', 100]])
|
||||
const paidAmounts = new Map([['101', 0], ['102', 0], ['103', 0], ['201', 0]])
|
||||
const statuses = new Map([['101', 'ISSUED'], ['102', 'ISSUED'], ['103', 'ISSUED'], ['201', 'ISSUED']])
|
||||
const versions = new Map([['101', 1], ['102', 1], ['103', 1], ['201', 1]])
|
||||
const actionIdempotency = new Map()
|
||||
const settlementToBill = new Map([['101', '11'], ['102', '12'], ['103', '13']])
|
||||
const billToSettlement = new Map([...settlementToBill].map(([s, b]) => [b, s]))
|
||||
const fixture = { marker: 'T1', customerId: '9', voucherFileId: '77',
|
||||
@@ -33,11 +37,21 @@ function fakeContext(overrides = {}) {
|
||||
async request(method, path, body, token, headers) {
|
||||
calls.push({ method, path, body, headers })
|
||||
const match = path.match(/\/settlements\/(\d+)/)
|
||||
if (method === 'GET' && match) return response({ settlementId: match[1], outstandingAmount: balances.get(match[1]), status: balances.get(match[1]) === 0 ? 'PAID' : 'PARTIALLY_PAID', version: calls.length })
|
||||
if (path.endsWith('/receipts')) { for (const allocation of body.allocations) balances.set(billToSettlement.get(String(allocation.billId)), balances.get(billToSettlement.get(String(allocation.billId))) - allocation.amount); return response({ receiptId: calls.length, allocations: body.allocations }) }
|
||||
if (method === 'GET' && match) return response({ settlementId: match[1], outstandingAmount: balances.get(match[1]), paidAmount: paidAmounts.get(match[1]), status: statuses.get(match[1]), version: versions.get(match[1]) })
|
||||
if (match && path.endsWith('/actions')) {
|
||||
const targetId = match[1]; const idem = headers['Idempotency-Key']; const fingerprint = JSON.stringify(body)
|
||||
const prior = actionIdempotency.get(idem)
|
||||
if (prior) return prior.fingerprint === fingerprint ? prior.response : response(null, 409, 'FIN_IDEMPOTENCY_CONFLICT')
|
||||
if (body.action === 'VOID' && paidAmounts.get(targetId) > 0) return response(null, 409, 'FIN_SETTLEMENT_ALREADY_PAID')
|
||||
if (body.expectedVersion !== versions.get(targetId)) return response(null, 409, 'FIN_VERSION_CONFLICT')
|
||||
const nextStatus = body.action === 'VOID' ? 'VOIDED' : 'ISSUED'; statuses.set(targetId, nextStatus); versions.set(targetId, versions.get(targetId) + 1)
|
||||
const result = response({ settlementId: targetId, outstandingAmount: balances.get(targetId), paidAmount: paidAmounts.get(targetId), status: nextStatus, version: versions.get(targetId) })
|
||||
actionIdempotency.set(idem, { fingerprint, response: result }); return result
|
||||
}
|
||||
if (path.endsWith('/receipts')) { for (const allocation of body.allocations) { const targetId = billToSettlement.get(String(allocation.billId)); balances.set(targetId, balances.get(targetId) - allocation.amount); paidAmounts.set(targetId, paidAmounts.get(targetId) + allocation.amount); statuses.set(targetId, balances.get(targetId) === 0 ? 'PAID' : 'PARTIALLY_PAID'); versions.set(targetId, versions.get(targetId) + 1) } return response({ receiptId: calls.length, allocations: body.allocations }) }
|
||||
if (match && path.endsWith('/transactions')) {
|
||||
const current = balances.get(match[1]); if (body.amount > current || current === 0) return response(null, 409, 'FIN_409')
|
||||
balances.set(match[1], current - body.amount); return response({ settlementId: match[1], outstandingAmount: current - body.amount, status: current - body.amount === 0 ? 'PAID' : 'PARTIALLY_PAID', version: calls.length })
|
||||
balances.set(match[1], current - body.amount); paidAmounts.set(match[1], paidAmounts.get(match[1]) + body.amount); statuses.set(match[1], current - body.amount === 0 ? 'PAID' : 'PARTIALLY_PAID'); versions.set(match[1], versions.get(match[1]) + 1); return response({ settlementId: match[1], outstandingAmount: current - body.amount, paidAmount: paidAmounts.get(match[1]), status: statuses.get(match[1]), version: versions.get(match[1]) })
|
||||
}
|
||||
throw new Error(`unexpected ${method} ${path}`)
|
||||
} }
|
||||
@@ -64,6 +78,24 @@ test('run 使用 receipts 完成应收部分、全额及多单分配,使用 tr
|
||||
assert.ok(context.createdResources.financeFullFixtures.payments.final)
|
||||
assert.equal(context.createdResources.financeFullFixtures.settlementFinalStates.receivable.outstandingAmount, 0)
|
||||
assert.equal(context.createdResources.financeFullFixtures.settlementFinalStates.payable.outstandingAmount, 0)
|
||||
const actions = context.calls.filter((call) => call.path.endsWith('/actions'))
|
||||
assert.deepEqual(actions.slice(0, 4).map((call) => call.body.action), ['VOID', 'VOID', 'REOPEN', 'REOPEN'])
|
||||
assert.equal(actions[0].headers['Idempotency-Key'], actions[1].headers['Idempotency-Key'])
|
||||
assert.equal(actions[0].headers['Idempotency-Key'], actions[2].headers['Idempotency-Key'])
|
||||
assert.notEqual(actions[2].body.reason, actions[0].body.reason)
|
||||
assert.notEqual(actions[3].headers['Idempotency-Key'], actions[0].headers['Idempotency-Key'])
|
||||
assert.ok(context.assertions.find((item) => item.name === '已收付款结算单拒绝作废且状态金额不变')?.condition)
|
||||
})
|
||||
|
||||
test('正式结算动作仅使用本轮 fixture,覆盖 VOID/REOPEN、重放、异参冲突和已付款拒绝', async () => {
|
||||
const context = fakeContext()
|
||||
await run(context)
|
||||
const actions = context.calls.filter((call) => call.path.match(/\/settlements\/(101|102|103|201)\/actions$/))
|
||||
assert.equal(actions.length, 5)
|
||||
assert.ok(actions.every((call) => ['101', '102', '103', '201'].includes(call.path.match(/\/settlements\/(\d+)\/actions$/)[1])))
|
||||
for (const name of ['未收付款结算单作废', '结算单作废同键同参幂等重放', '结算单动作同键异参冲突', '已作废结算单重开', '已收付款结算单拒绝作废且状态金额不变']) {
|
||||
assert.ok(context.assertions.find((item) => item.name === name)?.condition, name)
|
||||
}
|
||||
})
|
||||
|
||||
test('冻结共享输入使用 4 条 statements、3 条 AR 和 payableSettlements 数组', async () => {
|
||||
|
||||
Reference in New Issue
Block a user