feat: add number library routing and cdr location support

This commit is contained in:
hectorzhao
2026-06-24 11:39:32 +08:00
parent 5fa1bd35e9
commit 7057fd3c42
63 changed files with 6361 additions and 566 deletions
+19 -37
View File
@@ -48,7 +48,6 @@ export interface CdrRatingTransaction {
lockCustomer(customerId: string): Promise<LockedCustomer | null>;
createRated(input: CreateRatedInput): Promise<StoredRatedCdr>;
markRawStatus(rawCdrId: string, status: 'RATED' | 'SKIPPED' | 'FAILED'): Promise<void>;
createCustomerCharge(input: CreateCustomerChargeInput): Promise<void>;
updateCustomerBalance(customerId: string, balance: Prisma.Decimal): Promise<void>;
}
@@ -67,16 +66,6 @@ export interface CreateRatedInput {
vendorRate: Prisma.InputJsonValue;
}
export interface CreateCustomerChargeInput {
id: string;
customerId: string;
rawCdrId: string;
eventId: string;
amount: Prisma.Decimal;
beforeBalance: Prisma.Decimal;
afterBalance: Prisma.Decimal;
}
export class CdrRatingService {
constructor(private readonly store: CdrRatingStore) {}
@@ -144,15 +133,6 @@ export class CdrRatingService {
cycleRate: vendorGateway.cycleRate.toFixed(6)
}
});
await tx.createCustomerCharge({
id: prefixedId('cdrchg'),
customerId: customer.id,
rawCdrId: raw.id,
eventId: event.event_id,
amount: customerFee.negated().toDecimalPlaces(6),
beforeBalance: customer.balance,
afterBalance
});
await tx.updateCustomerBalance(customer.id, afterBalance);
await tx.markRawStatus(raw.id, 'RATED');
@@ -202,6 +182,11 @@ class PrismaCdrRatingTransaction implements CdrRatingTransaction {
sourceIp: emptyToNull(event.source_ip),
caller: event.caller || 'unknown',
callee: event.callee || 'unknown',
calleeCityCode: emptyToNull(event.callee_city_code),
calleeCityName: emptyToNull(event.callee_city_name),
calleeProvinceName: emptyToNull(event.callee_province_name),
calleeOperator: numberCarrier(event.callee_operator),
calleeNumberType: phoneNumberType(event.callee_number_type),
vendorId: nullableId(event.vendor_id),
vendorGatewayId: nullableId(event.vendor_gateway_id),
lineGroupId: nullableId(event.line_group_id),
@@ -253,22 +238,6 @@ class PrismaCdrRatingTransaction implements CdrRatingTransaction {
});
}
async createCustomerCharge(input: CreateCustomerChargeInput): Promise<void> {
await this.tx.customerRecharge.create({
data: {
id: input.id,
customerId: input.customerId,
amount: input.amount,
beforeBalance: input.beforeBalance,
afterBalance: input.afterBalance,
idempotencyKey: `cdr:${input.eventId}`,
remark: `CDR_CHARGE:${input.rawCdrId}`,
status: 'SUCCEEDED',
createdBy: 'worker-cdr'
}
});
}
async updateCustomerBalance(customerId: string, balance: Prisma.Decimal): Promise<void> {
await this.tx.customer.update({
where: { id: customerId },
@@ -326,7 +295,7 @@ function parseOptionalInt(value: string): number | null {
return null;
}
const parsed = Number.parseInt(value, 10);
return Number.isInteger(parsed) ? parsed : null;
return Number.isInteger(parsed) && parsed >= -2147483648 && parsed <= 2147483647 ? parsed : null;
}
function parseOptionalCdrDate(value: string): Date | null {
@@ -361,6 +330,11 @@ function eventToJson(event: ParsedCdrStreamEvent): Prisma.InputJsonValue {
source_ip: event.source_ip,
caller: event.caller,
callee: event.callee,
callee_city_code: event.callee_city_code,
callee_city_name: event.callee_city_name,
callee_province_name: event.callee_province_name,
callee_operator: event.callee_operator,
callee_number_type: event.callee_number_type,
customer_id: event.customer_id,
customer_gateway_id: event.customer_gateway_id,
customer_gateway_policy_id: event.customer_gateway_policy_id,
@@ -378,3 +352,11 @@ function eventToJson(event: ParsedCdrStreamEvent): Prisma.InputJsonValue {
created_at: event.created_at
};
}
function numberCarrier(value: string): 'MOBILE' | 'UNICOM' | 'TELECOM' | 'BROADCAST' | 'MVNO' | 'UNKNOWN' {
return value === 'MOBILE' || value === 'UNICOM' || value === 'TELECOM' || value === 'BROADCAST' || value === 'MVNO' ? value : 'UNKNOWN';
}
function phoneNumberType(value: string): 'MOBILE' | 'LANDLINE' | 'INTERNATIONAL' | 'UNKNOWN' {
return value === 'MOBILE' || value === 'LANDLINE' || value === 'INTERNATIONAL' ? value : 'UNKNOWN';
}