cd_prediction.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. """
  2. Cd预测模型API接口
  3. @description: 提供作物Cd和有效态Cd的预测与可视化功能
  4. """
  5. from fastapi import APIRouter, HTTPException, BackgroundTasks, UploadFile, File, Form
  6. from fastapi.responses import FileResponse
  7. from typing import Dict, Any, Optional, List
  8. import os
  9. import logging
  10. import io
  11. import pandas as pd
  12. from ..services.cd_prediction_service import CdPredictionService
  13. router = APIRouter()
  14. # 设置日志
  15. logger = logging.getLogger(__name__)
  16. # =============================================================================
  17. # 县市查询接口
  18. # =============================================================================
  19. @router.get("/supported-counties",
  20. summary="获取支持的县市列表",
  21. description="获取系统当前支持进行Cd预测的县市列表")
  22. async def get_supported_counties() -> Dict[str, Any]:
  23. """
  24. 获取支持的县市列表
  25. @returns {Dict[str, Any]} 支持的县市信息
  26. """
  27. try:
  28. service = CdPredictionService()
  29. counties = service.get_supported_counties_info()
  30. return {
  31. "success": True,
  32. "message": "获取支持县市列表成功",
  33. "data": {
  34. "counties": counties,
  35. "total_count": len(counties)
  36. }
  37. }
  38. except Exception as e:
  39. logger.error(f"获取支持县市列表失败: {str(e)}")
  40. raise HTTPException(
  41. status_code=500,
  42. detail=f"获取支持县市列表失败: {str(e)}"
  43. )
  44. # =============================================================================
  45. # 一键生成并获取地图接口
  46. # =============================================================================
  47. @router.post("/crop-cd/generate-and-get-map",
  48. summary="一键生成并获取作物Cd预测地图",
  49. description="根据县名和CSV数据生成作物Cd预测地图并直接返回图片文件")
  50. async def generate_and_get_crop_cd_map(
  51. county_name: str = Form(..., description="县市名称,如:乐昌市"),
  52. data_file: UploadFile = File(..., description="CSV格式的环境因子数据文件,前两列为经纬度,后续列与areatest.csv结构一致")
  53. ):
  54. """
  55. 一键生成并获取作物Cd预测地图
  56. @param county_name: 县市名称
  57. @param data_file: CSV数据文件,前两列为经纬度坐标,后面几列和areatest.csv的结构一致
  58. @returns {FileResponse} 预测地图文件
  59. """
  60. try:
  61. logger.info(f"开始为{county_name}一键生成作物Cd预测地图")
  62. # 验证文件格式
  63. if not data_file.filename.endswith('.csv'):
  64. raise HTTPException(status_code=400, detail="仅支持CSV格式文件")
  65. # 读取CSV数据
  66. content = await data_file.read()
  67. df = pd.read_csv(io.StringIO(content.decode('utf-8')))
  68. # 验证数据格式
  69. if df.shape[1] < 3:
  70. raise HTTPException(
  71. status_code=400,
  72. detail="数据至少需要3列:前两列为经纬度,后续列为环境因子"
  73. )
  74. # 重命名前两列为标准的经纬度列名
  75. df.columns = ['longitude', 'latitude'] + list(df.columns[2:])
  76. service = CdPredictionService()
  77. # 验证数据
  78. validation_result = service.validate_input_data(df, county_name)
  79. if not validation_result['valid']:
  80. raise HTTPException(
  81. status_code=400,
  82. detail=f"数据验证失败: {', '.join(validation_result['errors'])}"
  83. )
  84. # 保存临时数据文件
  85. temp_file_path = service.save_temp_data(df, county_name)
  86. # 生成预测结果
  87. result = await service.generate_crop_cd_prediction_for_county(
  88. county_name=county_name,
  89. data_file=temp_file_path
  90. )
  91. if not result['map_path'] or not os.path.exists(result['map_path']):
  92. raise HTTPException(status_code=500, detail="地图文件生成失败")
  93. return FileResponse(
  94. path=result['map_path'],
  95. filename=f"{county_name}_crop_cd_prediction_map.jpg",
  96. media_type="image/jpeg"
  97. )
  98. except HTTPException:
  99. raise
  100. except Exception as e:
  101. logger.error(f"为{county_name}一键生成作物Cd预测地图失败: {str(e)}")
  102. raise HTTPException(
  103. status_code=500,
  104. detail=f"为{county_name}一键生成作物Cd预测地图失败: {str(e)}"
  105. )
  106. @router.post("/effective-cd/generate-and-get-map",
  107. summary="一键生成并获取有效态Cd预测地图",
  108. description="根据县名和CSV数据生成有效态Cd预测地图并直接返回图片文件")
  109. async def generate_and_get_effective_cd_map(
  110. county_name: str = Form(..., description="县市名称,如:乐昌市"),
  111. data_file: UploadFile = File(..., description="CSV格式的环境因子数据文件,前两列为经纬度,后续列与areatest.csv结构一致")
  112. ):
  113. """
  114. 一键生成并获取有效态Cd预测地图
  115. @param county_name: 县市名称
  116. @param data_file: CSV数据文件,前两列为经纬度坐标,后面几列和areatest.csv的结构一致
  117. @returns {FileResponse} 预测地图文件
  118. """
  119. try:
  120. logger.info(f"开始为{county_name}一键生成有效态Cd预测地图")
  121. # 验证文件格式
  122. if not data_file.filename.endswith('.csv'):
  123. raise HTTPException(status_code=400, detail="仅支持CSV格式文件")
  124. # 读取CSV数据
  125. content = await data_file.read()
  126. df = pd.read_csv(io.StringIO(content.decode('utf-8')))
  127. # 验证数据格式
  128. if df.shape[1] < 3:
  129. raise HTTPException(
  130. status_code=400,
  131. detail="数据至少需要3列:前两列为经纬度,后续列为环境因子"
  132. )
  133. # 重命名前两列为标准的经纬度列名
  134. df.columns = ['longitude', 'latitude'] + list(df.columns[2:])
  135. service = CdPredictionService()
  136. # 验证数据
  137. validation_result = service.validate_input_data(df, county_name)
  138. if not validation_result['valid']:
  139. raise HTTPException(
  140. status_code=400,
  141. detail=f"数据验证失败: {', '.join(validation_result['errors'])}"
  142. )
  143. # 保存临时数据文件
  144. temp_file_path = service.save_temp_data(df, county_name)
  145. # 生成预测结果
  146. result = await service.generate_effective_cd_prediction_for_county(
  147. county_name=county_name,
  148. data_file=temp_file_path
  149. )
  150. if not result['map_path'] or not os.path.exists(result['map_path']):
  151. raise HTTPException(status_code=500, detail="地图文件生成失败")
  152. return FileResponse(
  153. path=result['map_path'],
  154. filename=f"{county_name}_effective_cd_prediction_map.jpg",
  155. media_type="image/jpeg"
  156. )
  157. except HTTPException:
  158. raise
  159. except Exception as e:
  160. logger.error(f"为{county_name}一键生成有效态Cd预测地图失败: {str(e)}")
  161. raise HTTPException(
  162. status_code=500,
  163. detail=f"为{county_name}一键生成有效态Cd预测地图失败: {str(e)}"
  164. )
  165. # =============================================================================
  166. # 获取最新预测结果接口(无需重新计算)
  167. # =============================================================================
  168. @router.get("/crop-cd/latest-map/{county_name}",
  169. summary="获取作物Cd最新地图",
  170. description="直接返回指定县市的最新作物Cd预测地图,无需重新计算")
  171. async def get_latest_crop_cd_map(county_name: str):
  172. """
  173. 获取指定县市的最新作物Cd预测地图
  174. @param county_name: 县市名称,如:乐昌市
  175. @returns {FileResponse} 最新的预测地图文件
  176. """
  177. try:
  178. logger.info(f"获取{county_name}的最新作物Cd预测地图")
  179. service = CdPredictionService()
  180. # 验证县市是否支持
  181. if not service.is_county_supported(county_name):
  182. raise HTTPException(
  183. status_code=404,
  184. detail=f"不支持的县市: {county_name}"
  185. )
  186. # 查找最新的地图文件
  187. map_pattern = f"crop_cd_{county_name}_prediction_map_*.jpg"
  188. map_files = []
  189. # 在输出目录中查找相关文件
  190. import glob
  191. output_dir = service.output_figures_dir
  192. search_pattern = os.path.join(output_dir, map_pattern)
  193. map_files = glob.glob(search_pattern)
  194. if not map_files:
  195. raise HTTPException(
  196. status_code=404,
  197. detail=f"未找到{county_name}的作物Cd预测地图,请先执行预测"
  198. )
  199. # 选择最新的文件(按修改时间排序)
  200. latest_map = max(map_files, key=os.path.getmtime)
  201. if not os.path.exists(latest_map):
  202. raise HTTPException(status_code=404, detail="地图文件不存在")
  203. return FileResponse(
  204. path=latest_map,
  205. filename=f"{county_name}_latest_crop_cd_map.jpg",
  206. media_type="image/jpeg"
  207. )
  208. except HTTPException:
  209. raise
  210. except Exception as e:
  211. logger.error(f"获取{county_name}最新作物Cd地图失败: {str(e)}")
  212. raise HTTPException(
  213. status_code=500,
  214. detail=f"获取{county_name}最新作物Cd地图失败: {str(e)}"
  215. )
  216. @router.get("/effective-cd/latest-map/{county_name}",
  217. summary="获取有效态Cd最新地图",
  218. description="直接返回指定县市的最新有效态Cd预测地图,无需重新计算")
  219. async def get_latest_effective_cd_map(county_name: str):
  220. """
  221. 获取指定县市的最新有效态Cd预测地图
  222. @param county_name: 县市名称,如:乐昌市
  223. @returns {FileResponse} 最新的预测地图文件
  224. """
  225. try:
  226. logger.info(f"获取{county_name}的最新有效态Cd预测地图")
  227. service = CdPredictionService()
  228. # 验证县市是否支持
  229. if not service.is_county_supported(county_name):
  230. raise HTTPException(
  231. status_code=404,
  232. detail=f"不支持的县市: {county_name}"
  233. )
  234. # 查找最新的地图文件
  235. map_pattern = f"effective_cd_{county_name}_prediction_map_*.jpg"
  236. map_files = []
  237. # 在输出目录中查找相关文件
  238. import glob
  239. output_dir = service.output_figures_dir
  240. search_pattern = os.path.join(output_dir, map_pattern)
  241. map_files = glob.glob(search_pattern)
  242. if not map_files:
  243. raise HTTPException(
  244. status_code=404,
  245. detail=f"未找到{county_name}的有效态Cd预测地图,请先执行预测"
  246. )
  247. # 选择最新的文件(按修改时间排序)
  248. latest_map = max(map_files, key=os.path.getmtime)
  249. if not os.path.exists(latest_map):
  250. raise HTTPException(status_code=404, detail="地图文件不存在")
  251. return FileResponse(
  252. path=latest_map,
  253. filename=f"{county_name}_latest_effective_cd_map.jpg",
  254. media_type="image/jpeg"
  255. )
  256. except HTTPException:
  257. raise
  258. except Exception as e:
  259. logger.error(f"获取{county_name}最新有效态Cd地图失败: {str(e)}")
  260. raise HTTPException(
  261. status_code=500,
  262. detail=f"获取{county_name}最新有效态Cd地图失败: {str(e)}"
  263. )
  264. @router.get("/crop-cd/latest-histogram/{county_name}",
  265. summary="获取作物Cd最新直方图",
  266. description="直接返回指定县市的最新作物Cd预测直方图,无需重新计算")
  267. async def get_latest_crop_cd_histogram(county_name: str):
  268. """
  269. 获取指定县市的最新作物Cd预测直方图
  270. @param county_name: 县市名称,如:乐昌市
  271. @returns {FileResponse} 最新的预测直方图文件
  272. """
  273. try:
  274. logger.info(f"获取{county_name}的最新作物Cd预测直方图")
  275. service = CdPredictionService()
  276. # 验证县市是否支持
  277. if not service.is_county_supported(county_name):
  278. raise HTTPException(
  279. status_code=404,
  280. detail=f"不支持的县市: {county_name}"
  281. )
  282. # 查找最新的直方图文件
  283. histogram_pattern = f"crop_cd_{county_name}_prediction_histogram_*.jpg"
  284. histogram_files = []
  285. # 在输出目录中查找相关文件
  286. import glob
  287. output_dir = service.output_figures_dir
  288. search_pattern = os.path.join(output_dir, histogram_pattern)
  289. histogram_files = glob.glob(search_pattern)
  290. if not histogram_files:
  291. raise HTTPException(
  292. status_code=404,
  293. detail=f"未找到{county_name}的作物Cd预测直方图,请先执行预测"
  294. )
  295. # 选择最新的文件(按修改时间排序)
  296. latest_histogram = max(histogram_files, key=os.path.getmtime)
  297. if not os.path.exists(latest_histogram):
  298. raise HTTPException(status_code=404, detail="直方图文件不存在")
  299. return FileResponse(
  300. path=latest_histogram,
  301. filename=f"{county_name}_latest_crop_cd_histogram.jpg",
  302. media_type="image/jpeg"
  303. )
  304. except HTTPException:
  305. raise
  306. except Exception as e:
  307. logger.error(f"获取{county_name}最新作物Cd直方图失败: {str(e)}")
  308. raise HTTPException(
  309. status_code=500,
  310. detail=f"获取{county_name}最新作物Cd直方图失败: {str(e)}"
  311. )
  312. @router.get("/effective-cd/latest-histogram/{county_name}",
  313. summary="获取有效态Cd最新直方图",
  314. description="直接返回指定县市的最新有效态Cd预测直方图,无需重新计算")
  315. async def get_latest_effective_cd_histogram(county_name: str):
  316. """
  317. 获取指定县市的最新有效态Cd预测直方图
  318. @param county_name: 县市名称,如:乐昌市
  319. @returns {FileResponse} 最新的预测直方图文件
  320. """
  321. try:
  322. logger.info(f"获取{county_name}的最新有效态Cd预测直方图")
  323. service = CdPredictionService()
  324. # 验证县市是否支持
  325. if not service.is_county_supported(county_name):
  326. raise HTTPException(
  327. status_code=404,
  328. detail=f"不支持的县市: {county_name}"
  329. )
  330. # 查找最新的直方图文件
  331. histogram_pattern = f"effective_cd_{county_name}_prediction_histogram_*.jpg"
  332. histogram_files = []
  333. # 在输出目录中查找相关文件
  334. import glob
  335. output_dir = service.output_figures_dir
  336. search_pattern = os.path.join(output_dir, histogram_pattern)
  337. histogram_files = glob.glob(search_pattern)
  338. if not histogram_files:
  339. raise HTTPException(
  340. status_code=404,
  341. detail=f"未找到{county_name}的有效态Cd预测直方图,请先执行预测"
  342. )
  343. # 选择最新的文件(按修改时间排序)
  344. latest_histogram = max(histogram_files, key=os.path.getmtime)
  345. if not os.path.exists(latest_histogram):
  346. raise HTTPException(status_code=404, detail="直方图文件不存在")
  347. return FileResponse(
  348. path=latest_histogram,
  349. filename=f"{county_name}_latest_effective_cd_histogram.jpg",
  350. media_type="image/jpeg"
  351. )
  352. except HTTPException:
  353. raise
  354. except Exception as e:
  355. logger.error(f"获取{county_name}最新有效态Cd直方图失败: {str(e)}")
  356. raise HTTPException(
  357. status_code=500,
  358. detail=f"获取{county_name}最新有效态Cd直方图失败: {str(e)}"
  359. )
  360. @router.get("/latest-results/{county_name}",
  361. summary="获取县市最新预测结果概览",
  362. description="获取指定县市所有最新预测结果的概览信息")
  363. async def get_latest_results_overview(county_name: str):
  364. """
  365. 获取指定县市的最新预测结果概览
  366. @param county_name: 县市名称,如:乐昌市
  367. @returns {Dict[str, Any]} 最新预测结果的概览信息
  368. """
  369. try:
  370. logger.info(f"获取{county_name}的最新预测结果概览")
  371. service = CdPredictionService()
  372. # 验证县市是否支持
  373. if not service.is_county_supported(county_name):
  374. raise HTTPException(
  375. status_code=404,
  376. detail=f"不支持的县市: {county_name}"
  377. )
  378. import glob
  379. output_dir = service.output_figures_dir
  380. # 查找所有相关文件
  381. result_files = {
  382. "crop_cd_maps": [],
  383. "crop_cd_histograms": [],
  384. "effective_cd_maps": [],
  385. "effective_cd_histograms": []
  386. }
  387. # 作物Cd地图
  388. crop_map_pattern = os.path.join(output_dir, f"crop_cd_{county_name}_prediction_map_*.jpg")
  389. result_files["crop_cd_maps"] = glob.glob(crop_map_pattern)
  390. # 作物Cd直方图
  391. crop_hist_pattern = os.path.join(output_dir, f"crop_cd_{county_name}_prediction_histogram_*.jpg")
  392. result_files["crop_cd_histograms"] = glob.glob(crop_hist_pattern)
  393. # 有效态Cd地图
  394. eff_map_pattern = os.path.join(output_dir, f"effective_cd_{county_name}_prediction_map_*.jpg")
  395. result_files["effective_cd_maps"] = glob.glob(eff_map_pattern)
  396. # 有效态Cd直方图
  397. eff_hist_pattern = os.path.join(output_dir, f"effective_cd_{county_name}_prediction_histogram_*.jpg")
  398. result_files["effective_cd_histograms"] = glob.glob(eff_hist_pattern)
  399. # 构建结果概览
  400. overview = {
  401. "county_name": county_name,
  402. "available_results": {},
  403. "latest_files": {},
  404. "total_results": 0
  405. }
  406. for result_type, files in result_files.items():
  407. if files:
  408. # 按修改时间排序,获取最新文件
  409. latest_file = max(files, key=os.path.getmtime)
  410. file_stats = os.stat(latest_file)
  411. overview["available_results"][result_type] = {
  412. "count": len(files),
  413. "latest_timestamp": file_stats.st_mtime,
  414. "latest_size": file_stats.st_size
  415. }
  416. overview["latest_files"][result_type] = {
  417. "filename": os.path.basename(latest_file),
  418. "path": latest_file,
  419. "download_url": f"/api/cd-prediction/{result_type.replace('_', '-')}/latest-{'map' if 'map' in result_type else 'histogram'}/{county_name}"
  420. }
  421. overview["total_results"] += len(files)
  422. if overview["total_results"] == 0:
  423. raise HTTPException(
  424. status_code=404,
  425. detail=f"未找到{county_name}的任何预测结果,请先执行预测"
  426. )
  427. return {
  428. "success": True,
  429. "message": f"获取{county_name}预测结果概览成功",
  430. "data": overview
  431. }
  432. except HTTPException:
  433. raise
  434. except Exception as e:
  435. logger.error(f"获取{county_name}预测结果概览失败: {str(e)}")
  436. raise HTTPException(
  437. status_code=500,
  438. detail=f"获取{county_name}预测结果概览失败: {str(e)}"
  439. )
  440. # =============================================================================
  441. # 预测结果统计信息接口
  442. # =============================================================================
  443. @router.get("/crop-cd/statistics/{county_name}",
  444. summary="获取作物Cd预测统计信息",
  445. description="获取指定县市的作物Cd预测结果的详细统计信息")
  446. async def get_crop_cd_statistics(county_name: str):
  447. """
  448. 获取指定县市的作物Cd预测统计信息
  449. @param county_name: 县市名称,如:乐昌市
  450. @returns {Dict[str, Any]} 预测结果的统计信息
  451. """
  452. try:
  453. logger.info(f"获取{county_name}的作物Cd预测统计信息")
  454. service = CdPredictionService()
  455. # 验证县市是否支持
  456. if not service.is_county_supported(county_name):
  457. raise HTTPException(
  458. status_code=404,
  459. detail=f"不支持的县市: {county_name}"
  460. )
  461. # 获取预测结果数据
  462. stats = service.get_crop_cd_statistics(county_name)
  463. if not stats:
  464. raise HTTPException(
  465. status_code=404,
  466. detail=f"未找到{county_name}的作物Cd预测结果,请先执行预测"
  467. )
  468. return {
  469. "success": True,
  470. "message": f"获取{county_name}作物Cd预测统计信息成功",
  471. "data": stats
  472. }
  473. except HTTPException:
  474. raise
  475. except Exception as e:
  476. logger.error(f"获取{county_name}作物Cd预测统计信息失败: {str(e)}")
  477. raise HTTPException(
  478. status_code=500,
  479. detail=f"获取{county_name}作物Cd预测统计信息失败: {str(e)}"
  480. )
  481. @router.get("/effective-cd/statistics/{county_name}",
  482. summary="获取有效态Cd预测统计信息",
  483. description="获取指定县市的有效态Cd预测结果的详细统计信息")
  484. async def get_effective_cd_statistics(county_name: str):
  485. """
  486. 获取指定县市的有效态Cd预测统计信息
  487. @param county_name: 县市名称,如:乐昌市
  488. @returns {Dict[str, Any]} 预测结果的统计信息
  489. """
  490. try:
  491. logger.info(f"获取{county_name}的有效态Cd预测统计信息")
  492. service = CdPredictionService()
  493. # 验证县市是否支持
  494. if not service.is_county_supported(county_name):
  495. raise HTTPException(
  496. status_code=404,
  497. detail=f"不支持的县市: {county_name}"
  498. )
  499. # 获取预测结果数据
  500. stats = service.get_effective_cd_statistics(county_name)
  501. if not stats:
  502. raise HTTPException(
  503. status_code=404,
  504. detail=f"未找到{county_name}的有效态Cd预测结果,请先执行预测"
  505. )
  506. return {
  507. "success": True,
  508. "message": f"获取{county_name}有效态Cd预测统计信息成功",
  509. "data": stats
  510. }
  511. except HTTPException:
  512. raise
  513. except Exception as e:
  514. logger.error(f"获取{county_name}有效态Cd预测统计信息失败: {str(e)}")
  515. raise HTTPException(
  516. status_code=500,
  517. detail=f"获取{county_name}有效态Cd预测统计信息失败: {str(e)}"
  518. )
  519. @router.get("/combined-statistics/{county_name}",
  520. summary="获取综合预测统计信息",
  521. description="获取指定县市的作物Cd和有效态Cd预测结果的综合统计信息")
  522. async def get_combined_statistics(county_name: str):
  523. """
  524. 获取指定县市的综合预测统计信息
  525. @param county_name: 县市名称,如:乐昌市
  526. @returns {Dict[str, Any]} 综合预测结果的统计信息
  527. """
  528. try:
  529. logger.info(f"获取{county_name}的综合预测统计信息")
  530. service = CdPredictionService()
  531. # 验证县市是否支持
  532. if not service.is_county_supported(county_name):
  533. raise HTTPException(
  534. status_code=404,
  535. detail=f"不支持的县市: {county_name}"
  536. )
  537. # 获取综合统计信息
  538. stats = service.get_combined_statistics(county_name)
  539. if not stats:
  540. raise HTTPException(
  541. status_code=404,
  542. detail=f"未找到{county_name}的预测结果,请先执行预测"
  543. )
  544. return {
  545. "success": True,
  546. "message": f"获取{county_name}综合预测统计信息成功",
  547. "data": stats
  548. }
  549. except HTTPException:
  550. raise
  551. except Exception as e:
  552. logger.error(f"获取{county_name}综合预测统计信息失败: {str(e)}")
  553. raise HTTPException(
  554. status_code=500,
  555. detail=f"获取{county_name}综合预测统计信息失败: {str(e)}"
  556. )
  557. @router.get("/all-statistics",
  558. summary="获取所有县市统计概览",
  559. description="获取所有支持县市的预测结果统计概览")
  560. async def get_all_statistics():
  561. """
  562. 获取所有支持县市的预测结果统计概览
  563. @returns {Dict[str, Any]} 所有县市的统计概览
  564. """
  565. try:
  566. logger.info("获取所有县市统计概览")
  567. service = CdPredictionService()
  568. # 获取所有县市的统计概览
  569. all_stats = service.get_all_counties_statistics()
  570. return {
  571. "success": True,
  572. "message": "获取所有县市统计概览成功",
  573. "data": all_stats
  574. }
  575. except Exception as e:
  576. logger.error(f"获取所有县市统计概览失败: {str(e)}")
  577. raise HTTPException(
  578. status_code=500,
  579. detail=f"获取所有县市统计概览失败: {str(e)}"
  580. )