service.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. from datetime import datetime
  2. from typing import Optional, cast, List
  3. from redis.asyncio import Redis
  4. from app.api.v1.module_system.auth.schema import AuthSchema
  5. from app.core.alipay import AlipayClient
  6. from app.core.exceptions import CustomException
  7. from app.core.logger import log
  8. from app.utils.snowflake import get_snowflake_id_str
  9. from .crud import EnterpriseCRUD
  10. from .enums import EnterpriseStatusEnum
  11. from .schema import (
  12. EnterpriseApplyInviteSchema,
  13. EnterpriseCreateOrUpdateSchema,
  14. EnterpriseInviteOutSchema,
  15. EnterpriseListOutSchema,
  16. EnterpriseOperationOutSchema,
  17. )
  18. from alipay.aop.api.domain.EnterpriseInfoDTO import EnterpriseInfoDTO
  19. class EnterpriseService:
  20. """企业服务层"""
  21. @classmethod
  22. async def create_service_from_alipay(
  23. cls, auth: AuthSchema, data: EnterpriseCreateOrUpdateSchema
  24. ) -> EnterpriseOperationOutSchema:
  25. """
  26. 创建企业记录(仅创建记录,不申请邀请码)
  27. """
  28. crud = EnterpriseCRUD(auth)
  29. data.validate_enterprise_id()
  30. enterprise = await crud.create(data.model_dump(exclude_none=True))
  31. if not enterprise:
  32. raise CustomException(msg="创建企业记录失败")
  33. return EnterpriseOperationOutSchema(enterprise_id=enterprise.enterprise_id)
  34. @classmethod
  35. async def apply_invite_service(
  36. cls, auth: AuthSchema, redis: Redis, data: EnterpriseApplyInviteSchema
  37. ) -> EnterpriseInviteOutSchema:
  38. """
  39. 申请企业邀请码(可多次调用,但需检查是否已存在且未过期)
  40. 调用: alipay.commerce.ec.enterprise.registerinvite.create
  41. """
  42. from alipay.aop.api.request.AlipayCommerceEcEnterpriseRegisterinviteCreateRequest import (
  43. AlipayCommerceEcEnterpriseRegisterinviteCreateRequest,
  44. )
  45. from alipay.aop.api.domain.AlipayCommerceEcEnterpriseRegisterinviteCreateModel import (
  46. AlipayCommerceEcEnterpriseRegisterinviteCreateModel, EnterpriseBaseInfoDTO, EnterpriseProfilesDTO
  47. )
  48. from alipay.aop.api.response.AlipayCommerceEcEnterpriseRegisterinviteCreateResponse import (
  49. AlipayCommerceEcEnterpriseRegisterinviteCreateResponse
  50. )
  51. model = AlipayCommerceEcEnterpriseRegisterinviteCreateModel()
  52. model.out_biz_no = get_snowflake_id_str(auth.tenant_id) # 支付宝接口需要 out_biz_no,用业务主键 enterprise_id
  53. model.identity_type = data.identity_type
  54. model.identity = data.identity
  55. model.identity_open_id = data.identity_open_id
  56. # model.register_mode = None
  57. # model.sign_fund_way = None
  58. request = AlipayCommerceEcEnterpriseRegisterinviteCreateRequest()
  59. request.biz_model = model
  60. client = AlipayClient.get_client()
  61. response = client.execute(request)
  62. if not response:
  63. raise CustomException(msg="申请邀请码失败: 无响应")
  64. result = AlipayCommerceEcEnterpriseRegisterinviteCreateResponse()
  65. result.parse_response_content(response)
  66. if not result.is_success():
  67. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  68. raise CustomException(msg=f"申请邀请码失败: {result.msg}")
  69. invite_time = datetime.now()
  70. invite_out = EnterpriseInviteOutSchema(
  71. pc_invite_url=result.pc_invite_url or "",
  72. invite_time=invite_time,
  73. expire_time=result.expire_time,
  74. )
  75. # 根据out_biz_no关联tenant_id存储redis,方便后期支付宝回调用于创建企业, 过期时间为三个月
  76. # await redis.set(f"alipay:notify:{model.out_biz_no}", auth.tenant_id, ex=60 * 5 * 24 * 30)
  77. return invite_out
  78. @classmethod
  79. async def detail_service(cls, auth: AuthSchema, enterprise_id: str) -> EnterpriseInfoDTO:
  80. """
  81. 查询企业详情
  82. 调用: alipay.commerce.ec.enterprise.info.query
  83. """
  84. from alipay.aop.api.request.AlipayCommerceEcEnterpriseInfoQueryRequest import AlipayCommerceEcEnterpriseInfoQueryRequest
  85. from alipay.aop.api.domain.AlipayCommerceEcEnterpriseInfoQueryModel import AlipayCommerceEcEnterpriseInfoQueryModel
  86. from alipay.aop.api.response.AlipayCommerceEcEnterpriseInfoQueryResponse import AlipayCommerceEcEnterpriseInfoQueryResponse
  87. model = AlipayCommerceEcEnterpriseInfoQueryModel()
  88. model.enterprise_id = enterprise_id
  89. request = AlipayCommerceEcEnterpriseInfoQueryRequest()
  90. request.biz_model = model
  91. client = AlipayClient.get_client()
  92. response = client.execute(request)
  93. if not response:
  94. raise CustomException(msg="查询企业详情失败: 无响应")
  95. result = AlipayCommerceEcEnterpriseInfoQueryResponse()
  96. result.parse_response_content(response)
  97. if not result.is_success():
  98. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  99. raise CustomException(msg=f"查询企业详情失败: {result.msg}")
  100. return result.enterprise_info # type: ignore
  101. @classmethod
  102. async def all_service(
  103. cls,
  104. auth: AuthSchema,
  105. ) -> list[EnterpriseListOutSchema]:
  106. if not auth.tenant_id or auth.tenant_id == 1:
  107. return []
  108. crud = EnterpriseCRUD(auth)
  109. models = await crud.list()
  110. if not models or len(models) == 0:
  111. return []
  112. return [EnterpriseListOutSchema.model_validate(model) for model in models]
  113. @classmethod
  114. async def list_service(
  115. cls,
  116. auth: AuthSchema,
  117. page_no: int = 1,
  118. page_size: int = 20,
  119. search: dict | None = None,
  120. ) -> dict:
  121. """
  122. 查询企业列表
  123. """
  124. crud = EnterpriseCRUD(auth)
  125. offset = (page_no - 1) * page_size
  126. return await crud.page(
  127. offset=offset,
  128. limit=page_size,
  129. order_by=[{"id": "desc"}],
  130. search=search or {},
  131. out_schema=EnterpriseListOutSchema,
  132. )
  133. @classmethod
  134. async def update_service_from_alipay(
  135. cls, auth: AuthSchema, data: EnterpriseCreateOrUpdateSchema
  136. ) -> EnterpriseOperationOutSchema:
  137. """
  138. 修改企业信息
  139. 调用: alipay.commerce.ec.enterprise.info.modify (可选)
  140. """
  141. crud = EnterpriseCRUD(auth)
  142. data.validate_enterprise_id()
  143. enterprise_id = cast(str, data.enterprise_id)
  144. enterprise = await crud.get_by_enterprise_id(enterprise_id=enterprise_id)
  145. if not enterprise:
  146. enterprise = await EnterpriseService.create_service_from_alipay(auth, data)
  147. else:
  148. update_data = data.model_dump(exclude_none=True, exclude={"enterprise_id"})
  149. await crud.update_by_enterprise_id(enterprise_id, update_data)
  150. # 当状态为签约成功或认证时,调用支付宝查询企业详情
  151. if data.status and data.status in [
  152. EnterpriseStatusEnum.ENTERPRISE_ACTIVATED.value,
  153. EnterpriseStatusEnum.ENTERPRISE_AUTH.value
  154. ]:
  155. log.info(f"从支付宝平台查询企业详情: enterprise_id={enterprise_id}")
  156. enterprise_info = await EnterpriseService.detail_service(auth, enterprise_id)
  157. # 同步支付宝查询到的企业信息
  158. query_data = EnterpriseCreateOrUpdateSchema(
  159. # enterprise_id=result.enterprise_info.enterprise_id,
  160. account_id=enterprise_info.account_id,
  161. name=enterprise_info.enterprise_name,
  162. short_name=enterprise_info.enterprise_alias,
  163. )
  164. await crud.update_by_enterprise_id(
  165. enterprise_id, query_data.model_dump(exclude_none=True, exclude={"enterprise_id"}))
  166. return EnterpriseOperationOutSchema(enterprise_id=enterprise_id)
  167. @classmethod
  168. async def enterprise_unsign_service(
  169. cls, auth: AuthSchema, enterprise_id: str
  170. ) -> EnterpriseOperationOutSchema:
  171. """
  172. 企业解约
  173. 调用: alipay.commerce.ec.enterprise.unsign
  174. 注意: 支付宝异步通知后才会更新状态,此处仅调用接口
  175. """
  176. crud = EnterpriseCRUD(auth)
  177. enterprise = await crud.get_by_enterprise_id(enterprise_id)
  178. if not enterprise:
  179. raise CustomException(msg="企业不存在")
  180. if enterprise.status == EnterpriseStatusEnum.ENTERPRISE_UNSIGN.value:
  181. raise CustomException(msg="企业已解约")
  182. if enterprise.status == EnterpriseStatusEnum.ENTERPRISE_WITHDRAW.value:
  183. raise CustomException(msg="企业已注销,无法解约")
  184. client = AlipayClient.get_client()
  185. from alipay.aop.api.request.AlipayCommerceEcEnterpriseUnsignRequest import (
  186. AlipayCommerceEcEnterpriseUnsignRequest,
  187. )
  188. from alipay.aop.api.domain.AlipayCommerceEcEnterpriseUnsignModel import (
  189. AlipayCommerceEcEnterpriseUnsignModel,
  190. )
  191. from alipay.aop.api.response.AlipayCommerceEcEnterpriseUnsignResponse import (
  192. AlipayCommerceEcEnterpriseUnsignResponse,
  193. )
  194. model = AlipayCommerceEcEnterpriseUnsignModel()
  195. model.enterprise_id = enterprise.enterprise_id
  196. request = AlipayCommerceEcEnterpriseUnsignRequest()
  197. request.biz_model = model
  198. response = client.execute(request)
  199. if not response:
  200. raise CustomException(msg="企业解约申请失败: 无响应")
  201. result = AlipayCommerceEcEnterpriseUnsignResponse()
  202. result.parse_response_content(response)
  203. if not result.is_success():
  204. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  205. raise CustomException(msg=f"企业解约申请失败: {result.msg}")
  206. return EnterpriseOperationOutSchema(enterprise_id=enterprise.enterprise_id)
  207. @classmethod
  208. async def enterprise_delete_service(
  209. cls, auth: AuthSchema, enterprise_id: str
  210. ) -> EnterpriseOperationOutSchema:
  211. """
  212. 企业注销
  213. 调用: alipay.commerce.ec.enterprise.delete
  214. 注意: 支付宝异步通知后才会更新状态,此处仅调用接口
  215. """
  216. crud = EnterpriseCRUD(auth)
  217. enterprise = await crud.get_by_enterprise_id(enterprise_id)
  218. if not enterprise:
  219. raise CustomException(msg="企业不存在")
  220. if enterprise.status == EnterpriseStatusEnum.ENTERPRISE_WITHDRAW.value:
  221. raise CustomException(msg="企业已注销")
  222. if enterprise.status != EnterpriseStatusEnum.ENTERPRISE_UNSIGN.value:
  223. raise CustomException(msg="请先完成解约再注销")
  224. client = AlipayClient.get_client()
  225. from alipay.aop.api.request.AlipayCommerceEcEnterpriseDeleteRequest import (
  226. AlipayCommerceEcEnterpriseDeleteRequest,
  227. )
  228. from alipay.aop.api.domain.AlipayCommerceEcEnterpriseDeleteModel import (
  229. AlipayCommerceEcEnterpriseDeleteModel,
  230. )
  231. from alipay.aop.api.response.AlipayCommerceEcEnterpriseDeleteResponse import (
  232. AlipayCommerceEcEnterpriseDeleteResponse,
  233. )
  234. model = AlipayCommerceEcEnterpriseDeleteModel()
  235. model.enterprise_id = enterprise.enterprise_id
  236. request = AlipayCommerceEcEnterpriseDeleteRequest()
  237. request.biz_model = model
  238. response = client.execute(request)
  239. if not response:
  240. raise CustomException(msg="企业注销申请失败: 无响应")
  241. result = AlipayCommerceEcEnterpriseDeleteResponse()
  242. result.parse_response_content(response)
  243. if not result.is_success():
  244. log.error(f"支付宝接口调用失败: {result.code} - {result.msg}")
  245. raise CustomException(msg=f"企业注销申请失败: {result.msg}")
  246. return EnterpriseOperationOutSchema(enterprise_id=enterprise.enterprise_id)