service.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. # -*- coding: utf-8 -*-
  2. import io
  3. from fastapi import UploadFile
  4. import pandas as pd
  5. from redis.asyncio import Redis
  6. from app.common.enums import RedisInitKeyConfig
  7. from app.core.base_schema import BatchSetAvailable
  8. from app.core.exceptions import CustomException
  9. from app.core.redis_crud import RedisCURD
  10. from app.utils.excel_util import ExcelUtil
  11. from app.core.logger import log
  12. from app.api.v1.module_system.auth.schema import AuthSchema
  13. from .schema import BizMecCreateSchema, BizMecUpdateSchema, BizMecOutSchema, BizMecQueryParam
  14. from .crud import BizMecCRUD
  15. from ..crane.crud import BizCraneCRUD
  16. from ..crane.model import BizCraneModel
  17. class BizMecService:
  18. """
  19. 机构信息服务层
  20. """
  21. @classmethod
  22. async def detail_mec_service(cls, auth: AuthSchema, id: int) -> dict:
  23. """详情"""
  24. obj = await BizMecCRUD(auth).get_by_id_mec_crud(id=id)
  25. if not obj:
  26. raise CustomException(msg="该数据不存在")
  27. crane = await BizCraneCRUD(auth).get_by_id_crane_crud_for_no(obj.crane_no)
  28. res = BizMecOutSchema.model_validate(obj).model_dump()
  29. res['crane_name'] = crane.crane_name
  30. return res
  31. @classmethod
  32. async def detail_mec_service_for_no(cls, auth: AuthSchema, mec_no: str) -> dict:
  33. """详情"""
  34. obj = await BizMecCRUD(auth).get_by_id_mec_crud_for_no(mec_no=mec_no)
  35. if not obj:
  36. raise CustomException(msg="该数据不存在")
  37. return BizMecOutSchema.model_validate(obj).model_dump()
  38. @classmethod
  39. async def list_mec_service(cls, auth: AuthSchema, search: BizMecQueryParam | None = None, order_by: list[dict] | None = None) -> list[dict]:
  40. """列表查询"""
  41. search_dict = search.__dict__ if search else None
  42. obj_list = await BizMecCRUD(auth).list_mec_crud(search=search_dict, order_by=order_by)
  43. return [BizMecOutSchema.model_validate(obj).model_dump() for obj in obj_list]
  44. @classmethod
  45. async def page_mec_service(cls, auth: AuthSchema, page_no: int, page_size: int, search: BizMecQueryParam | None = None, order_by: list[dict] | None = None) -> dict:
  46. """分页查询(数据库分页)"""
  47. search_dict = search.__dict__ if search else {}
  48. order_by_list = order_by or [{'id': 'asc'}]
  49. offset = (page_no - 1) * page_size
  50. result = await BizMecCRUD(auth).page_mec_crud(
  51. offset=offset,
  52. limit=page_size,
  53. order_by=order_by_list,
  54. search=search_dict
  55. )
  56. for item in result.get('items'):
  57. crane_model: BizCraneModel | None = await BizCraneCRUD(auth).get_by_id_crane_crud_for_no(crane_no=item['crane_no'])
  58. item['crane_name'] = crane_model.crane_name
  59. return result
  60. @classmethod
  61. async def create_mec_service(cls, auth: AuthSchema, data: BizMecCreateSchema,redis: Redis) -> dict:
  62. """创建"""
  63. # 检查唯一性约束
  64. obj = await BizMecCRUD(auth).create_mec_crud(data=data)
  65. if obj:
  66. # 更新缓存中数据
  67. await RedisCURD(redis).clear(f"{RedisInitKeyConfig.VAR_DICT.key}:*")
  68. return BizMecOutSchema.model_validate(obj).model_dump()
  69. @classmethod
  70. async def update_mec_service(cls, auth: AuthSchema, id: int, data: BizMecUpdateSchema,redis: Redis) -> dict:
  71. """更新"""
  72. # 检查数据是否存在
  73. obj = await BizMecCRUD(auth).get_by_id_mec_crud(id=id)
  74. if not obj:
  75. raise CustomException(msg='更新失败,该数据不存在')
  76. # 检查唯一性约束
  77. obj = await BizMecCRUD(auth).update_mec_crud(id=id, data=data)
  78. if obj:
  79. # 更新缓存中数据
  80. await RedisCURD(redis).clear(f"{RedisInitKeyConfig.VAR_DICT.key}:*")
  81. return BizMecOutSchema.model_validate(obj).model_dump()
  82. @classmethod
  83. async def delete_mec_service(cls, auth: AuthSchema, ids: list[int],redis: Redis) -> None:
  84. """删除"""
  85. if len(ids) < 1:
  86. raise CustomException(msg='删除失败,删除对象不能为空')
  87. for id in ids:
  88. obj = await BizMecCRUD(auth).get_by_id_mec_crud(id=id)
  89. if not obj:
  90. raise CustomException(msg=f'删除失败,ID为{id}的数据不存在')
  91. await BizMecCRUD(auth).delete_mec_crud(ids=ids)
  92. # 更新缓存中数据
  93. await RedisCURD(redis).clear(f"{RedisInitKeyConfig.VAR_DICT.key}:*")
  94. @classmethod
  95. async def set_available_mec_service(cls, auth: AuthSchema, data: BatchSetAvailable,redis: Redis) -> None:
  96. """批量设置状态"""
  97. await BizMecCRUD(auth).set_available_mec_crud(ids=data.ids, status=data.status)
  98. # 更新缓存中数据
  99. await RedisCURD(redis).clear(f"{RedisInitKeyConfig.VAR_DICT.key}:*")
  100. @classmethod
  101. async def batch_export_mec_service(cls, obj_list: list[dict]) -> bytes:
  102. """批量导出"""
  103. mapping_dict = {
  104. 'id': '编号 ',
  105. 'crane_no': '起重机编号 ',
  106. 'mec_no': '机构编号 ',
  107. 'mec_category': '机构分类 MecCategory',
  108. 'mec_type': '机构类型 MecType',
  109. 'hoist_weight': '起升重量 起升机构',
  110. 'hoist_height': '起升高度 起升机构',
  111. 'hoist_speed': '起升速度 起升机构',
  112. 'rope_count': '钢线绳受力根数 起升机构',
  113. 'rope_max_pull': '钢丝绳最大拉力 起升机构',
  114. 'rope_diameter': '钢丝绳直径 起升机构',
  115. 'rope_model': '钢丝绳型号 起升机构',
  116. 'car_speed': '速度 平移机构',
  117. 'wheel_span': '轮距 平移机构',
  118. 'track_span': '轨距 平移机构',
  119. 'wheel_pressure': '轮压 平移机构',
  120. 'track_model': '轨道型号 平移机构',
  121. 'track_diameter': '车轨直径 平移机构',
  122. 'track_count': '车轨数量 平移机构',
  123. 'word_system': '工作制度 ',
  124. 'reducer_model': '减速机型号 ',
  125. 'reducer_trans_ratio': '减速机传动比 ',
  126. 'reducer_total_trans_ratio': '减速机总传动比 ',
  127. 'motor_model': '电机型号 ',
  128. 'motor_voltage': '电机额定电压 ',
  129. 'motor_current': '电机额定电流 ',
  130. 'motor_power': '电机额定功率 ',
  131. 'motor_speed': '电机转速 ',
  132. 'motor_count': '电机数量 ',
  133. 'bradk_model': '制动器型号 ',
  134. 'brake_torque': '制动器制动力矩 ',
  135. 'brake_wheel_diameter': '制动器轮径 ',
  136. 'brake_count': '制动器数量 ',
  137. 'sort': '排序 ',
  138. 'uuid': 'UUID全局唯一标识',
  139. 'status': '是否启用(0:启用 1:禁用)',
  140. 'description': '备注/描述',
  141. 'created_time': '创建时间',
  142. 'updated_time': '更新时间',
  143. 'hoist_weight_var_code': '重量关联变量',
  144. 'stroke_var_code': '行程高度变量',
  145. 'is_canvas_show': '是否首页动画展示',
  146. 'is_canvas_move': '是否启用动画',
  147. 'gear_select_var_codes': '机构选择变量',
  148. 'vib_gear_value': '振动分析挡位',
  149. 'updated_id': '更新者ID',
  150. }
  151. data = obj_list.copy()
  152. for item in data:
  153. # 状态转换
  154. if 'status' in item:
  155. item['status'] = '启用' if item.get('status') == '0' else '停用'
  156. # 创建者转换
  157. creator_info = item.get('creator')
  158. if isinstance(creator_info, dict):
  159. item['creator'] = creator_info.get('name', '未知')
  160. elif creator_info is None:
  161. item['creator'] = '未知'
  162. return ExcelUtil.export_list2excel(list_data=data, mapping_dict=mapping_dict)
  163. @classmethod
  164. async def batch_import_mec_service(cls, auth: AuthSchema, file: UploadFile, update_support: bool = False) -> str:
  165. """批量导入"""
  166. header_dict = {
  167. '编号 ': 'id',
  168. '起重机编号 ': 'crane_no',
  169. '机构编号 ': 'mec_no',
  170. '机构分类 MecCategory': 'mec_category',
  171. '机构类型 MecType': 'mec_type',
  172. '起升重量 起升机构': 'hoist_weight',
  173. '起升高度 起升机构': 'hoist_height',
  174. '起升速度 起升机构': 'hoist_speed',
  175. '钢线绳受力根数 起升机构': 'rope_count',
  176. '钢丝绳最大拉力 起升机构': 'rope_max_pull',
  177. '钢丝绳直径 起升机构': 'rope_diameter',
  178. '钢丝绳型号 起升机构': 'rope_model',
  179. '速度 平移机构': 'car_speed',
  180. '轮距 平移机构': 'wheel_span',
  181. '轨距 平移机构': 'track_span',
  182. '轮压 平移机构': 'wheel_pressure',
  183. '轨道型号 平移机构': 'track_model',
  184. '车轨直径 平移机构': 'track_diameter',
  185. '车轨数量 平移机构': 'track_count',
  186. '工作制度 ': 'word_system',
  187. '减速机型号 ': 'reducer_model',
  188. '减速机传动比 ': 'reducer_trans_ratio',
  189. '减速机总传动比 ': 'reducer_total_trans_ratio',
  190. '电机型号 ': 'motor_model',
  191. '电机额定电压 ': 'motor_voltage',
  192. '电机额定电流 ': 'motor_current',
  193. '电机额定功率 ': 'motor_power',
  194. '电机转速 ': 'motor_speed',
  195. '电机数量 ': 'motor_count',
  196. '制动器型号 ': 'bradk_model',
  197. '制动器制动力矩 ': 'brake_torque',
  198. '制动器轮径 ': 'brake_wheel_diameter',
  199. '制动器数量 ': 'brake_count',
  200. '排序 ': 'sort',
  201. 'UUID全局唯一标识': 'uuid',
  202. '是否启用(0:启用 1:禁用)': 'status',
  203. '备注/描述': 'description',
  204. '创建时间': 'created_time',
  205. '更新时间': 'updated_time',
  206. '重量关联变量': 'hoist_weight_var_code',
  207. '行程高度变量': 'stroke_var_code',
  208. '是否首页动画展示': 'is_canvas_show',
  209. '是否启用动画': 'is_canvas_move',
  210. '机构选择变量': 'gear_select_var_codes',
  211. '振动分析挡位': 'vib_gear_value',
  212. }
  213. try:
  214. contents = await file.read()
  215. df = pd.read_excel(io.BytesIO(contents))
  216. await file.close()
  217. if df.empty:
  218. raise CustomException(msg="导入文件为空")
  219. missing_headers = [header for header in header_dict.keys() if header not in df.columns]
  220. if missing_headers:
  221. raise CustomException(msg=f"导入文件缺少必要的列: {', '.join(missing_headers)}")
  222. df.rename(columns=header_dict, inplace=True)
  223. # 验证必填字段
  224. error_msgs = []
  225. success_count = 0
  226. count = 0
  227. for index, row in df.iterrows():
  228. count += 1
  229. try:
  230. data = {
  231. "id": row['id'],
  232. "crane_no": row['crane_no'],
  233. "mec_no": row['mec_no'],
  234. "mec_category": row['mec_category'],
  235. "mec_type": row['mec_type'],
  236. "hoist_weight": row['hoist_weight'],
  237. "hoist_height": row['hoist_height'],
  238. "hoist_speed": row['hoist_speed'],
  239. "rope_count": row['rope_count'],
  240. "rope_max_pull": row['rope_max_pull'],
  241. "rope_diameter": row['rope_diameter'],
  242. "rope_model": row['rope_model'],
  243. "car_speed": row['car_speed'],
  244. "wheel_span": row['wheel_span'],
  245. "track_span": row['track_span'],
  246. "wheel_pressure": row['wheel_pressure'],
  247. "track_model": row['track_model'],
  248. "track_diameter": row['track_diameter'],
  249. "track_count": row['track_count'],
  250. "word_system": row['word_system'],
  251. "reducer_model": row['reducer_model'],
  252. "reducer_trans_ratio": row['reducer_trans_ratio'],
  253. "reducer_total_trans_ratio": row['reducer_total_trans_ratio'],
  254. "motor_model": row['motor_model'],
  255. "motor_voltage": row['motor_voltage'],
  256. "motor_current": row['motor_current'],
  257. "motor_power": row['motor_power'],
  258. "motor_speed": row['motor_speed'],
  259. "motor_count": row['motor_count'],
  260. "bradk_model": row['bradk_model'],
  261. "brake_torque": row['brake_torque'],
  262. "brake_wheel_diameter": row['brake_wheel_diameter'],
  263. "brake_count": row['brake_count'],
  264. "sort": row['sort'],
  265. "uuid": row['uuid'],
  266. "status": row['status'],
  267. "description": row['description'],
  268. "created_time": row['created_time'],
  269. "updated_time": row['updated_time'],
  270. "hoist_weight_var_code": row['hoist_weight_var_code'],
  271. "stroke_var_code": row['stroke_var_code'],
  272. "is_canvas_show": row['is_canvas_show'],
  273. "is_canvas_move": row['is_canvas_move'],
  274. "gear_select_var_codes": row['gear_select_var_codes'],
  275. "vib_gear_value": row['vib_gear_value'],
  276. }
  277. # 使用CreateSchema做校验后入库
  278. create_schema = BizMecCreateSchema.model_validate(data)
  279. # 检查唯一性约束
  280. await BizMecCRUD(auth).create_mec_crud(data=create_schema)
  281. success_count += 1
  282. except Exception as e:
  283. error_msgs.append(f"第{count}行: {str(e)}")
  284. continue
  285. result = f"成功导入 {success_count} 条数据"
  286. if error_msgs:
  287. result += "\n错误信息:\n" + "\n".join(error_msgs)
  288. return result
  289. except Exception as e:
  290. log.error(f"批量导入失败: {str(e)}")
  291. raise CustomException(msg=f"导入失败: {str(e)}")
  292. @classmethod
  293. async def import_template_download_mec_service(cls) -> bytes:
  294. """下载导入模板"""
  295. header_list = [
  296. '编号 ',
  297. '起重机编号 ',
  298. '机构编号 ',
  299. '机构分类 MecCategory',
  300. '机构类型 MecType',
  301. '起升重量 起升机构',
  302. '起升高度 起升机构',
  303. '起升速度 起升机构',
  304. '钢线绳受力根数 起升机构',
  305. '钢丝绳最大拉力 起升机构',
  306. '钢丝绳直径 起升机构',
  307. '钢丝绳型号 起升机构',
  308. '速度 平移机构',
  309. '轮距 平移机构',
  310. '轨距 平移机构',
  311. '轮压 平移机构',
  312. '轨道型号 平移机构',
  313. '车轨直径 平移机构',
  314. '车轨数量 平移机构',
  315. '工作制度 ',
  316. '减速机型号 ',
  317. '减速机传动比 ',
  318. '减速机总传动比 ',
  319. '电机型号 ',
  320. '电机额定电压 ',
  321. '电机额定电流 ',
  322. '电机额定功率 ',
  323. '电机转速 ',
  324. '电机数量 ',
  325. '制动器型号 ',
  326. '制动器制动力矩 ',
  327. '制动器轮径 ',
  328. '制动器数量 ',
  329. '排序 ',
  330. 'UUID全局唯一标识',
  331. '是否启用(0:启用 1:禁用)',
  332. '备注/描述',
  333. '创建时间',
  334. '更新时间',
  335. '重量关联变量',
  336. '行程高度变量',
  337. '是否首页动画展示',
  338. '是否启用动画',
  339. '机构选择变量',
  340. '振动分析挡位',
  341. ]
  342. selector_header_list = []
  343. option_list = []
  344. # 添加下拉选项
  345. return ExcelUtil.get_excel_template(
  346. header_list=header_list,
  347. selector_header_list=selector_header_list,
  348. option_list=option_list
  349. )