Files
lotteryLaravel/e2e/tests/api/helpers/draw-settlement.ts
kang 3f29229499
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
fix: 部分收付累计释额并扩展结算 E2E 与 CI
账期收付改为按累计已付同步 credit_ledger,修复多笔 partial_paid 与坏账核销重复释额;新增 API E2E(占成、权限、部分收付/坏账)与 GitHub Actions e2e-api job。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 09:48:01 +08:00

133 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 开奖 → 结算 → 派彩 确定性流水线poll + tick避免 test.skip
*/
import { expect, type APIRequestContext } from '@playwright/test';
import {
buildAllResultItems,
finishDrawCooldown,
forceCloseDraw,
resolveDrawId,
tickDraws,
waitForDrawStatus,
} from '../_helper';
const CURRENCY = (process.env.LOTTERY_DEFAULT_CURRENCY ?? 'NPR').toUpperCase();
export interface DrawSettlementResult {
drawNo: string;
drawId: number;
batchId: number;
balanceBefore?: number;
balanceAfter?: number;
}
export async function runWalletDrawSettlement(params: {
adminToken: string;
playerToken: string;
drawNo: string;
betNumber?: string;
/** 公布头奖号码;默认与 betNumber 相同(必中)。传不同值可测玩家输单。 */
publishWinNumber?: string;
betAmount?: number;
balanceBefore?: number;
expectWalletIncrease?: boolean;
}): Promise<DrawSettlementResult> {
const drawNo = params.drawNo;
const betNumber = params.betNumber ?? '1234';
const publishWinNumber = params.publishWinNumber ?? betNumber;
const betAmount = params.betAmount ?? 10_000;
const player = await pwPlayerCtx(params.playerToken);
let balanceBefore: number | undefined;
if (params.expectWalletIncrease !== false) {
balanceBefore =
params.balanceBefore ??
Number((await (await player.get('/api/v1/wallet/balance')).json()).data.balance);
}
const place = await player.post('/api/v1/ticket/place', {
data: {
draw_id: drawNo,
currency_code: CURRENCY,
client_trace_id: `e2e-settle-${Date.now()}`,
lines: [{ number: betNumber, play_code: 'straight', amount: betAmount }],
},
});
expect(place.ok(), `place status=${place.status()}`).toBeTruthy();
const placeBody = (await place.json()) as any;
expect(placeBody.code).toBe(0);
await player.dispose();
await forceCloseDraw(drawNo);
await waitForDrawStatus(drawNo, ['closed', 'review', 'cooldown', 'settling', 'settled'], 15);
const admin = await pwAdminCtx(params.adminToken);
const drawId = await resolveDrawId(admin, drawNo);
const store = await admin.post(`/api/v1/admin/draws/${drawId}/result-batches`, {
data: { items: buildAllResultItems(publishWinNumber) },
});
expect(store.ok(), `store batch status=${store.status()}`).toBeTruthy();
const storeBody = (await store.json()) as any;
expect(storeBody.code).toBe(0);
const batchId: number = storeBody.data.batch.id;
const pub = await admin.post(`/api/v1/admin/draws/${drawId}/result-batches/${batchId}/publish`);
expect(pub.ok(), `publish status=${pub.status()}`).toBeTruthy();
const pubBody = (await pub.json()) as any;
expect(pubBody.code).toBe(0);
await finishDrawCooldown(drawNo);
await waitForDrawStatus(drawNo, ['settling', 'settled'], 25);
const insp = await waitForDrawStatus(drawNo, ['settled'], 25);
if (String(insp.data?.status ?? insp.status) !== 'settled') {
const settle = await admin.post(`/api/v1/admin/draws/${drawId}/settlement/run`);
const settleBody = (await settle.json()) as any;
if (settleBody.code !== 0) {
await tickDraws();
}
await waitForDrawStatus(drawNo, ['settled'], 25);
}
const list = await admin.get(`/api/v1/admin/settlement-batches?size=20`);
const lb = (await list.json()) as any;
const ourBatch = lb.data.items.find((b: any) => b.draw_id === drawId);
if (ourBatch && ourBatch.status === 'pending_review') {
await admin.post(`/api/v1/admin/settlement-batches/${ourBatch.id}/approve`, {
data: { remark: 'e2e approve' },
});
await admin.post(`/api/v1/admin/settlement-batches/${ourBatch.id}/payout`);
} else {
await tickDraws();
await waitForDrawStatus(drawNo, ['settled'], 15);
}
await admin.dispose();
let balanceAfter: number | undefined;
if (params.expectWalletIncrease !== false) {
const player2 = await pwPlayerCtx(params.playerToken);
const bal1 = (await (await player2.get('/api/v1/wallet/balance')).json()) as any;
balanceAfter = Number(bal1.data.balance);
await player2.dispose();
}
return { drawNo, drawId, batchId, balanceBefore, balanceAfter };
}
async function pwAdminCtx(token: string): Promise<APIRequestContext> {
const { request: pwRequest } = await import('@playwright/test');
return pwRequest.newContext({
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
});
}
async function pwPlayerCtx(token: string): Promise<APIRequestContext> {
const { request: pwRequest } = await import('@playwright/test');
return pwRequest.newContext({
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
});
}