DcFastCharge.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. import arrow
  5. from arrow import Arrow
  6. from django.conf import settings
  7. from apilib.monetary import RMB
  8. from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND, APP_TYPE
  9. from apps.web.eventer import EventBuilder
  10. from apps.web.eventer.base import WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf
  11. from apps.web.helpers import get_wechat_auth_bridge
  12. from apps.web.user.models import MyUser
  13. logger = logging.getLogger(__name__)
  14. class builder(EventBuilder):
  15. def __getEvent__(self, device_event):
  16. if 'order_id' in device_event:
  17. if device_event['order_type'] == 'com_start':
  18. return MyComNetPayAckEvent(self.deviceAdapter, device_event)
  19. if 'event_type' in device_event:
  20. return
  21. class FaseChargeEvent(WorkEvent):
  22. def do(self, **args):
  23. pass
  24. class StartAckEventPreProcessor(AckEventProcessorIntf):
  25. def analysis_reason(self, reason):
  26. FINISHED_CHARGE_REASON_MAP = {
  27. '00': u'人工终止',
  28. '01': u'自动充满',
  29. '02': u'后台终止',
  30. '03': u'故障终止',
  31. # 服务器定义的停止事件
  32. -2: u'检测到设备未在充电工作状态,结束本次充电',
  33. }
  34. return FINISHED_CHARGE_REASON_MAP.get(reason, reason)
  35. def analysis_charge_method(self,charge_method):
  36. MAP = {
  37. '00': '自动充满',
  38. '01': '定额充电',
  39. '02': '定量充电',
  40. '03': '定时充电',
  41. '04': '备用',
  42. '05': '手动充电',
  43. }
  44. return MAP.get(charge_method, charge_method)
  45. def pre_processing(self, device, event_data):
  46. def cleaning_data(filed, new_filed=None, ratio=1.0,to_int=False):
  47. if filed in event_data:
  48. if not new_filed:
  49. new_filed = filed
  50. event_data[new_filed] = round(float(event_data.pop(filed, 0))*ratio, 2)
  51. if to_int:
  52. event_data[new_filed] = int(event_data[new_filed])
  53. else:
  54. logger.info('{} not in event_data '.format(filed))
  55. cleaning_data('port', 'port', to_int=True)
  56. cleaning_data('start_soc', 'startSoc', to_int=True)
  57. cleaning_data('finished_soc', 'finishedSoc', to_int=True)
  58. cleaning_data('duration', to_int=True)
  59. cleaning_data('used_money', 'usedMoney', ratio=0.01)
  60. cleaning_data('start_money', 'startMoney', ratio=0.01)
  61. cleaning_data('start_elec', 'startElec', ratio=0.01)
  62. cleaning_data('finished_elec', 'finishedElec', ratio=0.01)
  63. cleaning_data('used_elec', 'usedElec', ratio=0.01)
  64. if 'reason' in event_data:
  65. event_data['reasonDesc'] = self.analysis_reason(event_data['reason'])
  66. if 'charge_method' in event_data:
  67. event_data['chargeMethodDesc'] = self.analysis_charge_method(event_data['charge_method'])
  68. try:
  69. if 'start_time' in event_data:
  70. event_data['sts'] = arrow.get(event_data['start_time'], 'YYYY-MM-DD HH:mm:ss', tzinfo = settings.TIME_ZONE).timestamp
  71. if 'finished_time' in event_data:
  72. event_data['fts'] = arrow.get(event_data['finished_time'], 'YYYY-MM-DD HH:mm:ss', tzinfo = settings.TIME_ZONE).timestamp
  73. except Exception:
  74. pass
  75. return event_data
  76. def cleaning_data(self, filed, ratio=1.0):
  77. pass
  78. class MyComNetPayAckEvent(ComNetPayAckEvent):
  79. def __init__(self, smartBox, event_data):
  80. super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  81. def post_after_start(self, order=None):
  82. pass
  83. def post_after_finish(self, order=None):
  84. pass
  85. def merge_order(self, master_order, sub_orders):
  86. portDict = {}
  87. billingMethod = master_order.package.get('billingMethod', 'coins')
  88. portDict['consumeType'] = billingMethod
  89. needTime, needElec = self.deviceAdapter._check_package(master_order.package)
  90. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  91. portDict = {
  92. }
  93. if master_order.paymentInfo['via'] == 'virtualCard':
  94. portDict.update({
  95. 'vCardId': master_order.virtual_card_id
  96. })
  97. all_coins = master_order.package['coins']
  98. all_price = master_order.package['price']
  99. all_consume_time_value = needTime
  100. all_consume_elec_value = needElec
  101. for sub_order in sub_orders:
  102. all_coins += sub_order.package['coins']
  103. all_price += sub_order.package['price']
  104. sub_needTime, sub_needElec = self.deviceAdapter._check_package(sub_order.package)
  105. all_consume_time_value += sub_needTime
  106. all_consume_elec_value += sub_needElec
  107. portDict['coins'] = str(0)
  108. portDict['price'] = str(0)
  109. portDict['all_consume_time_value'] = str(all_consume_time_value)
  110. portDict['all_consume_elec_value'] = str(all_consume_elec_value)
  111. portDict['needKind'] = 'needTime'
  112. portDict['needValue'] = 999 # 显示充满自停
  113. portDict['unit'] = u'分钟'
  114. portDict['estimatedTs'] = int(start_time.timestamp + 720 * 60)
  115. return portDict
  116. def do_finished_event(self, master_order, sub_orders, merge_order_info):
  117. startElec, finishedElec, usedElec = self.event_data.get('startElec'), self.event_data.get('finishedElec'), self.event_data.get('usedElec'),
  118. startMoney, usedMoney = self.event_data.get('startElec'), self.event_data.get('startElec'),
  119. duration = self.event_data.get('duration')
  120. coins = RMB(merge_order_info.get('coins', 0))
  121. usedFee = RMB(usedMoney)
  122. consumeDict = {
  123. 'reason': self.event_data['reasonDesc'],
  124. 'chargeIndex': str(master_order.used_port),
  125. DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
  126. DEALER_CONSUMPTION_AGG_KIND.ELEC: usedElec,
  127. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.deviceAdapter.calc_elec_fee(usedElec),
  128. }
  129. auto_refund = self.device.is_auto_refund
  130. refundProtectionTime = self.device.get('otherConf', {}).get('deviceConfigs', {}).get('refundProtectionTime',5)
  131. user = MyUser.objects(openId=master_order.openId,
  132. groupId=master_order.groupId).first() # type: MyUser
  133. backCoins = RMB(0)
  134. if duration < refundProtectionTime:
  135. backCoins = coins
  136. usedFee = RMB(0)
  137. else:
  138. if auto_refund:
  139. if coins < usedFee:
  140. backCoins = coins
  141. else:
  142. backCoins = coins - usedFee
  143. else:
  144. usedFee = coins
  145. logger.debug('{} auto refund enable switch is {}, refund protect time = {} backMoney={}'.format(
  146. repr(self.device), str(auto_refund), refundProtectionTime, backCoins))
  147. extra = []
  148. extra.append({u'本次使用时长': u'{}(分钟)'.format(duration)})
  149. if master_order.paymentInfo['via'] == 'free':
  150. extra.append({u'消费金额': u'当前设备免费使用'})
  151. elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']:
  152. extra.append({u'消费金额': '{}(金币)'.format(usedFee)})
  153. if backCoins > RMB(0):
  154. extra.append({u'退款金额': '{}(金币)'.format(backCoins)})
  155. for sub_order in sub_orders[::-1]:
  156. need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, sub_order.coin)
  157. user.clear_frozen_balance(str(sub_order.id), sub_order.paymentInfo['deduct'], need_back_coins)
  158. sub_order.update_service_info({
  159. DEALER_CONSUMPTION_AGG_KIND.COIN: sub_order.coin.mongo_amount,
  160. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: need_consume_coins.mongo_amount,
  161. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount})
  162. need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, master_order.coin)
  163. user.clear_frozen_balance(str(master_order.id), master_order.paymentInfo['deduct'], need_back_coins)
  164. consumeDict.update({
  165. DEALER_CONSUMPTION_AGG_KIND.COIN: master_order.coin.mongo_amount,
  166. DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: need_consume_coins.mongo_amount,
  167. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount
  168. })
  169. master_order.update_service_info(consumeDict)
  170. else:
  171. logger.error('not net pay rather user virtual card pay. something is wrong.')
  172. return
  173. auth_bridge = get_wechat_auth_bridge(source=self.device,
  174. app_type=APP_TYPE.WECHAT_USER_MANAGER)
  175. self.notify_user_service_complete(
  176. service_name='充电',
  177. openid=user.get_bound_pay_openid(auth_bridge.bound_openid_key),
  178. port=str(master_order.used_port),
  179. address=master_order.address,
  180. reason=self.event_data.get('reasonDesc'),
  181. finished_time=master_order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  182. extra=extra)
  183. def _calc_refund_info(self, backCoins, orderCoin):
  184. if backCoins >= orderCoin:
  185. need_back_coins = orderCoin
  186. need_consume_coins = RMB(0)
  187. backCoins -= orderCoin
  188. else:
  189. need_back_coins = backCoins
  190. need_consume_coins = orderCoin - need_back_coins
  191. backCoins = RMB(0)
  192. return need_back_coins, need_consume_coins, backCoins