wxlz.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. # coding=utf-8
  2. import datetime
  3. import logging
  4. from arrow import Arrow
  5. from django.conf import settings
  6. from typing import TYPE_CHECKING
  7. from apilib.monetary import VirtualCoin, RMB
  8. from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND, START_DEVICE_STATUS
  9. from apps.web.core.device_define.wxlz import DefaultParams
  10. from apps.web.eventer import EventBuilder
  11. from apps.web.eventer.base import WorkEvent, ComNetPayAckEvent
  12. from apps.web.user.utils import freeze_user_balance, clear_frozen_user_balance
  13. from apps.web.utils import set_start_key_status
  14. from apps.web.user.models import MyUser
  15. if TYPE_CHECKING:
  16. from apps.web.eventer import Event
  17. from apps.web.user.models import ConsumeRecord
  18. logger = logging.getLogger(__name__)
  19. class builder(EventBuilder):
  20. def __getEvent__(self, device_event): # type:(dict)->Event
  21. if "funCode" in device_event:
  22. if device_event["funCode"] == "async_settings":
  23. return AysncSettingsEvent(self.deviceAdapter, device_event)
  24. # 启动时候的回复解析
  25. elif "order" in device_event:
  26. return ComStartOrderAckEvent(self.deviceAdapter, device_event["order"])
  27. # 需要ack的信息
  28. elif "order_id" in device_event:
  29. return ComStartOrderAckEvent(self.deviceAdapter, device_event)
  30. else:
  31. logger.warning("device = {}, receive not registered event = {}".format(self.device.devNo, device_event))
  32. return
  33. class ComStartOrderAckEvent(ComNetPayAckEvent):
  34. def do_running_order(self, order, result): # type: (ConsumeRecord, dict) -> None
  35. """
  36. 处理运行订单
  37. :param order: 用户服务器订单
  38. :param result: device_event 设备侧订单
  39. :return:
  40. """
  41. # 订单消息已经被回复过
  42. if order.status in ["running", "finished"]:
  43. logger.debug('order<{}> no need to deal. this has done.'.format(repr(order)))
  44. return
  45. # 启动设备的时候 设备实际启动成功 但是订单串口超时 不知道订单的明确状态 后面启动时间又重新上报
  46. if order.status == "unknown":
  47. errorDesc = u"设备信号恢复,订单正常运行"
  48. logger.info("order <{}> timeout to running")
  49. # 正常运行的订单
  50. else:
  51. errorDesc = u""
  52. if 'master' in result:
  53. order.association = {
  54. 'master': result['master']
  55. }
  56. order.servicedInfo.update({'masterOrderNo': result['master']})
  57. order.errorDesc = errorDesc
  58. order.isNormal = True
  59. order.status = 'running'
  60. order.startTime = datetime.datetime.fromtimestamp(result['sts'])
  61. order.save()
  62. set_start_key_status(start_key=order.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(order.id))
  63. def do_finished_order(self, order, result): # type: (ConsumeRecord, dict) -> None
  64. """
  65. 处理结束运行订单
  66. :param order: 用户服务器订单
  67. :param result: device_event 设备侧订单
  68. :return:
  69. """
  70. # 子单归并主单
  71. if 'sub' in result:
  72. order.association = {
  73. 'sub': [item['order_id'] for item in self.event_data['sub']]
  74. }
  75. order.servicedInfo.update(
  76. {'subOrderNo': '{}'.format(', '.join([item['order_id'] for item in self.event_data['sub']]))})
  77. # 主单归并自身
  78. elif 'master' in result:
  79. order.association = {
  80. 'master': result['master']
  81. }
  82. order.servicedInfo.update({'masterOrderNo': result['master']})
  83. # 此时理论上服务器订单状态有三种可能(finished在上层已经被排除)
  84. # 正常的状态 相当于订单由运行状态 即将切换为finished状态
  85. if order.status == "running":
  86. order.isNormal = True
  87. order.status = "finished"
  88. order.errorDesc = u""
  89. order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
  90. # 非正常状态 相当于订单最开始串口超时 然后直接变为结束
  91. elif order.status == "unknown":
  92. order.isNormal = True
  93. order.status = "finished"
  94. order.errorDesc = u"设备信号恢复,订单正常结束(0001)"
  95. order.startTime = datetime.datetime.fromtimestamp(result['sts'])
  96. order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
  97. # 正常状态 相当于订单启动失败或者是中间running单没有上来
  98. elif order.status == "created":
  99. order.isNormal = True
  100. order.status = "finished"
  101. order.errorDesc = u"设备信号恢复,订单正常结束(0002)"
  102. order.startTime = datetime.datetime.fromtimestamp(result['sts'])
  103. order.finishedTime = datetime.datetime.fromtimestamp(result['fts'])
  104. else:
  105. logger.warning('order<{}> status = <{}> to finished. no dealwith'.format(repr(order), order.status))
  106. order.save()
  107. set_start_key_status(start_key=order.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(order.id))
  108. def do_finished_event(self, order, sub_orders, merge_order_info): # type:(ConsumeRecord, list, dict) -> None
  109. """
  110. 订单的状态已经完成 进一步事件 扣费等等
  111. :param order: 处理完毕的订单(主订单)
  112. :param sub_orders: 子订单
  113. :param merge_order_info: 合并单的信息
  114. :return:
  115. """
  116. order.reload()
  117. needTime = self.event_data["charge_time"] # 订购的总时长 单位分钟
  118. duration = self.event_data["duration"] # 订单从开始运行到结束的时间 单位秒
  119. cst = self.event_data["cst"] # 订单的花费 单位 0.001分 方便模块累加
  120. money = self.event_data["money"] # 订单的花费 单位 1分 实际需要扣除的费用
  121. user = MyUser.objects.filter(openId=order.openId, groupId=self.device.groupId).first() # type:MyUser
  122. chargeTime = duration / 60
  123. if self.device.group.is_free:
  124. consumeMoney = VirtualCoin(0)
  125. paymentInfo = {
  126. 'via': 'free',
  127. 'money': RMB(0.00).mongo_amount,
  128. 'coins': VirtualCoin(0.00).mongo_amount,
  129. 'deduct': []
  130. }
  131. else:
  132. consumeMoney = VirtualCoin(money / 100.0)
  133. paymentInfo = {
  134. 'via': 'coins',
  135. 'money': RMB(consumeMoney).mongo_amount,
  136. 'coins': VirtualCoin(consumeMoney).mongo_amount,
  137. 'deduct': [{
  138. 'id': str(user.id),
  139. 'field': 'balance',
  140. 'before': VirtualCoin(user.balance).mongo_amount,
  141. 'coins': VirtualCoin(consumeMoney).mongo_amount
  142. }]
  143. }
  144. order.paymentInfo = paymentInfo
  145. order.coin = consumeMoney
  146. order.money = consumeMoney
  147. order.save()
  148. # 对于用户的金额进行结算
  149. freeze_user_balance(self.device, self.device.group, order)
  150. clear_frozen_user_balance(self.device, order, duration, spendElec=0, backCoins=VirtualCoin(0), user=user)
  151. user.pay(consumeMoney)
  152. # 组织消费信息
  153. consumeDict = {
  154. "cst": cst,
  155. "reason": self._get_finish_reason(),
  156. "billing_power": self.event_data.get("billing_power") or 0,
  157. DEALER_CONSUMPTION_AGG_KIND.DURATION: chargeTime,
  158. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: consumeMoney.mongo_amount,
  159. }
  160. extra = [
  161. {u"本次订购时长": "{}分钟".format(needTime)},
  162. {u"本次实际使用时长": "{}分钟".format(chargeTime)},
  163. {u"消费金额": "{}(金币)".format(consumeMoney.amount)}
  164. ]
  165. order.update_service_info(consumeDict)
  166. self.notify_user_service_complete(
  167. service_name='充电',
  168. openid=user.managerialOpenId,
  169. port=str(order.used_port),
  170. address=order.address,
  171. reason=consumeDict["reason"],
  172. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  173. extra=extra
  174. )
  175. def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict
  176. """
  177. 后付费 合单 只合并订购时间
  178. :param master_order:
  179. :param sub_orders:
  180. :return:
  181. """
  182. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  183. portCache = {
  184. "openId": master_order.openId,
  185. "consumeType": "mobile",
  186. "needKind": "needTime",
  187. "estimatedTs": int(start_time.timestamp + 720 * 60 * 60),
  188. "coins": str(master_order.coin)
  189. }
  190. needTime = self.deviceAdapter._check_package(master_order.package)
  191. for _sub in sub_orders:
  192. needTime += self.deviceAdapter._check_package(_sub.package)
  193. portCache["needValue"] = needTime
  194. return portCache
  195. def _get_finish_reason(self):
  196. if "reason" in self.event_data:
  197. return DefaultParams.REASON_MAP.get(self.event_data["reason"], DefaultParams.DEFAULT_REASON)
  198. return DefaultParams.DEFAULT_REASON
  199. class AysncSettingsEvent(WorkEvent):
  200. def do(self, **args):
  201. self.deviceAdapter._async_settings()
  202. return