fix: use user log stat endpoint for finance upstream costs
This commit is contained in:
@@ -166,8 +166,19 @@ def fetch_upstream_cost_sub2api(upstream: Upstream, target_date: date) -> tuple[
|
|||||||
# Upstream cost — New-API / Nox-API
|
# Upstream cost — New-API / Nox-API
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _new_api_failure_message(data: Any) -> str | None:
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
return None
|
||||||
|
message = data.get("message") or data.get("detail") or data.get("error")
|
||||||
|
if data.get("success") is False:
|
||||||
|
return str(message or "上游返回 success=false")
|
||||||
|
if message and data.get("error"):
|
||||||
|
return str(message)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def fetch_upstream_cost_new_api(upstream: Upstream, target_date: date) -> tuple[float, str | None]:
|
def fetch_upstream_cost_new_api(upstream: Upstream, target_date: date) -> tuple[float, str | None]:
|
||||||
"""Fetch cost for New-API/Nox-API upstream via /api/log/stat.
|
"""Fetch cost for New-API/Nox-API upstream via /api/log/self/stat.
|
||||||
|
|
||||||
Keeps UpstreamClient open for the full request so that:
|
Keeps UpstreamClient open for the full request so that:
|
||||||
- login_password token refresh is available if the stat call gets 401
|
- login_password token refresh is available if the stat call gets 401
|
||||||
@@ -193,9 +204,12 @@ def fetch_upstream_cost_new_api(upstream: Upstream, target_date: date) -> tuple[
|
|||||||
client.ensure_authenticated()
|
client.ensure_authenticated()
|
||||||
quota_per_unit = client._new_api_quota_per_unit()
|
quota_per_unit = client._new_api_quota_per_unit()
|
||||||
params = {"type": 2, "start_timestamp": start_ts, "end_timestamp": end_ts}
|
params = {"type": 2, "start_timestamp": start_ts, "end_timestamp": end_ts}
|
||||||
resp = client._send_request("GET", client._url("/api/log/stat"), params=params)
|
resp = client._send_request("GET", client._url("/api/log/self/stat"), params=params)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
|
failure_message = _new_api_failure_message(data)
|
||||||
|
if failure_message:
|
||||||
|
return 0.0, failure_message
|
||||||
payload = data.get("data", data) if isinstance(data, dict) else {}
|
payload = data.get("data", data) if isinstance(data, dict) else {}
|
||||||
if isinstance(payload, dict):
|
if isinstance(payload, dict):
|
||||||
quota = payload.get("quota")
|
quota = payload.get("quota")
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ def test_upstream_cost_new_api_success(monkeypatch):
|
|||||||
assert err is None
|
assert err is None
|
||||||
assert abs(amount - 10.0) < 1e-6 # 5_000_000 / 500_000 = 10.0
|
assert abs(amount - 10.0) < 1e-6 # 5_000_000 / 500_000 = 10.0
|
||||||
assert mock_client_instance.calls[0]["method"] == "GET"
|
assert mock_client_instance.calls[0]["method"] == "GET"
|
||||||
assert mock_client_instance.calls[0]["url"] == "http://up.test/api/log/stat"
|
assert mock_client_instance.calls[0]["url"] == "http://up.test/api/log/self/stat"
|
||||||
assert mock_client_instance.calls[0]["params"] == {
|
assert mock_client_instance.calls[0]["params"] == {
|
||||||
"type": 2,
|
"type": 2,
|
||||||
"start_timestamp": 1782921600,
|
"start_timestamp": 1782921600,
|
||||||
@@ -290,6 +290,33 @@ def test_upstream_cost_new_api_success(monkeypatch):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_upstream_cost_new_api_zero_quota_success(monkeypatch):
|
||||||
|
u = FakeUpstream(api_prefix="", auth_type="new_api_token")
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.services.finance_service.UpstreamClient",
|
||||||
|
lambda **kw: FakeUpstreamClientCtx(response={"data": {"quota": 0}}),
|
||||||
|
)
|
||||||
|
|
||||||
|
amount, err = fetch_upstream_cost_new_api(u, date(2026, 7, 2))
|
||||||
|
|
||||||
|
assert err is None
|
||||||
|
assert amount == 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_upstream_cost_new_api_success_false_uses_remote_message(monkeypatch):
|
||||||
|
u = FakeUpstream(api_prefix="", auth_type="new_api_token")
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"app.services.finance_service.UpstreamClient",
|
||||||
|
lambda **kw: FakeUpstreamClientCtx(response={"success": False, "message": "无权访问日志统计"}),
|
||||||
|
)
|
||||||
|
|
||||||
|
amount, err = fetch_upstream_cost_new_api(u, date(2026, 7, 2))
|
||||||
|
|
||||||
|
assert amount == 0.0
|
||||||
|
assert err == "无权访问日志统计"
|
||||||
|
assert "quota" not in err
|
||||||
|
|
||||||
|
|
||||||
def test_upstream_cost_new_api_missing_quota(monkeypatch):
|
def test_upstream_cost_new_api_missing_quota(monkeypatch):
|
||||||
u = FakeUpstream(api_prefix="", auth_type="new_api_token")
|
u = FakeUpstream(api_prefix="", auth_type="new_api_token")
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
|||||||
Reference in New Issue
Block a user