sijiang.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import time
  5. from mongoengine.errors import DoesNotExist
  6. from django.utils.functional import cached_property
  7. from apilib.monetary import RMB, VirtualCoin
  8. from apilib.utils_datetime import to_datetime
  9. from apps.web.eventer.base import WorkEvent, FaultEvent
  10. from apps.web.eventer import EventBuilder
  11. from apps.web.south_intf.platform import notify_event_to_north
  12. from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang
  13. from apps.web.south_intf.zhongtian import report_zhongtian_service_complete, report_zhongtian_refund
  14. from apps.web.user.models import UserVirtualCard, VCardConsumeRecord, ServiceProgress, MyUser, Card
  15. from apps.web.api.models import APIStartDeviceRecord
  16. from apps.web.constant import Const
  17. from apilib.systypes import StrEnum
  18. from apps.web.core.accounting import Accounting
  19. from apps.web.device.models import Device, Group
  20. from apps.web.eventer.errors import NoCommandHandlerAvailable
  21. logger = logging.getLogger(__name__)
  22. class builder(EventBuilder):
  23. def __getEvent__(self, device_event):
  24. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  25. if event_data is None or 'cmdCode' not in event_data:
  26. return None
  27. if event_data['cmdCode'] in ['05', '03']:
  28. return ChargingSiJiangWorkEvent(self.deviceAdapter, event_data)
  29. if event_data['cmdCode'] == '0D':
  30. return FaultEvent(self.deviceAdapter, event_data)
  31. class SI_JIANG_EVENT_CODE(StrEnum):
  32. OFFLINE_COIN_REPORT = '03'
  33. SWIPE_CARD_REPORT = '04'
  34. CHARGE_FINISHED = '05'
  35. ERROR = '0D'
  36. class ChargingSiJiangWorkEvent(WorkEvent):
  37. @cached_property
  38. def handler_map(self):
  39. return {
  40. '03': self.handle_OFFLINE_COIN_REPORT,
  41. '05': self.handle_CHARGE_FINISHED
  42. }
  43. def support_playback(self):
  44. return self.event_data['cmdCode'] in ['05']
  45. def do(self, **args):
  46. devNo = self.device['devNo']
  47. logger.info('si-jiang charging event detected, devNo=%s, curInfo=%s' % (devNo, self.event_data))
  48. cmdCode = self.event_data['cmdCode']
  49. try:
  50. self.handler_map[cmdCode]()
  51. except KeyError:
  52. raise NoCommandHandlerAvailable('device %s got no recognizable cmd %s ' % (devNo, cmdCode))
  53. def handle_OFFLINE_COIN_REPORT(self):
  54. """
  55. 投币事件上报
  56. :return:
  57. """
  58. Accounting.recordOfflineCoin(
  59. self.device,
  60. int(time.time()),
  61. int(self.event_data['coin'])
  62. )
  63. self.deviceAdapter.get_port_status_from_dev()
  64. def handle_CHARGE_FINISHED(self):
  65. """
  66. :return:
  67. """
  68. devNo = self.device['devNo']
  69. port = str(self.event_data['port'])
  70. recvTime = to_datetime(self.recvTime)
  71. try:
  72. lineInfo = Device.clear_port_control_cache(self.device.devNo, port)
  73. logger.debug(
  74. 'device({}) receive charge finished message. lineInfo={}'.format(self.device['devNo'], lineInfo, ))
  75. if 'startTime' not in lineInfo:
  76. logger.debug('startTime not found')
  77. return
  78. startTime = to_datetime(lineInfo['startTime'])
  79. usedTime = self.event_data.get('duration',
  80. int(round(((recvTime - startTime).total_seconds() / 60.0))))
  81. leftTime = self.event_data['leftTime']
  82. actualNeedTime = lineInfo['needTime']
  83. group = Group.get_group(self.device['groupId'])
  84. consumeDict = {
  85. 'reason': self.event_data['reason'],
  86. 'leftTime': leftTime,
  87. 'chargeIndex': port,
  88. 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
  89. 'duration': usedTime
  90. }
  91. # 如果是刷卡的,直接更新消费记录,然后发送通知消息,不支持退费
  92. if 'cardNo' in lineInfo:
  93. card = Card.objects(cardNo=lineInfo['cardNo'], agentId=self.dealer.agentId).get()
  94. consumeDict.update({'balance': lineInfo['balance']})
  95. ServiceProgress.update_progress_and_consume_rcd(
  96. self.device['ownerId'],
  97. {
  98. 'open_id': lineInfo['openId'],
  99. 'port': int(port),
  100. 'device_imei': self.device['devNo'],
  101. 'isFinished': False
  102. }, consumeDict
  103. )
  104. #: 通知服务结束
  105. title = u'%s 卡号:%s,按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
  106. self.event_data['reason'],
  107. lineInfo['cardNo'],
  108. actualNeedTime,
  109. leftTime
  110. )
  111. service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (self.device['logicalCode'], port, group['address'])
  112. finishTime = recvTime.strftime('%Y-%m-%d %H:%M:%S')
  113. self.notify_service_complete(card.managerialOpenId, title=title, service=service, finishTime=finishTime)
  114. elif 'consumeRcdId' in lineInfo:
  115. coins = RMB(lineInfo['coins'])
  116. backCoins = self.get_backCoins(coins=coins, leftTime=leftTime, actualNeedTime=actualNeedTime)
  117. vCardId = lineInfo.get('vCardId')
  118. try:
  119. vCard = UserVirtualCard.objects.get(id=vCardId)
  120. except DoesNotExist:
  121. logger.info('can not find the vCard id = %s' % vCardId)
  122. return
  123. managerialOpenId = self.get_managerialOpenId_by_openId(lineInfo['openId'])
  124. title = u'%s 按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
  125. self.event_data['reason'],
  126. actualNeedTime,
  127. leftTime)
  128. service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
  129. self.device['logicalCode'], port, group['address'])
  130. self.notify_service_complete(managerialOpenId=managerialOpenId, title=title, service=service)
  131. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  132. {'open_id': lineInfo['openId'], 'port': int(port),
  133. 'device_imei': self.device['devNo'],
  134. 'isFinished': False}, consumeDict)
  135. consumeRcdId = lineInfo.get('consumeRcdId', None)
  136. if consumeRcdId is None:
  137. logger.error('can not find consume rcd id')
  138. return
  139. try:
  140. vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
  141. except DoesNotExist:
  142. logger.error('can not find the consume rcd id = %s' % consumeRcdId)
  143. return
  144. vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, backCoins.mongo_amount)
  145. else:
  146. #: 这里需要考虑API调用的和普通使用场景
  147. if 'extOrderNo' in lineInfo:
  148. record = APIStartDeviceRecord.get_api_record(self.device['logicalCode'], lineInfo['extOrderNo'])
  149. if not record:
  150. logger.debug('cannot find api start device record')
  151. return
  152. if record.postActionTriggered:
  153. logger.debug('api({}) post action has done.'.format(lineInfo['extOrderNo']))
  154. return
  155. report_zhongtian_service_complete(
  156. event_code = '16',
  157. record = record,
  158. orderNo = lineInfo['extOrderNo'],
  159. deviceCode = self.device['logicalCode'],
  160. groupName = group['groupName'],
  161. address = group['address'],
  162. actualNeedTime = actualNeedTime,
  163. leftTime = leftTime,
  164. finishedState = self.event_data['finishedState'])
  165. if self.device.is_auto_refund:
  166. coins = VirtualCoin(lineInfo['coins'])
  167. money = RMB(lineInfo['money'])
  168. backCoins = self.get_backCoins(coins = coins, leftTime = leftTime,
  169. actualNeedTime = actualNeedTime)
  170. backMoney = self.get_backMoney(money = money, leftTime = leftTime,
  171. actualNeedTime = actualNeedTime)
  172. report_zhongtian_refund(
  173. eventCode = '16',
  174. record = record,
  175. orderNo = lineInfo['extOrderNo'],
  176. deviceCode = self.device['logicalCode'],
  177. groupName = group['groupName'],
  178. address = group['address'],
  179. backMoney = str(backMoney),
  180. backCoins = str(backCoins),
  181. actualNeedTime = actualNeedTime,
  182. leftTime = leftTime,
  183. finishedState = self.event_data['finishedState']
  184. )
  185. else:
  186. user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device['groupId']).first()
  187. # 通知服务结束
  188. if user:
  189. title = u'%s 按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
  190. self.event_data['reason'],
  191. actualNeedTime,
  192. leftTime
  193. )
  194. service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
  195. self.device['logicalCode'],
  196. port,
  197. group['address']
  198. )
  199. self.notify_service_complete(managerialOpenId=user.managerialOpenId, title=title,
  200. service=service)
  201. # 如果需要退款,计算退款数据.
  202. if self.device.is_auto_refund:
  203. coins = VirtualCoin(lineInfo['coins'])
  204. money = RMB(lineInfo.get('money', 0))
  205. backCoins = self.get_backCoins(coins, leftTime, actualNeedTime)
  206. refundRMB = self.get_backMoney(money, leftTime, actualNeedTime)
  207. if backCoins > coins or refundRMB > money:
  208. logger.error(
  209. 'refund amount exceeds expectations dev=<{}>, coins=<{}> backCoins=<{}> price=<{}> refundRMB=<{}>'.format(
  210. self.device.devNo, coins, backCoins, money, refundRMB))
  211. return
  212. # refund_net_pay会做保护. 如果不支持, 就不会往缓存塞money,payInfo
  213. refundCash = 'refundRMB_device_event' in self.device.owner.features
  214. if (backCoins <= VirtualCoin(0)) and (refundRMB <= RMB(0)):
  215. pass
  216. else:
  217. if refundCash:
  218. title = u'%s 按动态功率计算总时间为:%s分钟,还有:%s分钟的时间未使用完,给您退回剩余金额:%s元' % (
  219. service, actualNeedTime, leftTime, refundRMB)
  220. backCount = u'金额:%s 元' % refundRMB
  221. else:
  222. title = u'%s 按动态功率计算总时间为:%s分钟,还有:%s分钟的时间未使用完,给您退还成相应的金币:%s元,您下次可以接着使用哦' % (service, actualNeedTime, leftTime, backCoins)
  223. backCount = u'金币:%s' % backCoins
  224. self.refund_net_pay(user, lineInfo, refundRMB, backCoins, consumeDict, refundCash)
  225. self.notify_refund_coins(managerialOpenId=user.managerialOpenId, title=title,
  226. backCount=backCount)
  227. ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device['ownerId'],
  228. queryDict={
  229. 'open_id': lineInfo['openId'],
  230. 'port': int(port),
  231. 'device_imei': self.device['devNo'],
  232. 'isFinished': False
  233. },
  234. consumeDict=consumeDict)
  235. else:
  236. ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
  237. {'open_id': lineInfo['openId'],
  238. 'port': int(port),
  239. 'device_imei': self.device['devNo'],
  240. 'isFinished': False}, consumeDict)
  241. return
  242. except Exception as e:
  243. logger.exception(e)
  244. finally:
  245. notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL,
  246. desc = self.event_data['reason'])
  247. send_event_to_zhejiang(self.dealer, self.device, self.event_data)