service.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. import asyncio
  2. from app.api.v1.module_system.auth.schema import AuthSchema
  3. from app.core.alipay import AlipayClient
  4. from app.core.exceptions import CustomException
  5. from app.core.logger import log
  6. from app.plugin.module_payment.expense.institution.schema import InstitutionListOutSchema, InstitutionCreateSchema
  7. from .crud import InstitutionCRUD
  8. from .enums import InstitutionStatusEnum
  9. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionCreateRequest import (
  10. AlipayEbppInvoiceInstitutionCreateRequest,
  11. )
  12. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionCreateModel import (
  13. AlipayEbppInvoiceInstitutionCreateModel,
  14. )
  15. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionCreateResponse import (
  16. AlipayEbppInvoiceInstitutionCreateResponse,
  17. )
  18. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionPageinfoQueryRequest import (
  19. AlipayEbppInvoiceInstitutionPageinfoQueryRequest,
  20. )
  21. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionPageinfoQueryModel import (
  22. AlipayEbppInvoiceInstitutionPageinfoQueryModel,
  23. )
  24. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionPageinfoQueryResponse import (
  25. AlipayEbppInvoiceInstitutionPageinfoQueryResponse,
  26. )
  27. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionDetailinfoQueryRequest import (
  28. AlipayEbppInvoiceInstitutionDetailinfoQueryRequest,
  29. )
  30. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionDetailinfoQueryModel import (
  31. AlipayEbppInvoiceInstitutionDetailinfoQueryModel,
  32. )
  33. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionDetailinfoQueryResponse import (
  34. AlipayEbppInvoiceInstitutionDetailinfoQueryResponse,
  35. )
  36. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionDeleteRequest import (
  37. AlipayEbppInvoiceInstitutionDeleteRequest,
  38. )
  39. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionDeleteModel import (
  40. AlipayEbppInvoiceInstitutionDeleteModel,
  41. )
  42. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionDeleteResponse import (
  43. AlipayEbppInvoiceInstitutionDeleteResponse,
  44. )
  45. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionModifyRequest import (
  46. AlipayEbppInvoiceInstitutionModifyRequest,
  47. )
  48. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionModifyModel import (
  49. AlipayEbppInvoiceInstitutionModifyModel,
  50. )
  51. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionModifyResponse import (
  52. AlipayEbppInvoiceInstitutionModifyResponse,
  53. )
  54. class InstitutionService:
  55. """费控制度服务层"""
  56. @classmethod
  57. def _execute_alipay(cls, request):
  58. """同步执行支付宝调用(通过线程池避免阻塞事件循环)"""
  59. client = AlipayClient.get_client()
  60. return client.execute(request)
  61. @classmethod
  62. async def create_institution_service(
  63. cls, auth: AuthSchema, data: AlipayEbppInvoiceInstitutionCreateModel
  64. ) -> AlipayEbppInvoiceInstitutionCreateResponse:
  65. """
  66. 创建费控制度(仅调 institution.create,不包含串联流程)
  67. 调用: alipay.ebpp.invoice.institution.create
  68. """
  69. if data.enterprise_id is None:
  70. raise CustomException(msg="创建费控制度失败: 企业ID不能为空")
  71. data.currency = 'CNY'
  72. request = AlipayEbppInvoiceInstitutionCreateRequest()
  73. request.biz_model = data
  74. response = await asyncio.to_thread(cls._execute_alipay, request)
  75. if not response:
  76. raise CustomException(msg="创建费控制度失败: 无响应")
  77. result = AlipayEbppInvoiceInstitutionCreateResponse()
  78. result.parse_response_content(response)
  79. if not result.is_success():
  80. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  81. raise CustomException(msg=f"创建费控制度失败: {result.msg}")
  82. return result
  83. @classmethod
  84. async def create_institution_full_flow(
  85. cls,
  86. auth: AuthSchema,
  87. institution_model: AlipayEbppInvoiceInstitutionCreateModel,
  88. enterprise_id: str,
  89. scope_data: dict | None = None,
  90. issuerule_data: dict | None = None,
  91. ) -> dict:
  92. """
  93. 创建费控制度(完整串联流程)
  94. 流程:
  95. 1. institution.create → 获取 institution_id
  96. 2. scope.modify ← 如有适用成员数据(scope_data)
  97. 3. issuerule.create ← 如为"按固定周期发放"(issuerule_data)
  98. 4. 保存到本地DB
  99. """
  100. # 第1步:创建制度
  101. institution_result = await cls.create_institution_service(auth=auth, data=institution_model)
  102. institution_id = institution_result.institution_id
  103. try:
  104. # 第2步:设置适用成员(如有)
  105. scope_modified = False
  106. if scope_data and scope_data.get("adapter_type") and scope_data.get("adapter_type") != "NONE":
  107. await InstitutionScopeService.scope_modify_service(
  108. auth=auth,
  109. institution_id=institution_id,
  110. data={
  111. "enterprise_id": enterprise_id,
  112. "adapter_type": scope_data["adapter_type"],
  113. "owner_type": scope_data.get("owner_type"),
  114. "add_owner_id_list": scope_data.get("add_owner_id_list"),
  115. },
  116. )
  117. scope_modified = True
  118. log.info(f"成员设置成功: institution_id={institution_id}")
  119. # 第3步:创建自动发放规则(如为"按固定周期发放")
  120. issue_rule_id = None
  121. if issuerule_data:
  122. issuerule_result = await IssueruleService.create_issuerule_service(
  123. auth=auth,
  124. institution_id=institution_id,
  125. enterprise_id=enterprise_id,
  126. quota_type=issuerule_data.get("quota_type", "CAP"),
  127. issue_type=issuerule_data.get("issue_type", "ISSUE_MONTH"),
  128. issue_amount_value=issuerule_data.get("issue_amount_value", "0"),
  129. outer_source_id=issuerule_data.get("outer_source_id"),
  130. issue_rule_name=issuerule_data.get("issue_rule_name"),
  131. effective_period=issuerule_data.get("effective_period"),
  132. invalid_mode=issuerule_data.get("invalid_mode", 0),
  133. share_mode=issuerule_data.get("share_mode", 0),
  134. )
  135. issue_rule_id = issuerule_result.get("issue_rule_id")
  136. log.info(f"发放规则创建成功: institution_id={institution_id}, issue_rule_id={issue_rule_id}")
  137. except Exception as e:
  138. # 子步骤失败:删除已创建的支付宝制度(补偿事务)
  139. log.error(f"创建串联流程失败: {e},开始回滚 institution_id={institution_id}")
  140. try:
  141. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionDeleteRequest import (
  142. AlipayEbppInvoiceInstitutionDeleteRequest,
  143. )
  144. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionDeleteModel import (
  145. AlipayEbppInvoiceInstitutionDeleteModel,
  146. )
  147. rollback_model = AlipayEbppInvoiceInstitutionDeleteModel()
  148. rollback_model.institution_id = institution_id
  149. rollback_model.enterprise_id = enterprise_id
  150. req = AlipayEbppInvoiceInstitutionDeleteRequest()
  151. req.biz_model = rollback_model
  152. await asyncio.to_thread(cls._execute_alipay, req)
  153. log.info(f"回滚成功: 已删除 institution_id={institution_id}")
  154. except Exception as rollback_err:
  155. log.error(f"回滚失败: {rollback_err}")
  156. raise
  157. # 第4步:保存到本地DB
  158. create_data = InstitutionCreateSchema(
  159. enterprise_id=enterprise_id,
  160. institution_id=institution_id,
  161. institution_name=getattr(institution_model, 'institution_name', None),
  162. institution_desc=getattr(institution_model, 'institution_desc', None),
  163. scene_type=getattr(institution_model, 'scene_type', None),
  164. expense_type=getattr(institution_model, 'expense_type', None),
  165. expense_sub_type=getattr(institution_model, 'expense_sub_type', None),
  166. status=InstitutionStatusEnum.INSTITUTION_CREATE.value,
  167. effective=getattr(institution_model, 'effective', None),
  168. effective_start_date=getattr(institution_model, 'effective_start_date', None),
  169. effective_end_date=getattr(institution_model, 'effective_end_date', None),
  170. consult_mode=getattr(institution_model, 'consult_mode', None),
  171. multi_employee_share_mode=getattr(institution_model, 'multi_employee_share_mode', None),
  172. currency=getattr(institution_model, 'currency', None)
  173. )
  174. create_data_dict = create_data.model_dump(exclude_unset=True)
  175. if issue_rule_id:
  176. create_data_dict["issue_rule_id"] = issue_rule_id
  177. crud = InstitutionCRUD(auth)
  178. await crud.create(create_data_dict)
  179. return {
  180. "institution_id": institution_id,
  181. "scope_modified": scope_modified,
  182. "issue_rule_id": issue_rule_id,
  183. }
  184. @classmethod
  185. async def pageinfo_query_service(
  186. cls,
  187. auth: AuthSchema,
  188. enterprise_id: str,
  189. page_no: int = 1,
  190. page_size: int = 20,
  191. institution_name: str | None = None,
  192. ) -> dict:
  193. """
  194. 从支付宝查询费控制度列表
  195. 调用: alipay.ebpp.invoice.institution.pageinfo.query
  196. 失败时降级到本地DB
  197. """
  198. try:
  199. model = AlipayEbppInvoiceInstitutionPageinfoQueryModel()
  200. model.enterprise_id = enterprise_id
  201. model.page_num = page_no
  202. model.page_size = page_size
  203. if institution_name:
  204. model.institution_name = institution_name
  205. req = AlipayEbppInvoiceInstitutionPageinfoQueryRequest()
  206. req.biz_model = model
  207. response = await asyncio.to_thread(cls._execute_alipay, req)
  208. if response:
  209. result = AlipayEbppInvoiceInstitutionPageinfoQueryResponse()
  210. result.parse_response_content(response)
  211. if result.is_success():
  212. return {
  213. "page_no": getattr(result, 'page_num', page_no) or page_no,
  214. "page_size": getattr(result, 'page_size', page_size) or page_size,
  215. "total": getattr(result, 'total_page_count', 0) or 0,
  216. "list": getattr(result, 'institution_list', []) or [],
  217. }
  218. log.warning("支付宝 pageinfo.query 失败,降级到本地DB")
  219. except Exception as e:
  220. log.warning(f"支付宝 pageinfo.query 异常: {e},降级到本地DB")
  221. # 降级:查本地DB
  222. crud = InstitutionCRUD(auth)
  223. search = {"enterprise_id": enterprise_id}
  224. if institution_name:
  225. search["institution_name"] = institution_name
  226. offset = (page_no - 1) * page_size
  227. return await crud.page(
  228. offset=offset,
  229. limit=page_size,
  230. order_by=[{"id": "desc"}],
  231. search=search,
  232. out_schema=InstitutionListOutSchema,
  233. )
  234. @classmethod
  235. async def detailinfo_query_service(
  236. cls,
  237. auth: AuthSchema,
  238. institution_id: str,
  239. enterprise_id: str,
  240. ) -> dict | None:
  241. """
  242. 从支付宝查询费控制度详情
  243. 调用: alipay.ebpp.invoice.institution.detailinfo.query
  244. 失败时降级到本地DB
  245. """
  246. try:
  247. model = AlipayEbppInvoiceInstitutionDetailinfoQueryModel()
  248. model.institution_id = institution_id
  249. model.enterprise_id = enterprise_id
  250. req = AlipayEbppInvoiceInstitutionDetailinfoQueryRequest()
  251. req.biz_model = model
  252. response = await asyncio.to_thread(cls._execute_alipay, req)
  253. if response:
  254. result = AlipayEbppInvoiceInstitutionDetailinfoQueryResponse()
  255. result.parse_response_content(response)
  256. if result.is_success():
  257. return result.to_alipay_dict()
  258. log.warning("支付宝 detailinfo.query 失败,降级到本地DB")
  259. except Exception as e:
  260. log.warning(f"支付宝 detailinfo.query 异常: {e},降级到本地DB")
  261. # 降级:查本地DB
  262. crud = InstitutionCRUD(auth)
  263. obj = await crud.get(institution_id=institution_id, enterprise_id=enterprise_id)
  264. if obj:
  265. return InstitutionListOutSchema.model_validate(obj).model_dump()
  266. return None
  267. @classmethod
  268. async def list_service(
  269. cls,
  270. auth: AuthSchema,
  271. page_no: int = 1,
  272. page_size: int = 20,
  273. search: dict | None = None,
  274. ) -> dict:
  275. """
  276. 查询费控制度列表
  277. 优先调支付宝,失败降级到本地DB
  278. """
  279. enterprise_id = (search or {}).get("enterprise_id", "")
  280. institution_name = (search or {}).get("name") or (search or {}).get("institution_name")
  281. if enterprise_id:
  282. return await cls.pageinfo_query_service(
  283. auth=auth,
  284. enterprise_id=enterprise_id,
  285. page_no=page_no,
  286. page_size=page_size,
  287. institution_name=institution_name,
  288. )
  289. # 无 enterprise_id 时直接查本地
  290. crud = InstitutionCRUD(auth)
  291. offset = (page_no - 1) * page_size
  292. return await crud.page(
  293. offset=offset,
  294. limit=page_size,
  295. order_by=[{"id": "desc"}],
  296. search=search or {},
  297. out_schema=InstitutionListOutSchema,
  298. )
  299. @classmethod
  300. async def delete_institution_service(
  301. cls, auth: AuthSchema, data: AlipayEbppInvoiceInstitutionDeleteModel
  302. ) -> AlipayEbppInvoiceInstitutionDeleteResponse:
  303. """
  304. 删除费控制度
  305. 调用: alipay.ebpp.invoice.institution.delete
  306. """
  307. request = AlipayEbppInvoiceInstitutionDeleteRequest()
  308. request.biz_model = data
  309. response = await asyncio.to_thread(cls._execute_alipay, request)
  310. if not response:
  311. raise CustomException(msg="删除费控制度失败: 无响应")
  312. result = AlipayEbppInvoiceInstitutionDeleteResponse()
  313. result.parse_response_content(response)
  314. if not result.is_success():
  315. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  316. raise CustomException(msg=f"删除费控制度失败: {result.msg}")
  317. # 同步删除本地记录
  318. try:
  319. crud = InstitutionCRUD(auth)
  320. institution_id = getattr(data, 'institution_id', None)
  321. if institution_id:
  322. obj = await crud.get(institution_id=institution_id)
  323. if obj:
  324. await crud.delete(ids=[obj.id])
  325. log.info(f"已删除本地记录: institution_id={institution_id}")
  326. except Exception as e:
  327. log.warning(f"删除本地记录失败(不影响支付宝侧): {e}")
  328. return result
  329. @classmethod
  330. async def modify_institution_service(
  331. cls, auth: AuthSchema, data: AlipayEbppInvoiceInstitutionModifyModel
  332. ) -> AlipayEbppInvoiceInstitutionModifyResponse:
  333. """
  334. 编辑费控制度
  335. 调用: alipay.ebpp.invoice.institution.modify
  336. """
  337. if data.institution_id is None:
  338. raise CustomException(msg="编辑费控制度失败: 制度ID不能为空")
  339. request = AlipayEbppInvoiceInstitutionModifyRequest()
  340. request.biz_model = data
  341. response = await asyncio.to_thread(cls._execute_alipay, request)
  342. if not response:
  343. raise CustomException(msg="编辑费控制度失败: 无响应")
  344. result = AlipayEbppInvoiceInstitutionModifyResponse()
  345. result.parse_response_content(response)
  346. if not result.is_success():
  347. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  348. raise CustomException(msg=f"编辑费控制度失败: {result.msg}")
  349. # 同步更新本地数据库状态
  350. try:
  351. crud = InstitutionCRUD(auth)
  352. institution_id = getattr(data, 'institution_id', None)
  353. if institution_id:
  354. update_data = {}
  355. if hasattr(data, 'institution_name') and data.institution_name:
  356. update_data['institution_name'] = data.institution_name
  357. if hasattr(data, 'institution_desc') and data.institution_desc:
  358. update_data['institution_desc'] = data.institution_desc
  359. if hasattr(data, 'effective') and data.effective is not None:
  360. update_data['effective'] = data.effective
  361. update_data['status'] = (
  362. InstitutionStatusEnum.INSTITUTION_EFFECTIVE.value
  363. if data.effective == "1"
  364. else InstitutionStatusEnum.INSTITUTION_INVALID.value
  365. )
  366. if hasattr(data, 'effective_start_date') and data.effective_start_date:
  367. update_data['effective_start_date'] = data.effective_start_date
  368. if hasattr(data, 'effective_end_date') and data.effective_end_date:
  369. update_data['effective_end_date'] = data.effective_end_date
  370. if update_data:
  371. await crud.update_by_institution_id(institution_id, update_data)
  372. log.info(f"已更新本地记录: institution_id={institution_id}")
  373. except Exception as e:
  374. log.warning(f"更新本地记录失败(不影响支付宝侧): {e}")
  375. return result
  376. class InstitutionScopeService:
  377. """费控制度成员范围服务层"""
  378. @classmethod
  379. def _execute_alipay(cls, request):
  380. """同步执行支付宝调用"""
  381. client = AlipayClient.get_client()
  382. return client.execute(request)
  383. @classmethod
  384. async def scope_modify_service(
  385. cls,
  386. auth: AuthSchema,
  387. institution_id: str,
  388. data: dict,
  389. ) -> dict:
  390. """
  391. 设置/修改制度成员范围
  392. 调用: alipay.ebpp.invoice.institution.scope.modify
  393. """
  394. try:
  395. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionScopeModifyRequest import (
  396. AlipayEbppInvoiceInstitutionScopeModifyRequest,
  397. )
  398. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionScopeModifyModel import (
  399. AlipayEbppInvoiceInstitutionScopeModifyModel,
  400. )
  401. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionScopeModifyResponse import (
  402. AlipayEbppInvoiceInstitutionScopeModifyResponse,
  403. )
  404. except ImportError:
  405. raise CustomException(msg="支付宝SDK未正确安装(alipay-ebpp-invoice-institution-scope-modify)")
  406. model = AlipayEbppInvoiceInstitutionScopeModifyModel()
  407. model.institution_id = institution_id
  408. model.enterprise_id = data.get("enterprise_id", "")
  409. model.adapter_type = data.get("adapter_type", "EMPLOYEE_ALL")
  410. if data.get("owner_type"):
  411. model.owner_type = data["owner_type"]
  412. if data.get("add_owner_id_list"):
  413. model.add_owner_id_list = data["add_owner_id_list"]
  414. if data.get("delete_owner_id_list"):
  415. model.delete_owner_id_list = data["delete_owner_id_list"]
  416. request = AlipayEbppInvoiceInstitutionScopeModifyRequest()
  417. request.biz_model = model
  418. response = await asyncio.to_thread(cls._execute_alipay, request)
  419. if not response:
  420. raise CustomException(msg="设置制度成员失败: 无响应")
  421. result = AlipayEbppInvoiceInstitutionScopeModifyResponse()
  422. result.parse_response_content(response)
  423. if not result.is_success():
  424. log.error(f"设置制度成员失败: {result.code} - {result.msg}")
  425. raise CustomException(msg=f"设置制度成员失败: {result.msg}")
  426. return {"result": True}
  427. @classmethod
  428. async def scopepageinfo_query_service(
  429. cls,
  430. auth: AuthSchema,
  431. institution_id: str,
  432. enterprise_id: str | None = None,
  433. page_num: int = 1,
  434. page_size: int = 20,
  435. owner_type: str | None = None,
  436. ) -> dict:
  437. """
  438. 查询制度成员范围
  439. 调用: alipay.ebpp.invoice.institution.scopepageinfo.query
  440. """
  441. try:
  442. from alipay.aop.api.request.AlipayEbppInvoiceInstitutionScopepageinfoQueryRequest import (
  443. AlipayEbppInvoiceInstitutionScopepageinfoQueryRequest,
  444. )
  445. from alipay.aop.api.domain.AlipayEbppInvoiceInstitutionScopepageinfoQueryModel import (
  446. AlipayEbppInvoiceInstitutionScopepageinfoQueryModel,
  447. )
  448. from alipay.aop.api.response.AlipayEbppInvoiceInstitutionScopepageinfoQueryResponse import (
  449. AlipayEbppInvoiceInstitutionScopepageinfoQueryResponse,
  450. )
  451. except ImportError:
  452. raise CustomException(msg="支付宝SDK未正确安装(alipay-ebpp-invoice-institution-scopepageinfo-query)")
  453. model = AlipayEbppInvoiceInstitutionScopepageinfoQueryModel()
  454. model.institution_id = institution_id
  455. model.page_num = page_num
  456. model.page_size = page_size
  457. if enterprise_id:
  458. model.enterprise_id = enterprise_id
  459. if owner_type:
  460. model.owner_type = owner_type
  461. request = AlipayEbppInvoiceInstitutionScopepageinfoQueryRequest()
  462. request.biz_model = model
  463. response = await asyncio.to_thread(cls._execute_alipay, request)
  464. if not response:
  465. raise CustomException(msg="查询制度成员失败: 无响应")
  466. result = AlipayEbppInvoiceInstitutionScopepageinfoQueryResponse()
  467. result.parse_response_content(response)
  468. if not result.is_success():
  469. log.error(f"查询制度成员失败: {result.code} - {result.msg}")
  470. raise CustomException(msg=f"查询制度成员失败: {result.msg}")
  471. return {
  472. "page_num": getattr(result, 'page_num', page_num) or page_num,
  473. "page_size": getattr(result, 'page_size', page_size) or page_size,
  474. "total_page_count": getattr(result, 'total_page_count', 0) or 0,
  475. "adapter_type": getattr(result, 'adapter_type', None),
  476. "owner_id_list": getattr(result, 'owner_id_list', []) or [],
  477. "owner_open_id_list": getattr(result, 'onwer_open_id_list', []) or [],
  478. "scope_info_list": [
  479. {
  480. "adapter_type": getattr(result, 'adapter_type', None),
  481. "owner_id_list": getattr(result, 'owner_id_list', []) or [],
  482. "owner_open_id_list": getattr(result, 'onwer_open_id_list', []) or [],
  483. }
  484. ] if getattr(result, 'adapter_type', None) else [],
  485. }
  486. class IssueruleService:
  487. """自动额度发放规则服务层"""
  488. ISSUE_TYPE_MAP = {
  489. "daily": "ISSUE_DAY",
  490. "weekly": "ISSUE_WEEK",
  491. "monthly": "ISSUE_MONTH",
  492. "quarterly": "ISSUE_QUARTER",
  493. "yearly": "ISSUE_YEAR",
  494. }
  495. @classmethod
  496. def _execute_alipay(cls, request):
  497. client = AlipayClient.get_client()
  498. return client.execute(request)
  499. @classmethod
  500. async def create_issuerule_service(
  501. cls,
  502. auth: AuthSchema,
  503. institution_id: str,
  504. enterprise_id: str,
  505. quota_type: str,
  506. issue_type: str,
  507. issue_amount_value: str,
  508. outer_source_id: str | None = None,
  509. issue_rule_name: str | None = None,
  510. effective_period: str | None = None,
  511. invalid_mode: int | None = None,
  512. share_mode: int | None = None,
  513. ) -> dict:
  514. """
  515. 创建自动额度发放规则
  516. 调用: alipay.ebpp.invoice.issuerule.create
  517. """
  518. try:
  519. from alipay.aop.api.request.AlipayEbppInvoiceIssueruleCreateRequest import (
  520. AlipayEbppInvoiceIssueruleCreateRequest,
  521. )
  522. from alipay.aop.api.domain.AlipayEbppInvoiceIssueruleCreateModel import (
  523. AlipayEbppInvoiceIssueruleCreateModel,
  524. )
  525. from alipay.aop.api.response.AlipayEbppInvoiceIssueruleCreateResponse import (
  526. AlipayEbppInvoiceIssueruleCreateResponse,
  527. )
  528. except ImportError:
  529. raise CustomException(msg="支付宝SDK未正确安装(alipay-ebpp-invoice-issuerule-create)")
  530. # 参数约束校验
  531. if quota_type == "CAP" and invalid_mode is not None and invalid_mode != 1:
  532. raise CustomException(msg="余额类型(CP)的发放规则必须为可累计(invalid_mode=1)")
  533. if quota_type == "COUNT" and share_mode is not None and share_mode != 0:
  534. raise CustomException(msg="次卡类型(COUNT)的发放规则不可转赠(share_mode=0)")
  535. model = AlipayEbppInvoiceIssueruleCreateModel()
  536. model.target_type = "INSTITUTION"
  537. model.target_id = institution_id
  538. model.quota_type = quota_type
  539. model.issue_type = issue_type
  540. model.issue_amount_value = issue_amount_value
  541. model.enterprise_id = enterprise_id
  542. if outer_source_id:
  543. model.outer_source_id = outer_source_id
  544. if issue_rule_name:
  545. model.issue_rule_name = issue_rule_name
  546. if effective_period:
  547. model.effective_period = effective_period
  548. if invalid_mode is not None:
  549. model.invalid_mode = invalid_mode
  550. if share_mode is not None:
  551. model.share_mode = share_mode
  552. request = AlipayEbppInvoiceIssueruleCreateRequest()
  553. request.biz_model = model
  554. response = await asyncio.to_thread(cls._execute_alipay, request)
  555. if not response:
  556. raise CustomException(msg="创建发放规则失败: 无响应")
  557. result = AlipayEbppInvoiceIssueruleCreateResponse()
  558. result.parse_response_content(response)
  559. if not result.is_success():
  560. log.error(f"创建发放规则失败: {result.code} - {result.msg}")
  561. raise CustomException(msg=f"创建发放规则失败: {result.msg}")
  562. return {
  563. "issue_rule_id": getattr(result, 'issue_rule_id', None),
  564. }
  565. @classmethod
  566. async def modify_issuerule_service(
  567. cls,
  568. auth: AuthSchema,
  569. institution_id: str,
  570. issue_rule_id: str,
  571. enterprise_id: str,
  572. quota_type: str | None = None,
  573. issue_type: str | None = None,
  574. issue_amount_value: str | None = None,
  575. issue_rule_name: str | None = None,
  576. effective: str | None = None,
  577. effective_period: str | None = None,
  578. invalid_mode: int | None = None,
  579. share_mode: int | None = None,
  580. ) -> dict:
  581. """
  582. 编辑自动额度发放规则
  583. 调用: alipay.ebpp.invoice.issuerule.modify
  584. """
  585. try:
  586. from alipay.aop.api.request.AlipayEbppInvoiceIssueruleModifyRequest import (
  587. AlipayEbppInvoiceIssueruleModifyRequest,
  588. )
  589. from alipay.aop.api.domain.AlipayEbppInvoiceIssueruleModifyModel import (
  590. AlipayEbppInvoiceIssueruleModifyModel,
  591. )
  592. from alipay.aop.api.response.AlipayEbppInvoiceIssueruleModifyResponse import (
  593. AlipayEbppInvoiceIssueruleModifyResponse,
  594. )
  595. except ImportError:
  596. raise CustomException(msg="支付宝SDK未正确安装(alipay-ebpp-invoice-issuerule-modify)")
  597. model = AlipayEbppInvoiceIssueruleModifyModel()
  598. model.target_type = "INSTITUTION"
  599. model.target_id = institution_id
  600. model.issue_rule_id = issue_rule_id
  601. model.action = "MODIFY_BASIC_INFO"
  602. model.enterprise_id = enterprise_id
  603. if issue_rule_name:
  604. model.issue_rule_name = issue_rule_name
  605. if quota_type:
  606. model.quota_type = quota_type
  607. if issue_type:
  608. model.issue_type = issue_type
  609. if issue_amount_value:
  610. model.issue_amount_value = issue_amount_value
  611. if effective is not None:
  612. model.effective = effective
  613. if effective_period:
  614. model.effective_period = effective_period
  615. if invalid_mode is not None:
  616. model.invalid_mode = invalid_mode
  617. if share_mode is not None:
  618. model.share_mode = share_mode
  619. request = AlipayEbppInvoiceIssueruleModifyRequest()
  620. request.biz_model = model
  621. response = await asyncio.to_thread(cls._execute_alipay, request)
  622. if not response:
  623. raise CustomException(msg="编辑发放规则失败: 无响应")
  624. result = AlipayEbppInvoiceIssueruleModifyResponse()
  625. result.parse_response_content(response)
  626. if not result.is_success():
  627. log.error(f"编辑发放规则失败: {result.code} - {result.msg}")
  628. raise CustomException(msg=f"编辑发放规则失败: {result.msg}")
  629. return {"result": True}
  630. @classmethod
  631. async def delete_issuerule_service(
  632. cls,
  633. auth: AuthSchema,
  634. institution_id: str,
  635. issue_rule_id_list: list[str],
  636. enterprise_id: str,
  637. ) -> dict:
  638. """
  639. 删除自动额度发放规则
  640. 调用: alipay.ebpp.invoice.issuerule.delete
  641. """
  642. try:
  643. from alipay.aop.api.request.AlipayEbppInvoiceIssueruleDeleteRequest import (
  644. AlipayEbppInvoiceIssueruleDeleteRequest,
  645. )
  646. from alipay.aop.api.domain.AlipayEbppInvoiceIssueruleDeleteModel import (
  647. AlipayEbppInvoiceIssueruleDeleteModel,
  648. )
  649. from alipay.aop.api.response.AlipayEbppInvoiceIssueruleDeleteResponse import (
  650. AlipayEbppInvoiceIssueruleDeleteResponse,
  651. )
  652. except ImportError:
  653. raise CustomException(msg="支付宝SDK未正确安装(alipay-ebpp-invoice-issuerule-delete)")
  654. model = AlipayEbppInvoiceIssueruleDeleteModel()
  655. model.target_type = "INSTITUTION"
  656. model.target_id = institution_id
  657. model.issue_rule_id_list = issue_rule_id_list
  658. model.enterprise_id = enterprise_id
  659. request = AlipayEbppInvoiceIssueruleDeleteRequest()
  660. request.biz_model = model
  661. response = await asyncio.to_thread(cls._execute_alipay, request)
  662. if not response:
  663. raise CustomException(msg="删除发放规则失败: 无响应")
  664. result = AlipayEbppInvoiceIssueruleDeleteResponse()
  665. result.parse_response_content(response)
  666. if not result.is_success():
  667. log.error(f"删除发放规则失败: {result.code} - {result.msg}")
  668. raise CustomException(msg=f"删除发放规则失败: {result.msg}")
  669. return {"result": True}