123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import time
- from typing import TYPE_CHECKING
- from apilib.monetary import RMB, VirtualCoin
- from apilib.utils_datetime import to_datetime
- from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND
- from apps.web.core.accounting import Accounting
- from apps.web.device.models import Device
- from apps.web.device.timescale import FluentedEngine
- from apps.web.eventer import EventBuilder
- from apps.web.eventer.base import FaultEvent, WorkEvent
- from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, Card, MyUser, CardRechargeOrder, \
- Redpack, RechargeRecord
- if TYPE_CHECKING:
- from apps.web.device.models import GroupDict
- 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:
- return None
- if not event_data.has_key('cmdCode'):
- return
- if 'duration' in device_event:
- event_data.update({'duration': device_event['duration']})
- if 'v' in device_event:
- event_data.update({'v': device_event['v']})
- if event_data['cmdCode'] in ['03', '04', '05', '25', '26', '28']:
- return ChargingYUNCHONGWorkEvent(self.deviceAdapter, event_data)
- if event_data['cmdCode'] == '0D':
- return FaultEvent(self.deviceAdapter, event_data)
- class ChargingYUNCHONGWorkEvent(WorkEvent):
- def __parse_device_finished_data(self, event_data):
- duration = event_data.get('duration', -1)
- if duration != -1:
- if 'v' in event_data:
- duration = (duration / 60)
- elec = event_data.get('elec', -1)
- if elec != -1:
- if 'v' in event_data:
- elec = round(elec / (10000.0 * 3600.0), 3)
- else:
- elec = round(elec / 3600.0, 3)
- logger.debug('device duration is {}, device elec is {}'.format(duration, elec))
- return duration, elec
- @property
- def support_playback(self):
- if self.event_data['cmdCode'] in ['05']:
- return True
- else:
- return False
- def get_back_info(self, leftTime, actualNeedTime, lineInfo):
- coins = VirtualCoin(lineInfo['coins'])
- price = RMB(lineInfo['price'])
- refundedCoins = VirtualCoin(0)
- refundedMoney = RMB(0)
- if leftTime != 65535:
- refundedCoins = coins * (float(leftTime) / float(actualNeedTime))
- refundedMoney = RMB(price * (float(leftTime) / float(actualNeedTime)))
- else:
- refundedCoins = coins
- refundedMoney = RMB(price)
- if refundedCoins > coins:
- refundedCoins = coins
- if refundedMoney > price:
- refundedMoney = price
- return refundedMoney, refundedCoins
- def do(self, **args):
- logger.info('[yunchong]charging event detected, devNo=%s,info=%s' % (self.device.devNo, self.event_data))
- if self.event_data['cmdCode'] == '03': # 投币数据
- if 'today_coins' in self.event_data and 'ts' in self.event_data:
- Accounting.syncOfflineCoin(
- self.device,
- datetime.datetime.fromtimestamp(self.event_data['ts']).strftime('%Y-%m-%d'),
- self.event_data['today_coins'])
- if self.event_data['coins'] > 0:
- FluentedEngine().in_put_coins_udp(devNo=self.device.devNo,
- ts=int(time.time()),
- coins=self.event_data['coins'],
- mode='uart')
- else:
- # 老的流程会单条记录上报记录
- Accounting.recordOfflineCoin(device=self.device,
- report_ts=int(time.time()),
- coins=int(self.event_data['coins']),
- mode='uart',
- port=self.event_data.get('port', None))
- elif self.event_data['cmdCode'] == '05':
- devNo = self.device.devNo
- port = str(self.event_data['port'])
- try:
- lineInfo = Device.clear_port_control_cache(devNo, port)
- if not lineInfo:
- logger.debug('get null control cache from {}'.format(repr(self.device)))
- return
- if 'openId' not in lineInfo:
- logger.debug('openId not in line info. ignore it.')
- return
- if 'redpackInfo' in lineInfo:
- for _info in lineInfo['redpackInfo']:
- redpack = Redpack.get_one(_info['redpackId'])
- redpackCoins = VirtualCoin(redpack['redpackCoins'])
- lineInfo['coins'] = float(VirtualCoin(lineInfo['coins']) - redpackCoins)
- redpackMoney = RMB(redpack['redpackMoney'])
- lineInfo['price'] = float(RMB(lineInfo['price']) - redpackMoney)
- logger.debug('redpack is <{}> redpack money is: {}; redpack coins is: {}'.format(
- redpack.get('id'), str(redpackMoney.amount), str(redpackCoins.amount)))
- lineInfo['coins'] = 0 if lineInfo['coins'] < 0 else lineInfo['coins']
- lineInfo['price'] = 0 if lineInfo['price'] < 0 else lineInfo['price']
- return self.do_time_finish(devNo, port, lineInfo)
- finally:
- pass
- elif self.event_data['cmdCode'] == '26':
- cardNo = self.event_data['cardNo']
- card = self.update_card_dealer_and_type(cardNo, 'ID')
- if not card:
- return
- #: 首先检查订单,并进行充值
- #: 用户首先在手机客户端充值,需要这个时刻刷卡上报事件将充值的订单同步上来
- card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id))
- if card_recharge_order:
- self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order)
- card.reload()
- try:
- # 直接回复余额和随机码
- if not card.frozen:
- if card.balance <= VirtualCoin(0):
- self.deviceAdapter.response_card_balance(cardNo, 0)
- else:
- self.deviceAdapter.response_card_balance(cardNo, int(card.balance))
- else:
- self.deviceAdapter.response_card_balance(cardNo, 0) # 挂失卡,直接回复余额为0
- except Exception, e:
- pass
- elif self.event_data['cmdCode'] == '28':
- cardNo = self.event_data['cardNo']
- card = self.update_card_dealer_and_type(cardNo, 'ID')
- if not card:
- return
- port = str(self.event_data['port'])
- Device.update_dev_control_cache(self.device.devNo, {
- port: {
- 'openId': card.openId,
- 'price': self.event_data['coins'],
- 'coins': self.event_data['coins'],
- 'cardId': str(card.id),
- 'startTime': int(time.time()),
- 'status': Const.DEV_WORK_STATUS_WORKING
- }
- })
- balance = card.balance - RMB(self.event_data['coins']) # type: RMB
- consumeMoney = RMB(self.event_data['coins'])
- desc = u'正在刷卡使用,卡号:%s,卡名称:%s,端口号:%s,扣费:%s,余额:%s' % (
- card.cardNo, card.cardName,
- self.event_data['port'],
- self.event_data['coins'], balance)
- orderNo, cardOrderNo = \
- self.record_consume_for_card(card=card,
- money=consumeMoney,
- desc=desc,
- servicedInfo={
- 'balance': balance.mongo_amount,
- 'coin': self.event_data['coins'],
- 'port': self.event_data['port']})
- self.update_card_balance(card, balance)
- ServiceProgress.register_card_service(self.device, self.event_data['port'], card,
- {
- 'orderNo': orderNo,
- 'coin': str(self.event_data['coins']),
- 'cardOrderNo': cardOrderNo
- })
- self.notify_balance_has_consume_for_card(card, consumeMoney, desc)
- def do_time_finish(self, devNo, port, lineInfo):
- logger.info("[{} do_time_finish] devNo = {}, port = {}, lineInfo = {}, event = {}".format(
- self.__class__.__name__, devNo, port, lineInfo, self.event_data))
- try:
- if not self.dealer:
- logger.error(
- "[{} do_time_finish] dealer {} is not found!".format(self.__class__.__name__, self.device.ownerId))
- return
- recvTime = to_datetime(self.recvTime)
- if 'startTime' in lineInfo:
- startTime = to_datetime(lineInfo['startTime'])
- if startTime > recvTime:
- logger.error('start time is bigger than now time,devNo={}'.format(devNo))
- serverDuration = -1
- else:
- serverDuration = int((recvTime - startTime).total_seconds() / 60.0)
- else:
- logger.info('lineinfo has not startTime,devNo=%s' % devNo)
- serverDuration = -1
- leftTime = self.event_data['leftTime']
- logger.debug('serverDuration = {}; leftTime = {}; lineInfo = {}'.format(
- serverDuration, leftTime, lineInfo))
- if leftTime == 65535: # 设备返回65535说明端口没有启动
- leftTimeStr = u'端口未使用'
- actualNeedTime = 0
- usedTime = 0
- leftTime = 65535
- elif serverDuration < 0: # duration参数错误
- logger.debug('serverDuration and deviceDuration is valid.')
- actualNeedTime = -1
- usedTime = -1
- leftTimeStr = ''
- else:
- usedTime = serverDuration
- leftTime = self.event_data['leftTime']
- leftTimeStr = leftTime
- actualNeedTime = usedTime + leftTime
- if actualNeedTime == -1:
- logger.debug('has no actual need time. ignore this event.')
- return
- refundProtectionTime = self.device.my_obj.otherConf.get('refundProtectionTime', 5)
- if usedTime < refundProtectionTime:
- rechargeIds = list()
- payInfo = lineInfo.get('payInfo', list())
- for item in payInfo:
- if 'rechargeRcdId' not in item:
- continue
- rechargeIds.append(item['rechargeRcdId'])
- refundedMoney, refundedCoins = self.get_back_info(
- leftTime=leftTime, actualNeedTime=actualNeedTime, lineInfo=lineInfo)
- logger.debug('refund switch = {}, protect time = {}, lefttime = {}, usedTime = {}, need time = {}, back coins = {}, back money = {}'.format(
- self.device.is_auto_refund, leftTime, usedTime, actualNeedTime, refundedCoins, refundedMoney, refundProtectionTime))
- if usedTime < refundProtectionTime:
- refundedCoins = VirtualCoin(lineInfo['coins'])
- refundedMoney = RMB(lineInfo['price'])
- elif not self.device.is_auto_refund:
- refundedCoins = VirtualCoin(0)
- refundedMoney = RMB(0)
- group = self.device.group # type: GroupDict
- consumeDict = {
- 'reason': self.event_data['reason'],
- 'leftTime': leftTimeStr,
- 'chargeIndex': port,
- 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
- 'duration': usedTime,
- 'serverDuration': serverDuration,
- 'uartData': self.event_data.get('uartData', '')
- }
- if 'cardId' in lineInfo:
- cardId = lineInfo['cardId']
- card = Card.objects.get(id=cardId)
- consumeDict = {
- 'chargeIndex': port,
- 'reason': self.event_data['reason'],
- 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime,
- 'duration': usedTime,
- 'leftTime': leftTimeStr
- }
- self.notify_user_service_complete(
- service_name=u'充电',
- openid=card.managerialOpenId if card else '',
- port=port,
- address=group['address'],
- reason=self.event_data['reason'],
- finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
- extra=[{
- u'实体卡号': card.cardNo
- }]
- )
- coins = VirtualCoin(lineInfo['coins'])
- if refundedCoins > VirtualCoin(0):
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundedCoins.mongo_amount,
- DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: (coins - refundedCoins).mongo_amount
- })
- self.refund_money_for_card(refundedCoins, str(card.id))
- else:
- consumeDict.update({
- DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins.mongo_amount
- })
- ServiceProgress.update_progress_and_consume_rcd(
- self.device['ownerId'],
- {'open_id': lineInfo['openId'], 'device_imei': self.device['devNo'],
- 'port': lineInfo['port'], 'isFinished': False}, consumeDict)
- elif 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']:
- vCardId = lineInfo['vCardId']
- vCard = UserVirtualCard.objects(id=vCardId).first() # type: UserVirtualCard
- if not vCard:
- logger.info('can not find the vCard id = %s' % vCardId)
- return
- try:
- ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
- {'open_id': lineInfo['openId'],
- 'port': int(port),
- 'device_imei': self.device.devNo,
- 'isFinished': False}, consumeDict)
- if refundedCoins > VirtualCoin(0):
- vCardConsumeRcd = VCardConsumeRecord.objects.get(id=lineInfo['consumeRcdId'])
- vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, refundedCoins.mongo_amount)
- finally:
- user = MyUser.objects(openId=lineInfo['openId'], groupId=self.device.groupId).first()
- self.notify_user_service_complete(
- service_name=u'充电',
- openid=user.managerialOpenId if user else '',
- port=port,
- address=group['address'],
- reason=self.event_data['reason'],
- finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
- extra=[
- {u'虚拟卡号': vCard.cardNo}
- ]
- )
- else:
- user = MyUser.objects(openId=lineInfo['openId'],
- groupId=self.device.groupId).first()
- if not user:
- logger.warning(
- 'not find user<openId={}, groupId={}>'.format(lineInfo['openId'], self.device.groupId))
- return
- coins = VirtualCoin(lineInfo['coins'])
- try:
- is_cash = False
- if 'refundRMB_device_event' in self.device.owner.features and refundedMoney > RMB(0):
- is_cash = True
- consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: VirtualCoin(coins).mongo_amount})
- if refundedMoney > RMB(0) or refundedCoins > VirtualCoin(0):
- consumeDict.update(
- {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundedCoins).mongo_amount})
- self.refund_net_pay(user, lineInfo, refundedMoney, refundedCoins, consumeDict, is_cash)
- ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId,
- {'open_id': lineInfo['openId'],
- 'port': int(port),
- 'device_imei': self.device.devNo,
- 'isFinished': False}, consumeDict)
- finally:
- extra = []
- if DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH in consumeDict:
- real_refund = RMB(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_CASH])
- if real_refund > RMB(0):
- extra.append({u'消费金额': '{}(元)'.format(RMB(lineInfo['price']) - real_refund)})
- extra.append({u'退款金额': '{}(元)'.format(real_refund)})
- else:
- extra.append({u'消费金额': '{}(元)'.format(lineInfo['price'])})
- elif DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS in consumeDict:
- real_refund = VirtualCoin(consumeDict[DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS])
- if real_refund > VirtualCoin(0):
- extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins) - real_refund)})
- extra.append({u'退款金额': '{}(金币)'.format(real_refund)})
- else:
- extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
- else:
- extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))})
- self.notify_user_service_complete(
- service_name=u'充电',
- openid=user.managerialOpenId if user else '',
- port=port,
- address=group['address'],
- reason=self.event_data['reason'],
- finished_time=recvTime.strftime('%Y-%m-%d %H:%M:%S'),
- extra=extra)
- except Exception as e:
- logger.exception(e)
|