transaction.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. from mongoengine import DoesNotExist
  6. from typing import TYPE_CHECKING
  7. from apilib.monetary import RMB
  8. from apilib.utils_sys import MemcachedLock
  9. from apps.web.constant import START_DEVICE_STATUS, USER_RECHARGE_TYPE, Const
  10. from apps.web.core.exceptions import ServiceException
  11. from apps.web.core.payment import WithdrawGateway
  12. from apps.web.core.services import StartDeviceEngine
  13. from apps.web.exceptions import DuplicatedOperationError
  14. from apps.web.helpers import get_user_manager_agent
  15. from apps.web.report.ledger import Ledger
  16. from apps.web.user.models import MyUser, ConsumeRecord, CardRechargeOrder, Card, CardRechargeRecord, RechargeRecord
  17. from apps.web.user.utils import get_consume_order
  18. from apps.web.user.utils2 import ConsumeOrderStateEngine, generate_net_payment
  19. from apps.web.utils import set_start_key_status
  20. logger = logging.getLogger(__name__)
  21. if TYPE_CHECKING:
  22. from apps.web.device.models import GroupDict
  23. def is_after_ledger(recharge_record):
  24. # type: (RechargeRecord)->bool
  25. if not WithdrawGateway.is_ledger(recharge_record.withdraw_source_key):
  26. return False
  27. after_ledger = False
  28. if recharge_record.device.devTypeCode in (
  29. Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY,
  30. Const.DEVICE_TYPE_CODE_CHANGING_CY4,
  31. Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY_V2,
  32. Const.DEVICE_TYPE_CODE_CHANGING_CY_POWER,
  33. Const.DEVICE_TYPE_CODE_DUIBIJI,
  34. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE,
  35. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE_ONLINECARD,
  36. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_SIX):
  37. after_ledger = True
  38. return after_ledger
  39. def is_refund_cash(recharge_record, device):
  40. _refund_cash = False
  41. if recharge_record.is_temp_package:
  42. _refund_cash = True
  43. elif device.devTypeCode in [
  44. Const.DEVICE_TYPE_CODE_CHANGING_WEIFULE,
  45. Const.DEVICE_TYPE_CODE_CHANGING_WEIFULE2,
  46. Const.DEVICE_TYPE_CODE_WEIFULE_MINI,
  47. Const.DEVICE_TYPE_CODE_WEIFULE_TOUCH_PAD,
  48. Const.DEVICE_TYPE_CODE_WEIFULE_ANJIAN,
  49. Const.DEVICE_TYPE_CODE_CHANGING_SOCKET,
  50. Const.DEVICE_TYPE_CODE_WEIFULE_WASHER
  51. ]:
  52. _refund_cash = False
  53. elif ('refundRMB_device_event' in recharge_record.owner.features) or \
  54. device.devTypeCode in (
  55. Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY,
  56. Const.DEVICE_TYPE_CODE_CHANGING_CY4,
  57. Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY_V2,
  58. Const.DEVICE_TYPE_CODE_CHANGING_CY_POWER,
  59. Const.DEVICE_TYPE_CODE_DUIBIJI,
  60. Const.DEVICE_TYPE_CODE_CAR_CHARGING_JN,
  61. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE,
  62. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE_ONLINECARD,
  63. Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_SIX
  64. ):
  65. _refund_cash = True
  66. return _refund_cash
  67. def recharge(user, recharge_record):
  68. # type: (MyUser, RechargeRecord)->None
  69. """
  70. 优惠充值流程
  71. :param user:
  72. :param recharge_record:
  73. :return:
  74. """
  75. # 经销商分账
  76. ledger = Ledger(USER_RECHARGE_TYPE.RECHARGE, recharge_record)
  77. ledger.execute(stats=True)
  78. # 用户充值
  79. user.recharge(money=RMB(recharge_record.money), bestowMoney=RMB(recharge_record.coins - recharge_record.money))
  80. user.account_recharge(recharge_record)
  81. def recharge_start_device(user, recharge_record): # type:(MyUser, RechargeRecord) -> None
  82. """
  83. 充值启动设备
  84. 充值并启动设备 实际上还是先充值到用户的余额 然后利用用户的余额直接启动设备
  85. """
  86. # 首先还是先给用户充值
  87. recharge(user, recharge_record)
  88. orderNo = recharge_record.attachParas.get("orderNo")
  89. if not orderNo:
  90. logger.error('[recharge_start_device] recharge order <{}> has no start orderNo'.format(repr(recharge_record)))
  91. return
  92. order = get_consume_order(orderNo) # type: ConsumeRecord
  93. if not order:
  94. logger.error("[recharge_start_device] recharge order <{}> {} has not find order".format(repr(recharge_record), orderNo))
  95. # 鉴别不支持启动设备的订单 直接返回
  96. locker = MemcachedLock(key=order.startLockKey, value='1', expire=180)
  97. if not locker.acquire():
  98. logger.error(
  99. '[_start_device ERROR] cannot get device lock<{}>, openId={}, devNo={}, port={}'.format(order.startLockKey, order.openId, order.devNo, order.port)
  100. )
  101. raise DuplicatedOperationError(u'订单运行中 请刷新界面查看订单详情')
  102. try:
  103. proxy = StartDeviceEngine(order) # type: StartDeviceEngine
  104. # 检查订单的支付情况 更多的是余额检验 检验完成之后 根据订单的支付选择 决定是否生成支付信息并添加
  105. paymentInfo = proxy.get_payment_info()
  106. paymentInfo and order.update_payment(paymentInfo)
  107. except ServiceException as se:
  108. ConsumeOrderStateEngine(order).to_failure(se.result["description"])
  109. set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.TIMEOUT)
  110. return
  111. # 这个地方只负责将设备的启动传递到新的线程 对于设备启动的结果实际上是未知的
  112. release_locker = True
  113. try:
  114. proxy.start()
  115. except ServiceException as e:
  116. logger.info("[_start_device ERROR] service exception = {}".format(e.result))
  117. set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.FAILURE)
  118. except Exception as e:
  119. logger.exception('[_start_device ERROR](order={}) error={}'.format(order, e))
  120. set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.FAILURE)
  121. finally:
  122. logger.debug('release_locker = {}'.format(release_locker))
  123. locker.release()
  124. def recharge_for_order(user, recharge_record):
  125. # type: (MyUser, RechargeRecord)->None
  126. recharge(user, recharge_record)
  127. try:
  128. order = get_consume_order(recharge_record.attachParas.get("orderNo"))
  129. if not order:
  130. logger.error("[recharge_for_order] no find the order which paid for!!!!!!!")
  131. return
  132. # 执行一次订单的扣款
  133. payment = generate_net_payment(order)
  134. order.update_payment(payment)
  135. order.frozen_payer_balance()
  136. order.clear_payer_frozen()
  137. ConsumeOrderStateEngine(order).to_finished()
  138. except Exception as e:
  139. logger.exception(e)
  140. def recharge_for_card(user, recharge_record):
  141. # 经销商分账
  142. ledger = Ledger(USER_RECHARGE_TYPE.RECHARGE, recharge_record)
  143. ledger.execute(stats=True)
  144. # 查找卡
  145. cardId, cardNo = recharge_record.attachParas["cardId"], recharge_record.attachParas["cardNo"]
  146. # 创建充值记录
  147. cardOrder = CardRechargeOrder.new_one(
  148. openId=recharge_record.openId,
  149. cardId=cardId,
  150. cardNo=cardNo,
  151. money=recharge_record.money,
  152. coins=recharge_record.coins,
  153. group=recharge_record.group,
  154. rechargeId=recharge_record.id
  155. )
  156. if cardOrder is None:
  157. logger.error("[recharge_for_card] create card order error, recharge order = {}".format(recharge_record))
  158. return
  159. try:
  160. card = Card.objects.get(id=cardId)
  161. except DoesNotExist:
  162. logger.warning("[recharge_for_card] not find card, card ={}, recharge order = {}".format(cardId, recharge_record))
  163. return
  164. # ID卡的 直接充值到卡里面
  165. if not card.is_id_card:
  166. return
  167. status = Card.get_card_status(str(card.id))
  168. if status == "busy":
  169. logger.warning("[recharge_for_card] card recharge, card ={}, recharge order = {}".format(cardId, recharge_record))
  170. return
  171. Card.set_card_status(str(card.id), 'busy')
  172. try:
  173. cardOrder.update_after_recharge_id_card(
  174. device=recharge_record.device,
  175. balance=card.balance+cardOrder.coins,
  176. preBalance=card.balance
  177. )
  178. CardRechargeRecord.add_record(
  179. card=card, group=recharge_record.group, order=cardOrder
  180. )
  181. # 卡充值
  182. card.recharge(cardOrder.chargeAmount, cardOrder.bestowAmount)
  183. # 统计卡的余额变化
  184. card.account_recharge(recharge_record)
  185. except Exception as e:
  186. logger.exception("[recharge_for_card] recharge id card error, recharge order = {}, error = {}".format(recharge_record, e))
  187. finally:
  188. Card.set_card_status(str(card.id), "idle")
  189. def post_pay(record):
  190. # type:(RechargeRecord)->None
  191. try:
  192. logger.info('post pay for record({})'.format(repr(record)))
  193. record.reload()
  194. if not record.owner:
  195. logger.warning('{} check failure. owner<id={}> is not exist.'.format(repr(record), record.ownerId))
  196. return
  197. group = record.group # type: GroupDict
  198. if not group:
  199. logger.error('group(%s) is not exist' % record.groupId)
  200. return
  201. if group.ownerId != record.ownerId:
  202. logger.warning('{} check failure. ownerId of record<{}> != ownerId of group<{}>.'.format(
  203. repr(record), record.ownerId, record.group.ownerId))
  204. return
  205. if not record.openId:
  206. logger.error('openid is null. is not valid record.')
  207. return
  208. user = MyUser.objects(openId=record.openId, groupId=record.groupId).first()
  209. if not user:
  210. agentId = record.owner.agentId
  211. product_agent = get_user_manager_agent(record.owner)
  212. user = MyUser.get_or_create(
  213. app_platform_type=record.gateway,
  214. open_id=record.openId,
  215. group_id=record.groupId,
  216. **{
  217. 'agentId': agentId,
  218. 'productAgentId': str(product_agent.id)
  219. })
  220. diff_ts = (datetime.datetime.now() - record.dateTimeAdded).total_seconds()
  221. logger.debug(
  222. '{} in ({}) via={} attachParas={} record={} diff={}'.format(
  223. repr(user), record.gateway, record.via, record.attachParas, repr(record), diff_ts))
  224. if record.via == USER_RECHARGE_TYPE.RECHARGE:
  225. return recharge(user, record)
  226. elif record.via == USER_RECHARGE_TYPE.START_DEVICE:
  227. return recharge_start_device(user, record)
  228. elif record.via == USER_RECHARGE_TYPE.RECHARGE_CASH:
  229. return recharge_for_order(user, record)
  230. elif record.via == USER_RECHARGE_TYPE.RECHARGE_CARD:
  231. return recharge_for_card(user, record)
  232. else:
  233. logger.error('invalid pay type. recharge record id = %s' % str(record.id))
  234. except Exception as e:
  235. logger.exception(e)