refactor: fix architecture violations with provider patterns and dependency inversion
Major changes: - Add AuditProvider protocol for cross-module audit logging - Move customer order operations to orders module (dependency inversion) - Add customer order metrics via MetricsProvider pattern - Fix missing db parameter in get_admin_context() calls - Move ProductMedia relationship to catalog module (proper ownership) - Add marketplace breakdown stats to marketplace_widgets New files: - contracts/audit.py - AuditProviderProtocol - core/services/audit_aggregator.py - Aggregates audit providers - monitoring/services/audit_provider.py - Monitoring audit implementation - orders/services/customer_order_service.py - Customer order operations - orders/routes/api/vendor_customer_orders.py - Customer order endpoints - catalog/services/product_media_service.py - Product media service - Architecture documentation for patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,11 @@ import logging
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.contracts.widgets import (
|
||||
BreakdownWidget,
|
||||
DashboardWidget,
|
||||
DashboardWidgetProviderProtocol,
|
||||
ListWidget,
|
||||
WidgetBreakdownItem,
|
||||
WidgetContext,
|
||||
WidgetListItem,
|
||||
)
|
||||
@@ -187,7 +189,7 @@ class MarketplaceWidgetProvider:
|
||||
.count()
|
||||
)
|
||||
|
||||
return [
|
||||
widgets = [
|
||||
DashboardWidget(
|
||||
key="marketplace.recent_imports",
|
||||
widget_type="list",
|
||||
@@ -204,6 +206,82 @@ class MarketplaceWidgetProvider:
|
||||
)
|
||||
]
|
||||
|
||||
# Add marketplace breakdown widget
|
||||
breakdown_widget = self._get_marketplace_breakdown_widget(db)
|
||||
if breakdown_widget:
|
||||
widgets.append(breakdown_widget)
|
||||
|
||||
return widgets
|
||||
|
||||
def _get_marketplace_breakdown_widget(
|
||||
self,
|
||||
db: Session,
|
||||
) -> DashboardWidget | None:
|
||||
"""
|
||||
Get a breakdown widget showing statistics per marketplace.
|
||||
|
||||
Returns:
|
||||
DashboardWidget with BreakdownWidget data, or None if no data
|
||||
"""
|
||||
from sqlalchemy import func
|
||||
|
||||
from app.modules.marketplace.models import MarketplaceProduct
|
||||
|
||||
try:
|
||||
marketplace_stats = (
|
||||
db.query(
|
||||
MarketplaceProduct.marketplace,
|
||||
func.count(MarketplaceProduct.id).label("total_products"),
|
||||
func.count(func.distinct(MarketplaceProduct.vendor_name)).label(
|
||||
"unique_vendors"
|
||||
),
|
||||
func.count(func.distinct(MarketplaceProduct.brand)).label(
|
||||
"unique_brands"
|
||||
),
|
||||
)
|
||||
.filter(MarketplaceProduct.marketplace.isnot(None))
|
||||
.group_by(MarketplaceProduct.marketplace)
|
||||
.all()
|
||||
)
|
||||
|
||||
if not marketplace_stats:
|
||||
return None
|
||||
|
||||
total_products = sum(stat.total_products for stat in marketplace_stats)
|
||||
|
||||
breakdown_items = [
|
||||
WidgetBreakdownItem(
|
||||
label=stat.marketplace or "Unknown",
|
||||
value=stat.total_products,
|
||||
secondary_value=stat.unique_vendors,
|
||||
percentage=(
|
||||
round(stat.total_products / total_products * 100, 1)
|
||||
if total_products > 0
|
||||
else 0
|
||||
),
|
||||
icon="globe",
|
||||
)
|
||||
for stat in marketplace_stats
|
||||
]
|
||||
|
||||
return DashboardWidget(
|
||||
key="marketplace.breakdown",
|
||||
widget_type="breakdown",
|
||||
title="Products by Marketplace",
|
||||
category="marketplace",
|
||||
data=BreakdownWidget(
|
||||
items=breakdown_items,
|
||||
total=total_products,
|
||||
),
|
||||
icon="chart-pie",
|
||||
description="Product distribution across marketplaces",
|
||||
order=30,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to get marketplace breakdown widget: {e}")
|
||||
return None
|
||||
|
||||
|
||||
# Singleton instance
|
||||
marketplace_widget_provider = MarketplaceWidgetProvider()
|
||||
|
||||
Reference in New Issue
Block a user