'commit'
This commit is contained in:
@@ -6,7 +6,6 @@ from typing import List, Optional, Dict, Any
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse, FileResponse, JSONResponse
|
||||
|
||||
from DbKit.Db import Db
|
||||
from Config.Config import DB_URL
|
||||
from Util.LlmUtil import get_llm_response
|
||||
from Tools.T6_Export import export_excel, DorisExcelExporter, extract_hourly_prices_from_schedule
|
||||
@@ -24,31 +23,35 @@ from Model.YltAnalyticsModel import (
|
||||
PriceSeries,
|
||||
PriceComparisonResponse,
|
||||
PriceComparisonSummary,
|
||||
YltAnalyticsModel,
|
||||
)
|
||||
|
||||
|
||||
from DbKit.Db import Db
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
db = Db(db_url=DB_URL)
|
||||
# db = Db(db_url=DB_URL) # Removed direct db instance
|
||||
|
||||
|
||||
async def init_db():
|
||||
db = Db()
|
||||
await db.init_db()
|
||||
|
||||
|
||||
async def close_db():
|
||||
db = Db()
|
||||
await db.close()
|
||||
|
||||
|
||||
@router.get("/api/operators/hourly-prices")
|
||||
async def get_operators_hourly_prices():
|
||||
operators = ["新电途", "特来电", "驿来特", "艾特吉易充"]
|
||||
exporter = DorisExcelExporter(db_url=DB_URL)
|
||||
await exporter.init()
|
||||
model = YltAnalyticsModel()
|
||||
try:
|
||||
result = []
|
||||
for op in operators:
|
||||
rows = await exporter.fetch_current_station_rows(op)
|
||||
rows = await model.fetch_current_station_rows(op)
|
||||
if not rows:
|
||||
result.append({"operator": op, "series": [None] * 24})
|
||||
continue
|
||||
@@ -71,11 +74,72 @@ async def get_operators_hourly_prices():
|
||||
else:
|
||||
avg_series.append(None)
|
||||
result.append({"operator": op, "series": avg_series})
|
||||
finally:
|
||||
await exporter.close()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
return {"operators": result}
|
||||
|
||||
|
||||
@router.get("/api/operators/price-trends")
|
||||
async def get_operators_price_trends(days: int = 7):
|
||||
operators = ["新电途", "特来电", "驿来特", "艾特吉易充"]
|
||||
|
||||
model = YltAnalyticsModel()
|
||||
rows = await model.get_operators_price_trends(days)
|
||||
|
||||
# 数据结构: { operator: { date_str: [sums_of_24h, counts_of_24h] } }
|
||||
trend_data = {}
|
||||
for op in operators:
|
||||
trend_data[op] = {}
|
||||
|
||||
for row in rows:
|
||||
op = row.get("operator")
|
||||
if op not in trend_data:
|
||||
continue
|
||||
d_str = str(row.get("date_str"))
|
||||
schedule_json = row.get("schedule_json")
|
||||
|
||||
if d_str not in trend_data[op]:
|
||||
trend_data[op][d_str] = {"sums": [0.0] * 24, "counts": [0] * 24}
|
||||
|
||||
series = extract_hourly_prices_from_schedule(schedule_json)
|
||||
for i in range(24):
|
||||
v = series[i]
|
||||
if v is not None:
|
||||
trend_data[op][d_str]["sums"][i] += float(v)
|
||||
trend_data[op][d_str]["counts"][i] += 1
|
||||
|
||||
# 转换为 ECharts 友好格式
|
||||
# 1. 获取所有日期并排序
|
||||
all_dates = sorted(list(set(str(row.get("date_str")) for row in rows)))
|
||||
|
||||
# 2. 为每个运营商计算每天的平均价格(24小时的平均值)
|
||||
series_result = []
|
||||
for op in operators:
|
||||
op_trend = []
|
||||
for d in all_dates:
|
||||
if d in trend_data[op]:
|
||||
day_stats = trend_data[op][d]
|
||||
day_avg_sum = 0.0
|
||||
day_hour_count = 0
|
||||
for i in range(24):
|
||||
if day_stats["counts"][i] > 0:
|
||||
day_avg_sum += (day_stats["sums"][i] / day_stats["counts"][i])
|
||||
day_hour_count += 1
|
||||
|
||||
if day_hour_count > 0:
|
||||
op_trend.append(round(day_avg_sum / day_hour_count, 4))
|
||||
else:
|
||||
op_trend.append(None)
|
||||
else:
|
||||
op_trend.append(None)
|
||||
series_result.append({"name": op, "data": op_trend})
|
||||
|
||||
return {
|
||||
"dates": all_dates,
|
||||
"series": series_result
|
||||
}
|
||||
|
||||
|
||||
@router.get("/api/export/prices-zip")
|
||||
async def export_prices_zip():
|
||||
operators = ["新电途", "特来电", "驿来特", "艾特吉易充"]
|
||||
@@ -177,26 +241,8 @@ async def ai_pricing_strategy_summary():
|
||||
|
||||
@router.get("/api/ylt/stations", response_model=List[StationBase])
|
||||
async def list_ylt_stations(q: Optional[str] = None):
|
||||
base_sql = """
|
||||
SELECT
|
||||
p.station_hash,
|
||||
p.operator,
|
||||
p.station_name,
|
||||
p.address,
|
||||
p.coord_x,
|
||||
p.coord_y,
|
||||
s.current_price
|
||||
FROM t_station_profile_scd p
|
||||
LEFT JOIN t_station_status_scd s
|
||||
ON p.station_hash = s.station_hash AND s.is_current = 1
|
||||
WHERE p.operator = '驿来特'
|
||||
"""
|
||||
params: Dict[str, Any] = {}
|
||||
if q:
|
||||
base_sql += " AND (p.station_name LIKE :kw OR p.address LIKE :kw)"
|
||||
params["kw"] = f"%{q}%"
|
||||
base_sql += " ORDER BY p.station_name"
|
||||
rows = await db.find(base_sql, params)
|
||||
model = YltAnalyticsModel()
|
||||
rows = await model.list_ylt_stations(q)
|
||||
result: List[StationBase] = []
|
||||
for r in rows:
|
||||
result.append(
|
||||
@@ -225,21 +271,8 @@ def haversine_km(lon1: float, lat1: float, lon2: float, lat2: float) -> float:
|
||||
|
||||
|
||||
async def fetch_current_stations() -> List[dict]:
|
||||
sql = """
|
||||
SELECT
|
||||
p.station_hash,
|
||||
p.operator,
|
||||
p.station_name,
|
||||
p.address,
|
||||
p.coord_x,
|
||||
p.coord_y,
|
||||
s.current_price
|
||||
FROM t_station_profile_scd p
|
||||
LEFT JOIN t_station_status_scd s
|
||||
ON p.station_hash = s.station_hash AND s.is_current = 1
|
||||
WHERE p.is_current = 1
|
||||
"""
|
||||
return await db.find(sql, {})
|
||||
model = YltAnalyticsModel()
|
||||
return await model.fetch_current_stations()
|
||||
|
||||
|
||||
async def build_geo_competition(station_hash: str, radius_km: float = 3.0) -> GeoCompetitionResponse:
|
||||
@@ -318,17 +351,8 @@ async def build_geo_competition(station_hash: str, radius_km: float = 3.0) -> Ge
|
||||
|
||||
|
||||
async def fetch_station_schedule_json(station_hash: str) -> Optional[str]:
|
||||
sql = """
|
||||
SELECT schedule_json
|
||||
FROM t_station_price_schedule_scd
|
||||
WHERE station_hash = :h AND is_current = 1
|
||||
ORDER BY valid_start_time DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
rows = await db.find(sql, {"h": station_hash})
|
||||
if not rows:
|
||||
return None
|
||||
value = rows[0].get("schedule_json")
|
||||
model = YltAnalyticsModel()
|
||||
value = await model.fetch_station_schedule_json(station_hash)
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
|
||||
Reference in New Issue
Block a user