feat: improve application access and money precision

This commit is contained in:
hectorzhao
2026-07-16 17:54:05 +08:00
parent 9d5c507007
commit faa716b8d0
49 changed files with 1699 additions and 489 deletions
+19 -16
View File
@@ -1,5 +1,6 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { assertMoneyUnits, moneyToNumber } from '../common/money';
import { PrismaService } from '../prisma/prisma.service';
export interface CreateTenantAccountDto {
@@ -87,7 +88,8 @@ export class BillingService {
}
createAccount(data: CreateTenantAccountDto) {
assertCreditAmount(data.creditCents ?? 0);
assertMoneyUnits(data.balanceCents ?? 0, '账户余额', { allowNegative: true });
assertMoneyUnits(data.creditCents ?? 0, '授信额度', { allowNegative: true });
const createData: Prisma.TenantAccountUncheckedCreateInput = {
tenantId: data.tenantId,
balanceCents: data.balanceCents ?? 0,
@@ -98,7 +100,7 @@ export class BillingService {
}
async updateCreditLimit(tenantId: string, data: UpdateCreditLimitDto) {
assertCreditAmount(data.creditCents);
assertMoneyUnits(data.creditCents, '授信额度', { allowNegative: true });
const account = await this.getAccountOrCreate(tenantId);
const updated = await this.prisma.tenantAccount.update({
where: { tenantId },
@@ -112,7 +114,7 @@ export class BillingService {
resource: 'tenant_account',
resourceId: account.id,
detail: {
previousCreditCents: account.creditCents,
previousCreditCents: moneyToNumber(account.creditCents),
creditCents: data.creditCents,
remark: data.remark,
} as Prisma.InputJsonValue,
@@ -148,7 +150,7 @@ export class BillingService {
},
select: { relatedId: true, balanceAfter: true },
});
const balanceAfterByOrderId = new Map(transactions.map((transaction) => [transaction.relatedId, transaction.balanceAfter]));
const balanceAfterByOrderId = new Map(transactions.map((transaction) => [transaction.relatedId, moneyToNumber(transaction.balanceAfter)]));
return orders.map((order) => ({
...order,
@@ -158,6 +160,7 @@ export class BillingService {
async createRechargeOrder(data: CreateRechargeOrderDto) {
const amountCents = data.amountCents;
assertMoneyUnits(amountCents, '充值金额', { allowNegative: true, allowZero: false });
const order = await this.prisma.rechargeOrder.create({
data: {
tenantId: data.tenantId,
@@ -211,7 +214,10 @@ export class BillingService {
estimateSmsCost(data: EstimateSmsCostDto) {
const billingUnits = estimateBillingUnits(data.content);
const unitPrice = data.unitPrice ?? 0;
assertMoneyUnits(unitPrice, '短信单价');
const totalUnits = billingUnits * data.phoneCount;
const amountCents = totalUnits * unitPrice;
assertMoneyUnits(amountCents, '短信计费金额');
return {
tenantId: data.tenantId,
applicationId: data.applicationId,
@@ -221,20 +227,22 @@ export class BillingService {
billingUnitsPerMessage: billingUnits,
totalBillingUnits: totalUnits,
unitPrice,
amountCents: totalUnits * unitPrice,
amountCents,
};
}
async checkAccount(data: BillingActionDto) {
const account = await this.getAccountOrCreate(data.tenantId);
const requiredAmount = data.amountCents ?? 0;
const availableAmount = account.balanceCents + account.creditCents;
const balanceCents = moneyToNumber(account.balanceCents);
const creditCents = moneyToNumber(account.creditCents);
const availableAmount = balanceCents + creditCents;
return {
tenantId: data.tenantId,
requiredAmount,
availableAmount,
balanceCents: account.balanceCents,
creditCents: account.creditCents,
balanceCents,
creditCents,
canSend: availableAmount > 0,
};
}
@@ -316,6 +324,7 @@ export class BillingService {
}
createRule(data: CreateBillingRuleDto) {
assertMoneyUnits(data.unitPrice, '计费规则单价');
return this.prisma.billingRule.create({
data: {
code: data.code,
@@ -337,7 +346,7 @@ export class BillingService {
private async applyAccountDelta(data: CreateAccountTransactionDto) {
const account = await this.getAccountOrCreate(data.tenantId);
const nextBalance = account.balanceCents + (data.amountCents ?? 0);
const nextBalance = moneyToNumber(account.balanceCents) + (data.amountCents ?? 0);
await this.prisma.tenantAccount.update({
where: { tenantId: data.tenantId },
data: {
@@ -359,12 +368,6 @@ export class BillingService {
}
}
function assertCreditAmount(creditCents: number) {
if (!Number.isInteger(creditCents)) {
throw new BadRequestException('授信额度必须为整数金额(分)');
}
}
function estimateBillingUnits(content: string) {
const length = [...content].length;
if (length <= 70) {