feat: 迭代
This commit is contained in:
@@ -8,21 +8,18 @@ export class PlatformAccount {
|
||||
@Column({ type: 'varchar', length: 50, comment: '账户名称(如:主账户、备用账户)' })
|
||||
account_name: string;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '可用余额' })
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '可用余额(平台净收益 = total_income - total_expense)' })
|
||||
balance: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '冻结余额(提现中)' })
|
||||
frozen_balance: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计收入(订单收入)' })
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计收入(服务费收入)' })
|
||||
total_income: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计支出(商家结算+退款)' })
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计支出(邀请返现支出)' })
|
||||
total_expense: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计服务费收入' })
|
||||
total_service_fee: number;
|
||||
|
||||
@VersionColumn({ comment: '乐观锁版本号' })
|
||||
version: number;
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, Index, VersionColumn } from 'typeorm';
|
||||
|
||||
@Entity('system_accounts')
|
||||
export class SystemAccount {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint', unsigned: true })
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, comment: '账户名称(如:主账户)' })
|
||||
account_name: string;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '可用余额(total_income - total_refund - total_withdrawn)' })
|
||||
balance: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计收入(用户实付总额)' })
|
||||
total_income: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计退款' })
|
||||
total_refund: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0, comment: '累计提现(所有提现)' })
|
||||
total_withdrawn: number;
|
||||
|
||||
@VersionColumn({ comment: '乐观锁版本号' })
|
||||
version: number;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: ['active', 'frozen', 'closed'],
|
||||
default: 'active',
|
||||
comment: '状态'
|
||||
})
|
||||
@Index()
|
||||
status: 'active' | 'frozen' | 'closed';
|
||||
|
||||
@CreateDateColumn({ comment: '创建时间' })
|
||||
created_at: Date;
|
||||
|
||||
@UpdateDateColumn({ comment: '更新时间' })
|
||||
updated_at: Date;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, Index } from 'typeorm';
|
||||
|
||||
@Entity('system_transactions')
|
||||
@Index('uk_transaction_no', ['transactionNo'], { unique: true })
|
||||
@Index('idx_account_id', ['accountId'])
|
||||
@Index('idx_transaction_type', ['transactionType'])
|
||||
@Index('idx_business', ['businessType', 'businessId'])
|
||||
@Index('idx_created_at', ['createdAt'])
|
||||
export class SystemTransaction {
|
||||
@PrimaryGeneratedColumn({ type: 'bigint', unsigned: true, comment: '流水ID' })
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 32, name: 'transaction_no', comment: '交易流水号(全局唯一)' })
|
||||
transactionNo: string;
|
||||
|
||||
@Column({ type: 'bigint', unsigned: true, name: 'account_id', comment: '系统账户ID' })
|
||||
accountId: number;
|
||||
|
||||
@Column({ type: 'enum', enum: ['income', 'expense'], comment: '方向:income-收入/expense-支出' })
|
||||
direction: 'income' | 'expense';
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, comment: '金额(正数)' })
|
||||
amount: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, name: 'balance_before', comment: '交易前余额' })
|
||||
balanceBefore: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2, name: 'balance_after', comment: '交易后余额' })
|
||||
balanceAfter: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, name: 'transaction_type', comment: '交易类型' })
|
||||
transactionType: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, name: 'business_type', comment: '业务类型:order_payment/refund/withdraw' })
|
||||
businessType: string;
|
||||
|
||||
@Column({ type: 'bigint', unsigned: true, nullable: true, name: 'business_id', comment: '业务ID(订单ID/退款ID/提现ID等)' })
|
||||
businessId: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 32, nullable: true, name: 'business_no', comment: '业务单号' })
|
||||
businessNo: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: true, comment: '备注' })
|
||||
remark: string;
|
||||
|
||||
@CreateDateColumn({ type: 'datetime', name: 'created_at', comment: '创建时间' })
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { MktUserInviteStats } from '@/entities/mkt-user-invite-stats.entity';
|
||||
import { MktInviteWithdrawal } from '@/entities/mkt-invite-withdrawal.entity';
|
||||
import { Order } from '@/entities/order.entity';
|
||||
import { User } from '@/entities/user.entity';
|
||||
import { FinanceModule } from '@/modules/shared/finance/finance.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -21,6 +22,7 @@ import { User } from '@/entities/user.entity';
|
||||
Order,
|
||||
User,
|
||||
]),
|
||||
FinanceModule,
|
||||
],
|
||||
controllers: [ActivityController],
|
||||
providers: [ActivityService],
|
||||
|
||||
@@ -8,6 +8,8 @@ import { MktUserInviteStats } from '@/entities/mkt-user-invite-stats.entity';
|
||||
import { MktInviteWithdrawal } from '@/entities/mkt-invite-withdrawal.entity';
|
||||
import { Order } from '@/entities/order.entity';
|
||||
import { User } from '@/entities/user.entity';
|
||||
import { AccountService } from '@/modules/shared/finance/account.service';
|
||||
import { TransactionService } from '@/modules/shared/finance/transaction.service';
|
||||
import {
|
||||
BindInvitationDto,
|
||||
CreateInviteWithdrawalDto,
|
||||
@@ -33,6 +35,8 @@ export class ActivityService {
|
||||
private orderRepo: Repository<Order>,
|
||||
@InjectRepository(User)
|
||||
private userRepo: Repository<User>,
|
||||
private accountService: AccountService,
|
||||
private transactionService: TransactionService,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
@@ -415,6 +419,30 @@ export class ActivityService {
|
||||
);
|
||||
}
|
||||
|
||||
// 账户操作:给邀请人用户账户增加返现金额,从平台账户扣减
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
// 给用户账户增加返现
|
||||
await this.accountService.addUserBalance(
|
||||
invitation.inviterId,
|
||||
amount,
|
||||
transactionNo,
|
||||
'invite_cashback',
|
||||
cashback.id,
|
||||
order.orderNo,
|
||||
`邀请返现 - 订单${order.orderNo}第${orderIndex}单`,
|
||||
);
|
||||
|
||||
// 从平台账户扣减返现支出
|
||||
await this.accountService.deductPlatformCashback(
|
||||
amount,
|
||||
transactionNo,
|
||||
'invite_cashback',
|
||||
cashback.id,
|
||||
order.orderNo,
|
||||
`邀请返现支出 - 用户${invitation.inviterId}订单${order.orderNo}`,
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
this.logger.log(
|
||||
`订单 ${order.orderNo} 完成,邀请人 ${invitation.inviterId} 获得第${orderIndex}单返现 ${amount} 元(比例${rate * 100}%)`,
|
||||
|
||||
@@ -329,11 +329,10 @@ export class OrderService {
|
||||
paidAt: new Date(),
|
||||
});
|
||||
|
||||
// 记录平台账户收入
|
||||
// 记录系统总账户收入(用户实付金额)
|
||||
const transactionNo = `TXN${Date.now()}${Math.floor(Math.random() * 10000)}`;
|
||||
await this.accountService.addPlatformBalance(
|
||||
await this.accountService.addSystemIncome(
|
||||
order.payAmount,
|
||||
order.serviceFee || 0,
|
||||
transactionNo,
|
||||
'order_payment',
|
||||
order.id,
|
||||
@@ -378,11 +377,10 @@ export class OrderService {
|
||||
paidAt: new Date(),
|
||||
});
|
||||
|
||||
// 记录平台账户收入
|
||||
// 记录系统总账户收入(用户实付金额)
|
||||
const transactionNo = `TXN${Date.now()}${Math.floor(Math.random() * 10000)}`;
|
||||
await this.accountService.addPlatformBalance(
|
||||
await this.accountService.addSystemIncome(
|
||||
order.payAmount,
|
||||
order.serviceFee || 0,
|
||||
transactionNo,
|
||||
'order_payment',
|
||||
order.id,
|
||||
|
||||
@@ -4,9 +4,11 @@ import { Repository, DataSource, FindOptionsWhere } from 'typeorm';
|
||||
import { UserAccount } from '@/entities/user-account.entity';
|
||||
import { MerchantAccount } from '@/entities/merchant-account.entity';
|
||||
import { PlatformAccount } from '@/entities/platform-account.entity';
|
||||
import { SystemAccount } from '@/entities/system-account.entity';
|
||||
import { UserTransaction } from '@/entities/user-transaction.entity';
|
||||
import { MerchantTransaction } from '@/entities/merchant-transaction.entity';
|
||||
import { PlatformTransaction } from '@/entities/platform-transaction.entity';
|
||||
import { SystemTransaction } from '@/entities/system-transaction.entity';
|
||||
import { QueryUserAccountsDto, QueryMerchantAccountsDto } from './dto/account.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -18,12 +20,16 @@ export class AccountService {
|
||||
private merchantAccountRepo: Repository<MerchantAccount>,
|
||||
@InjectRepository(PlatformAccount)
|
||||
private platformAccountRepo: Repository<PlatformAccount>,
|
||||
@InjectRepository(SystemAccount)
|
||||
private systemAccountRepo: Repository<SystemAccount>,
|
||||
@InjectRepository(UserTransaction)
|
||||
private userTransactionRepo: Repository<UserTransaction>,
|
||||
@InjectRepository(MerchantTransaction)
|
||||
private merchantTransactionRepo: Repository<MerchantTransaction>,
|
||||
@InjectRepository(PlatformTransaction)
|
||||
private platformTransactionRepo: Repository<PlatformTransaction>,
|
||||
@InjectRepository(SystemTransaction)
|
||||
private systemTransactionRepo: Repository<SystemTransaction>,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
@@ -429,10 +435,9 @@ export class AccountService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台账户增加余额(订单收入)
|
||||
* 平台账户增加服务费收入
|
||||
*/
|
||||
async addPlatformBalance(
|
||||
amount: number,
|
||||
async addPlatformServiceFee(
|
||||
serviceFee: number,
|
||||
transactionNo: string,
|
||||
businessType: string,
|
||||
@@ -454,14 +459,12 @@ export class AccountService {
|
||||
throw new NotFoundException('平台账户不存在');
|
||||
}
|
||||
|
||||
const balanceBefore = Number(account.balance);
|
||||
const amountNum = Number(amount);
|
||||
const serviceFeeNum = Number(serviceFee);
|
||||
const balanceAfter = parseFloat((balanceBefore + amountNum).toFixed(2));
|
||||
const balanceBefore = Number(account.balance);
|
||||
const balanceAfter = parseFloat((balanceBefore + serviceFeeNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.total_income = parseFloat((Number(account.total_income) + amountNum).toFixed(2));
|
||||
account.total_service_fee = parseFloat((Number(account.total_service_fee) + serviceFeeNum).toFixed(2));
|
||||
account.total_income = parseFloat((Number(account.total_income) + serviceFeeNum).toFixed(2));
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
@@ -470,10 +473,10 @@ export class AccountService {
|
||||
transaction_no: transactionNo,
|
||||
account_id: account.id,
|
||||
direction: 'income',
|
||||
amount,
|
||||
amount: serviceFeeNum,
|
||||
balance_before: balanceBefore,
|
||||
balance_after: balanceAfter,
|
||||
transaction_type: '订单收入',
|
||||
transaction_type: '服务费收入',
|
||||
business_type: businessType,
|
||||
business_id: businessId,
|
||||
business_no: businessNo,
|
||||
@@ -491,12 +494,11 @@ export class AccountService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台账户扣减余额(结算/返现/提现)
|
||||
* 平台账户扣减邀请返现支出
|
||||
*/
|
||||
async deductPlatformBalance(
|
||||
amount: number,
|
||||
async deductPlatformCashback(
|
||||
cashbackAmount: number,
|
||||
transactionNo: string,
|
||||
transactionType: string,
|
||||
businessType: string,
|
||||
businessId: number,
|
||||
businessNo: string,
|
||||
@@ -516,21 +518,12 @@ export class AccountService {
|
||||
throw new NotFoundException('平台账户不存在');
|
||||
}
|
||||
|
||||
const cashbackNum = Number(cashbackAmount);
|
||||
const balanceBefore = Number(account.balance);
|
||||
if (balanceBefore < amount) {
|
||||
throw new BadRequestException('平台账户余额不足');
|
||||
}
|
||||
|
||||
const balanceAfter = balanceBefore - amount;
|
||||
const balanceAfter = parseFloat((balanceBefore - cashbackNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.total_expense = Number(account.total_expense) + amount;
|
||||
|
||||
if (transactionType === '商家结算') {
|
||||
} else if (transactionType === '邀请返现') {
|
||||
} else if (transactionType === '提现') {
|
||||
}
|
||||
|
||||
account.total_expense = parseFloat((Number(account.total_expense) + cashbackNum).toFixed(2));
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
@@ -539,10 +532,73 @@ export class AccountService {
|
||||
transaction_no: transactionNo,
|
||||
account_id: account.id,
|
||||
direction: 'expense',
|
||||
amount,
|
||||
amount: cashbackNum,
|
||||
balance_before: balanceBefore,
|
||||
balance_after: balanceAfter,
|
||||
transaction_type: transactionType,
|
||||
transaction_type: '邀请返现',
|
||||
business_type: businessType,
|
||||
business_id: businessId,
|
||||
business_no: businessNo,
|
||||
remark,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(transaction);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台账户扣减提现
|
||||
*/
|
||||
async deductPlatformWithdrawal(
|
||||
amount: number,
|
||||
transactionNo: string,
|
||||
businessType: string,
|
||||
businessId: number,
|
||||
businessNo: string,
|
||||
remark: string,
|
||||
): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const account = await queryRunner.manager.findOne(PlatformAccount, {
|
||||
where: { account_name: '主账户' },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('平台账户不存在');
|
||||
}
|
||||
|
||||
const amountNum = Number(amount);
|
||||
const balanceBefore = Number(account.balance);
|
||||
|
||||
if (balanceBefore < amountNum) {
|
||||
throw new BadRequestException('平台账户余额不足');
|
||||
}
|
||||
|
||||
const balanceAfter = parseFloat((balanceBefore - amountNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
|
||||
const transaction = this.platformTransactionRepo.create({
|
||||
transaction_no: transactionNo,
|
||||
account_id: account.id,
|
||||
direction: 'expense',
|
||||
amount: amountNum,
|
||||
balance_before: balanceBefore,
|
||||
balance_after: balanceAfter,
|
||||
transaction_type: '提现',
|
||||
business_type: businessType,
|
||||
business_id: businessId,
|
||||
business_no: businessNo,
|
||||
@@ -709,4 +765,200 @@ export class AccountService {
|
||||
totalPendingSettlement: Number(result?.totalPendingSettlement || 0),
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 系统总账户操作方法
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 获取系统总账户
|
||||
*/
|
||||
async getSystemAccount(accountName: string = '主账户'): Promise<SystemAccount> {
|
||||
const account = await this.systemAccountRepo.findOne({ where: { account_name: accountName } });
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException(
|
||||
`系统总账户"${accountName}"不存在,请先执行数据库初始化脚本创建系统总账户`
|
||||
);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统总账户增加收入(用户支付订单)
|
||||
*/
|
||||
async addSystemIncome(
|
||||
amount: number,
|
||||
transactionNo: string,
|
||||
businessType: string,
|
||||
businessId: number,
|
||||
businessNo: string,
|
||||
remark: string,
|
||||
): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const account = await queryRunner.manager.findOne(SystemAccount, {
|
||||
where: { account_name: '主账户' },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('系统总账户不存在');
|
||||
}
|
||||
|
||||
const amountNum = Number(amount);
|
||||
const balanceBefore = Number(account.balance);
|
||||
const balanceAfter = parseFloat((balanceBefore + amountNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.total_income = parseFloat((Number(account.total_income) + amountNum).toFixed(2));
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
|
||||
const transaction = this.systemTransactionRepo.create({
|
||||
transactionNo: transactionNo,
|
||||
accountId: account.id,
|
||||
direction: 'income',
|
||||
amount: amountNum,
|
||||
balanceBefore: balanceBefore,
|
||||
balanceAfter: balanceAfter,
|
||||
transactionType: '用户支付',
|
||||
businessType: businessType,
|
||||
businessId: businessId,
|
||||
businessNo: businessNo,
|
||||
remark,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(transaction);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统总账户增加退款支出
|
||||
*/
|
||||
async addSystemRefund(
|
||||
amount: number,
|
||||
transactionNo: string,
|
||||
businessType: string,
|
||||
businessId: number,
|
||||
businessNo: string,
|
||||
remark: string,
|
||||
): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const account = await queryRunner.manager.findOne(SystemAccount, {
|
||||
where: { account_name: '主账户' },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('系统总账户不存在');
|
||||
}
|
||||
|
||||
const amountNum = Number(amount);
|
||||
const balanceBefore = Number(account.balance);
|
||||
const balanceAfter = parseFloat((balanceBefore - amountNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.total_refund = parseFloat((Number(account.total_refund) + amountNum).toFixed(2));
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
|
||||
const transaction = this.systemTransactionRepo.create({
|
||||
transactionNo: transactionNo,
|
||||
accountId: account.id,
|
||||
direction: 'expense',
|
||||
amount: amountNum,
|
||||
balanceBefore: balanceBefore,
|
||||
balanceAfter: balanceAfter,
|
||||
transactionType: '退款',
|
||||
businessType: businessType,
|
||||
businessId: businessId,
|
||||
businessNo: businessNo,
|
||||
remark,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(transaction);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统总账户增加提现支出
|
||||
*/
|
||||
async addSystemWithdrawal(
|
||||
amount: number,
|
||||
transactionNo: string,
|
||||
businessType: string,
|
||||
businessId: number,
|
||||
businessNo: string,
|
||||
remark: string,
|
||||
): Promise<void> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const account = await queryRunner.manager.findOne(SystemAccount, {
|
||||
where: { account_name: '主账户' },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new NotFoundException('系统总账户不存在');
|
||||
}
|
||||
|
||||
const amountNum = Number(amount);
|
||||
const balanceBefore = Number(account.balance);
|
||||
const balanceAfter = parseFloat((balanceBefore - amountNum).toFixed(2));
|
||||
|
||||
account.balance = balanceAfter;
|
||||
account.total_withdrawn = parseFloat((Number(account.total_withdrawn) + amountNum).toFixed(2));
|
||||
account.version += 1;
|
||||
|
||||
await queryRunner.manager.save(account);
|
||||
|
||||
const transaction = this.systemTransactionRepo.create({
|
||||
transactionNo: transactionNo,
|
||||
accountId: account.id,
|
||||
direction: 'expense',
|
||||
amount: amountNum,
|
||||
balanceBefore: balanceBefore,
|
||||
balanceAfter: balanceAfter,
|
||||
transactionType: '提现',
|
||||
businessType: businessType,
|
||||
businessId: businessId,
|
||||
businessNo: businessNo,
|
||||
remark,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(transaction);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ import { SettlementItem } from '@/entities/settlement-item.entity';
|
||||
import { UserWithdrawal } from '@/entities/user-withdrawal.entity';
|
||||
import { MerchantWithdrawal } from '@/entities/merchant-withdrawal.entity';
|
||||
import { PlatformWithdrawal } from '@/entities/platform-withdrawal.entity';
|
||||
import { MktInviteWithdrawal } from '@/entities/mkt-invite-withdrawal.entity';
|
||||
import { UserAccount } from '@/entities/user-account.entity';
|
||||
import { MerchantAccount } from '@/entities/merchant-account.entity';
|
||||
import { PlatformAccount } from '@/entities/platform-account.entity';
|
||||
import { SystemAccount } from '@/entities/system-account.entity';
|
||||
import { UserTransaction } from '@/entities/user-transaction.entity';
|
||||
import { MerchantTransaction } from '@/entities/merchant-transaction.entity';
|
||||
import { PlatformTransaction } from '@/entities/platform-transaction.entity';
|
||||
import { SystemTransaction } from '@/entities/system-transaction.entity';
|
||||
import { DailyReconciliation } from '@/entities/daily-reconciliation.entity';
|
||||
import { PlatformConfig } from '@/entities/platform-config.entity';
|
||||
import { Merchant } from '@/entities/merchant.entity';
|
||||
@@ -33,12 +36,15 @@ import { MerchantModule } from '@/modules/merchant/merchant.module';
|
||||
UserWithdrawal,
|
||||
MerchantWithdrawal,
|
||||
PlatformWithdrawal,
|
||||
MktInviteWithdrawal,
|
||||
UserAccount,
|
||||
MerchantAccount,
|
||||
PlatformAccount,
|
||||
SystemAccount,
|
||||
UserTransaction,
|
||||
MerchantTransaction,
|
||||
PlatformTransaction,
|
||||
SystemTransaction,
|
||||
DailyReconciliation,
|
||||
PlatformConfig,
|
||||
Merchant,
|
||||
|
||||
@@ -3,8 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Order } from '@/entities/order.entity';
|
||||
import { PlatformAccount } from '@/entities/platform-account.entity';
|
||||
import { PlatformTransaction } from '@/entities/platform-transaction.entity';
|
||||
import { AccountService } from './account.service';
|
||||
import { TransactionService } from './transaction.service';
|
||||
import Wechatpay = require('wechatpay-node-v3');
|
||||
|
||||
@Injectable()
|
||||
@@ -14,10 +14,8 @@ export class RefundService {
|
||||
constructor(
|
||||
@InjectRepository(Order)
|
||||
private orderRepo: Repository<Order>,
|
||||
@InjectRepository(PlatformAccount)
|
||||
private platformAccountRepo: Repository<PlatformAccount>,
|
||||
@InjectRepository(PlatformTransaction)
|
||||
private platformTransactionRepo: Repository<PlatformTransaction>,
|
||||
private accountService: AccountService,
|
||||
private transactionService: TransactionService,
|
||||
private dataSource: DataSource,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
@@ -179,7 +177,7 @@ export class RefundService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台账户记录退款支出(仅用于记账)
|
||||
* 记录系统总账户退款
|
||||
* @param order 订单信息
|
||||
* @param queryRunner 事务查询器
|
||||
*/
|
||||
@@ -187,34 +185,17 @@ export class RefundService {
|
||||
order: Order,
|
||||
queryRunner: any,
|
||||
): Promise<void> {
|
||||
// 获取平台主账户
|
||||
const platformAccount = await queryRunner.manager.findOne(PlatformAccount, {
|
||||
where: { accountType: 'main' },
|
||||
});
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
if (!platformAccount) {
|
||||
throw new BadRequestException('平台账户不存在');
|
||||
}
|
||||
|
||||
// 创建退款支出交易记录(仅记账,实际退款通过第三方支付)
|
||||
const transaction = queryRunner.manager.create(PlatformTransaction, {
|
||||
accountId: platformAccount.id,
|
||||
type: 'refund_expense',
|
||||
amount: order.payAmount,
|
||||
balance: platformAccount.balance, // 余额不变,仅记录
|
||||
relatedType: 'order',
|
||||
relatedId: order.id,
|
||||
description: `订单退款 - ${order.orderNo}`,
|
||||
metadata: {
|
||||
orderId: order.id,
|
||||
orderNo: order.orderNo,
|
||||
userId: order.userId,
|
||||
paymentMethod: order.paymentMethod,
|
||||
refundAmount: order.payAmount,
|
||||
},
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(transaction);
|
||||
// 记录系统总账户退款
|
||||
await this.accountService.addSystemRefund(
|
||||
order.payAmount,
|
||||
transactionNo,
|
||||
'order_refund',
|
||||
order.id,
|
||||
order.orderNo,
|
||||
`订单退款 - ${order.orderNo}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { SystemAccount } from '@/entities/system-account.entity';
|
||||
import { PlatformAccount } from '@/entities/platform-account.entity';
|
||||
import { UserAccount } from '@/entities/user-account.entity';
|
||||
import { MerchantAccount } from '@/entities/merchant-account.entity';
|
||||
import { PlatformTransaction } from '@/entities/platform-transaction.entity';
|
||||
import { Order } from '@/entities/order.entity';
|
||||
import { Settlement } from '@/entities/settlement.entity';
|
||||
import { MerchantWithdrawal } from '@/entities/merchant-withdrawal.entity';
|
||||
import { UserWithdrawal } from '@/entities/user-withdrawal.entity';
|
||||
import { PlatformWithdrawal } from '@/entities/platform-withdrawal.entity';
|
||||
import { MktInviteWithdrawal } from '@/entities/mkt-invite-withdrawal.entity';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@Injectable()
|
||||
export class ReportService {
|
||||
constructor(
|
||||
@InjectRepository(SystemAccount)
|
||||
private systemAccountRepo: Repository<SystemAccount>,
|
||||
@InjectRepository(PlatformAccount)
|
||||
private platformAccountRepo: Repository<PlatformAccount>,
|
||||
@InjectRepository(UserAccount)
|
||||
@@ -24,12 +31,26 @@ export class ReportService {
|
||||
private orderRepo: Repository<Order>,
|
||||
@InjectRepository(Settlement)
|
||||
private settlementRepo: Repository<Settlement>,
|
||||
@InjectRepository(MerchantWithdrawal)
|
||||
private merchantWithdrawalRepo: Repository<MerchantWithdrawal>,
|
||||
@InjectRepository(UserWithdrawal)
|
||||
private userWithdrawalRepo: Repository<UserWithdrawal>,
|
||||
@InjectRepository(PlatformWithdrawal)
|
||||
private platformWithdrawalRepo: Repository<PlatformWithdrawal>,
|
||||
@InjectRepository(MktInviteWithdrawal)
|
||||
private mktInviteWithdrawalRepo: Repository<MktInviteWithdrawal>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 财务总览
|
||||
*/
|
||||
async getOverview() {
|
||||
// 获取系统总账户
|
||||
const systemAccount = await this.systemAccountRepo.findOne({
|
||||
where: { account_name: '系统总账户' },
|
||||
});
|
||||
|
||||
// 获取平台账户
|
||||
const platformAccount = await this.platformAccountRepo.findOne({
|
||||
where: { account_name: '主账户' },
|
||||
});
|
||||
@@ -68,11 +89,26 @@ export class ReportService {
|
||||
.getRawOne();
|
||||
|
||||
return {
|
||||
// 系统总账户金额(用户实付 - 退款 - 提现)
|
||||
systemTotalAmount: Number(systemAccount?.balance || 0),
|
||||
totalUserPaid: Number(systemAccount?.total_income || 0),
|
||||
totalRefund: Number(systemAccount?.total_refund || 0),
|
||||
totalWithdrawn: Number(systemAccount?.total_withdrawn || 0),
|
||||
|
||||
// 平台净收益(服务费 - 邀请返现)
|
||||
platformBalance: Number(platformAccount?.balance || 0),
|
||||
merchantTotalBalance: Number(merchantStats?.totalBalance || 0),
|
||||
platformTotalIncome: Number(platformAccount?.total_income || 0),
|
||||
platformTotalExpense: Number(platformAccount?.total_expense || 0),
|
||||
|
||||
// 商家账户统计
|
||||
totalMerchantBalance: Number(merchantStats?.totalBalance || 0),
|
||||
merchantCount: Number(merchantStats?.count || 0),
|
||||
userTotalBalance: Number(userStats?.totalBalance || 0),
|
||||
|
||||
// 用户账户统计
|
||||
totalUserBalance: Number(userStats?.totalBalance || 0),
|
||||
userCount: Number(userStats?.count || 0),
|
||||
|
||||
// 今日统计
|
||||
todayOrders,
|
||||
todayIncome: Number(todayIncome?.sum || 0),
|
||||
todayExpense: Number(todayExpense?.sum || 0),
|
||||
|
||||
@@ -167,6 +167,7 @@ export class SettlementService {
|
||||
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
// 给商家账户增加余额(订单金额 - 服务费)
|
||||
await this.accountService.addMerchantBalance(
|
||||
merchantId,
|
||||
settlementAmount,
|
||||
@@ -177,14 +178,14 @@ export class SettlementService {
|
||||
`商家周结算:${periodStart} ~ ${periodEnd}`
|
||||
);
|
||||
|
||||
await this.accountService.deductPlatformBalance(
|
||||
settlementAmount,
|
||||
// 给平台账户增加服务费收入
|
||||
await this.accountService.addPlatformServiceFee(
|
||||
serviceFee,
|
||||
transactionNo,
|
||||
'商家结算',
|
||||
'settlement',
|
||||
settlement.id,
|
||||
settlementNo,
|
||||
`商家 ${merchantId} 周结算:${periodStart} ~ ${periodEnd}`
|
||||
`商家 ${merchantId} 周结算服务费:${periodStart} ~ ${periodEnd}`
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
@@ -155,10 +155,10 @@ export class WithdrawalService {
|
||||
|
||||
const account = await this.accountService.getPlatformAccount();
|
||||
|
||||
// 计算可提现金额 = 累计服务费 - 冻结余额
|
||||
// 注意:这里使用 total_service_fee 是为了确保只提现平台收入
|
||||
// 计算可提现金额 = 累计收入 - 冻结余额
|
||||
// 注意:这里使用 total_income 是为了确保只提现平台收入
|
||||
// 未来如果有其他收入(广告费、会员费等),需要累加到可提现金额中
|
||||
const availableAmount = Number(account.total_service_fee) - Number(account.frozen_balance);
|
||||
const availableAmount = Number(account.total_income) - Number(account.frozen_balance);
|
||||
|
||||
if (availableAmount < amount) {
|
||||
throw new BadRequestException(`可提现金额不足,当前可提现:${availableAmount.toFixed(2)}元`);
|
||||
@@ -294,6 +294,7 @@ export class WithdrawalService {
|
||||
try {
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
// 扣减用户账户余额
|
||||
await this.accountService.deductUserBalance(
|
||||
withdrawal.userId,
|
||||
Number(withdrawal.actualAmount),
|
||||
@@ -304,6 +305,16 @@ export class WithdrawalService {
|
||||
`用户提现 - ${withdrawal.paymentChannel}`
|
||||
);
|
||||
|
||||
// 记录系统总账户提现
|
||||
await this.accountService.addSystemWithdrawal(
|
||||
Number(withdrawal.actualAmount),
|
||||
transactionNo,
|
||||
'user_withdraw',
|
||||
withdrawal.id,
|
||||
withdrawal.withdrawNo,
|
||||
`用户 ${withdrawal.userId} 提现 - ${withdrawal.paymentChannel}`,
|
||||
);
|
||||
|
||||
await queryRunner.manager.update(UserWithdrawal, id, {
|
||||
status: 'paid',
|
||||
paymentNo,
|
||||
@@ -415,6 +426,7 @@ export class WithdrawalService {
|
||||
try {
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
// 扣减商家账户余额
|
||||
await this.accountService.deductMerchantBalance(
|
||||
withdrawal.merchantId,
|
||||
Number(withdrawal.actualAmount),
|
||||
@@ -425,6 +437,16 @@ export class WithdrawalService {
|
||||
`商家提现 - ${withdrawal.bankName}`
|
||||
);
|
||||
|
||||
// 记录系统总账户提现
|
||||
await this.accountService.addSystemWithdrawal(
|
||||
Number(withdrawal.actualAmount),
|
||||
transactionNo,
|
||||
'merchant_withdraw',
|
||||
withdrawal.id,
|
||||
withdrawal.withdrawNo,
|
||||
`商家 ${withdrawal.merchantId} 提现 - ${withdrawal.bankName}`,
|
||||
);
|
||||
|
||||
await queryRunner.manager.update(MerchantWithdrawal, id, {
|
||||
status: 'paid',
|
||||
paymentNo,
|
||||
@@ -536,10 +558,9 @@ export class WithdrawalService {
|
||||
try {
|
||||
const transactionNo = this.transactionService.generateTransactionNo();
|
||||
|
||||
await this.accountService.deductPlatformBalance(
|
||||
await this.accountService.deductPlatformWithdrawal(
|
||||
Number(withdrawal.actualAmount),
|
||||
transactionNo,
|
||||
'提现',
|
||||
'withdraw',
|
||||
withdrawal.id,
|
||||
withdrawal.withdrawNo,
|
||||
|
||||
Reference in New Issue
Block a user