64dc85bd14
- inventory业务层下沉(薄路由+service orchestration模式): - customer/supplier/warehouse -> master_data_service - material_routes(价格历史/趋势/供应商关联)-> material_service - product_routes(CRUD/BOM/from-task跨模块桥接)-> product_service - dashboard_routes(首页统计/低库存预警)-> dashboard_service - inventory侧新增service回归覆盖(dashboard 2 / master_data 10 / material 10 / product 14),含跨模块桥接测试种子 - Pydantic v2弃用清零:全仓14处 class Config 全部迁移到 model_config = ConfigDict(from_attributes=True)(含 shared auth) - datetime.utcnow() 弃用清零:auth_service 3处统一改 datetime.now(timezone.utc) - 同步文档:STATUS / ROADMAP / TECH_DEBT(D12清偿)/ AGENTS 代码地图 测试基线:126 passed, 4 skipped(无deprecation warning) Co-Authored-By: Claude Code <noreply@anthropic.com>
72 lines
3.0 KiB
Python
72 lines
3.0 KiB
Python
"""仪表盘聚合业务服务层
|
|
|
|
将 dashboard_routes 中的聚合查询与统计编排下沉到此,
|
|
路由层只做依赖注入与响应返回。
|
|
"""
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from inventory.models import Customer, Inventory, Product, PurchaseOrder, SalesOrder, Supplier, Warehouse
|
|
|
|
|
|
class DashboardService:
|
|
"""仪表盘统计服务"""
|
|
|
|
@staticmethod
|
|
async def get_dashboard(db_session: AsyncSession) -> dict:
|
|
material_count = await db_session.scalar(
|
|
select(func.count(Product.id)).where(Product.is_active == True, Product.item_type == "material")
|
|
) or 0
|
|
finished_product_count = await db_session.scalar(
|
|
select(func.count(Product.id)).where(Product.is_active == True, Product.item_type == "finished")
|
|
) or 0
|
|
supplier_count = await db_session.scalar(select(func.count(Supplier.id)).where(Supplier.is_active == True)) or 0
|
|
customer_count = await db_session.scalar(select(func.count(Customer.id)).where(Customer.is_active == True)) or 0
|
|
warehouse_count = await db_session.scalar(select(func.count(Warehouse.id)).where(Warehouse.is_active == True)) or 0
|
|
|
|
total_stock = await db_session.scalar(
|
|
select(func.sum(Inventory.quantity))
|
|
.join(Product, Inventory.product_id == Product.id)
|
|
.where(Product.item_type == "material")
|
|
) or 0
|
|
total_value = await db_session.scalar(
|
|
select(func.sum(Inventory.quantity * Product.cost_price))
|
|
.join(Product, Inventory.product_id == Product.id)
|
|
.where(Product.item_type == "material")
|
|
) or 0
|
|
|
|
pending_purchase = await db_session.scalar(
|
|
select(func.count(PurchaseOrder.id)).where(PurchaseOrder.status == "pending")
|
|
) or 0
|
|
pending_sales = await db_session.scalar(
|
|
select(func.count(SalesOrder.id)).where(SalesOrder.status == "pending")
|
|
) or 0
|
|
|
|
low_stock_products = await db_session.execute(
|
|
select(Product, Inventory)
|
|
.join(Inventory, Product.id == Inventory.product_id)
|
|
.where(Product.item_type == "material")
|
|
.where(Inventory.quantity <= Product.min_stock)
|
|
.limit(10)
|
|
)
|
|
low_stock = [
|
|
{"id": p.id, "name": p.name, "sku": p.sku, "quantity": i.quantity, "min_stock": p.min_stock}
|
|
for p, i in low_stock_products.all()
|
|
]
|
|
|
|
return {
|
|
"finished_product_count": finished_product_count,
|
|
"material_count": material_count,
|
|
"supplier_count": supplier_count,
|
|
"customer_count": customer_count,
|
|
"warehouse_count": warehouse_count,
|
|
"total_stock": total_stock,
|
|
"total_value": round(total_value, 2),
|
|
"pending_purchase": pending_purchase,
|
|
"pending_sales": pending_sales,
|
|
"low_stock_products": low_stock,
|
|
}
|
|
|
|
|
|
dashboard_service = DashboardService()
|