feat: freeze app display name in billing mirror (migration 037)#49
Merged
Conversation
A deleted app's bill rows rendered as "unknown app": billing-engine deliberately held no app names, so the frontend resolved them from the live app registry by app_id — which loses deleted apps. Freeze the name in the mirror so the bill is self-contained (the same posture as created_module_count / app_base_snapshots). - Migration 037: ms_billing.apps.name (nullable), NEVER cleared on delete. - RegisterApp stamps the name on first registration (frozen across retries via ON CONFLICT DO NOTHING); SyncAppModules updates it while the app is live and freezes it once deleted (SetAppName, WHERE deleted_at IS NULL — same gate as SetAppModuleCount). - The account bill (AccountAppBill) and single-app bill (GetAppBillResponse) now carry `name` + a server-authoritative `is_deleted` flag. computeAppBill reads the mirror UNCONDITIONALLY (hoisted out of the un-snapshotted branch) so name/deleted show on charged periods too — the frontend uses is_deleted to show a deleted app's charges in a dialog instead of linking to the gone app page. - Also wire migrations 029-037 into scripts/init-db.sql (it was stuck at 028 — a fresh `make db-init` / CI / reset DB was missing the entire base-fee v2 schema, which 503'd every billing route). Additive, back-compat: name stays NULL until api-platform sends it. Unit + integration suites green (migrations 001->037, real Postgres). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A deleted app's bill rows render as "未知的 App" (unknown app): billing-engine deliberately stored no app names, so the frontend resolved display names from the live app registry by
app_id— which loses deleted apps. This freezes the name in the billing mirror so the bill is self-contained (the same "freeze at write time" pattern billing already uses forcreated_module_countandapp_base_snapshots).What (billing-engine — the foundation; api-platform + frontend follow)
ms_billing.apps.name(nullable) — stamped by RegisterApp, updated by SyncAppModules while live, never cleared on delete.RegisterApp.Name(frozen on first registration, immutable across retries) +SyncAppModules.Name *string(rename while live; no-op once deleted viaSetAppName … WHERE deleted_at IS NULL, same gate asSetAppModuleCount).AccountAppBillandGetAppBillResponsegainname+ a server-authoritativeis_deletedflag.computeAppBillnow reads the mirror unconditionally (hoisted out of the un-snapshotted branch) so name/deleted appear on charged periods too — the frontend readsis_deletedto open a charge-breakdown dialog for a deleted app instead of linking to the gone app page.init-db.sql: wired migrations 029–037 (it was stuck at 028 — a freshmake db-init/ CI / reset DB was missing the entire base-fee-v2 schema and 503'd every billing route; this closes that latent bug too).Additive + back-compat:
namestays NULL until api-platform sends it; an older api-platform is unaffected. Land this first, then api-platform, then frontend.Verification
go build,go vet(incl.-tags integration),gofmtclean; unit + integration suites green — migrations 001→037 applied against real Postgres. New tests: name freeze lifecycle (register → live rename → freeze-on-delete) and the hoist guard (name + is_deleted on a charged/snapshotted period).Follow-ons (this PR is step 1 of 3)
RegisterApp/SyncAppModulesmirror-sync calls (sourced from the app record it already holds).BillSummaryCard.tsx: prefer the frozenname; branch the row onis_deleted— live app links as today, deleted app opens a web-ui-kitDialogre-presenting the charge breakdown already in theAccountAppBillrow (no new fetch).apps.nameentry (schema-docs convention).🤖 Generated with Claude Code