feat(admin,api,player): 结算预览分页、统计图表与返水限额
完善结算计算与预览 API(含后端分页),加强管理端结算/返水/权限,并优化玩家端投注单与队徽展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../shared/prisma/prisma.service';
|
||||
import { Decimal } from '@prisma/client/runtime/library';
|
||||
import { generateTransactionId } from '../../shared/common/decorators';
|
||||
|
||||
type TxClient = Prisma.TransactionClient;
|
||||
|
||||
@Injectable()
|
||||
export class WalletService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
@@ -19,7 +22,7 @@ export class WalletService {
|
||||
});
|
||||
}
|
||||
|
||||
private async lockWallet(tx: Parameters<Parameters<PrismaService['$transaction']>[0]>[0], userId: bigint) {
|
||||
private async lockWallet(tx: TxClient, userId: bigint) {
|
||||
const wallets = await tx.$queryRaw<Array<{ id: bigint; available_balance: Decimal; frozen_balance: Decimal; version: number }>>`
|
||||
SELECT id, available_balance, frozen_balance, version FROM wallets WHERE user_id = ${userId} FOR UPDATE
|
||||
`;
|
||||
@@ -163,24 +166,25 @@ export class WalletService {
|
||||
payout: Decimal,
|
||||
betId: string,
|
||||
result: 'WIN' | 'LOSE' | 'PUSH' | 'VOID' | 'HALF_WIN' | 'HALF_LOSE',
|
||||
tx?: TxClient,
|
||||
) {
|
||||
const txTypeMap: Record<string, string> = {
|
||||
WIN: 'BET_SETTLE_WIN',
|
||||
LOSE: 'BET_SETTLE_LOSE',
|
||||
PUSH: 'BET_SETTLE_PUSH',
|
||||
VOID: 'BET_VOID_REFUND',
|
||||
HALF_WIN: 'BET_SETTLE_WIN',
|
||||
HALF_LOSE: 'BET_SETTLE_LOSE',
|
||||
};
|
||||
const run = async (client: TxClient) => {
|
||||
const txTypeMap: Record<string, string> = {
|
||||
WIN: 'BET_SETTLE_WIN',
|
||||
LOSE: 'BET_SETTLE_LOSE',
|
||||
PUSH: 'BET_SETTLE_PUSH',
|
||||
VOID: 'BET_VOID_REFUND',
|
||||
HALF_WIN: 'BET_SETTLE_WIN',
|
||||
HALF_LOSE: 'BET_SETTLE_LOSE',
|
||||
};
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const w = await this.lockWallet(tx, userId);
|
||||
const w = await this.lockWallet(client, userId);
|
||||
const avail = new Decimal(w.available_balance);
|
||||
const frozen = new Decimal(w.frozen_balance);
|
||||
const frozenAfter = frozen.sub(stake);
|
||||
const balanceAfter = avail.add(payout);
|
||||
|
||||
await tx.wallet.update({
|
||||
await client.wallet.update({
|
||||
where: { id: w.id },
|
||||
data: {
|
||||
availableBalance: balanceAfter,
|
||||
@@ -189,7 +193,7 @@ export class WalletService {
|
||||
},
|
||||
});
|
||||
|
||||
await tx.walletTransaction.create({
|
||||
await client.walletTransaction.create({
|
||||
data: {
|
||||
transactionId: generateTransactionId(),
|
||||
userId,
|
||||
@@ -204,7 +208,55 @@ export class WalletService {
|
||||
referenceId: betId,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (tx) return run(tx);
|
||||
return this.prisma.$transaction(run);
|
||||
}
|
||||
|
||||
/** 重结算差额调整:delta = 新派彩 - 原派彩,允许扣回导致负余额 */
|
||||
async applyResettleDelta(
|
||||
userId: bigint,
|
||||
delta: Decimal,
|
||||
betNo: string,
|
||||
tx?: TxClient,
|
||||
) {
|
||||
if (delta.eq(0)) return;
|
||||
|
||||
const run = async (client: TxClient) => {
|
||||
const w = await this.lockWallet(client, userId);
|
||||
const avail = new Decimal(w.available_balance);
|
||||
const balanceAfter = avail.add(delta);
|
||||
const txType = delta.gt(0) ? 'BET_SETTLE_WIN' : 'RESETTLE_REVERSE';
|
||||
|
||||
await client.wallet.update({
|
||||
where: { id: w.id },
|
||||
data: {
|
||||
availableBalance: balanceAfter,
|
||||
version: { increment: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
await client.walletTransaction.create({
|
||||
data: {
|
||||
transactionId: generateTransactionId(),
|
||||
userId,
|
||||
walletId: w.id,
|
||||
transactionType: txType,
|
||||
amount: delta,
|
||||
balanceBefore: avail,
|
||||
balanceAfter,
|
||||
frozenBefore: w.frozen_balance,
|
||||
frozenAfter: w.frozen_balance,
|
||||
referenceType: 'BET',
|
||||
referenceId: betNo,
|
||||
remark: 'Resettlement adjustment',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (tx) return run(tx);
|
||||
return this.prisma.$transaction(run);
|
||||
}
|
||||
|
||||
async getTransactions(userId: bigint, page = 1, pageSize = 20) {
|
||||
|
||||
Reference in New Issue
Block a user