gaoborui_second.py 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import json
  5. import logging
  6. import time
  7. from arrow import Arrow
  8. from django.conf import settings
  9. from typing import TYPE_CHECKING
  10. from apilib.monetary import RMB, VirtualCoin, Ratio
  11. from apps.web.constant import Const, APP_TYPE, DEALER_CONSUMPTION_AGG_KIND, DeviceCmdCode
  12. from apps.web.core.exceptions import ServiceException
  13. from apps.web.core.networking import MessageSender
  14. from apps.web.device.models import Device
  15. from apps.web.device.timescale import FluentedEngine
  16. from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf, \
  17. IcStartAckEvent, IdStartAckEvent
  18. from apps.web.eventer import EventBuilder
  19. from apps.web.helpers import get_wechat_auth_bridge
  20. from apps.web.user.models import VCardConsumeRecord, ServiceProgress, CardRechargeOrder, MyUser, ConsumeRecord
  21. if TYPE_CHECKING:
  22. from apps.web.device.models import DeviceDict
  23. logger = logging.getLogger(__name__)
  24. class builder(EventBuilder):
  25. def __getEvent__(self, device_event):
  26. # 订单机制事件
  27. if 'order_id' in device_event:
  28. if device_event['order_type'] == 'com_start':
  29. return MyComNetPayAckEvent(self.deviceAdapter, device_event)
  30. if device_event['order_type'] == 'ic_recharge':
  31. pass
  32. if device_event['order_type'] == 'ic_start':
  33. return OfflineCardStartAckEvent(self.deviceAdapter, device_event)
  34. if device_event['order_type'] == 'id_start':
  35. return OnlineCardStartAckEvent(self.deviceAdapter, device_event)
  36. if device_event['order_type'] == 'card_refund':
  37. pass
  38. else:
  39. if 'data' not in device_event:
  40. return None
  41. event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
  42. if event_data is None or 'cmdCode' not in event_data:
  43. return None
  44. if event_data.get('subCmd') == '3F':
  45. return GaoBoRuiFaultEvent(self.deviceAdapter, event_data)
  46. else:
  47. return GeneralEvent(self.deviceAdapter, event_data)
  48. class GeneralEvent(WorkEvent):
  49. SUCCESS_01 = '01'
  50. BALANCE_NOT_ENOUGH_02 = '02'
  51. INVALID_CARD_03 = '03'
  52. FREEZE_CARD_04 = '04'
  53. def do(self):
  54. if self.event_data['subCmd'] == '35':
  55. self._do_start_with_offline_card()
  56. elif self.event_data['subCmd'] == '36':
  57. self._do_get_balance()
  58. elif self.event_data['subCmd'] == '3B':
  59. self._do_sync_balance_with_offline_card()
  60. elif self.event_data['subCmd'] == '32':
  61. self._graph_power_point()
  62. def _do_get_balance(self):
  63. cardNo = str(int(self.event_data.get('card_no'), 16))
  64. logger.info('receive cardNo:{}'.format(cardNo))
  65. card = self.update_card_dealer_and_type(cardNo)
  66. mqtt_data = {
  67. 'funCode': self.event_data.get('cmdCode')
  68. }
  69. if not card or not card.openId:
  70. data = self.event_data.get('session')
  71. data += '36'
  72. data += self.INVALID_CARD_03
  73. data += self.event_data.get('card_no')
  74. data += self.event_data.get('fee')
  75. data += '{:0>4X}'.format(0)
  76. logger.info('no this card! card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo))
  77. elif card.frozen:
  78. data = self.event_data.get('session')
  79. data += '36'
  80. data += self.FREEZE_CARD_04
  81. data += self.event_data.get('card_no')
  82. data += self.event_data.get('fee')
  83. data += '{:0>4X}'.format(0)
  84. logger.info('card is frozen card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo))
  85. else:
  86. # 是否存在没有到账的余额 进行充值
  87. card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
  88. self.recharge_id_card(
  89. card=card,
  90. rechargeType='append',
  91. order=card_recharge_order
  92. )
  93. card.reload()
  94. fee = RMB(int(self.event_data.get('fee'), 16)) * 0.1
  95. if card.balance >= RMB(fee):
  96. data = self.event_data.get('session')
  97. data += '36'
  98. data += self.SUCCESS_01
  99. data += self.event_data.get('card_no')
  100. data += self.event_data.get('fee')
  101. data += '{:0>4X}'.format(int((card.balance - fee) * 10))
  102. else:
  103. data = self.event_data.get('session')
  104. data += '36'
  105. data += self.BALANCE_NOT_ENOUGH_02
  106. data += self.event_data.get('card_no')
  107. data += self.event_data.get('fee')
  108. data += '{:0>4X}'.format(int((card.balance - fee) * 10))
  109. mqtt_data['data'] = data
  110. self.send_mqtt(data=mqtt_data, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE)
  111. def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC):
  112. """
  113. 发送mqtt 指令默认210 返回data
  114. """
  115. result = MessageSender.send(self.device, cmd,
  116. data)
  117. if 'rst' in result and result['rst'] != 0:
  118. if result['rst'] == -1:
  119. raise ServiceException(
  120. {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1})
  121. elif result['rst'] == 1:
  122. raise ServiceException(
  123. {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1})
  124. else:
  125. if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]:
  126. return
  127. return result.get('data', 'ok')
  128. def _do_start_with_offline_card(self):
  129. cardNo = str(int(self.event_data.get('card_no'), 16))
  130. port = self.event_data.get('port')
  131. fee = self.event_data.get('fee')
  132. balance = self.event_data.get('balance')
  133. billingType = self.event_data.get('billingType')
  134. logger.info('receive cardNo:{}'.format(cardNo))
  135. card = self.update_card_dealer_and_type(cardNo, cardType='IC', balance=RMB(balance))
  136. if not card:
  137. logger.info('no this card! card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo))
  138. return
  139. if card.frozen:
  140. # 停止此离线卡的充电行为
  141. self.deviceAdapter.stop_charging_port(port)
  142. return
  143. lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port))
  144. if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING:
  145. pass
  146. else:
  147. logger.info('_do_card_start_charge the once!!')
  148. # 记录ID卡的消费
  149. attachParas = {
  150. 'chargeIndex': port
  151. }
  152. servicedInfo = {
  153. 'cardNo': cardNo,
  154. 'chargeIndex': port
  155. }
  156. orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(fee), servicedInfo=servicedInfo,
  157. attachParas=attachParas)
  158. # 记录缓存信息 可以在个人中心以及经销商的端口管理显示
  159. lineInfo = {
  160. 'port': str(port),
  161. 'cardNo': cardNo,
  162. 'openId': card.openId,
  163. 'payCount': 1,
  164. 'coins': fee,
  165. 'status': Const.DEV_WORK_STATUS_WORKING,
  166. 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  167. 'orderNo': orderNo,
  168. 'cardOrderNo': cardOrderNo,
  169. 'billingType': billingType,
  170. 'consumeType': 'card'
  171. }
  172. if 'needTime' in self.event_data:
  173. lineInfo.update({'needTime': self.event_data['needTime']})
  174. if 'needElec' in self.event_data:
  175. lineInfo.update({'needElec': self.event_data['needElec']})
  176. ServiceProgress.register_card_service(
  177. self.device,
  178. int(port),
  179. card,
  180. {
  181. 'orderNo': orderNo,
  182. 'coin': RMB(fee).mongo_amount,
  183. 'cardOrderNo': cardOrderNo
  184. }
  185. )
  186. Device.update_port_control_cache(self.device.devNo, lineInfo)
  187. def _do_sync_balance_with_offline_card(self):
  188. card_no = self.event_data.get('card_no')
  189. cardNo = str(int(card_no, 16))
  190. port = self.event_data.get('port')
  191. fee = self.event_data.get('fee')
  192. balance = self.event_data.get('balance')
  193. logger.info('receive cardNo:{}'.format(cardNo))
  194. self.update_card_dealer_and_type(cardNo, cardType='IC', balance=RMB(balance))
  195. logging.info('Sync balance completed <port-{}> <card-{}>, <hex-{}>, <fee-{}>, <now-{}>'.format(port, cardNo,
  196. card_no, fee,
  197. balance))
  198. def _graph_power_point(self):
  199. powers = self.event_data.get('powers')
  200. for port, power in powers.items():
  201. FluentedEngine().in_power_udp(devNo=self.device.devNo,
  202. port=port,
  203. ts=int(time.time()),
  204. power=power,
  205. voltage=None,
  206. current=None)
  207. class GaoBoRuiEvent(WorkEvent):
  208. def do(self):
  209. pass
  210. class GaoBoRuiFaultEvent(FaultEvent):
  211. def do(self, **args):
  212. # 将告警的消息打入相应的缓存
  213. port = self.event_data.get('port')
  214. # 0 表示整机
  215. if not port:
  216. part = str(0)
  217. else:
  218. part = str(port)
  219. warningData = {
  220. 'warningStatus': 2,
  221. 'warningDesc': self.event_data['statusInfo'],
  222. 'warningTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
  223. 'warningUart': self.event_data['uart']
  224. }
  225. Device.update_dev_warning_cache(self.device.devNo, {part: warningData})
  226. super(GaoBoRuiFaultEvent, self).do()
  227. class StartAckEventPreProcessor(AckEventProcessorIntf):
  228. def analysis_reason(self, reason, fault_code=None):
  229. FINISHED_CHARGE_REASON_MAP = {
  230. 0: u'购买的充电时间或者电量已经用完',
  231. 1: u'充满自停',
  232. 2: u'刷卡用户按停止键',
  233. 3: u'未接负载30秒自停',
  234. 4: u'充电器移除',
  235. 5: u'充电功率超限',
  236. # 服务器定义的停止事件
  237. 97: u'用户使用离线卡手动操作退费 ,充电结束',
  238. 98: u'用户手动点击结束按钮结束充电',
  239. 99: u'远程点击停止按钮, 充电结束',
  240. }
  241. if reason == 0xEE:
  242. if fault_code:
  243. fault_code = '{:X}'.format(fault_code)[-1]
  244. FAULT_ERROR_MAP = {
  245. '1': '(键盘故障)',
  246. '2': '(显示器故障)',
  247. '3': '(刷卡板故障)',
  248. '4': '(子网故障)',
  249. '5': '(存储故障)',
  250. '6': '(过温故障)',
  251. '7': '(烟雾故障)',
  252. '8': '(漏电)',
  253. '9': '(短路)',
  254. 'A': '(过压)',
  255. 'B': '(欠压)',
  256. 'C': '(整机过流)',
  257. 'D': '(恶意操作)',
  258. 'E': '(继电器粘连) ',
  259. 'F': '(其它故障) ',
  260. }
  261. return '充电桩发生故障' + FAULT_ERROR_MAP.get(fault_code)
  262. return '充电桩发生故障'
  263. return FINISHED_CHARGE_REASON_MAP.get(reason)
  264. def pre_processing(self, device, event_data):
  265. # type:(DeviceDict, dict)->dict
  266. source = json.dumps(event_data, indent=4)
  267. event_data['source'] = source
  268. if 'myDuration' in event_data:
  269. duration = event_data.pop('myDuration', 0)
  270. event_data['duration'] = round((duration + 59) / 60.0, 1)
  271. if 'myElec' in event_data:
  272. myElec = event_data.pop('myElec', 0)
  273. event_data['elec'] = round(myElec / 3600000.0, 2)
  274. if 'fts' in event_data and 'sts' in event_data:
  275. duration = event_data['fts'] - event_data['sts']
  276. event_data['duration'] = round((duration + 59) / 60.0, 1)
  277. if 'elec' in event_data:
  278. event_data['elec'] = round(event_data.pop('elec') * 0.01, 2)
  279. if 'openId' in event_data:
  280. pass
  281. if 'power' in event_data:
  282. event_data['power'] = round(event_data['power'] * 0.1, 1)
  283. if 'status' in event_data and event_data['status'] == 'finished':
  284. event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason'), event_data.get('fault_code'))
  285. if 'leftTime' in event_data:
  286. pass
  287. if 'leftElec' in event_data:
  288. event_data['leftElec'] = round(event_data['leftElec'] * 0.01, 2)
  289. return event_data
  290. class MyComNetPayAckEvent(ComNetPayAckEvent):
  291. def __init__(self, smartBox, event_data):
  292. super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor())
  293. def post_before_start(self, order=None):
  294. # 记录处理的源数据报文
  295. uart_source = getattr(order, 'uart_source', [])
  296. uart_source.append({
  297. 'rece_running': self.event_data.get('source'),
  298. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  299. })
  300. order.uart_source = uart_source
  301. order.save()
  302. def post_after_start(self, order=None):
  303. pass
  304. def post_before_finish(self, order=None):
  305. # 记录处理的源数据报文
  306. uart_source = getattr(order, 'uart_source', [])
  307. uart_source.append({
  308. 'rece_finished': self.event_data.get('source'),
  309. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  310. })
  311. order.uart_source = uart_source
  312. order.save()
  313. def post_after_finish(self, order=None):
  314. pass
  315. def merge_order(self, master_order, sub_orders):
  316. # type:(ConsumeRecord, list)->dict
  317. cType, cValue = self.event_data['cType'], self.event_data['cValue']
  318. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  319. portDict = {
  320. }
  321. all_coins = master_order.package['coins']
  322. all_price = master_order.package['price']
  323. for sub_order in sub_orders:
  324. all_coins += sub_order.package['coins']
  325. all_price += sub_order.package['price']
  326. if 'sub' in self.event_data:
  327. for sub_data in self.event_data['sub']:
  328. cValue += sub_data['cValue']
  329. portDict['coins'] = str(all_coins)
  330. portDict['price'] = str(all_price)
  331. portDict['cType'] = cType
  332. if cType == 1: # 时间
  333. portDict['needKind'] = 'needTime'
  334. portDict['needValue'] = cValue
  335. portDict['estimatedTs'] = int(start_time.timestamp + cValue * 60)
  336. portDict['unit'] = u'分钟'
  337. elif cType == 2: # 电量
  338. portDict['needKind'] = 'needElec'
  339. portDict['needValue'] = cValue
  340. portDict['estimatedTs'] = int(start_time.timestamp + 720 * 60)
  341. portDict['unit'] = u'度'
  342. portDict['consumeType'] = 'mobile'
  343. return portDict
  344. def do_finished_event(self, master_order, sub_orders, merge_order_info):
  345. # type: (ConsumeRecord, [ConsumeRecord], dict)->None
  346. self._do_finished(master_order, sub_orders, merge_order_info)
  347. def insert_vCard_consume_record(self, vCard, order, success, consumeTotal, consumeDay):
  348. try:
  349. if success and consumeDay['count'] > 0:
  350. record = VCardConsumeRecord(
  351. orderNo=VCardConsumeRecord.make_no(order.logicalCode),
  352. openId=order.openId,
  353. nickname=order.nickname,
  354. cardId=str(vCard.id),
  355. dealerId=vCard.dealerId,
  356. devNo=order.devNo,
  357. devTypeCode = order.devTypeCode,
  358. devTypeName = order.dev_type_name,
  359. logicalCode=order.logicalCode,
  360. groupId=order.groupId,
  361. address=order.address,
  362. groupNumber=order.groupNumber,
  363. groupName=order.groupName,
  364. attachParas=order.attachParas,
  365. consumeData=consumeTotal,
  366. consumeDayData=consumeDay
  367. )
  368. record.save()
  369. except Exception, e:
  370. logger.exception(e)
  371. def _do_finished(self, order, sub_orders, merge_order_info):
  372. # type: (ConsumeRecord, list, dict)->None
  373. duration, elec, cType, leftTime, leftElec = self.event_data.get('duration', 0), self.event_data.get('elec',
  374. 0), self.event_data.get(
  375. 'cType'), self.event_data.get('leftTime', 0), self.event_data.get('leftElec', 0)
  376. coins, price, needValue = VirtualCoin(merge_order_info['coins']), RMB(merge_order_info['price']), \
  377. merge_order_info['needValue']
  378. refundRatio = 0.0
  379. if cType == 1: # 按时计费
  380. if leftTime > needValue:
  381. leftTime = needValue
  382. refundRatio = leftTime * 1.0 / needValue
  383. elif cType == 2: # 按量计费
  384. if leftElec > needValue:
  385. leftElec = needValue
  386. refundRatio = leftElec * 1.0 / needValue
  387. backCoins = coins * refundRatio
  388. # refundRMB = price * refundRatio
  389. auto_refund = self.device.is_auto_refund
  390. refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5)
  391. user = MyUser.objects(openId=order.openId,
  392. groupId=order.groupId).first() # type: MyUser
  393. if duration < refundProtectionTime:
  394. backCoins = coins
  395. usedFee = VirtualCoin(0)
  396. else:
  397. if auto_refund:
  398. usedFee = coins - backCoins
  399. else:
  400. usedFee = coins
  401. logger.debug('{} auto refund enable switch is {}, refund protect time = {} backMoney={}'.format(
  402. repr(self.device), str(auto_refund), refundProtectionTime, backCoins))
  403. extra = []
  404. extra.append({u'本次使用时长': u'{}(分钟)'.format(duration)})
  405. if order.paymentInfo['via'] == 'free':
  406. extra.append({u'消费金额': u'当前设备免费使用'})
  407. elif order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']:
  408. extra.append({u'消费金额': '{}(金币)'.format(usedFee)})
  409. if backCoins > VirtualCoin(0):
  410. extra.append({u'退款金额': '{}(金币)'.format(backCoins)})
  411. order_processing_list = [order] + sub_orders
  412. for order_ in order_processing_list[::-1]:
  413. consumeDict = {
  414. 'reason': self.event_data.get('reasonDesc', None),
  415. 'chargeIndex': str(order.used_port)
  416. }
  417. need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, order_.coin)
  418. user.clear_frozen_balance(str(order_.id), order_.paymentInfo['deduct'],
  419. back_coins=need_back_coins,
  420. consume_coins=need_consume_coins)
  421. consumeDict.update({
  422. DEALER_CONSUMPTION_AGG_KIND.COIN: order_.coin.mongo_amount,
  423. DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount,
  424. })
  425. # 子单直接更新
  426. if order_ in sub_orders:
  427. order_.update_service_info(consumeDict)
  428. # 主单需要填写聚合信息后进行更新
  429. else:
  430. consumeDict.update({
  431. DEALER_CONSUMPTION_AGG_KIND.DURATION: duration,
  432. DEALER_CONSUMPTION_AGG_KIND.ELEC: elec,
  433. DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.deviceAdapter.calc_elec_fee(elec),
  434. })
  435. order.update_service_info(consumeDict)
  436. else:
  437. logger.error('not net pay rather user virtual card pay. something is wrong.')
  438. return
  439. auth_bridge = get_wechat_auth_bridge(source=self.device,
  440. app_type=APP_TYPE.WECHAT_USER_MANAGER)
  441. self.notify_user_service_complete(
  442. service_name='充电',
  443. openid=user.get_bound_pay_openid(auth_bridge.bound_openid_key),
  444. port=str(order.used_port),
  445. address=order.address,
  446. reason=self.event_data.get('reasonDesc'),
  447. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  448. extra=extra)
  449. def _calc_refund_info(self, backCoins, orderCoin):
  450. if backCoins >= orderCoin:
  451. need_back_coins = orderCoin
  452. need_consume_coins = VirtualCoin(0)
  453. backCoins -= orderCoin
  454. else:
  455. need_back_coins = backCoins
  456. need_consume_coins = orderCoin - need_back_coins
  457. backCoins = VirtualCoin(0)
  458. return need_back_coins, need_consume_coins, backCoins
  459. class CardStartAckEventPreProcessor(AckEventProcessorIntf):
  460. def analysis_reason(self, reason, fault_code=None):
  461. FINISHED_CHARGE_REASON_MAP = {
  462. 0: u'购买的充电时间或者电量已经用完',
  463. 1: u'充满自停',
  464. 2: u'刷卡用户按停止键',
  465. 3: u'未接负载30秒自停',
  466. 4: u'充电器移除',
  467. 5: u'充电功率超限',
  468. # 服务器定义的停止事件
  469. 97: u'用户使用离线卡手动操作退费 ,充电结束',
  470. 98: u'用户手动点击结束按钮结束充电',
  471. 99: u'远程点击停止按钮, 充电结束',
  472. }
  473. if reason == 0xEE:
  474. if fault_code:
  475. fault_code = '{:X}'.format(fault_code)[-1]
  476. FAULT_ERROR_MAP = {
  477. '1': '(键盘故障)',
  478. '2': '(显示器故障)',
  479. '3': '(刷卡板故障)',
  480. '4': '(子网故障)',
  481. '5': '(存储故障)',
  482. '6': '(过温故障)',
  483. '7': '(烟雾故障)',
  484. '8': '(漏电)',
  485. '9': '(短路)',
  486. 'A': '(过压)',
  487. 'B': '(欠压)',
  488. 'C': '(整机过流)',
  489. 'D': '(恶意操作)',
  490. 'E': '(继电器粘连) ',
  491. 'F': '(其它故障) ',
  492. }
  493. return '充电桩发生故障' + FAULT_ERROR_MAP.get(fault_code)
  494. return '充电桩发生故障'
  495. return FINISHED_CHARGE_REASON_MAP.get(reason)
  496. def pre_processing(self, device, event_data):
  497. # type:(DeviceDict, dict)->dict
  498. source = json.dumps(event_data, indent=4)
  499. event_data['source'] = source
  500. if 'cardNo' in event_data:
  501. cardNo = '{}'.format(int(event_data['cardNo'], 16))
  502. event_data['cardNo'] = cardNo
  503. if 'myDuration' in event_data:
  504. duration = event_data.pop('myDuration', 0)
  505. event_data['myDuration'] = round((duration + 59) / 60.0, 2)
  506. if 'myElec' in event_data:
  507. myElec = event_data.pop('myElec', 0)
  508. event_data['myElec'] = round(myElec / 3600000.0 * 0.1, 2) # 功率上报的功率是x10的
  509. if 'fts' in event_data and 'sts' in event_data:
  510. duration = event_data['fts'] - event_data['sts']
  511. event_data['duration'] = round((duration + 59) / 60.0, 2)
  512. if 'elec' in event_data:
  513. event_data['elec'] = round(event_data.pop('elec') * 0.01, 2)
  514. if 'needTime' in event_data:
  515. pass
  516. if 'needElec' in event_data:
  517. event_data['needElec'] = round(event_data['power'] * 0.01, 2)
  518. if 'power' in event_data:
  519. event_data['power'] = round(event_data['power'] * 0.1, 1)
  520. if 'status' in event_data and event_data['status'] == 'finished':
  521. event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason'), event_data.get('fault_code'))
  522. if 'leftTime' in event_data:
  523. pass
  524. if 'leftElec' in event_data:
  525. event_data['leftElec'] = round(event_data['leftElec'] * 0.01, 2)
  526. if 'fee' in event_data:
  527. event_data['fee'] = round(event_data['fee'] * 0.1, 2)
  528. if 'refundFee' in event_data:
  529. event_data['refundFee'] = round(event_data['refundFee'] * 0.1, 2)
  530. if 'balance' in event_data:
  531. event_data['balance'] = round(event_data['balance'] * 0.1, 2)
  532. if 'sub' in event_data:
  533. for sub_dict in event_data['sub']:
  534. if 'fee' in sub_dict:
  535. sub_dict['fee'] = round(sub_dict['fee'] * 0.1, 2)
  536. if 'power' in sub_dict:
  537. sub_dict['power'] = round(sub_dict['power'] * 0.1, 1)
  538. if 'needTime' in event_data:
  539. pass
  540. if 'needElec' in event_data:
  541. event_data['needElec'] = round(event_data['power'] * 0.01, 2)
  542. return event_data
  543. class OfflineCardStartAckEvent(IcStartAckEvent):
  544. def __init__(self, smartBox, event_data):
  545. super(OfflineCardStartAckEvent, self).__init__(smartBox, event_data, CardStartAckEventPreProcessor())
  546. def post_before_start(self, order=None):
  547. # 记录处理的源数据报文
  548. uart_source = getattr(order, 'uart_source', [])
  549. uart_source.append({
  550. 'rece_running': self.event_data.get('source'),
  551. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  552. })
  553. order.uart_source = uart_source
  554. order.save()
  555. def post_after_start(self, order=None):
  556. pass
  557. def post_before_finish(self, order=None):
  558. # 记录处理的源数据报文
  559. uart_source = getattr(order, 'uart_source', [])
  560. uart_source.append({
  561. 'rece_finished': self.event_data.get('source'),
  562. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  563. })
  564. order.uart_source = uart_source
  565. order.save()
  566. def post_after_finish(self, order=None):
  567. pass
  568. def merge_order(self, master_order, sub_orders):
  569. # type:(ConsumeRecord, list)->dict
  570. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  571. portDict = {
  572. }
  573. cType = self.event_data['cType']
  574. all_coins = VirtualCoin(master_order.coin)
  575. all_money = RMB(master_order.money)
  576. all_consume_time = self.event_data.get('needTime', 0)
  577. all_consume_elec = self.event_data.get('needElec', 0)
  578. for sub_order in sub_orders:
  579. all_coins += VirtualCoin(sub_order.coin)
  580. all_money += RMB(sub_order.money)
  581. if self.event_data.get('sub', []):
  582. for _ in self.event_data['sub']:
  583. all_consume_time += _.get('needTime', 0)
  584. all_consume_elec += _.get('needElec', 0)
  585. portDict['cType'] = cType
  586. portDict['coins'] = str(all_coins)
  587. portDict['money'] = str(all_money)
  588. if cType == 1:
  589. portDict['needValue'] = all_consume_time
  590. portDict['needKind'] = 'needTime'
  591. portDict['unit'] = u'分钟'
  592. portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60)
  593. # portDict['all_consume_time_value'] = all_consume_time
  594. elif cType == 2:
  595. portDict['needValue'] = all_consume_elec
  596. portDict['needKind'] = 'needElec'
  597. portDict['unit'] = u'度'
  598. portDict['estimatedTs'] = int(start_time.timestamp + 3600 * 12)
  599. # portDict['all_consume_elec_value'] = all_consume_elec
  600. portDict['consumeType'] = 'card'
  601. return portDict
  602. def _do_finished(self, order, merge_order_info):
  603. # type: (ConsumeRecord, dict)->None
  604. # 离线卡 只记录 不处理!!!
  605. duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0)
  606. consumeDict = {
  607. 'reason': self.event_data.get('reasonDesc', None),
  608. 'chargeIndex': str(order.used_port),
  609. 'cardNo': self.event_data['cardNo'],
  610. }
  611. leftTime = self.event_data.get('leftTime', 0)
  612. leftElec = self.event_data.get('leftElec', 0)
  613. refundFee = self.event_data.get('refundFee', 0)
  614. balance = self.event_data.get('balance', 0)
  615. if 'sub' in self.event_data:
  616. for sub_info in self.event_data['sub'][::-1]:
  617. sub_order = ConsumeRecord.objects.filter(devNo=self.device.devNo, startKey=sub_info['order_id']).first()
  618. if not sub_order:
  619. continue
  620. subConsumeDict = {
  621. 'reason': self.event_data.get('reasonDesc', None),
  622. 'chargeIndex': str(order.used_port),
  623. 'cardNo': self.event_data['cardNo'],
  624. }
  625. if 'refundFee' in self.event_data:
  626. # 做一个数据保护
  627. if refundFee > merge_order_info['coins']:
  628. refundFee = merge_order_info['coins']
  629. sub_fee = sub_info.get('fee')
  630. if refundFee > sub_fee:
  631. subConsumeDict.update({
  632. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0,
  633. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: sub_fee,
  634. })
  635. refundFee -= sub_fee
  636. else:
  637. consumeFee = sub_fee - refundFee
  638. subConsumeDict.update({
  639. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: consumeFee,
  640. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundFee,
  641. })
  642. refundFee = 0
  643. else:
  644. if 'needTime' in self.event_data:
  645. # 做一个数据保护
  646. if leftTime > merge_order_info['needValue']:
  647. leftTime = merge_order_info['needValue']
  648. needTime = sub_info.get('needTime')
  649. subConsumeDict.update({
  650. 'needTime': needTime,
  651. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: sub_order.coin,
  652. })
  653. if leftTime > sub_info.get('needTime'):
  654. # 全退
  655. subConsumeDict.update({
  656. 'leftTime': needTime,
  657. 'duration': 0,
  658. })
  659. leftTime -= needTime
  660. else:
  661. # 部分退
  662. usedTime = needTime - leftTime
  663. leftTime = 0
  664. subConsumeDict.update({
  665. 'leftTime': leftTime,
  666. 'duration': usedTime,
  667. })
  668. elif 'needElec' in self.event_data:
  669. # 做一个数据保护
  670. if leftElec > merge_order_info['needValue']:
  671. leftElec = merge_order_info['needValue']
  672. needElec = sub_info.get('needElec')
  673. subConsumeDict.update({
  674. 'needElec': needElec,
  675. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: sub_order.coin,
  676. })
  677. if leftElec > needElec:
  678. # 全退
  679. subConsumeDict.update({
  680. 'leftElec': needElec,
  681. 'elec': 0,
  682. 'elecFee': self.deviceAdapter.calc_elec_fee(0),
  683. })
  684. leftElec -= needElec
  685. else:
  686. # 部分退
  687. usedElec = needElec - leftElec
  688. subConsumeDict.update({
  689. 'leftElec': leftElec,
  690. 'elec': usedElec,
  691. 'elecFee': self.deviceAdapter.calc_elec_fee(elec),
  692. })
  693. leftElec = 0
  694. sub_order.update_service_info(subConsumeDict)
  695. if 'refundFee' in self.event_data:
  696. fee = self.event_data.get('fee', 0)
  697. if refundFee > fee:
  698. consumeDict.update({
  699. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0,
  700. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: fee,
  701. })
  702. else:
  703. consumeFee = fee - refundFee
  704. consumeDict.update({
  705. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: consumeFee,
  706. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundFee,
  707. })
  708. else:
  709. if 'needTime' in self.event_data:
  710. if leftTime > self.event_data['needTime']:
  711. # 全退
  712. consumeDict.update({
  713. 'leftTime': self.event_data['needTime'],
  714. 'duration': 0,
  715. 'needTime': self.event_data['needTime']
  716. })
  717. else:
  718. # 部分退
  719. usedTime = self.event_data['needTime'] - leftTime
  720. consumeDict.update({
  721. 'leftTime': leftTime,
  722. 'duration': usedTime,
  723. 'needTime': self.event_data['needTime']
  724. })
  725. if 'needElec' in self.event_data:
  726. if leftElec > self.event_data['needElec']:
  727. # 全退
  728. consumeDict.update({
  729. 'leftElec': self.event_data['needElec'],
  730. 'elec': 0,
  731. 'needElec': self.event_data['needElec'],
  732. 'elecFee': self.deviceAdapter.calc_elec_fee(0),
  733. })
  734. else:
  735. # 部分退
  736. usedElec = self.event_data['needElec'] - leftElec
  737. consumeDict.update({
  738. 'leftElec': leftTime,
  739. 'elec': usedElec,
  740. 'needElec': self.event_data['needElec'],
  741. 'elecFee': self.deviceAdapter.calc_elec_fee(usedElec),
  742. })
  743. order.update_service_info(consumeDict)
  744. extra = [
  745. {u'线下卡': '{}--No:{}'.format(self.card.cardName, self.card.cardNo)},
  746. {u'本次使用时长': '{}(分钟)'.format(duration)},
  747. ]
  748. if refundFee > 0:
  749. extra.append({u'退费金额': '{}'.format(refundFee)})
  750. extra.append({u'卡余额': '{}'.format(balance)})
  751. self.notify_user_service_complete(
  752. service_name='充电',
  753. openid=self.card.managerialOpenId,
  754. port=str(order.used_port),
  755. address=order.address,
  756. reason=self.event_data.get('reasonDesc'),
  757. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  758. extra=extra)
  759. def do_finished_event(self, order, merge_order_info):
  760. # type:(ConsumeRecord, dict)->None
  761. self._do_finished(order, merge_order_info)
  762. def checkout_order(self, order):
  763. # 离线卡 只记录不处理
  764. pass
  765. class OnlineCardStartAckEvent(IdStartAckEvent):
  766. def __init__(self, smartBox, event_data):
  767. super(OnlineCardStartAckEvent, self).__init__(smartBox, event_data, CardStartAckEventPreProcessor())
  768. def post_before_start(self, order=None):
  769. # 记录处理的源数据报文
  770. uart_source = getattr(order, 'uart_source', [])
  771. uart_source.append({
  772. 'rece_running': self.event_data.get('source'),
  773. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  774. })
  775. order.uart_source = uart_source
  776. order.save()
  777. def post_after_start(self, order=None):
  778. pass
  779. def post_before_finish(self, order=None):
  780. # 记录处理的源数据报文
  781. uart_source = getattr(order, 'uart_source', [])
  782. uart_source.append({
  783. 'rece_finished': self.event_data.get('source'),
  784. 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  785. })
  786. order.uart_source = uart_source
  787. order.save()
  788. def post_after_finish(self, order=None):
  789. pass
  790. def merge_order(self, master_order, sub_orders):
  791. # type:(ConsumeRecord, list)->dict
  792. start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE)
  793. portDict = {
  794. }
  795. cType = self.event_data['cType']
  796. all_coins = VirtualCoin(master_order.coin)
  797. all_money = RMB(master_order.money)
  798. all_consume_time = self.event_data.get('needTime', 0)
  799. all_consume_elec = self.event_data.get('needElec', 0)
  800. for sub_order in sub_orders:
  801. all_coins += VirtualCoin(sub_order.coin)
  802. all_money += RMB(sub_order.money)
  803. if self.event_data.get('sub', []):
  804. for _ in self.event_data['sub']:
  805. all_consume_time += _.get('needTime', 0)
  806. all_consume_elec += _.get('needElec', 0)
  807. portDict['cType'] = cType
  808. portDict['coins'] = str(all_coins)
  809. portDict['money'] = str(all_money)
  810. if cType == 1:
  811. portDict['needValue'] = all_consume_time
  812. portDict['needKind'] = 'needTime'
  813. portDict['unit'] = u'分钟'
  814. portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60)
  815. # portDict['all_consume_time_value'] = all_consume_time
  816. elif cType == 2:
  817. portDict['needValue'] = all_consume_elec
  818. portDict['needKind'] = 'needElec'
  819. portDict['unit'] = u'度'
  820. portDict['estimatedTs'] = int(start_time.timestamp + 3600 * 12)
  821. # portDict['all_consume_elec_value'] = all_consume_elec
  822. portDict['consumeType'] = 'card'
  823. return portDict
  824. def _do_finished(self, order, merge_order_info):
  825. # type: (ConsumeRecord, dict)->None
  826. duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0)
  827. consumeDict = {
  828. 'reason': self.event_data.get('reasonDesc', None),
  829. 'chargeIndex': str(order.used_port),
  830. 'cardNo': self.event_data['cardNo'],
  831. }
  832. leftTime = self.event_data.get('leftTime', 0)
  833. leftElec = self.event_data.get('leftElec', 0)
  834. coins = VirtualCoin(merge_order_info['coins'])
  835. money = RMB(merge_order_info['money'])
  836. auto_refund = self.device.is_auto_refund
  837. refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5)
  838. backCoins = VirtualCoin(0)
  839. usedFee = coins
  840. if duration <= refundProtectionTime:
  841. usedFee = VirtualCoin(0)
  842. if 'needTime' in self.event_data:
  843. leftTime = merge_order_info['needValue']
  844. if 'needElec' in self.event_data:
  845. leftElec = merge_order_info['needValue']
  846. else:
  847. if auto_refund:
  848. if 'needTime' in self.event_data:
  849. # 做一个数据保护
  850. if leftTime > merge_order_info['needValue']:
  851. leftTime = merge_order_info['needValue']
  852. backCoins = coins * Ratio(leftTime * 1.0 / merge_order_info['needValue'])
  853. elif 'needElec' in self.event_data:
  854. # 做一个数据保护
  855. if leftElec > merge_order_info['needValue']:
  856. leftElec = merge_order_info['needValue']
  857. backCoins = coins * Ratio(leftElec * 1.0 / merge_order_info['needValue'])
  858. usedFee = coins - backCoins
  859. else:
  860. pass
  861. # 分批塞入订单信息
  862. master_info = {
  863. 'order_id': self.event_data['order_id'],
  864. 'fee': self.event_data['fee'],
  865. }
  866. if 'needTime' in self.event_data:
  867. master_info.update({'needTime': self.event_data['needTime']})
  868. if 'needElec' in self.event_data:
  869. master_info.update({'needElec': self.event_data['needElec']})
  870. order_processing_list = [master_info]
  871. if 'sub' in self.event_data:
  872. order_processing_list += self.event_data['sub']
  873. # 订单服务信息与退款处理
  874. for info_ in order_processing_list[::-1]:
  875. order_ = ConsumeRecord.objects.filter(devNo=self.device.devNo, startKey=info_['order_id']).first()
  876. if not order_:
  877. continue
  878. if 'needTime' in self.event_data:
  879. needTime = info_.get('needTime')
  880. consumeDict.update({
  881. 'needTime': needTime,
  882. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: order_.coin,
  883. })
  884. if leftTime > info_.get('needTime'):
  885. # 全退
  886. back_ = VirtualCoin(order_.coin)
  887. consumeDict.update({
  888. 'leftTime': needTime,
  889. 'duration': 0,
  890. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0,
  891. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: order_.coin,
  892. })
  893. # 结单
  894. self.card.clear_frozen_balance(str(order_.id), back_)
  895. # 有退款 添加退款记录
  896. self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo)
  897. leftTime -= needTime
  898. else:
  899. # 部分退
  900. usedTime = needTime - leftTime
  901. coins_ = VirtualCoin(order_.coin)
  902. back_ = self.get_backCoins(coins_, leftTime, needTime)
  903. consumeDict.update({
  904. 'leftTime': leftTime,
  905. 'duration': usedTime,
  906. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins_ - back_,
  907. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: back_,
  908. })
  909. # 结单
  910. self.card.clear_frozen_balance(str(order_.id), back_)
  911. # 有退款 添加退款记录
  912. if back_ > VirtualCoin(0):
  913. self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo)
  914. leftTime = 0
  915. elif 'needElec' in self.event_data:
  916. needElec = info_.get('needElec')
  917. consumeDict.update({
  918. 'needElec': needElec,
  919. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: order_.coin,
  920. })
  921. if leftElec > needElec:
  922. # 全退
  923. back_ = VirtualCoin(order_.coin)
  924. consumeDict.update({
  925. 'leftElec': needElec,
  926. 'elec': 0,
  927. 'elecFee': self.deviceAdapter.calc_elec_fee(0),
  928. })
  929. # 结单
  930. self.card.clear_frozen_balance(str(order_.id), back_)
  931. # 有退款 添加退款记录
  932. self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo)
  933. leftTime -= needElec
  934. else:
  935. # 部分退
  936. usedElec = needElec - leftElec
  937. coins_ = VirtualCoin(order_.coin)
  938. back_ = self.get_backCoins(coins_, leftElec, needElec)
  939. consumeDict.update({
  940. 'leftElec': leftElec,
  941. 'elec': usedElec,
  942. 'elecFee': self.deviceAdapter.calc_elec_fee(elec),
  943. DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins_ - back_,
  944. DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: back_,
  945. })
  946. # 结单
  947. self.card.clear_frozen_balance(str(order_.id), back_)
  948. # 有退款 添加退款记录
  949. if back_ > VirtualCoin(0):
  950. self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo)
  951. leftElec = 0
  952. order_.update_service_info(consumeDict)
  953. self.card.reload()
  954. extra = []
  955. extra.append({u'在线卡': '{}--No:{}'.format(self.card.cardName, self.card.cardNo)})
  956. extra.append({u'本次使用时长': '{}(分钟)'.format(duration)})
  957. extra.append({u'本次消费金额': '{}(金币)'.format(usedFee)})
  958. extra.append({u'卡片当前余额': '{}(金币)'.format(self.card.balance.mongo_amount)})
  959. self.notify_user_service_complete(
  960. service_name='充电',
  961. openid=self.card.managerialOpenId,
  962. port=str(order.used_port),
  963. address=order.address,
  964. reason=self.event_data.get('reasonDesc'),
  965. finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'),
  966. extra=extra)
  967. def do_finished_event(self, order, merge_order_info):
  968. # type:(ConsumeRecord, dict)->None
  969. self._do_finished(order, merge_order_info)
  970. def checkout_order(self, order):
  971. # 在线卡 执行扣费
  972. fee = VirtualCoin(order.coin)
  973. self.card.freeze_balance(transaction_id=str(order.id), fee=fee)