173 lines
8.7 KiB
TypeScript
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;
|
|
}
|