hangxin.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import logging
  5. from decimal import Decimal
  6. from apilib.monetary import RMB
  7. from apilib.utils_datetime import to_datetime
  8. from apilib.utils_sys import memcache_lock
  9. from apps.web.device.models import Device, Group
  10. from apps.web.eventer import EventBuilder
  11. from apps.web.eventer.base import WorkEvent
  12. from apps.web.user.models import ServiceProgress, MyUser
  13. from apps.web.user.transaction_deprecated import refund_money
  14. logger = logging.getLogger(__name__)
  15. class builder(EventBuilder):
  16. def __getEvent__(self, device_event):
  17. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  18. if device_event['cmd'] == 100:
  19. return HangXinEvent(self.deviceAdapter, event_data)
  20. class HangXinEvent(WorkEvent):
  21. def __init__(self, smartBox, event_data):
  22. super(HangXinEvent, self).__init__(smartBox, event_data)
  23. @staticmethod
  24. def __get_cache_lock(devNo, portStr, openId):
  25. return "{devNo}-{portStr}-{openId}".format(devNo = devNo, portStr = portStr, openId = openId)
  26. def do(self, **args):
  27. # 获取设备相关信息
  28. devNo = self.device["devNo"]
  29. portStr = self.event_data.pop("portStr")
  30. portInfo = Device.get_dev_control_cache(devNo).get(portStr, {})
  31. openId = portInfo.get("openId")
  32. vCardId = portInfo.get("vCardId", None)
  33. # 此设备对于事件上报会有5次,第一次事件上报之后 获取设备的IMEI 操作者openId 以及端口号 加锁操作
  34. # 后续将此设备端口信息清空 释放锁
  35. if not openId:
  36. logger.info("event has been handled! ")
  37. return
  38. user = MyUser.objects(openId = openId, groupId = self.device['groupId']).first()
  39. group = Group.get_group(self.device["groupId"])
  40. # 计算返费金额
  41. money = RMB(portInfo['coins'])
  42. leftElec = self.event_data["leftElec"]
  43. needElec = portInfo["needElec"]
  44. backCoins = RMB(money.amount * Decimal(leftElec) / Decimal(needElec))
  45. now_time = str(datetime.datetime.now())[:19]
  46. startTime = portInfo.get("startTime")
  47. lock_id = self.__get_cache_lock(devNo, portStr, openId)
  48. with memcache_lock(lock_id, value = '1', expire = 10) as acquired:
  49. if acquired:
  50. # 通知用户服务结束
  51. try:
  52. consumeDict = {
  53. "chargeIndex": portStr,
  54. "reason": portInfo.get("desc"),
  55. "finishedTime": now_time,
  56. "duration": (to_datetime(now_time) - to_datetime(startTime)).total_seconds() / 60
  57. }
  58. if self.device.is_auto_refund and float(backCoins) > 0 and vCardId is None:
  59. refund_money(self.device, backCoins, openId)
  60. consumeDict.update({"refundedMoney": backCoins.mongo_amount})
  61. self.notify_user(
  62. user.managerialOpenId if user else '',
  63. 'refund_coins',
  64. **{
  65. 'title': u"退款通知",
  66. 'backCount': u'金币:%s' % backCoins,
  67. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  68. }
  69. )
  70. self.notify_user(
  71. user.managerialOpenId if user else "",
  72. 'service_complete',
  73. **{
  74. 'title': self.event_data.get("desc"),
  75. 'service': u'充电服务(设备编号:%s,分机:%s,地址:%s)' % (
  76. self.device['logicalCode'], portStr, group['address']),
  77. 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  78. 'remark': u'谢谢您的支持'
  79. }
  80. )
  81. ServiceProgress.update_progress_and_consume_rcd(
  82. self.device['ownerId'],
  83. {'open_id': openId, 'device_imei': self.device['devNo'], 'port': int(portStr),
  84. 'isFinished': False},
  85. consumeDict
  86. )
  87. except Exception as e:
  88. logger.exception('deal with hx devNo=%s event e=%s' % (devNo, e))
  89. finally:
  90. # 清空端口信息
  91. Device.clear_port_control_cache(devNo, portStr)
  92. # 电量告警计算
  93. self.elec_warning(float(needElec) - float(leftElec))
  94. else:
  95. logger.warning("has no acquired")
  96. def elec_warning(self, useElec):
  97. """
  98. 电量报警
  99. :param useElec: 设备上报的使用的电量
  100. :return:
  101. """
  102. otherConf = self.device.get("otherConf", dict())
  103. quotaElec = otherConf.get("quotaElec")
  104. warningElec = otherConf.get("warningElec")
  105. if not all([quotaElec, warningElec]):
  106. return
  107. # 真实使用电量和上报使用电量之间的比例关系
  108. realUseElec = float(useElec) / 11.1
  109. quotaElec = float("%.1f" % (float(quotaElec) - realUseElec))
  110. if quotaElec <= float(warningElec):
  111. group = Group.get_group(self.device["groupId"])
  112. self.notify_dealer(
  113. templateName = "device_fault",
  114. title = "注意!设备电量告警",
  115. device = u"{groupNumber}组-{logicalCode}".format(groupNumber = self.device["groupNumber"],
  116. logicalCode = self.device["logicalCode"]),
  117. location = u"{address}-{groupName}".format(address = group["address"], groupName = group["groupName"]),
  118. fault = u"当前设备电量为{}, 小于告警设备电量{},请及时为设备买电(此消息仅供电量参考)".format(quotaElec, warningElec),
  119. notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  120. )
  121. # 更新新的电量额度
  122. otherConf.update({"quotaElec": quotaElec})
  123. Device.objects.filter(devNo = self.device["devNo"]).update(otherConf = otherConf)
  124. Device.invalid_device_cache(self.device["devNo"])