project_quota.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. from typing import Optional
  2. import tools.utils as utils, threading
  3. from core.dtos import ProjectQuotaDto
  4. from core.models import ProjectQuotaModel
  5. from stores import ProjectQuotaStore, ChapterStore
  6. from core.log.log_record import LogRecordHelper
  7. from core.enum import OperationModule, OperationType, SendStatusEnum
  8. import executor
  9. class ProjectQuotaService:
  10. def __init__(self):
  11. self.store = ProjectQuotaStore()
  12. self.chapter_store = ChapterStore()
  13. self._logger = utils.get_logger()
  14. def get_quotas_paginated(
  15. self,
  16. budget_id: int,
  17. project_id: str,
  18. item_code: str,
  19. page: int = 1,
  20. page_size: int = 10,
  21. keyword: Optional[str] = None,
  22. send_status: Optional[int] = None,
  23. ):
  24. """获取项目定额列表
  25. Args:
  26. budget_id: 概算序号
  27. project_id: 项目编号
  28. item_code: 条目编号
  29. page: 页码
  30. page_size: 每页数量
  31. keyword: 关键字
  32. send_status: 发送状态
  33. Returns:
  34. dict: 包含总数和定额列表的字典
  35. """
  36. try:
  37. data = self.store.get_quotas_paginated(
  38. budget_id=budget_id,
  39. project_id=project_id,
  40. item_code=item_code,
  41. page=page,
  42. page_size=page_size,
  43. keyword=keyword,
  44. send_status=send_status,
  45. )
  46. return [
  47. ProjectQuotaDto.from_model(quota).to_dict()
  48. for quota in data.get("data", [])
  49. ], data.get("total", 0)
  50. except Exception as e:
  51. # 记录错误日志
  52. print(f"获取项目定额列表失败: {str(e)}")
  53. raise
  54. def get_quotas_by_task_paginated(
  55. self,
  56. task_id: int,
  57. budget_id,
  58. project_id,
  59. item_code,
  60. page: int = 1,
  61. page_size: int = 10,
  62. keyword: Optional[str] = None,
  63. send_status: Optional[int] = None,
  64. ):
  65. try:
  66. data = self.store.get_quotas_by_task_paginated(
  67. task_id=task_id,
  68. budget_id=budget_id,
  69. project_id=project_id,
  70. item_code=item_code,
  71. page=page,
  72. page_size=page_size,
  73. send_status=send_status,
  74. keyword=keyword,
  75. )
  76. return [
  77. ProjectQuotaDto.from_model(quota).to_dict()
  78. for quota in data.get("data", [])
  79. ], data.get("total", 0)
  80. except Exception as e:
  81. self._logger.error(f"获取任务项目定额列表失败: {str(e)}")
  82. raise
  83. def get_quota_dto(self, quota_id: int):
  84. try:
  85. return self.store.get_quota_dto(quota_id)
  86. except Exception as e:
  87. self._logger.error(f"获取定额条目DTO失败: {str(e)}")
  88. raise
  89. def get_quota(self, quota_id: int) -> Optional[ProjectQuotaModel]:
  90. """获取单个项目定额
  91. Args:
  92. quota_id: 定额ID
  93. Returns:
  94. Optional[ProjectQuotaDto]: 项目定额DTO对象
  95. """
  96. try:
  97. return self.store.get_quota(quota_id)
  98. except Exception as e:
  99. self._logger.error(f"获取定额条目失败: {str(e)}")
  100. raise
  101. def save_quota(self, quota_dto: ProjectQuotaDto) -> Optional[ProjectQuotaDto]:
  102. """保存定额"""
  103. try:
  104. # 业务验证
  105. if quota_dto.id == 0:
  106. quota_dto = self.create_quota(quota_dto)
  107. else:
  108. quota_dto = self.update_quota(quota_dto)
  109. if quota_dto.send_status != SendStatusEnum.NEW.value:
  110. self.update_send_status(quota_dto.id, SendStatusEnum.CHANGE.value)
  111. return quota_dto
  112. except Exception as e:
  113. self._logger.error(f"保存定额条目失败: {str(e)}")
  114. raise
  115. def create_quota(self, quota_dto: ProjectQuotaDto) -> ProjectQuotaDto:
  116. """创建项目定额
  117. Args:
  118. quota_dto: 定额DTO对象
  119. Returns:
  120. ProjectQuotaDto: 创建后的定额DTO对象
  121. """
  122. try:
  123. # 业务验证
  124. if not quota_dto.project_id or not quota_dto.budget_id:
  125. raise ValueError("项目编号和概算序号不能为空")
  126. LogRecordHelper.log_success(
  127. OperationType.CREATE,
  128. OperationModule.QUOTA,
  129. f"新增定额条目: {quota_dto.entry_name}",
  130. utils.to_str(quota_dto.to_dict()),
  131. )
  132. return self.store.create_quota(quota_dto)
  133. except Exception as e:
  134. self._logger.error(f"创建项目定额失败: {str(e)}")
  135. LogRecordHelper.log_fail(
  136. OperationType.CREATE,
  137. OperationModule.QUOTA,
  138. f"新增定额条目失败: {str(e)}",
  139. utils.to_str(quota_dto.to_dict()),
  140. )
  141. raise
  142. def update_quota(self, quota_dto: ProjectQuotaDto) -> Optional[ProjectQuotaDto]:
  143. """更新项目定额
  144. Args:
  145. quota_dto: 定额DTO对象
  146. Returns:
  147. Optional[ProjectQuotaDto]: 更新后的定额DTO对象
  148. """
  149. try:
  150. # 业务验证
  151. if not quota_dto.id:
  152. raise ValueError("定额ID不能为空")
  153. quota = self.get_quota_dto(quota_dto.id)
  154. log_data = utils.to_str(quota.to_dict())
  155. if quota:
  156. quota_dto.id = quota.id
  157. quota_dto.project_id = quota.project_id
  158. quota_dto.budget_id = quota.budget_id
  159. quota_dto.item_id = quota.item_id
  160. quota_dto.item_code = quota.item_code
  161. quota_dto.quota_id = quota.quota_id
  162. quota_dto.ex_file = quota.ex_file
  163. quota_dto.ex_row = quota.ex_row
  164. quota_dto.ex_cell = quota.ex_cell
  165. quota_dto.ex_unit = quota.ex_unit
  166. quota_dto.ex_amount = quota.ex_amount
  167. # self.update_process_status(quota_dto.id,4)
  168. LogRecordHelper.log_success(
  169. OperationType.UPDATE,
  170. OperationModule.QUOTA,
  171. f"修改定额条目: {quota_dto.entry_name}",
  172. log_data,
  173. utils.to_str(quota_dto.to_dict()),
  174. )
  175. return self.store.update_quota(quota_dto)
  176. except Exception as e:
  177. self._logger.error(f"更新项目定额失败: {str(e)}")
  178. LogRecordHelper.log_fail(
  179. OperationType.UPDATE,
  180. OperationModule.QUOTA,
  181. f"修改定额条目失败: {str(e)}",
  182. utils.to_str(quota_dto.to_dict()),
  183. )
  184. raise
  185. def delete_quota(self, quota_id: int) -> bool:
  186. """删除项目定额
  187. Args:
  188. quota_id: 定额ID
  189. Returns:
  190. bool: 删除是否成功
  191. """
  192. try:
  193. flag = self.store.delete_quota(quota_id)
  194. LogRecordHelper.log_success(
  195. OperationType.DELETE,
  196. OperationModule.QUOTA,
  197. f"删除定额条目: {quota_id}",
  198. f"{quota_id}",
  199. )
  200. return flag
  201. except Exception as e:
  202. self._logger.error(f"删除项目定额失败: {str(e)}")
  203. LogRecordHelper.log_fail(
  204. OperationType.DELETE,
  205. OperationModule.QUOTA,
  206. f"删除定额条目失败: {quota_id}",
  207. f"{quota_id}",
  208. )
  209. raise
  210. def save_change_chapter(self, project_id, ids, new_id):
  211. try:
  212. if not ids:
  213. return "请选择要变更的章节"
  214. chapter = self.chapter_store.get_chapter_dto(project_id, new_id)
  215. if not chapter:
  216. return "章节不存在"
  217. ids_arr = ids.split(",")
  218. err = ""
  219. for id_str in ids_arr:
  220. try:
  221. self.store.update_quota_chapter(
  222. int(id_str), chapter.item_id, chapter.item_code
  223. )
  224. except Exception as e:
  225. err += str(e) + ";"
  226. continue
  227. if err:
  228. LogRecordHelper.log_fail(
  229. OperationType.UPDATE,
  230. OperationModule.QUOTA,
  231. f"修改定额条目章节失败: {err}",
  232. ids,
  233. )
  234. return err
  235. LogRecordHelper.log_success(
  236. OperationType.UPDATE,
  237. OperationModule.QUOTA,
  238. f"修改定额条目章节",
  239. ids,
  240. )
  241. return None
  242. except Exception as e:
  243. msg = f"章节变更失败: {str(e)}"
  244. self._logger.error(msg)
  245. LogRecordHelper.log_fail(
  246. OperationType.UPDATE,
  247. OperationModule.QUOTA,
  248. f"修改定额条目章节失败: {str(e)}",
  249. ids,
  250. )
  251. return msg
  252. def update_send_status(self, quota_id: int, status: int, err: str = None) -> bool:
  253. """更新发送状态
  254. Args:
  255. quota_id: 定额ID
  256. status: 状态值
  257. err: 错误信息
  258. Returns:
  259. bool: 更新是否成功
  260. """
  261. try:
  262. return self.store.update_send_status(quota_id, status, err)
  263. except Exception as e:
  264. self._logger.error(f"更新项目定额发送状态失败: {str(e)}")
  265. raise
  266. # def start_process(self, quota_id: int) -> Optional[str]:
  267. # """启动处理"""
  268. # quota = self.get_quota_dto(quota_id)
  269. # if quota:
  270. # self.update_process_status(quota_id, 1)
  271. # thread = threading.Thread(target=self._process_quota, args=(quota,))
  272. # thread.start()
  273. # else:
  274. # return "定额条目没有查询到"
  275. #
  276. # def _process_quota(self, quota: ProjectQuotaDto):
  277. # try:
  278. # msg = executor.process_quota(quota)
  279. # if not msg:
  280. # self.start_send(quota.id)
  281. # except Exception as e:
  282. # self._logger.error(f"处理定额条目失败: {str(e)}")
  283. # self.update_process_status(quota.id, 3, str(e))
  284. # raise
  285. def start_send_by_ids(self, ids: str, is_cover: bool = False):
  286. try:
  287. id_list = ids.split(",")
  288. err = ""
  289. for _id in id_list:
  290. msg = self.start_send(int(_id), is_cover)
  291. if msg:
  292. err += f"{msg}[{_id}],"
  293. if err:
  294. LogRecordHelper.log_fail(
  295. OperationType.SEND,
  296. OperationModule.QUOTA,
  297. f"批量推送定额条目,部分失败: {err}",
  298. ids,
  299. )
  300. LogRecordHelper.log_success(
  301. OperationType.SEND,
  302. OperationModule.QUOTA,
  303. f"批量推送定额条目",
  304. ids,
  305. )
  306. return err
  307. except Exception as e:
  308. LogRecordHelper.log_fail(
  309. OperationType.SEND,
  310. OperationModule.QUOTA,
  311. f"批量推送定额条目失败: {str(e)}",
  312. ids,
  313. )
  314. self._logger.error(f"批量启动定额条目发送失败: {str(e)}")
  315. raise
  316. def start_send(self, _id: int, is_cover: bool = False) -> Optional[str]:
  317. """启动发送"""
  318. quota = self.get_quota_dto(_id)
  319. if quota:
  320. self.update_send_status(_id, 1)
  321. if not is_cover:
  322. quota.quota_id = 0
  323. thread = threading.Thread(target=self._send_quota, args=(quota,))
  324. thread.start()
  325. LogRecordHelper.log_success(
  326. OperationType.SEND,
  327. OperationModule.QUOTA,
  328. f"推送定额条目",
  329. f"ID:{_id},是否覆盖:{is_cover}",
  330. )
  331. return None
  332. else:
  333. return "定额条目没有查询到"
  334. def _send_quota(self, quota: ProjectQuotaDto):
  335. try:
  336. executor.send_quota(quota)
  337. except Exception as e:
  338. self._logger.error(f"发送定额条目失败: {str(e)}")
  339. self.update_send_status(quota.id, 3, str(e))
  340. raise