# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import json import logging import time from arrow import Arrow from django.conf import settings from typing import TYPE_CHECKING from apilib.monetary import RMB, VirtualCoin, Ratio from apps.web.constant import Const, APP_TYPE, DEALER_CONSUMPTION_AGG_KIND, DeviceCmdCode from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.device.models import Device from apps.web.device.timescale import FluentedEngine from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf, \ IcStartAckEvent, IdStartAckEvent from apps.web.eventer import EventBuilder from apps.web.helpers import get_wechat_auth_bridge from apps.web.user.models import VCardConsumeRecord, ServiceProgress, CardRechargeOrder, MyUser, ConsumeRecord if TYPE_CHECKING: from apps.web.device.models import DeviceDict logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): # 订单机制事件 if 'order_id' in device_event: if device_event['order_type'] == 'com_start': return MyComNetPayAckEvent(self.deviceAdapter, device_event) if device_event['order_type'] == 'ic_recharge': pass if device_event['order_type'] == 'ic_start': return OfflineCardStartAckEvent(self.deviceAdapter, device_event) if device_event['order_type'] == 'id_start': return OnlineCardStartAckEvent(self.deviceAdapter, device_event) if device_event['order_type'] == 'card_refund': pass else: if 'data' not in device_event: return None 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.get('subCmd') == '3F': return GaoBoRuiFaultEvent(self.deviceAdapter, event_data) else: return GeneralEvent(self.deviceAdapter, event_data) class GeneralEvent(WorkEvent): SUCCESS_01 = '01' BALANCE_NOT_ENOUGH_02 = '02' INVALID_CARD_03 = '03' FREEZE_CARD_04 = '04' def do(self): if self.event_data['subCmd'] == '35': self._do_start_with_offline_card() elif self.event_data['subCmd'] == '36': self._do_get_balance() elif self.event_data['subCmd'] == '3B': self._do_sync_balance_with_offline_card() elif self.event_data['subCmd'] == '32': self._graph_power_point() def _do_get_balance(self): cardNo = str(int(self.event_data.get('card_no'), 16)) logger.info('receive cardNo:{}'.format(cardNo)) card = self.update_card_dealer_and_type(cardNo) mqtt_data = { 'funCode': self.event_data.get('cmdCode') } if not card or not card.openId: data = self.event_data.get('session') data += '36' data += self.INVALID_CARD_03 data += self.event_data.get('card_no') data += self.event_data.get('fee') data += '{:0>4X}'.format(0) logger.info('no this card! card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo)) elif card.frozen: data = self.event_data.get('session') data += '36' data += self.FREEZE_CARD_04 data += self.event_data.get('card_no') data += self.event_data.get('fee') data += '{:0>4X}'.format(0) logger.info('card is frozen card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo)) else: # 是否存在没有到账的余额 进行充值 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) self.recharge_id_card( card=card, rechargeType='append', order=card_recharge_order ) card.reload() fee = RMB(int(self.event_data.get('fee'), 16)) * 0.1 if card.balance >= RMB(fee): data = self.event_data.get('session') data += '36' data += self.SUCCESS_01 data += self.event_data.get('card_no') data += self.event_data.get('fee') data += '{:0>4X}'.format(int((card.balance - fee) * 10)) else: data = self.event_data.get('session') data += '36' data += self.BALANCE_NOT_ENOUGH_02 data += self.event_data.get('card_no') data += self.event_data.get('fee') data += '{:0>4X}'.format(int((card.balance - fee) * 10)) mqtt_data['data'] = data self.send_mqtt(data=mqtt_data, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC): """ 发送mqtt 指令默认210 返回data """ result = MessageSender.send(self.device, cmd, data) if 'rst' in result and result['rst'] != 0: if result['rst'] == -1: raise ServiceException( {'result': 2, 'description': u'该设备正在玩命找网络,请您稍候再试', 'rst': -1}) elif result['rst'] == 1: raise ServiceException( {'result': 2, 'description': u'该设备忙,无响应,请您稍候再试。也可能是您的设备版本过低,暂时不支持此功能', 'rst': 1}) else: if cmd in [DeviceCmdCode.OPERATE_DEV_NO_RESPONSE, DeviceCmdCode.PASSTHROUGH_OPERATE_DEV_NO_RESPONSE]: return return result.get('data', 'ok') def _do_start_with_offline_card(self): cardNo = str(int(self.event_data.get('card_no'), 16)) port = self.event_data.get('port') fee = self.event_data.get('fee') balance = self.event_data.get('balance') billingType = self.event_data.get('billingType') logger.info('receive cardNo:{}'.format(cardNo)) card = self.update_card_dealer_and_type(cardNo, cardType='IC', balance=RMB(balance)) if not card: logger.info('no this card! card_no_hex<{}>, cardNo<{}>'.format(self.event_data.get('card_no'), cardNo)) return if card.frozen: # 停止此离线卡的充电行为 self.deviceAdapter.stop_charging_port(port) return lineInfo = Device.get_dev_control_cache(self.device.devNo).get(str(port)) if lineInfo.get('status') == Const.DEV_WORK_STATUS_WORKING: pass else: logger.info('_do_card_start_charge the once!!') # 记录ID卡的消费 attachParas = { 'chargeIndex': port } servicedInfo = { 'cardNo': cardNo, 'chargeIndex': port } orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(fee), servicedInfo=servicedInfo, attachParas=attachParas) # 记录缓存信息 可以在个人中心以及经销商的端口管理显示 lineInfo = { 'port': str(port), 'cardNo': cardNo, 'openId': card.openId, 'payCount': 1, 'coins': fee, 'status': Const.DEV_WORK_STATUS_WORKING, 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'orderNo': orderNo, 'cardOrderNo': cardOrderNo, 'billingType': billingType, 'consumeType': 'card' } if 'needTime' in self.event_data: lineInfo.update({'needTime': self.event_data['needTime']}) if 'needElec' in self.event_data: lineInfo.update({'needElec': self.event_data['needElec']}) ServiceProgress.register_card_service( self.device, int(port), card, { 'orderNo': orderNo, 'coin': RMB(fee).mongo_amount, 'cardOrderNo': cardOrderNo } ) Device.update_port_control_cache(self.device.devNo, lineInfo) def _do_sync_balance_with_offline_card(self): card_no = self.event_data.get('card_no') cardNo = str(int(card_no, 16)) port = self.event_data.get('port') fee = self.event_data.get('fee') balance = self.event_data.get('balance') logger.info('receive cardNo:{}'.format(cardNo)) self.update_card_dealer_and_type(cardNo, cardType='IC', balance=RMB(balance)) logging.info('Sync balance completed , , , '.format(port, cardNo, card_no, fee, balance)) def _graph_power_point(self): powers = self.event_data.get('powers') for port, power in powers.items(): FluentedEngine().in_power_udp(devNo=self.device.devNo, port=port, ts=int(time.time()), power=power, voltage=None, current=None) class GaoBoRuiEvent(WorkEvent): def do(self): pass class GaoBoRuiFaultEvent(FaultEvent): def do(self, **args): # 将告警的消息打入相应的缓存 port = self.event_data.get('port') # 0 表示整机 if not port: part = str(0) else: part = str(port) warningData = { 'warningStatus': 2, 'warningDesc': self.event_data['statusInfo'], 'warningTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'warningUart': self.event_data['uart'] } Device.update_dev_warning_cache(self.device.devNo, {part: warningData}) super(GaoBoRuiFaultEvent, self).do() class StartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason, fault_code=None): FINISHED_CHARGE_REASON_MAP = { 0: u'购买的充电时间或者电量已经用完', 1: u'充满自停', 2: u'刷卡用户按停止键', 3: u'未接负载30秒自停', 4: u'充电器移除', 5: u'充电功率超限', # 服务器定义的停止事件 97: u'用户使用离线卡手动操作退费 ,充电结束', 98: u'用户手动点击结束按钮结束充电', 99: u'远程点击停止按钮, 充电结束', } if reason == 0xEE: if fault_code: fault_code = '{:X}'.format(fault_code)[-1] FAULT_ERROR_MAP = { '1': '(键盘故障)', '2': '(显示器故障)', '3': '(刷卡板故障)', '4': '(子网故障)', '5': '(存储故障)', '6': '(过温故障)', '7': '(烟雾故障)', '8': '(漏电)', '9': '(短路)', 'A': '(过压)', 'B': '(欠压)', 'C': '(整机过流)', 'D': '(恶意操作)', 'E': '(继电器粘连) ', 'F': '(其它故障) ', } return '充电桩发生故障' + FAULT_ERROR_MAP.get(fault_code) return '充电桩发生故障' return FINISHED_CHARGE_REASON_MAP.get(reason) def pre_processing(self, device, event_data): # type:(DeviceDict, dict)->dict source = json.dumps(event_data, indent=4) event_data['source'] = source if 'myDuration' in event_data: duration = event_data.pop('myDuration', 0) event_data['duration'] = round((duration + 59) / 60.0, 1) if 'myElec' in event_data: myElec = event_data.pop('myElec', 0) event_data['elec'] = round(myElec / 3600000.0, 2) if 'fts' in event_data and 'sts' in event_data: duration = event_data['fts'] - event_data['sts'] event_data['duration'] = round((duration + 59) / 60.0, 1) if 'elec' in event_data: event_data['elec'] = round(event_data.pop('elec') * 0.01, 2) if 'openId' in event_data: pass if 'power' in event_data: event_data['power'] = round(event_data['power'] * 0.1, 1) if 'status' in event_data and event_data['status'] == 'finished': event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason'), event_data.get('fault_code')) if 'leftTime' in event_data: pass if 'leftElec' in event_data: event_data['leftElec'] = round(event_data['leftElec'] * 0.01, 2) return event_data class MyComNetPayAckEvent(ComNetPayAckEvent): def __init__(self, smartBox, event_data): super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_before_start(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_running': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_start(self, order=None): pass def post_before_finish(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_finished': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict cType, cValue = self.event_data['cType'], self.event_data['cValue'] start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { } all_coins = master_order.package['coins'] all_price = master_order.package['price'] for sub_order in sub_orders: all_coins += sub_order.package['coins'] all_price += sub_order.package['price'] if 'sub' in self.event_data: for sub_data in self.event_data['sub']: cValue += sub_data['cValue'] portDict['coins'] = str(all_coins) portDict['price'] = str(all_price) portDict['cType'] = cType if cType == 1: # 时间 portDict['needKind'] = 'needTime' portDict['needValue'] = cValue portDict['estimatedTs'] = int(start_time.timestamp + cValue * 60) portDict['unit'] = u'分钟' elif cType == 2: # 电量 portDict['needKind'] = 'needElec' portDict['needValue'] = cValue portDict['estimatedTs'] = int(start_time.timestamp + 720 * 60) portDict['unit'] = u'度' portDict['consumeType'] = 'mobile' return portDict def do_finished_event(self, master_order, sub_orders, merge_order_info): # type: (ConsumeRecord, [ConsumeRecord], dict)->None self._do_finished(master_order, sub_orders, merge_order_info) def insert_vCard_consume_record(self, vCard, order, success, consumeTotal, consumeDay): try: if success and consumeDay['count'] > 0: record = VCardConsumeRecord( orderNo=VCardConsumeRecord.make_no(order.logicalCode), openId=order.openId, nickname=order.nickname, cardId=str(vCard.id), dealerId=vCard.dealerId, devNo=order.devNo, devTypeCode = order.devTypeCode, devTypeName = order.dev_type_name, logicalCode=order.logicalCode, groupId=order.groupId, address=order.address, groupNumber=order.groupNumber, groupName=order.groupName, attachParas=order.attachParas, consumeData=consumeTotal, consumeDayData=consumeDay ) record.save() except Exception, e: logger.exception(e) def _do_finished(self, order, sub_orders, merge_order_info): # type: (ConsumeRecord, list, dict)->None duration, elec, cType, leftTime, leftElec = self.event_data.get('duration', 0), self.event_data.get('elec', 0), self.event_data.get( 'cType'), self.event_data.get('leftTime', 0), self.event_data.get('leftElec', 0) coins, price, needValue = VirtualCoin(merge_order_info['coins']), RMB(merge_order_info['price']), \ merge_order_info['needValue'] refundRatio = 0.0 if cType == 1: # 按时计费 if leftTime > needValue: leftTime = needValue refundRatio = leftTime * 1.0 / needValue elif cType == 2: # 按量计费 if leftElec > needValue: leftElec = needValue refundRatio = leftElec * 1.0 / needValue backCoins = coins * refundRatio # refundRMB = price * refundRatio auto_refund = self.device.is_auto_refund refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5) user = MyUser.objects(openId=order.openId, groupId=order.groupId).first() # type: MyUser if duration < refundProtectionTime: backCoins = coins usedFee = VirtualCoin(0) else: if auto_refund: usedFee = coins - backCoins else: usedFee = coins logger.debug('{} auto refund enable switch is {}, refund protect time = {} backMoney={}'.format( repr(self.device), str(auto_refund), refundProtectionTime, backCoins)) extra = [] extra.append({u'本次使用时长': u'{}(分钟)'.format(duration)}) if order.paymentInfo['via'] == 'free': extra.append({u'消费金额': u'当前设备免费使用'}) elif order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: extra.append({u'消费金额': '{}(金币)'.format(usedFee)}) if backCoins > VirtualCoin(0): extra.append({u'退款金额': '{}(金币)'.format(backCoins)}) order_processing_list = [order] + sub_orders for order_ in order_processing_list[::-1]: consumeDict = { 'reason': self.event_data.get('reasonDesc', None), 'chargeIndex': str(order.used_port) } need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, order_.coin) user.clear_frozen_balance(str(order_.id), order_.paymentInfo['deduct'], back_coins=need_back_coins, consume_coins=need_consume_coins) consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.COIN: order_.coin.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount, }) # 子单直接更新 if order_ in sub_orders: order_.update_service_info(consumeDict) # 主单需要填写聚合信息后进行更新 else: consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.DURATION: duration, DEALER_CONSUMPTION_AGG_KIND.ELEC: elec, DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.deviceAdapter.calc_elec_fee(elec), }) order.update_service_info(consumeDict) else: logger.error('not net pay rather user virtual card pay. something is wrong.') return auth_bridge = get_wechat_auth_bridge(source=self.device, app_type=APP_TYPE.WECHAT_USER_MANAGER) self.notify_user_service_complete( service_name='充电', openid=user.get_bound_pay_openid(auth_bridge.bound_openid_key), port=str(order.used_port), address=order.address, reason=self.event_data.get('reasonDesc'), finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def _calc_refund_info(self, backCoins, orderCoin): if backCoins >= orderCoin: need_back_coins = orderCoin need_consume_coins = VirtualCoin(0) backCoins -= orderCoin else: need_back_coins = backCoins need_consume_coins = orderCoin - need_back_coins backCoins = VirtualCoin(0) return need_back_coins, need_consume_coins, backCoins class CardStartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason, fault_code=None): FINISHED_CHARGE_REASON_MAP = { 0: u'购买的充电时间或者电量已经用完', 1: u'充满自停', 2: u'刷卡用户按停止键', 3: u'未接负载30秒自停', 4: u'充电器移除', 5: u'充电功率超限', # 服务器定义的停止事件 97: u'用户使用离线卡手动操作退费 ,充电结束', 98: u'用户手动点击结束按钮结束充电', 99: u'远程点击停止按钮, 充电结束', } if reason == 0xEE: if fault_code: fault_code = '{:X}'.format(fault_code)[-1] FAULT_ERROR_MAP = { '1': '(键盘故障)', '2': '(显示器故障)', '3': '(刷卡板故障)', '4': '(子网故障)', '5': '(存储故障)', '6': '(过温故障)', '7': '(烟雾故障)', '8': '(漏电)', '9': '(短路)', 'A': '(过压)', 'B': '(欠压)', 'C': '(整机过流)', 'D': '(恶意操作)', 'E': '(继电器粘连) ', 'F': '(其它故障) ', } return '充电桩发生故障' + FAULT_ERROR_MAP.get(fault_code) return '充电桩发生故障' return FINISHED_CHARGE_REASON_MAP.get(reason) def pre_processing(self, device, event_data): # type:(DeviceDict, dict)->dict source = json.dumps(event_data, indent=4) event_data['source'] = source if 'cardNo' in event_data: cardNo = '{}'.format(int(event_data['cardNo'], 16)) event_data['cardNo'] = cardNo if 'myDuration' in event_data: duration = event_data.pop('myDuration', 0) event_data['myDuration'] = round((duration + 59) / 60.0, 2) if 'myElec' in event_data: myElec = event_data.pop('myElec', 0) event_data['myElec'] = round(myElec / 3600000.0 * 0.1, 2) # 功率上报的功率是x10的 if 'fts' in event_data and 'sts' in event_data: duration = event_data['fts'] - event_data['sts'] event_data['duration'] = round((duration + 59) / 60.0, 2) if 'elec' in event_data: event_data['elec'] = round(event_data.pop('elec') * 0.01, 2) if 'needTime' in event_data: pass if 'needElec' in event_data: event_data['needElec'] = round(event_data['power'] * 0.01, 2) if 'power' in event_data: event_data['power'] = round(event_data['power'] * 0.1, 1) if 'status' in event_data and event_data['status'] == 'finished': event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason'), event_data.get('fault_code')) if 'leftTime' in event_data: pass if 'leftElec' in event_data: event_data['leftElec'] = round(event_data['leftElec'] * 0.01, 2) if 'fee' in event_data: event_data['fee'] = round(event_data['fee'] * 0.1, 2) if 'refundFee' in event_data: event_data['refundFee'] = round(event_data['refundFee'] * 0.1, 2) if 'balance' in event_data: event_data['balance'] = round(event_data['balance'] * 0.1, 2) if 'sub' in event_data: for sub_dict in event_data['sub']: if 'fee' in sub_dict: sub_dict['fee'] = round(sub_dict['fee'] * 0.1, 2) if 'power' in sub_dict: sub_dict['power'] = round(sub_dict['power'] * 0.1, 1) if 'needTime' in event_data: pass if 'needElec' in event_data: event_data['needElec'] = round(event_data['power'] * 0.01, 2) return event_data class OfflineCardStartAckEvent(IcStartAckEvent): def __init__(self, smartBox, event_data): super(OfflineCardStartAckEvent, self).__init__(smartBox, event_data, CardStartAckEventPreProcessor()) def post_before_start(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_running': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_start(self, order=None): pass def post_before_finish(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_finished': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { } cType = self.event_data['cType'] all_coins = VirtualCoin(master_order.coin) all_money = RMB(master_order.money) all_consume_time = self.event_data.get('needTime', 0) all_consume_elec = self.event_data.get('needElec', 0) for sub_order in sub_orders: all_coins += VirtualCoin(sub_order.coin) all_money += RMB(sub_order.money) if self.event_data.get('sub', []): for _ in self.event_data['sub']: all_consume_time += _.get('needTime', 0) all_consume_elec += _.get('needElec', 0) portDict['cType'] = cType portDict['coins'] = str(all_coins) portDict['money'] = str(all_money) if cType == 1: portDict['needValue'] = all_consume_time portDict['needKind'] = 'needTime' portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60) # portDict['all_consume_time_value'] = all_consume_time elif cType == 2: portDict['needValue'] = all_consume_elec portDict['needKind'] = 'needElec' portDict['unit'] = u'度' portDict['estimatedTs'] = int(start_time.timestamp + 3600 * 12) # portDict['all_consume_elec_value'] = all_consume_elec portDict['consumeType'] = 'card' return portDict def _do_finished(self, order, merge_order_info): # type: (ConsumeRecord, dict)->None # 离线卡 只记录 不处理!!! duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0) consumeDict = { 'reason': self.event_data.get('reasonDesc', None), 'chargeIndex': str(order.used_port), 'cardNo': self.event_data['cardNo'], } leftTime = self.event_data.get('leftTime', 0) leftElec = self.event_data.get('leftElec', 0) refundFee = self.event_data.get('refundFee', 0) balance = self.event_data.get('balance', 0) if 'sub' in self.event_data: for sub_info in self.event_data['sub'][::-1]: sub_order = ConsumeRecord.objects.filter(devNo=self.device.devNo, startKey=sub_info['order_id']).first() if not sub_order: continue subConsumeDict = { 'reason': self.event_data.get('reasonDesc', None), 'chargeIndex': str(order.used_port), 'cardNo': self.event_data['cardNo'], } if 'refundFee' in self.event_data: # 做一个数据保护 if refundFee > merge_order_info['coins']: refundFee = merge_order_info['coins'] sub_fee = sub_info.get('fee') if refundFee > sub_fee: subConsumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: sub_fee, }) refundFee -= sub_fee else: consumeFee = sub_fee - refundFee subConsumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: consumeFee, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundFee, }) refundFee = 0 else: if 'needTime' in self.event_data: # 做一个数据保护 if leftTime > merge_order_info['needValue']: leftTime = merge_order_info['needValue'] needTime = sub_info.get('needTime') subConsumeDict.update({ 'needTime': needTime, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: sub_order.coin, }) if leftTime > sub_info.get('needTime'): # 全退 subConsumeDict.update({ 'leftTime': needTime, 'duration': 0, }) leftTime -= needTime else: # 部分退 usedTime = needTime - leftTime leftTime = 0 subConsumeDict.update({ 'leftTime': leftTime, 'duration': usedTime, }) elif 'needElec' in self.event_data: # 做一个数据保护 if leftElec > merge_order_info['needValue']: leftElec = merge_order_info['needValue'] needElec = sub_info.get('needElec') subConsumeDict.update({ 'needElec': needElec, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: sub_order.coin, }) if leftElec > needElec: # 全退 subConsumeDict.update({ 'leftElec': needElec, 'elec': 0, 'elecFee': self.deviceAdapter.calc_elec_fee(0), }) leftElec -= needElec else: # 部分退 usedElec = needElec - leftElec subConsumeDict.update({ 'leftElec': leftElec, 'elec': usedElec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec), }) leftElec = 0 sub_order.update_service_info(subConsumeDict) if 'refundFee' in self.event_data: fee = self.event_data.get('fee', 0) if refundFee > fee: consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: fee, }) else: consumeFee = fee - refundFee consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: consumeFee, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: refundFee, }) else: if 'needTime' in self.event_data: if leftTime > self.event_data['needTime']: # 全退 consumeDict.update({ 'leftTime': self.event_data['needTime'], 'duration': 0, 'needTime': self.event_data['needTime'] }) else: # 部分退 usedTime = self.event_data['needTime'] - leftTime consumeDict.update({ 'leftTime': leftTime, 'duration': usedTime, 'needTime': self.event_data['needTime'] }) if 'needElec' in self.event_data: if leftElec > self.event_data['needElec']: # 全退 consumeDict.update({ 'leftElec': self.event_data['needElec'], 'elec': 0, 'needElec': self.event_data['needElec'], 'elecFee': self.deviceAdapter.calc_elec_fee(0), }) else: # 部分退 usedElec = self.event_data['needElec'] - leftElec consumeDict.update({ 'leftElec': leftTime, 'elec': usedElec, 'needElec': self.event_data['needElec'], 'elecFee': self.deviceAdapter.calc_elec_fee(usedElec), }) order.update_service_info(consumeDict) extra = [ {u'线下卡': '{}--No:{}'.format(self.card.cardName, self.card.cardNo)}, {u'本次使用时长': '{}(分钟)'.format(duration)}, ] if refundFee > 0: extra.append({u'退费金额': '{}'.format(refundFee)}) extra.append({u'卡余额': '{}'.format(balance)}) self.notify_user_service_complete( service_name='充电', openid=self.card.managerialOpenId, port=str(order.used_port), address=order.address, reason=self.event_data.get('reasonDesc'), finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_finished_event(self, order, merge_order_info): # type:(ConsumeRecord, dict)->None self._do_finished(order, merge_order_info) def checkout_order(self, order): # 离线卡 只记录不处理 pass class OnlineCardStartAckEvent(IdStartAckEvent): def __init__(self, smartBox, event_data): super(OnlineCardStartAckEvent, self).__init__(smartBox, event_data, CardStartAckEventPreProcessor()) def post_before_start(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_running': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_start(self, order=None): pass def post_before_finish(self, order=None): # 记录处理的源数据报文 uart_source = getattr(order, 'uart_source', []) uart_source.append({ 'rece_finished': self.event_data.get('source'), 'time': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) order.uart_source = uart_source order.save() def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { } cType = self.event_data['cType'] all_coins = VirtualCoin(master_order.coin) all_money = RMB(master_order.money) all_consume_time = self.event_data.get('needTime', 0) all_consume_elec = self.event_data.get('needElec', 0) for sub_order in sub_orders: all_coins += VirtualCoin(sub_order.coin) all_money += RMB(sub_order.money) if self.event_data.get('sub', []): for _ in self.event_data['sub']: all_consume_time += _.get('needTime', 0) all_consume_elec += _.get('needElec', 0) portDict['cType'] = cType portDict['coins'] = str(all_coins) portDict['money'] = str(all_money) if cType == 1: portDict['needValue'] = all_consume_time portDict['needKind'] = 'needTime' portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60) # portDict['all_consume_time_value'] = all_consume_time elif cType == 2: portDict['needValue'] = all_consume_elec portDict['needKind'] = 'needElec' portDict['unit'] = u'度' portDict['estimatedTs'] = int(start_time.timestamp + 3600 * 12) # portDict['all_consume_elec_value'] = all_consume_elec portDict['consumeType'] = 'card' return portDict def _do_finished(self, order, merge_order_info): # type: (ConsumeRecord, dict)->None duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0) consumeDict = { 'reason': self.event_data.get('reasonDesc', None), 'chargeIndex': str(order.used_port), 'cardNo': self.event_data['cardNo'], } leftTime = self.event_data.get('leftTime', 0) leftElec = self.event_data.get('leftElec', 0) coins = VirtualCoin(merge_order_info['coins']) money = RMB(merge_order_info['money']) auto_refund = self.device.is_auto_refund refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5) backCoins = VirtualCoin(0) usedFee = coins if duration <= refundProtectionTime: usedFee = VirtualCoin(0) if 'needTime' in self.event_data: leftTime = merge_order_info['needValue'] if 'needElec' in self.event_data: leftElec = merge_order_info['needValue'] else: if auto_refund: if 'needTime' in self.event_data: # 做一个数据保护 if leftTime > merge_order_info['needValue']: leftTime = merge_order_info['needValue'] backCoins = coins * Ratio(leftTime * 1.0 / merge_order_info['needValue']) elif 'needElec' in self.event_data: # 做一个数据保护 if leftElec > merge_order_info['needValue']: leftElec = merge_order_info['needValue'] backCoins = coins * Ratio(leftElec * 1.0 / merge_order_info['needValue']) usedFee = coins - backCoins else: pass # 分批塞入订单信息 master_info = { 'order_id': self.event_data['order_id'], 'fee': self.event_data['fee'], } if 'needTime' in self.event_data: master_info.update({'needTime': self.event_data['needTime']}) if 'needElec' in self.event_data: master_info.update({'needElec': self.event_data['needElec']}) order_processing_list = [master_info] if 'sub' in self.event_data: order_processing_list += self.event_data['sub'] # 订单服务信息与退款处理 for info_ in order_processing_list[::-1]: order_ = ConsumeRecord.objects.filter(devNo=self.device.devNo, startKey=info_['order_id']).first() if not order_: continue if 'needTime' in self.event_data: needTime = info_.get('needTime') consumeDict.update({ 'needTime': needTime, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: order_.coin, }) if leftTime > info_.get('needTime'): # 全退 back_ = VirtualCoin(order_.coin) consumeDict.update({ 'leftTime': needTime, 'duration': 0, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: 0, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: order_.coin, }) # 结单 self.card.clear_frozen_balance(str(order_.id), back_) # 有退款 添加退款记录 self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo) leftTime -= needTime else: # 部分退 usedTime = needTime - leftTime coins_ = VirtualCoin(order_.coin) back_ = self.get_backCoins(coins_, leftTime, needTime) consumeDict.update({ 'leftTime': leftTime, 'duration': usedTime, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins_ - back_, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: back_, }) # 结单 self.card.clear_frozen_balance(str(order_.id), back_) # 有退款 添加退款记录 if back_ > VirtualCoin(0): self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo) leftTime = 0 elif 'needElec' in self.event_data: needElec = info_.get('needElec') consumeDict.update({ 'needElec': needElec, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: order_.coin, }) if leftElec > needElec: # 全退 back_ = VirtualCoin(order_.coin) consumeDict.update({ 'leftElec': needElec, 'elec': 0, 'elecFee': self.deviceAdapter.calc_elec_fee(0), }) # 结单 self.card.clear_frozen_balance(str(order_.id), back_) # 有退款 添加退款记录 self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo) leftTime -= needElec else: # 部分退 usedElec = needElec - leftElec coins_ = VirtualCoin(order_.coin) back_ = self.get_backCoins(coins_, leftElec, needElec) consumeDict.update({ 'leftElec': leftElec, 'elec': usedElec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec), DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: coins_ - back_, DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: back_, }) # 结单 self.card.clear_frozen_balance(str(order_.id), back_) # 有退款 添加退款记录 if back_ > VirtualCoin(0): self.record_refund_money_for_card(back_, str(self.card.id), orderNo=order.orderNo) leftElec = 0 order_.update_service_info(consumeDict) self.card.reload() extra = [] extra.append({u'在线卡': '{}--No:{}'.format(self.card.cardName, self.card.cardNo)}) extra.append({u'本次使用时长': '{}(分钟)'.format(duration)}) extra.append({u'本次消费金额': '{}(金币)'.format(usedFee)}) extra.append({u'卡片当前余额': '{}(金币)'.format(self.card.balance.mongo_amount)}) self.notify_user_service_complete( service_name='充电', openid=self.card.managerialOpenId, port=str(order.used_port), address=order.address, reason=self.event_data.get('reasonDesc'), finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_finished_event(self, order, merge_order_info): # type:(ConsumeRecord, dict)->None self._do_finished(order, merge_order_info) def checkout_order(self, order): # 在线卡 执行扣费 fee = VirtualCoin(order.coin) self.card.freeze_balance(transaction_id=str(order.id), fee=fee)