agricultural_input_service.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. """
  2. 农业投入Cd通量计算服务
  3. @description: 根据Parameters表中的数据计算各个地区的农业投入输入Cd通量预测
  4. @author: AcidMap Team
  5. @version: 1.0.0
  6. """
  7. import logging
  8. from typing import Dict, Any, List, Optional
  9. from sqlalchemy.orm import sessionmaker
  10. from sqlalchemy import create_engine
  11. from ..database import SessionLocal, engine
  12. from ..models.parameters import Parameters
  13. class AgriculturalInputService:
  14. """
  15. 农业投入Cd通量计算服务类
  16. @description: 提供基于Parameters表数据的农业投入输入Cd通量预测计算功能
  17. """
  18. def __init__(self):
  19. """
  20. 初始化农业投入服务
  21. """
  22. self.logger = logging.getLogger(__name__)
  23. def calculate_cd_flux_by_area(self, area: str) -> Dict[str, Any]:
  24. """
  25. 根据地区计算农业投入输入Cd通量
  26. @param area: 地区名称
  27. @returns: 计算结果
  28. """
  29. try:
  30. with SessionLocal() as db:
  31. # 查询指定地区的参数
  32. parameter = db.query(Parameters).filter(Parameters.area == area).first()
  33. if not parameter:
  34. return {
  35. "success": False,
  36. "message": f"未找到地区 '{area}' 的参数数据",
  37. "data": None
  38. }
  39. # 计算农业投入输入Cd通量
  40. # 公式:F3*NF + F4*PF + F5*KF + F6*CF + F7*OF + F8*P + F9*FF + F10*AF
  41. cd_flux = (
  42. parameter.f3 * parameter.nf + # 氮肥
  43. parameter.f4 * parameter.pf + # 磷肥
  44. parameter.f5 * parameter.kf + # 钾肥
  45. parameter.f6 * parameter.cf + # 复合肥
  46. parameter.f7 * parameter.of + # 有机肥
  47. parameter.f8 * parameter.p + # 农药
  48. parameter.f9 * parameter.ff + # 农家肥
  49. parameter.f10 * parameter.af # 农膜
  50. )
  51. # 计算各项明细
  52. details = {
  53. "nitrogen_fertilizer": parameter.f3 * parameter.nf, # 氮肥贡献
  54. "phosphorus_fertilizer": parameter.f4 * parameter.pf, # 磷肥贡献
  55. "potassium_fertilizer": parameter.f5 * parameter.kf, # 钾肥贡献
  56. "compound_fertilizer": parameter.f6 * parameter.cf, # 复合肥贡献
  57. "organic_fertilizer": parameter.f7 * parameter.of, # 有机肥贡献
  58. "pesticide": parameter.f8 * parameter.p, # 农药贡献
  59. "farmyard_manure": parameter.f9 * parameter.ff, # 农家肥贡献
  60. "agricultural_film": parameter.f10 * parameter.af # 农膜贡献
  61. }
  62. return {
  63. "success": True,
  64. "message": f"成功计算地区 '{area}' 的农业投入输入Cd通量",
  65. "data": {
  66. "area": area,
  67. "total_cd_flux": round(cd_flux, 6),
  68. "unit": "g/ha/a",
  69. "details": {key: round(value, 6) for key, value in details.items()},
  70. "parameters_used": {
  71. "f3_nitrogen_cd_content": parameter.f3,
  72. "f4_phosphorus_cd_content": parameter.f4,
  73. "f5_potassium_cd_content": parameter.f5,
  74. "f6_compound_cd_content": parameter.f6,
  75. "f7_organic_cd_content": parameter.f7,
  76. "f8_pesticide_cd_content": parameter.f8,
  77. "f9_farmyard_cd_content": parameter.f9,
  78. "f10_film_cd_content": parameter.f10,
  79. "nf_nitrogen_usage": parameter.nf,
  80. "pf_phosphorus_usage": parameter.pf,
  81. "kf_potassium_usage": parameter.kf,
  82. "cf_compound_usage": parameter.cf,
  83. "of_organic_usage": parameter.of,
  84. "p_pesticide_usage": parameter.p,
  85. "ff_farmyard_usage": parameter.ff,
  86. "af_film_usage": parameter.af
  87. }
  88. }
  89. }
  90. except Exception as e:
  91. self.logger.error(f"计算地区 '{area}' 的Cd通量时发生错误: {str(e)}")
  92. return {
  93. "success": False,
  94. "message": f"计算失败: {str(e)}",
  95. "data": None
  96. }
  97. def calculate_all_areas_cd_flux(self) -> Dict[str, Any]:
  98. """
  99. 计算所有地区的农业投入输入Cd通量
  100. @returns: 所有地区的计算结果
  101. """
  102. try:
  103. with SessionLocal() as db:
  104. # 查询所有参数记录
  105. parameters = db.query(Parameters).all()
  106. if not parameters:
  107. return {
  108. "success": False,
  109. "message": "数据库中未找到任何参数数据",
  110. "data": None
  111. }
  112. results = []
  113. for param in parameters:
  114. # 计算每个地区的Cd通量
  115. cd_flux = (
  116. param.f3 * param.nf +
  117. param.f4 * param.pf +
  118. param.f5 * param.kf +
  119. param.f6 * param.cf +
  120. param.f7 * param.of +
  121. param.f8 * param.p +
  122. param.f9 * param.ff +
  123. param.f10 * param.af
  124. )
  125. details = {
  126. "nitrogen_fertilizer": param.f3 * param.nf,
  127. "phosphorus_fertilizer": param.f4 * param.pf,
  128. "potassium_fertilizer": param.f5 * param.kf,
  129. "compound_fertilizer": param.f6 * param.cf,
  130. "organic_fertilizer": param.f7 * param.of,
  131. "pesticide": param.f8 * param.p,
  132. "farmyard_manure": param.f9 * param.ff,
  133. "agricultural_film": param.f10 * param.af
  134. }
  135. result_item = {
  136. "area": param.area,
  137. "total_cd_flux": round(cd_flux, 6),
  138. "details": {key: round(value, 6) for key, value in details.items()}
  139. }
  140. results.append(result_item)
  141. # 按总通量排序
  142. results.sort(key=lambda x: x["total_cd_flux"], reverse=True)
  143. return {
  144. "success": True,
  145. "message": f"成功计算所有 {len(results)} 个地区的农业投入输入Cd通量",
  146. "data": {
  147. "total_areas": len(results),
  148. "unit": "g/ha/a",
  149. "results": results,
  150. "summary": {
  151. "max_cd_flux": max(results, key=lambda x: x["total_cd_flux"]) if results else None,
  152. "min_cd_flux": min(results, key=lambda x: x["total_cd_flux"]) if results else None,
  153. "average_cd_flux": round(sum(r["total_cd_flux"] for r in results) / len(results), 6) if results else 0
  154. }
  155. }
  156. }
  157. except Exception as e:
  158. self.logger.error(f"计算所有地区Cd通量时发生错误: {str(e)}")
  159. return {
  160. "success": False,
  161. "message": f"计算失败: {str(e)}",
  162. "data": None
  163. }
  164. def get_available_areas(self) -> Dict[str, Any]:
  165. """
  166. 获取数据库中所有可用的地区列表
  167. @returns: 可用地区列表
  168. """
  169. try:
  170. with SessionLocal() as db:
  171. # 查询所有不重复的地区
  172. areas = db.query(Parameters.area).distinct().all()
  173. area_list = [area[0] for area in areas if area[0]]
  174. return {
  175. "success": True,
  176. "message": f"成功获取 {len(area_list)} 个可用地区",
  177. "data": {
  178. "areas": sorted(area_list),
  179. "total_count": len(area_list)
  180. }
  181. }
  182. except Exception as e:
  183. self.logger.error(f"获取可用地区列表时发生错误: {str(e)}")
  184. return {
  185. "success": False,
  186. "message": f"获取失败: {str(e)}",
  187. "data": None
  188. }
  189. def calculate_cd_flux_with_custom_data(self, request) -> Dict[str, Any]:
  190. """
  191. 使用用户提供的自定义数据计算农业投入输入Cd通量
  192. @param request: 包含计算参数的请求对象
  193. @returns: 计算结果
  194. """
  195. try:
  196. # 进行参数验证
  197. if not self._validate_calculation_parameters(request):
  198. return {
  199. "success": False,
  200. "message": "提供的参数数据不完整或无效",
  201. "data": None
  202. }
  203. # 计算农业投入输入Cd通量
  204. # 公式:F3*NF + F4*PF + F5*KF + F6*CF + F7*OF + F8*P + F9*FF + F10*AF
  205. cd_flux = (
  206. request.f3_nitrogen_cd_content * request.nf_nitrogen_usage + # 氮肥
  207. request.f4_phosphorus_cd_content * request.pf_phosphorus_usage + # 磷肥
  208. request.f5_potassium_cd_content * request.kf_potassium_usage + # 钾肥
  209. request.f6_compound_cd_content * request.cf_compound_usage + # 复合肥
  210. request.f7_organic_cd_content * request.of_organic_usage + # 有机肥
  211. request.f8_pesticide_cd_content * request.p_pesticide_usage + # 农药
  212. request.f9_farmyard_cd_content * request.ff_farmyard_usage + # 农家肥
  213. request.f10_film_cd_content * request.af_film_usage # 农膜
  214. )
  215. # 计算各项明细贡献
  216. details = {
  217. "nitrogen_fertilizer": request.f3_nitrogen_cd_content * request.nf_nitrogen_usage,
  218. "phosphorus_fertilizer": request.f4_phosphorus_cd_content * request.pf_phosphorus_usage,
  219. "potassium_fertilizer": request.f5_potassium_cd_content * request.kf_potassium_usage,
  220. "compound_fertilizer": request.f6_compound_cd_content * request.cf_compound_usage,
  221. "organic_fertilizer": request.f7_organic_cd_content * request.of_organic_usage,
  222. "pesticide": request.f8_pesticide_cd_content * request.p_pesticide_usage,
  223. "farmyard_manure": request.f9_farmyard_cd_content * request.ff_farmyard_usage,
  224. "agricultural_film": request.f10_film_cd_content * request.af_film_usage
  225. }
  226. # 构建返回结果
  227. result_data = {
  228. "total_cd_flux": round(cd_flux, 6),
  229. "unit": "g/ha/a",
  230. "details": {key: round(value, 6) for key, value in details.items()},
  231. "input_parameters": {
  232. "cd_contents": {
  233. "f3_nitrogen_cd_content": request.f3_nitrogen_cd_content,
  234. "f4_phosphorus_cd_content": request.f4_phosphorus_cd_content,
  235. "f5_potassium_cd_content": request.f5_potassium_cd_content,
  236. "f6_compound_cd_content": request.f6_compound_cd_content,
  237. "f7_organic_cd_content": request.f7_organic_cd_content,
  238. "f8_pesticide_cd_content": request.f8_pesticide_cd_content,
  239. "f9_farmyard_cd_content": request.f9_farmyard_cd_content,
  240. "f10_film_cd_content": request.f10_film_cd_content
  241. },
  242. "usage_amounts": {
  243. "nf_nitrogen_usage": request.nf_nitrogen_usage,
  244. "pf_phosphorus_usage": request.pf_phosphorus_usage,
  245. "kf_potassium_usage": request.kf_potassium_usage,
  246. "cf_compound_usage": request.cf_compound_usage,
  247. "of_organic_usage": request.of_organic_usage,
  248. "p_pesticide_usage": request.p_pesticide_usage,
  249. "ff_farmyard_usage": request.ff_farmyard_usage,
  250. "af_film_usage": request.af_film_usage
  251. }
  252. }
  253. }
  254. # 如果有描述信息,添加到结果中
  255. if request.description:
  256. result_data["description"] = request.description
  257. return {
  258. "success": True,
  259. "message": "成功计算农业投入输入Cd通量",
  260. "data": result_data
  261. }
  262. except Exception as e:
  263. self.logger.error(f"使用自定义数据计算Cd通量时发生错误: {str(e)}")
  264. return {
  265. "success": False,
  266. "message": f"计算失败: {str(e)}",
  267. "data": None
  268. }
  269. def _validate_calculation_parameters(self, request) -> bool:
  270. """
  271. 验证计算参数的有效性
  272. @param request: 请求对象
  273. @returns: 验证是否通过
  274. """
  275. try:
  276. # 检查所有必需的参数是否存在且为非负数
  277. required_attrs = [
  278. 'f3_nitrogen_cd_content', 'f4_phosphorus_cd_content', 'f5_potassium_cd_content',
  279. 'f6_compound_cd_content', 'f7_organic_cd_content', 'f8_pesticide_cd_content',
  280. 'f9_farmyard_cd_content', 'f10_film_cd_content', 'nf_nitrogen_usage',
  281. 'pf_phosphorus_usage', 'kf_potassium_usage', 'cf_compound_usage',
  282. 'of_organic_usage', 'p_pesticide_usage', 'ff_farmyard_usage', 'af_film_usage'
  283. ]
  284. for attr in required_attrs:
  285. if not hasattr(request, attr):
  286. self.logger.warning(f"缺少必需参数: {attr}")
  287. return False
  288. value = getattr(request, attr)
  289. if value is None or value < 0:
  290. self.logger.warning(f"参数 {attr} 无效: {value}")
  291. return False
  292. return True
  293. except Exception as e:
  294. self.logger.error(f"验证参数时发生错误: {str(e)}")
  295. return False