feat(trading): 实现10%交易手续费进入积分股池
- 在成交时从卖方收益中扣除10%手续费 - 手续费流入积分股池(greenPoints/200万账户) - 添加详细分类账记录,包含买卖双方账户和来源标注 - Trade表新增fee字段记录每笔交易的手续费 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
192e2551bf
commit
c05bcc9a76
|
|
@ -0,0 +1,10 @@
|
|||
-- ============================================================================
|
||||
-- trading-service 添加交易手续费字段
|
||||
-- 10%交易手续费进入积分股池(greenPoints)
|
||||
-- ============================================================================
|
||||
|
||||
-- 添加手续费字段到 trades 表
|
||||
ALTER TABLE "trades" ADD COLUMN IF NOT EXISTS "fee" DECIMAL(30, 8) NOT NULL DEFAULT 0;
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON COLUMN "trades"."fee" IS '交易手续费(10%进入积分股池)';
|
||||
|
|
@ -200,7 +200,8 @@ model Trade {
|
|||
quantity Decimal @db.Decimal(30, 8) // 实际成交量
|
||||
burnQuantity Decimal @default(0) @map("burn_quantity") @db.Decimal(30, 8) // 卖出销毁量
|
||||
effectiveQty Decimal @default(0) @map("effective_qty") @db.Decimal(30, 8) // 有效量(quantity + burnQuantity)
|
||||
amount Decimal @db.Decimal(30, 8) // effectiveQty * price(卖出交易额)
|
||||
amount Decimal @db.Decimal(30, 8) // 卖方实际收到金额(扣除手续费后)
|
||||
fee Decimal @default(0) @db.Decimal(30, 8) // 交易手续费(10%进入积分股池)
|
||||
// 交易来源标识
|
||||
buyerSource String @default("USER") @map("buyer_source") // USER, MARKET_MAKER, DEX_BOT, SYSTEM
|
||||
sellerSource String @default("USER") @map("seller_source") // USER, MARKET_MAKER, DEX_BOT, SYSTEM
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
|||
import { OrderRepository } from '../../infrastructure/persistence/repositories/order.repository';
|
||||
import { TradingAccountRepository } from '../../infrastructure/persistence/repositories/trading-account.repository';
|
||||
import { CirculationPoolRepository } from '../../infrastructure/persistence/repositories/circulation-pool.repository';
|
||||
import { SharePoolRepository } from '../../infrastructure/persistence/repositories/share-pool.repository';
|
||||
import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository';
|
||||
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
|
||||
import { RedisService } from '../../infrastructure/redis/redis.service';
|
||||
|
|
@ -19,6 +20,9 @@ import {
|
|||
TradeExecutedPayload,
|
||||
} from '../../domain/events/trading.events';
|
||||
|
||||
// 交易手续费率:10%
|
||||
const TRADE_FEE_RATE = 0.1;
|
||||
|
||||
@Injectable()
|
||||
export class OrderService {
|
||||
private readonly logger = new Logger(OrderService.name);
|
||||
|
|
@ -28,6 +32,7 @@ export class OrderService {
|
|||
private readonly orderRepository: OrderRepository,
|
||||
private readonly accountRepository: TradingAccountRepository,
|
||||
private readonly circulationPoolRepository: CirculationPoolRepository,
|
||||
private readonly sharePoolRepository: SharePoolRepository,
|
||||
private readonly outboxRepository: OutboxRepository,
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly redis: RedisService,
|
||||
|
|
@ -184,10 +189,14 @@ export class OrderService {
|
|||
// 计算交易额
|
||||
// 买方支付:原始数量 × 价格(买方冻结的是这个金额)
|
||||
const buyerPayAmount = new Money(tradeQuantity.value.times(match.trade.price.value));
|
||||
// 卖方收到:有效数量 × 价格(含销毁倍数的收益)
|
||||
const sellerReceiveAmount = new Money(effectiveQuantity.value.times(match.trade.price.value));
|
||||
// 卖方总收益(含销毁倍数):有效数量 × 价格
|
||||
const sellerGrossAmount = new Money(effectiveQuantity.value.times(match.trade.price.value));
|
||||
// 交易手续费(10%):从卖方收益中扣除
|
||||
const tradeFee = new Money(sellerGrossAmount.value.times(TRADE_FEE_RATE));
|
||||
// 卖方实际收到:总收益 - 手续费
|
||||
const sellerReceiveAmount = new Money(sellerGrossAmount.value.minus(tradeFee.value));
|
||||
|
||||
// 保存成交记录(包含销毁信息和来源标识)
|
||||
// 保存成交记录(包含销毁信息、手续费和来源标识)
|
||||
await this.prisma.trade.create({
|
||||
data: {
|
||||
tradeNo: match.trade.tradeNo,
|
||||
|
|
@ -200,6 +209,7 @@ export class OrderService {
|
|||
burnQuantity: burnQuantity.value,
|
||||
effectiveQty: effectiveQuantity.value,
|
||||
amount: sellerReceiveAmount.value,
|
||||
fee: tradeFee.value,
|
||||
buyerSource: match.buyOrder.source,
|
||||
sellerSource: match.sellOrder.source,
|
||||
},
|
||||
|
|
@ -217,6 +227,23 @@ export class OrderService {
|
|||
this.logger.warn(`Failed to add shares to circulation pool: ${error}`);
|
||||
}
|
||||
|
||||
// 10%手续费进入积分股池(greenPoints)
|
||||
try {
|
||||
await this.sharePoolRepository.feeIn(
|
||||
tradeFee,
|
||||
match.trade.tradeNo,
|
||||
match.buyOrder.accountSequence,
|
||||
match.sellOrder.accountSequence,
|
||||
match.buyOrder.source,
|
||||
match.sellOrder.source,
|
||||
);
|
||||
this.logger.debug(
|
||||
`Trade fee added to share pool: ${tradeFee.toFixed(8)}, tradeNo=${match.trade.tradeNo}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to add trade fee to share pool: ${error}`);
|
||||
}
|
||||
|
||||
// 更新订单(包含销毁信息)
|
||||
await this.orderRepository.save(match.buyOrder);
|
||||
await this.orderRepository.saveWithBurnInfo(match.sellOrder, burnQuantity, effectiveQuantity);
|
||||
|
|
@ -228,7 +255,7 @@ export class OrderService {
|
|||
await this.accountRepository.save(buyerAccount);
|
||||
}
|
||||
|
||||
// 更新卖方账户(收到含销毁倍数的有效交易额)
|
||||
// 更新卖方账户(收到扣除手续费后的金额)
|
||||
const sellerAccount = await this.accountRepository.findByAccountSequence(match.sellOrder.accountSequence);
|
||||
if (sellerAccount) {
|
||||
sellerAccount.executeSell(tradeQuantity, sellerReceiveAmount, match.trade.tradeNo);
|
||||
|
|
@ -238,7 +265,7 @@ export class OrderService {
|
|||
this.logger.log(
|
||||
`Trade executed: ${match.trade.tradeNo}, price=${match.trade.price.toFixed(8)}, ` +
|
||||
`qty=${tradeQuantity.toFixed(8)}, burn=${burnQuantity.toFixed(8)}, ` +
|
||||
`buyerPay=${buyerPayAmount.toFixed(8)}, sellerReceive=${sellerReceiveAmount.toFixed(8)}`,
|
||||
`buyerPay=${buyerPayAmount.toFixed(8)}, fee=${tradeFee.toFixed(8)}, sellerReceive=${sellerReceiveAmount.toFixed(8)}`,
|
||||
);
|
||||
|
||||
// 发布成交事件
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export interface SharePoolEntity {
|
|||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export type SharePoolTransactionType = 'INJECT' | 'TRADE_IN' | 'TRADE_OUT';
|
||||
export type SharePoolTransactionType = 'INJECT' | 'TRADE_IN' | 'TRADE_OUT' | 'FEE_IN';
|
||||
|
||||
export interface SharePoolTransactionEntity {
|
||||
id: string;
|
||||
|
|
@ -70,6 +70,34 @@ export class SharePoolRepository {
|
|||
await this.updateBalance('TRADE_IN', amount, true, tradeId, `交易买入流入 ${amount.toFixed(8)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易手续费流入(交易成交时10%手续费进入积分股池)
|
||||
* @param amount 手续费金额
|
||||
* @param tradeNo 交易号
|
||||
* @param buyerSequence 买方账户序号
|
||||
* @param sellerSequence 卖方账户序号
|
||||
* @param buyerSource 买方来源(USER/MARKET_MAKER/DEX_BOT/SYSTEM)
|
||||
* @param sellerSource 卖方来源(USER/MARKET_MAKER/DEX_BOT/SYSTEM)
|
||||
*/
|
||||
async feeIn(
|
||||
amount: Money,
|
||||
tradeNo: string,
|
||||
buyerSequence: string,
|
||||
sellerSequence: string,
|
||||
buyerSource: string,
|
||||
sellerSource: string,
|
||||
): Promise<void> {
|
||||
const buyerLabel = buyerSource === 'MARKET_MAKER' ? '做市商' : buyerSource === 'USER' ? '用户' : buyerSource;
|
||||
const sellerLabel = sellerSource === 'MARKET_MAKER' ? '做市商' : sellerSource === 'USER' ? '用户' : sellerSource;
|
||||
await this.updateBalance(
|
||||
'FEE_IN',
|
||||
amount,
|
||||
true,
|
||||
tradeNo,
|
||||
`交易手续费(10%) ${amount.toFixed(8)}, 买方: ${buyerSequence}(${buyerLabel}), 卖方: ${sellerSequence}(${sellerLabel}), 交易号: ${tradeNo}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易流出(卖出时绿积分从股池流出)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue