feat: v2.0 — maintenance module, AI analysis, station power fix
- Add full 检修维护中心 (6.4): 3-type work orders (消缺/巡检/抄表), asset management, warehouse, work plans, billing settlement - Add AI智能分析 tab with LLM-powered diagnostics (StepFun + ZhipuAI) - Add AI模型配置 settings page (provider, temperature, prompts) - Fix station power accuracy: use API station total (station_power) instead of inverter-level computation — eliminates timing gaps - Add 7 new DB models, 4 new API routers, 5 new frontend pages - Migrations: 009 (maintenance expansion) + 010 (AI analysis) - Version bump: 1.6.1 → 2.0.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,9 @@ class OrderCreate(BaseModel):
|
||||
alarm_event_id: int | None = None
|
||||
priority: str = "medium"
|
||||
cost_estimate: float | None = None
|
||||
order_type: str = "repair" # repair, inspection, meter_reading
|
||||
station_name: str | None = None
|
||||
due_date: str | None = None
|
||||
|
||||
|
||||
class OrderUpdate(BaseModel):
|
||||
@@ -57,6 +60,8 @@ class OrderUpdate(BaseModel):
|
||||
status: str | None = None
|
||||
resolution: str | None = None
|
||||
actual_cost: float | None = None
|
||||
order_type: str | None = None
|
||||
station_name: str | None = None
|
||||
|
||||
|
||||
class DutyCreate(BaseModel):
|
||||
@@ -104,6 +109,9 @@ def _order_to_dict(o: RepairOrder) -> dict:
|
||||
"assigned_at": str(o.assigned_at) if o.assigned_at else None,
|
||||
"completed_at": str(o.completed_at) if o.completed_at else None,
|
||||
"closed_at": str(o.closed_at) if o.closed_at else None,
|
||||
"order_type": o.order_type,
|
||||
"station_name": o.station_name,
|
||||
"due_date": str(o.due_date) if o.due_date else None,
|
||||
}
|
||||
|
||||
|
||||
@@ -116,9 +124,11 @@ def _duty_to_dict(d: DutySchedule) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _generate_order_code() -> str:
|
||||
def _generate_order_code(order_type: str = "repair") -> str:
|
||||
now = datetime.now(timezone.utc)
|
||||
return f"WO-{now.strftime('%Y%m%d')}-{now.strftime('%H%M%S')}"
|
||||
prefix_map = {"repair": "XQ", "inspection": "XJ", "meter_reading": "CB"}
|
||||
prefix = prefix_map.get(order_type, "WO")
|
||||
return f"{prefix}-{now.strftime('%Y%m%d')}-{now.strftime('%H%M%S')}"
|
||||
|
||||
|
||||
# ── Inspection Plans ────────────────────────────────────────────────
|
||||
@@ -271,6 +281,7 @@ async def update_record(
|
||||
async def list_orders(
|
||||
status: str | None = None,
|
||||
priority: str | None = None,
|
||||
order_type: str | None = None,
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
@@ -281,6 +292,8 @@ async def list_orders(
|
||||
query = query.where(RepairOrder.status == status)
|
||||
if priority:
|
||||
query = query.where(RepairOrder.priority == priority)
|
||||
if order_type:
|
||||
query = query.where(RepairOrder.order_type == order_type)
|
||||
|
||||
count_q = select(func.count()).select_from(query.subquery())
|
||||
total = (await db.execute(count_q)).scalar()
|
||||
@@ -300,10 +313,12 @@ async def create_order(
|
||||
user: User = Depends(get_current_user),
|
||||
):
|
||||
order = RepairOrder(
|
||||
**data.model_dump(),
|
||||
code=_generate_order_code(),
|
||||
**data.model_dump(exclude={"due_date"}),
|
||||
code=_generate_order_code(data.order_type),
|
||||
created_by=user.id,
|
||||
)
|
||||
if data.due_date:
|
||||
order.due_date = datetime.fromisoformat(data.due_date)
|
||||
db.add(order)
|
||||
await db.flush()
|
||||
return _order_to_dict(order)
|
||||
|
||||
Reference in New Issue
Block a user