core:DTO/错误信封/时间口径/微分折算/dedupe_key/加密与 PBKDF2/契约 zod/logtape/上游客户端。 db:八表 Drizzle schema + 仓储 + drizzle 配置。
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
/**
|
||
* 用量聚合辅助(公共约定 §5:后端换算、微分取整、聚合先求和再一次取整)
|
||
*/
|
||
import {
|
||
microSumToUsdStr,
|
||
qpuAt,
|
||
quotaToMicroUsd,
|
||
quotaToUsdStr,
|
||
} from "./money.ts";
|
||
|
||
/** 上游事实:QuotaPerUnit 默认 500000(qpu_history 未采样时的回退) */
|
||
export const DEFAULT_QPU = 500_000;
|
||
|
||
export type QpuPoint = { qpu: number; effective_from: Date | number };
|
||
|
||
export function resolveQpuTimeline(
|
||
rows: readonly QpuPoint[],
|
||
): QpuPoint[] {
|
||
if (rows.length > 0) return [...rows];
|
||
return [{ qpu: DEFAULT_QPU, effective_from: 0 }];
|
||
}
|
||
|
||
export interface UsageSum {
|
||
count: number;
|
||
quota: number;
|
||
quota_usd: string;
|
||
}
|
||
|
||
export function emptyUsage(): UsageSum {
|
||
return { count: 0, quota: 0, quota_usd: "0.00" };
|
||
}
|
||
|
||
/** 逐行微分累加(历史正确性:按日志时刻取 QPU) */
|
||
export function sumUsage(
|
||
rows: ReadonlyArray<{ quota: number; created_at: Date | number }>,
|
||
timeline: readonly QpuPoint[],
|
||
): UsageSum {
|
||
let quota = 0;
|
||
let micro = 0;
|
||
for (const r of rows) {
|
||
quota += r.quota;
|
||
const qpu = qpuAt(r.created_at, timeline);
|
||
micro += quotaToMicroUsd(r.quota, qpu);
|
||
}
|
||
return { count: rows.length, quota, quota_usd: microSumToUsdStr(micro) };
|
||
}
|
||
|
||
export function usageAt(
|
||
quota: number,
|
||
created_at: Date | number,
|
||
timeline: readonly QpuPoint[],
|
||
): UsageSum {
|
||
const qpu = qpuAt(created_at, timeline);
|
||
return { count: 1, quota, quota_usd: quotaToUsdStr(quota, qpu) };
|
||
}
|
||
|
||
export function addUsage(a: UsageSum, b: UsageSum): UsageSum {
|
||
// 合并已成形的两段时,quota_usd 需重算——调用方应尽量走 sumUsage
|
||
return {
|
||
count: a.count + b.count,
|
||
quota: a.quota + b.quota,
|
||
quota_usd: a.quota_usd,
|
||
};
|
||
}
|