refactor: move capacity_forecast_service from billing to monitoring
Some checks failed
Some checks failed
Resolves the billing (core) → monitoring (optional) architecture violation by moving CapacityForecastService to the monitoring module where it belongs. - Create BillingMetricsProvider to expose subscription counts via stats_aggregator - Move CapacitySnapshot model from billing to monitoring - Replace direct MerchantSubscription queries with stats_aggregator calls - Fix middleware test mocks to cover StoreDomain/MerchantDomain fallback chains Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
115
app/modules/billing/services/billing_metrics.py
Normal file
115
app/modules/billing/services/billing_metrics.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# app/modules/billing/services/billing_metrics.py
|
||||
"""
|
||||
Metrics provider for the billing module.
|
||||
|
||||
Provides metrics for:
|
||||
- Subscription counts (total, active, trial)
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.contracts.metrics import (
|
||||
MetricsContext,
|
||||
MetricValue,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BillingMetricsProvider:
|
||||
"""
|
||||
Metrics provider for billing module.
|
||||
|
||||
Provides subscription metrics at the platform level.
|
||||
"""
|
||||
|
||||
@property
|
||||
def metrics_category(self) -> str:
|
||||
return "billing"
|
||||
|
||||
def get_store_metrics(
|
||||
self,
|
||||
db: Session,
|
||||
store_id: int,
|
||||
context: MetricsContext | None = None,
|
||||
) -> list[MetricValue]:
|
||||
"""
|
||||
Get metrics for a specific store.
|
||||
|
||||
Subscriptions are merchant-level, not store-level, so no store metrics.
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_platform_metrics(
|
||||
self,
|
||||
db: Session,
|
||||
platform_id: int,
|
||||
context: MetricsContext | None = None,
|
||||
) -> list[MetricValue]:
|
||||
"""
|
||||
Get subscription metrics aggregated for a platform.
|
||||
|
||||
Provides:
|
||||
- Total subscriptions
|
||||
- Active subscriptions (active + trial)
|
||||
- Trial subscriptions
|
||||
"""
|
||||
from app.modules.billing.models import MerchantSubscription, SubscriptionStatus
|
||||
|
||||
try:
|
||||
total_subs = (
|
||||
db.query(func.count(MerchantSubscription.id)).scalar() or 0
|
||||
)
|
||||
|
||||
active_subs = (
|
||||
db.query(func.count(MerchantSubscription.id))
|
||||
.filter(MerchantSubscription.status.in_(["active", "trial"]))
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
|
||||
trial_subs = (
|
||||
db.query(func.count(MerchantSubscription.id))
|
||||
.filter(MerchantSubscription.status == SubscriptionStatus.TRIAL.value)
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
|
||||
return [
|
||||
MetricValue(
|
||||
key="billing.total_subscriptions",
|
||||
value=total_subs,
|
||||
label="Total Subscriptions",
|
||||
category="billing",
|
||||
icon="credit-card",
|
||||
description="Total number of merchant subscriptions",
|
||||
),
|
||||
MetricValue(
|
||||
key="billing.active_subscriptions",
|
||||
value=active_subs,
|
||||
label="Active Subscriptions",
|
||||
category="billing",
|
||||
icon="check-circle",
|
||||
description="Subscriptions with active or trial status",
|
||||
),
|
||||
MetricValue(
|
||||
key="billing.trial_subscriptions",
|
||||
value=trial_subs,
|
||||
label="Trial Subscriptions",
|
||||
category="billing",
|
||||
icon="clock",
|
||||
description="Subscriptions currently in trial period",
|
||||
),
|
||||
]
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to get billing platform metrics: {e}")
|
||||
return []
|
||||
|
||||
|
||||
# Singleton instance
|
||||
billing_metrics_provider = BillingMetricsProvider()
|
||||
|
||||
__all__ = ["BillingMetricsProvider", "billing_metrics_provider"]
|
||||
Reference in New Issue
Block a user