123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import logging
- import time
- from mongoengine.errors import DoesNotExist
- from django.utils.functional import cached_property
- from apilib.monetary import RMB, VirtualCoin
- from apilib.utils_datetime import to_datetime
- from apps.web.eventer.base import WorkEvent, FaultEvent
- from apps.web.eventer import EventBuilder
- from apps.web.south_intf.platform import notify_event_to_north
- from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang
- from apps.web.south_intf.zhongtian import report_zhongtian_service_complete, report_zhongtian_refund
- from apps.web.user.models import UserVirtualCard, VCardConsumeRecord, ServiceProgress, MyUser, Card
- from apps.web.api.models import APIStartDeviceRecord
- from apps.web.constant import Const
- from apilib.systypes import StrEnum
- from apps.web.core.accounting import Accounting
- from apps.web.device.models import Device, Group
- from apps.web.eventer.errors import NoCommandHandlerAvailable
- logger = logging.getLogger(__name__)
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None or 'cmdCode' not in event_data:
- return None
- if event_data['cmdCode'] in ['05', '03']:
- return ChargingSiJiangWorkEvent(self.deviceAdapter, event_data)
- if event_data['cmdCode'] == '0D':
- return FaultEvent(self.deviceAdapter, event_data)
- class SI_JIANG_EVENT_CODE(StrEnum):
- OFFLINE_COIN_REPORT = '03'
- SWIPE_CARD_REPORT = '04'
- CHARGE_FINISHED = '05'
- ERROR = '0D'
- class ChargingSiJiangWorkEvent(WorkEvent):
- @cached_property
- def handler_map(self):
- return {
- '03': self.handle_OFFLINE_COIN_REPORT,
- '05': self.handle_CHARGE_FINISHED
- }
- def support_playback(self):
- return self.event_data['cmdCode'] in ['05']
- def do(self, **args):
- devNo = self.device['devNo']
- logger.info('si-jiang charging event detected, devNo=%s, curInfo=%s' % (devNo, self.event_data))
- cmdCode = self.event_data['cmdCode']
- try:
- self.handler_map[cmdCode]()
- except KeyError:
- raise NoCommandHandlerAvailable('device %s got no recognizable cmd %s ' % (devNo, cmdCode))
- def handle_OFFLINE_COIN_REPORT(self):
- """
- 投币事件上报
- :return:
- """
- Accounting.recordOfflineCoin(
- self.device,
- int(time.time()),
- int(self.event_data['coin'])
- )
- self.deviceAdapter.get_port_status_from_dev()
- def handle_CHARGE_FINISHED(self):
- """
- :return:
- """
- devNo = self.device['devNo']
- port = str(self.event_data['port'])
- recvTime = to_datetime(self.recvTime)
- try:
- lineInfo = Device.clear_port_control_cache(self.device.devNo, port)
- logger.debug(
- 'device({}) receive charge finished message. lineInfo={}'.format(self.device['devNo'], lineInfo, ))
- if 'startTime' not in lineInfo:
- logger.debug('startTime not found')
- return
- startTime = to_datetime(lineInfo['startTime'])
- usedTime = self.event_data.get('duration',
- int(round(((recvTime - startTime).total_seconds() / 60.0))))
- leftTime = self.event_data['leftTime']
- actualNeedTime = lineInfo['needTime']
- group = Group.get_group(self.device['groupId'])
- consumeDict = {
- 'reason': self.event_data['reason'],
- 'leftTime': leftTime,
- 'chargeIndex': port,
- 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
- 'duration': usedTime
- }
- # 如果是刷卡的,直接更新消费记录,然后发送通知消息,不支持退费
- if 'cardNo' in lineInfo:
- card = Card.objects(cardNo=lineInfo['cardNo'], agentId=self.dealer.agentId).get()
- consumeDict.update({'balance': lineInfo['balance']})
- ServiceProgress.update_progress_and_consume_rcd(
- self.device['ownerId'],
- {
- 'open_id': lineInfo['openId'],
- 'port': int(port),
- 'device_imei': self.device['devNo'],
- 'isFinished': False
- }, consumeDict
- )
- #: 通知服务结束
- title = u'%s 卡号:%s,按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
- self.event_data['reason'],
- lineInfo['cardNo'],
- actualNeedTime,
- leftTime
- )
- service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (self.device['logicalCode'], port, group['address'])
- finishTime = recvTime.strftime('%Y-%m-%d %H:%M:%S')
- self.notify_service_complete(card.managerialOpenId, title=title, service=service, finishTime=finishTime)
- elif 'consumeRcdId' in lineInfo:
- coins = RMB(lineInfo['coins'])
- backCoins = self.get_backCoins(coins=coins, leftTime=leftTime, actualNeedTime=actualNeedTime)
- vCardId = lineInfo.get('vCardId')
- try:
- vCard = UserVirtualCard.objects.get(id=vCardId)
- except DoesNotExist:
- logger.info('can not find the vCard id = %s' % vCardId)
- return
- managerialOpenId = self.get_managerialOpenId_by_openId(lineInfo['openId'])
- title = u'%s 按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
- self.event_data['reason'],
- actualNeedTime,
- leftTime)
- service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
- self.device['logicalCode'], port, group['address'])
- self.notify_service_complete(managerialOpenId=managerialOpenId, title=title, service=service)
- ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
- {'open_id': lineInfo['openId'], 'port': int(port),
- 'device_imei': self.device['devNo'],
- 'isFinished': False}, consumeDict)
- consumeRcdId = lineInfo.get('consumeRcdId', None)
- if consumeRcdId is None:
- logger.error('can not find consume rcd id')
- return
- try:
- vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId)
- except DoesNotExist:
- logger.error('can not find the consume rcd id = %s' % consumeRcdId)
- return
- vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, backCoins.mongo_amount)
- else:
- #: 这里需要考虑API调用的和普通使用场景
- if 'extOrderNo' in lineInfo:
- record = APIStartDeviceRecord.get_api_record(self.device['logicalCode'], lineInfo['extOrderNo'])
- if not record:
- logger.debug('cannot find api start device record')
- return
- if record.postActionTriggered:
- logger.debug('api({}) post action has done.'.format(lineInfo['extOrderNo']))
- return
- report_zhongtian_service_complete(
- event_code = '16',
- record = record,
- orderNo = lineInfo['extOrderNo'],
- deviceCode = self.device['logicalCode'],
- groupName = group['groupName'],
- address = group['address'],
- actualNeedTime = actualNeedTime,
- leftTime = leftTime,
- finishedState = self.event_data['finishedState'])
- if self.device.is_auto_refund:
- coins = VirtualCoin(lineInfo['coins'])
- money = RMB(lineInfo['money'])
- backCoins = self.get_backCoins(coins = coins, leftTime = leftTime,
- actualNeedTime = actualNeedTime)
- backMoney = self.get_backMoney(money = money, leftTime = leftTime,
- actualNeedTime = actualNeedTime)
- report_zhongtian_refund(
- eventCode = '16',
- record = record,
- orderNo = lineInfo['extOrderNo'],
- deviceCode = self.device['logicalCode'],
- groupName = group['groupName'],
- address = group['address'],
- backMoney = str(backMoney),
- backCoins = str(backCoins),
- actualNeedTime = actualNeedTime,
- leftTime = leftTime,
- finishedState = self.event_data['finishedState']
- )
- else:
- user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device['groupId']).first()
- # 通知服务结束
- if user:
- title = u'%s 按动态功率计算总时间为:%s分钟,剩余时间:%s分钟' % (
- self.event_data['reason'],
- actualNeedTime,
- leftTime
- )
- service = u'充电服务(设备编号:%s, 端口:%s,地址:%s)' % (
- self.device['logicalCode'],
- port,
- group['address']
- )
- self.notify_service_complete(managerialOpenId=user.managerialOpenId, title=title,
- service=service)
- # 如果需要退款,计算退款数据.
- if self.device.is_auto_refund:
- coins = VirtualCoin(lineInfo['coins'])
- money = RMB(lineInfo.get('money', 0))
-
- backCoins = self.get_backCoins(coins, leftTime, actualNeedTime)
- refundRMB = self.get_backMoney(money, leftTime, actualNeedTime)
- if backCoins > coins or refundRMB > money:
- logger.error(
- 'refund amount exceeds expectations dev=<{}>, coins=<{}> backCoins=<{}> price=<{}> refundRMB=<{}>'.format(
- self.device.devNo, coins, backCoins, money, refundRMB))
- return
- # refund_net_pay会做保护. 如果不支持, 就不会往缓存塞money,payInfo
- refundCash = 'refundRMB_device_event' in self.device.owner.features
- if (backCoins <= VirtualCoin(0)) and (refundRMB <= RMB(0)):
- pass
- else:
- if refundCash:
- title = u'%s 按动态功率计算总时间为:%s分钟,还有:%s分钟的时间未使用完,给您退回剩余金额:%s元' % (
- service, actualNeedTime, leftTime, refundRMB)
-
- backCount = u'金额:%s 元' % refundRMB
- else:
- title = u'%s 按动态功率计算总时间为:%s分钟,还有:%s分钟的时间未使用完,给您退还成相应的金币:%s元,您下次可以接着使用哦' % (service, actualNeedTime, leftTime, backCoins)
-
- backCount = u'金币:%s' % backCoins
- self.refund_net_pay(user, lineInfo, refundRMB, backCoins, consumeDict, refundCash)
-
- self.notify_refund_coins(managerialOpenId=user.managerialOpenId, title=title,
- backCount=backCount)
-
- ServiceProgress.update_progress_and_consume_rcd(ownerId=self.device['ownerId'],
- queryDict={
- 'open_id': lineInfo['openId'],
- 'port': int(port),
- 'device_imei': self.device['devNo'],
- 'isFinished': False
- },
- consumeDict=consumeDict)
- else:
- ServiceProgress.update_progress_and_consume_rcd(self.device['ownerId'],
- {'open_id': lineInfo['openId'],
- 'port': int(port),
- 'device_imei': self.device['devNo'],
- 'isFinished': False}, consumeDict)
- return
- except Exception as e:
- logger.exception(e)
- finally:
- notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL,
- desc = self.event_data['reason'])
- send_event_to_zhejiang(self.dealer, self.device, self.event_data)
|