fix: 澄清未知成本前利润语义

This commit is contained in:
Codex
2026-07-14 18:04:56 +02:00
parent f29c5bf4bf
commit 12b1350960
3 changed files with 15 additions and 15 deletions
@@ -101,11 +101,11 @@ bun scripts/cli.ts platform-infra sub2api codex-pool cleanup-probes --target D60
- `profit` 只读聚合 Sub2API 原生 `/api/v1/admin/usage`
- 默认窗口、盈利分组、自用分组、排除分组、人民币售价和账号采购成本来源由 `config/platform-infra/sub2api-codex-pool.yaml#profit` 声明;
- 原生 `actual_cost` 是标准 API 美元计费基数;`saleRateCnyPerApiUsd` 和账号名称末尾成本都表示每 1 美元标准 API 计费对应的人民币单价,不做汇率换算;
- 默认 Kubernetes 风格输出优先给出总收入、已确认成本、已确认利润、未知成本对应的 API USD 基数、完整性和参数化利润公式;
- 默认 Kubernetes 风格输出优先给出总收入、已确认成本、扣除已知成本后的待定利润基数、未知成本对应的 API USD 基数、完整性和参数化利润公式;
- 显式 `--json` 返回同一份稳定紧凑汇总,不携带完整账号数组,避免大窗口触发全局输出保护;
- `--accounts` 固定返回一页完整账号名称、账号 ID、请求数、Token、标准 API 美元计费基数、人民币采购单价、人民币收入、人民币上游成本和人民币利润贡献;存在后续页时返回由分类、分组、账号 ID 和名称生成的 opaque `--page-token` 命令,不提供手工 `limit`
- `--missing-costs` 单独返回缺失成本账号的完整名称、账号 ID、分组、分类和未知成本 API USD 基数,避免与账号分页叠加后再次触发全局输出保护;
- 汇总同时给出“不扣自用成本”和“扣除自用成本”两种口径,自用分组不计收入;缺失成本时已确认利润只表示待扣未知成本前的已知部分,最终利润必须按输出公式继续扣除未知账号成本,禁止把未知成本当作 0;
- 汇总同时给出“不扣自用成本”和“扣除自用成本”两种口径,自用分组不计收入;缺失成本时 `profitBeforeUnknownCostsCny` 只表示扣除已知成本后的待定利润基数,最终利润必须按输出公式继续扣除未知账号成本,禁止把未知成本当作 0;完整状态下该基数才等于最终利润;
- 账号名称末尾缺少采购成本系数,或存在未分类流量时,相关口径必须显示 `partial`,禁止把缺失值当作零;
- `--json` 保留精确十进制字符串、原生来源、时间窗、分页数和完整性明细;命令不修改 runtime。
@@ -120,7 +120,7 @@ function renderProfit(response: Record<string, unknown>): RenderedCliResult {
const summary = record(report.summary);
const completeness = record(report.completeness);
const confirmedCosts = record(summary.confirmedCostsCny);
const confirmedProfit = record(summary.confirmedProfitCny);
const profitBeforeUnknownCosts = record(summary.profitBeforeUnknownCostsCny);
const unknownBasis = record(summary.unknownCostBasisApiUsd);
const formulas = record(summary.profitFormula);
const disclosure = record(response.disclosure);
@@ -132,10 +132,10 @@ function renderProfit(response: Record<string, unknown>): RenderedCliResult {
"SUMMARY",
];
lines.push(table(
["SCOPE", "STATUS", "REVENUE_CNY", "PROFIT_POOL_COST_CNY", "SELF_USE_COST_CNY", "CONFIRMED_PROFIT_CNY", "UNKNOWN_API_USD"],
["SCOPE", "STATUS", "REVENUE_CNY", "PROFIT_POOL_COST_CNY", "SELF_USE_COST_CNY", "PROFIT_BEFORE_UNKNOWN_COSTS_CNY", "UNKNOWN_API_USD"],
[
["不扣自用", text(record(summary.withoutSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), "-", cny(confirmedProfit.withoutSelfUse), apiUsd(unknownBasis.profitPool)],
["扣除自用", text(record(summary.withSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), cny(confirmedCosts.selfUse), cny(confirmedProfit.withSelfUse), apiUsd(unknownBasis.total)],
["不扣自用", text(record(summary.withoutSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), "-", cny(profitBeforeUnknownCosts.withoutSelfUse), apiUsd(unknownBasis.profitPool)],
["扣除自用", text(record(summary.withSelfUse).status), cny(summary.revenueCny), cny(confirmedCosts.profitPool), cny(confirmedCosts.selfUse), cny(profitBeforeUnknownCosts.withSelfUse), apiUsd(unknownBasis.total)],
],
));
lines.push("", "FORMULA");
@@ -180,8 +180,8 @@ def run_profit_report():
self_cost = sum((profit_decimal(row["upstreamCostCny"]) for row in self_accounts if row["upstreamCostCny"] is not None), profit_decimal(0))
unknown_profit_api_usd = sum((profit_decimal(row["apiAmountUsd"]) for row in missing_profit), profit_decimal(0))
unknown_self_api_usd = sum((profit_decimal(row["apiAmountUsd"]) for row in missing_self), profit_decimal(0))
confirmed_without_self = revenue - profit_cost
confirmed_with_self = confirmed_without_self - self_cost
profit_before_unknown_without_self = revenue - profit_cost
profit_before_unknown_with_self = profit_before_unknown_without_self - self_cost
without_complete = not missing_profit and not unclassified
with_complete = without_complete and not missing_self
account_rows.sort(key=lambda row: (row["class"], row["groupName"], str(row["accountId"]), str(row["accountName"])))
@@ -201,9 +201,9 @@ def run_profit_report():
"selfUse": profit_decimal_text(self_cost),
"total": profit_decimal_text(profit_cost + self_cost),
},
"confirmedProfitCny": {
"withoutSelfUse": profit_decimal_text(confirmed_without_self),
"withSelfUse": profit_decimal_text(confirmed_with_self),
"profitBeforeUnknownCostsCny": {
"withoutSelfUse": profit_decimal_text(profit_before_unknown_without_self),
"withSelfUse": profit_decimal_text(profit_before_unknown_with_self),
},
"unknownCostBasisApiUsd": {
"profitPool": profit_decimal_text(unknown_profit_api_usd),
@@ -211,11 +211,11 @@ def run_profit_report():
"total": profit_decimal_text(unknown_profit_api_usd + unknown_self_api_usd),
},
"profitFormula": {
"withoutSelfUse": "confirmedProfitCny.withoutSelfUse - sum(missing profit account apiAmountUsd * unknown costRateCnyPerApiUsd)",
"withSelfUse": "confirmedProfitCny.withSelfUse - sum(all missing account apiAmountUsd * unknown costRateCnyPerApiUsd)",
"withoutSelfUse": "profitBeforeUnknownCostsCny.withoutSelfUse - sum(missing profit account apiAmountUsd * unknown costRateCnyPerApiUsd)",
"withSelfUse": "profitBeforeUnknownCostsCny.withSelfUse - sum(all missing account apiAmountUsd * unknown costRateCnyPerApiUsd)",
},
"withoutSelfUse": {"status": "complete" if without_complete else "partial", "profitCny": profit_decimal_text(confirmed_without_self) if without_complete else None},
"withSelfUse": {"status": "complete" if with_complete else "partial", "profitCny": profit_decimal_text(confirmed_with_self) if with_complete else None},
"withoutSelfUse": {"status": "complete" if without_complete else "partial", "profitCny": profit_decimal_text(profit_before_unknown_without_self) if without_complete else None},
"withSelfUse": {"status": "complete" if with_complete else "partial", "profitCny": profit_decimal_text(profit_before_unknown_with_self) if with_complete else None},
},
"completeness": {
"missingCostAccounts": [{"class": row["class"], "groupName": row["groupName"], "accountId": row["accountId"], "accountName": row["accountName"], "apiAmountUsd": row["apiAmountUsd"]} for row in missing_profit + missing_self],