feat: restore credit limits and harden SMS sending

This commit is contained in:
hectorzhao
2026-07-14 18:46:35 +08:00
parent 3e3b7a9d1a
commit c1a17699db
23 changed files with 796 additions and 56 deletions
+45 -4
View File
@@ -1,13 +1,20 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
export interface CreateTenantAccountDto {
tenantId: string;
balanceCents?: number;
creditCents?: number;
status?: string;
}
export interface UpdateCreditLimitDto {
creditCents: number;
operatorId?: string;
remark?: string;
}
export interface CreateAccountTransactionDto {
tenantId: string;
transactionType: string;
@@ -80,14 +87,40 @@ export class BillingService {
}
createAccount(data: CreateTenantAccountDto) {
assertCreditAmount(data.creditCents ?? 0);
const createData: Prisma.TenantAccountUncheckedCreateInput = {
tenantId: data.tenantId,
balanceCents: data.balanceCents ?? 0,
creditCents: data.creditCents ?? 0,
status: data.status ?? 'active',
};
return this.prisma.tenantAccount.create({ data: createData });
}
async updateCreditLimit(tenantId: string, data: UpdateCreditLimitDto) {
assertCreditAmount(data.creditCents);
const account = await this.getAccountOrCreate(tenantId);
const updated = await this.prisma.tenantAccount.update({
where: { tenantId },
data: { creditCents: data.creditCents },
});
await this.prisma.operationLog.create({
data: {
tenantId,
userId: data.operatorId,
action: 'billing.credit_limit_updated',
resource: 'tenant_account',
resourceId: account.id,
detail: {
previousCreditCents: account.creditCents,
creditCents: data.creditCents,
remark: data.remark,
} as Prisma.InputJsonValue,
},
});
return updated;
}
listRechargeOrders(tenantId?: string) {
return this.prisma.rechargeOrder.findMany({
where: tenantId ? { tenantId } : undefined,
@@ -195,12 +228,14 @@ export class BillingService {
async checkAccount(data: BillingActionDto) {
const account = await this.getAccountOrCreate(data.tenantId);
const requiredAmount = data.amountCents ?? 0;
const availableAmount = account.balanceCents;
const availableAmount = account.balanceCents + account.creditCents;
return {
tenantId: data.tenantId,
requiredAmount,
availableAmount,
canSend: availableAmount >= requiredAmount,
balanceCents: account.balanceCents,
creditCents: account.creditCents,
canSend: availableAmount > 0,
};
}
@@ -296,7 +331,7 @@ export class BillingService {
return this.prisma.tenantAccount.upsert({
where: { tenantId },
update: {},
create: { tenantId, balanceCents: 0, status: 'active' },
create: { tenantId, balanceCents: 0, creditCents: 0, status: 'active' },
});
}
@@ -324,6 +359,12 @@ 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) {