Files
NianAIGC/lib/client/billing-api.ts

173 lines
8.7 KiB
TypeScript

type PayloadRecord = Record<string, unknown>;
function invalid(context: string, path: string, expected: string): never {
const separator = path === "响应" ? "" : " ";
throw new Error(`${context}数据格式错误:${path}${separator}必须是${expected}`);
}
function requireRecord(value: unknown, path: string, context: string): PayloadRecord {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
invalid(context, path, "对象");
}
return value as PayloadRecord;
}
function requireArray(record: PayloadRecord, field: string, context: string, path = field): unknown[] {
if (!Array.isArray(record[field])) invalid(context, path, "数组");
return record[field];
}
function requireString(record: PayloadRecord, field: string, context: string, path: string): void {
if (typeof record[field] !== "string") invalid(context, path, "字符串");
}
function requireOptionalString(record: PayloadRecord, field: string, context: string, path: string): void {
if (record[field] !== undefined && record[field] !== null && typeof record[field] !== "string") {
invalid(context, path, "字符串");
}
}
function requireNumber(record: PayloadRecord, field: string, context: string, path: string): void {
if (typeof record[field] !== "number" || !Number.isFinite(record[field])) {
invalid(context, path, "有限数字");
}
}
function requireBoolean(record: PayloadRecord, field: string, context: string, path: string): void {
if (typeof record[field] !== "boolean") invalid(context, path, "布尔值");
}
function requireScalar(value: unknown, context: string, path: string): void {
if (!(["string", "number", "boolean"] as string[]).includes(typeof value)) {
invalid(context, path, "字符串、数字或布尔值");
}
if (typeof value === "number" && !Number.isFinite(value)) invalid(context, path, "有限数字");
}
function validateBillingAccount(value: unknown, context: string, path: string): void {
const account = requireRecord(value, path, context);
for (const field of ["accountName", "bankName", "accountNumber", "contact"]) {
requireOptionalString(account, field, context, `${path}.${field}`);
}
}
function validateWallet(value: unknown, context: string, path: string): void {
const wallet = requireRecord(value, path, context);
requireString(wallet, "organizationId", context, `${path}.organizationId`);
for (const field of ["balanceFen", "totalRechargedFen", "totalChargedFen"]) {
requireNumber(wallet, field, context, `${path}.${field}`);
}
requireString(wallet, "updatedAt", context, `${path}.updatedAt`);
}
function validateSummary(value: unknown, context: string, path: string): void {
const summary = requireRecord(value, path, context);
for (const field of ["rechargeFen", "chargedFen", "refundedFen", "netConsumedFen"]) {
requireNumber(summary, field, context, `${path}.${field}`);
}
}
function validateLedgerEntry(value: unknown, context: string, path: string): void {
const entry = requireRecord(value, path, context);
requireString(entry, "id", context, `${path}.id`);
requireString(entry, "organizationId", context, `${path}.organizationId`);
requireOptionalString(entry, "accountId", context, `${path}.accountId`);
requireOptionalString(entry, "jobId", context, `${path}.jobId`);
requireString(entry, "kind", context, `${path}.kind`);
requireNumber(entry, "deltaFen", context, `${path}.deltaFen`);
requireNumber(entry, "balanceAfterFen", context, `${path}.balanceAfterFen`);
requireString(entry, "description", context, `${path}.description`);
requireRecord(entry.metadata, `${path}.metadata`, context);
requireString(entry, "createdAt", context, `${path}.createdAt`);
if (Number.isNaN(Date.parse(entry.createdAt as string))) invalid(context, `${path}.createdAt`, "有效时间字符串");
}
function validateOrganization(value: unknown, context: string, path: string): void {
const organization = requireRecord(value, path, context);
requireString(organization, "id", context, `${path}.id`);
requireString(organization, "name", context, `${path}.name`);
}
function validateAdminOrganization(value: unknown, context: string, path: string): void {
const organization = requireRecord(value, path, context);
requireString(organization, "id", context, `${path}.id`);
requireString(organization, "name", context, `${path}.name`);
requireString(organization, "status", context, `${path}.status`);
validateWallet(organization.wallet, context, `${path}.wallet`);
}
function validateAdminMember(value: unknown, context: string, path: string): void {
const member = requireRecord(value, path, context);
for (const field of ["id", "displayName", "phone", "role", "status"]) {
requireString(member, field, context, `${path}.${field}`);
}
requireOptionalString(member, "organizationId", context, `${path}.organizationId`);
}
function validatePriceRule(value: unknown, context: string, path: string): void {
const rule = requireRecord(value, path, context);
for (const field of ["id", "provider", "capability", "unit"]) {
requireString(rule, field, context, `${path}.${field}`);
}
requireOptionalString(rule, "reqKey", context, `${path}.reqKey`);
requireOptionalString(rule, "variantKey", context, `${path}.variantKey`);
requireOptionalString(rule, "note", context, `${path}.note`);
requireNumber(rule, "standardUnitPriceFen", context, `${path}.standardUnitPriceFen`);
requireNumber(rule, "markupMultiplier", context, `${path}.markupMultiplier`);
requireBoolean(rule, "enabled", context, `${path}.enabled`);
if (rule.conditions !== undefined && rule.conditions !== null) {
requireRecord(rule.conditions, `${path}.conditions`, context);
}
if (rule.source !== undefined && rule.source !== null) {
const source = requireRecord(rule.source, `${path}.source`, context);
requireOptionalString(source, "url", context, `${path}.source.url`);
}
if (rule.parameterDimensions !== undefined && rule.parameterDimensions !== null) {
if (!Array.isArray(rule.parameterDimensions)) invalid(context, `${path}.parameterDimensions`, "数组");
rule.parameterDimensions.forEach((dimension, dimensionIndex) => {
const dimensionPath = `${path}.parameterDimensions[${dimensionIndex}]`;
const item = requireRecord(dimension, dimensionPath, context);
requireString(item, "key", context, `${dimensionPath}.key`);
requireOptionalString(item, "label", context, `${dimensionPath}.label`);
requireScalar(item.baselineValue, context, `${dimensionPath}.baselineValue`);
if (item.defaultValue !== undefined && item.defaultValue !== null) {
requireScalar(item.defaultValue, context, `${dimensionPath}.defaultValue`);
}
requireArray(item, "tiers", context, `${dimensionPath}.tiers`).forEach((tier, tierIndex) => {
const tierPath = `${dimensionPath}.tiers[${tierIndex}]`;
const tierItem = requireRecord(tier, tierPath, context);
requireScalar(tierItem.value, context, `${tierPath}.value`);
requireOptionalString(tierItem, "label", context, `${tierPath}.label`);
requireNumber(tierItem, "standardFactor", context, `${tierPath}.standardFactor`);
requireNumber(tierItem, "markupMultiplier", context, `${tierPath}.markupMultiplier`);
requireBoolean(tierItem, "enabled", context, `${tierPath}.enabled`);
requireOptionalString(tierItem, "note", context, `${tierPath}.note`);
});
});
}
}
export function parseAdminBillingPayload<T>(payload: T): T {
const context = "超管计费";
const record = requireRecord(payload, "响应", context);
validateBillingAccount(record.billingAccount, context, "billingAccount");
requireArray(record, "organizations", context).forEach((item, index) => validateAdminOrganization(item, context, `organizations[${index}]`));
requireArray(record, "members", context).forEach((item, index) => validateAdminMember(item, context, `members[${index}]`));
requireArray(record, "ledger", context).forEach((item, index) => validateLedgerEntry(item, context, `ledger[${index}]`));
requireArray(record, "priceRules", context).forEach((item, index) => validatePriceRule(item, context, `priceRules[${index}]`));
return payload;
}
export function parseMemberBillingPayload<T>(payload: T): T {
const context = "成员计费";
const record = requireRecord(payload, "响应", context);
validateOrganization(record.organization, context, "organization");
validateBillingAccount(record.billingAccount, context, "billingAccount");
validateWallet(record.wallet, context, "wallet");
requireArray(record, "ledger", context).forEach((item, index) => validateLedgerEntry(item, context, `ledger[${index}]`));
validateSummary(record.summary, context, "summary");
validateSummary(record.personal, context, "personal");
return payload;
}