Skip to content

fix(ops): derive the month flow counters from type, not from a sign no writer produces - #191

Merged
argszero merged 1 commit into
mainfrom
fix/ops-month-flow-by-type
Sep 13, 2026
Merged

argszero merged 1 commit into
mainfrom
fix/ops-month-flow-by-type

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

The operator dashboard's 「点数流入 / 点数流出」 pair (GET /api/ops/runtime) was computed with a sign filter — WHERE pts > 0 for inflow and WHERE pts < 0 for outflow — over a ledger that is type-keyed: every production writer stores a positive pts (billing.rs consume/earn, gift.rs, raise.rs, admin.rs, ops.rs), and the direction lives in the type column.

Consequences:

  • month_out matched no rows at all ⇒ the 「点数流出」 card rendered -0 permanently.
  • month_in summed every row, crediting consumption as inflow.

Runtime evidence through the real writers (POST /api/ops/credits + billing::settle, rows topup 100 / consume 7 / earn 6.3): the endpoint returned month_in = 113.3, month_out = 0.0 before this fix — the correct pair is 106.3 / 7.0. Injecting one negative-pts row made month_out become 2.0, proving the query was alive and that no writer emits a negative pts.

Related Issue

No linked issue — none exists for this defect. Recorded empty on purpose (verified via GraphQL closingIssuesReferences).

Changes

  • src/routes/ops.rs: month_in now filters type IN ('earn', 'topup', 'gift'); month_out now filters type IN ('consume', 'withdraw'). This mirrors the convention already used by wallet.rs's 7-day net series (CASE WHEN type IN ('earn','topup','gift') THEN pts ELSE -pts END) and the ops .mock sub-labels (收益 / 充值 / 赠送 vs 消费 / 提现).
  • ABS() dropped on the out side: no writer stores a negative magnitude, so SUM(pts) is correct.
  • No other production behaviour changes.

Tests

  • cargo test — 180 passed (baseline 179, +1)
  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • New test ops_runtime_month_flow_is_type_based drives the real writers (POST /api/ops/credits + billing::settle) and asserts month_in == 100 + round5(7 * 0.9) and month_out == 7.0 (expectations derived from the fixture + production constants, not hard-coded), plus a positive control: a month containing only a consume row ⇒ inflow must be 0.0.

Checklist

  • Branch naming follows the convention (fix/…)
  • Commit message uses Conventional Commits (fix(ops): …)
  • Single responsibility, minimal change

Severity: display-only. The ops runtime pane is reachable only with role = ops (the host) and no money path reads month_in / month_out — this is a permanently-wrong dashboard metric, not a billing bug.

A/B evidence. Reverting only the two WHERE clauses to the sign form turns exactly the new test red (observed month_in = 113.3, month_out = 0.0) while the other 179 stay green.

@argszero
argszero merged commit 562c8f6 into main Sep 13, 2026
1 check passed
@argszero
argszero deleted the fix/ops-month-flow-by-type branch September 13, 2026 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant