bindbox-game/.planning/ROADMAP.md
2026-03-21 16:37:24 +08:00

3.6 KiB
Raw Blame History

Roadmap: Bindbox Game 盈亏统计函数

Overview

Two reusable service-layer functions — QueryUserProfitLoss and QueryActivityProfitLoss — are built in a new internal/service/finance/ package. Phase 1 delivers correct, working functions covering the full P&L computation path. Phase 2 populates the per-asset-type breakdown slice, including Fragment synthesis cost. The result is a clean, testable service that callers can invoke without touching HTTP handler logic.

Phases

Phase Numbering:

  • Integer phases (1, 2, 3): Planned milestone work
  • Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED)

Decimal phases appear between their surrounding integers in numeric order.

  • Phase 1: Core P&L Functions - Scaffold the finance package and deliver working QueryUserProfitLoss / QueryActivityProfitLoss with correct revenue, cost, and profit
  • Phase 2: Per-Asset-Type Breakdown - Populate the ProfitLossBreakdown slice for all 5 asset types, including Fragment synthesis cost from its own table

Phase Details

Phase 1: Core P&L Functions

Goal: Callers can invoke QueryUserProfitLoss and QueryActivityProfitLoss and receive a correct ProfitLossResult with total revenue, cost, profit, and profit rate — with all edge cases handled Depends on: Nothing (first phase) Requirements: PNL-01, PNL-02, PNL-03, PNL-04, PNL-05, PNL-06, PNL-07, PNL-08, DIM-01, DIM-02, DIM-03, DIM-04, RET-01, RET-03, AST-01, QUA-01, QUA-02, QUA-03, QUA-04, QUA-05 Success Criteria (what must be TRUE):

  1. Calling QueryUserProfitLoss with a list of user IDs returns a ProfitLossResult where Revenue equals actual_amount + discount_amount for non-refunded orders only, and game-pass orders contribute draw_count × activity_price instead of cash revenue
  2. Calling QueryActivityProfitLoss with an activity ID returns a ProfitLossResult where Revenue is proportionally attributed per order (no double-counting when one order covers multiple activities)
  3. Both functions return an error (not silent zero) when any db.Scan() call fails
  4. Passing nil for StartTime/EndTime applies no time filter; passing nil/0 for AssetType returns aggregated totals across all asset types
  5. The package contains no call to GetDbW() — all queries route through the injected DbR handle; voided inventory (remark LIKE '%void%' or status=2) and refunded orders (status=3,4) are excluded from cost and revenue respectively Plans: TBD

Phase 2: Per-Asset-Type Breakdown

Goal: The ProfitLossBreakdown slice in every ProfitLossResult contains one entry per relevant asset type (Points, Coupon, ItemCard, Product, Fragment), with correct per-type cost including Fragment synthesis cost sourced from fragment_synthesis_logs Depends on: Phase 1 Requirements: AST-02, AST-03, RET-02 Success Criteria (what must be TRUE):

  1. A ProfitLossResult for an activity that awarded Points, Coupons, and Items contains exactly one ProfitLossBreakdown entry per asset type that had non-zero cost, with the correct cost value for each
  2. Fragment cost in the breakdown is sourced from fragment_synthesis_logs using the verified join path (not from user_inventory), and matches manual spot-checks against the database
  3. Summing all breakdown entries' Cost fields equals the total Cost field in the parent ProfitLossResult (no gaps or double-counting between breakdown and totals) Plans: TBD

Progress

Execution Order: Phases execute in numeric order: 1 → 2

Phase Plans Complete Status Completed
1. Core P&L Functions 0/TBD Not started -
2. Per-Asset-Type Breakdown 0/TBD Not started -