# -*- 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 apilib.monetary import RMB, VirtualCoin, Ratio from apilib.systypes import StrEnum from apilib.utils_string import make_title_from_dict from apps.web.agent.models import Agent from apps.web.constant import Const, DEALER_CONSUMPTION_AGG_KIND, FAULT_LEVEL, CONSUMETYPE, DeviceCmdCode from apps.web.core.adapter.xiaokedou import ChargingXiaoKeDouBox from apps.web.core.exceptions import ServiceException from apps.web.core.networking import MessageSender from apps.web.dealer.models import Dealer from apps.web.device.models import Group, Device, DeviceDict, GroupDict from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf, \ IcStartAckEvent, IcRechargeAckEvent, CardRefundAckEvent, IdStartAckEvent, VirtualCardStartAckEvent from apps.web.eventer import EventBuilder from apps.web.user.models import CardRechargeOrder, UserVirtualCard, Card, ConsumeRecord, RechargeRecord, \ VCardConsumeRecord, MyUser from apps.web.user.utils import freeze_user_balance from apilib.utils_sys import memcache_lock logger = logging.getLogger(__name__) class SWIPE_CARD_PARAM_OP(StrEnum): """ 卡的操作 格式 描述_命令码 """ #: 减少 (扣费) DECR_00 = '00' #: 增加 (充值, 退费) INCR_01 = '01' class SWIPE_CARD_RES(StrEnum): """ 回应卡的操作 格式 描述_命令码 """ SUCCESS_00 = '00' BALANCE_NOT_ENOUGH_01 = '01' INVALID_CARD_02 = '02' class CARD_TYPE(StrEnum): OFFLINE_CARD = "00" ONLINE_CARD = "01" MONTHLY_CARD = "02" FULL_CARD = "04" 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) elif device_event['order_type'] == 'ic_recharge': return MyIcRechargeAckEvent(self.deviceAdapter, device_event) elif device_event['order_type'] == 'ic_start': return MyIcStartAckEvent(self.deviceAdapter, device_event) elif device_event['order_type'] == 'id_start': return MyIdStartAckEvent(self.deviceAdapter, device_event) elif device_event['order_type'] == 'vir_start': return MyVirtualCardStartAckEvent(self.deviceAdapter, device_event) elif device_event['order_type'] == 'card_refund': return MyCardRefundAckEvent(self.deviceAdapter, device_event) if 'event_type' in device_event: if device_event['event_type'] == 'card': return CardEvent(self.deviceAdapter, device_event) else: return None event_data = self.analyze_event_data(device_event['data']) if event_data is None or 'cmdCode' not in event_data: return None if event_data['cmdCode'] in ['0A']: return JNDZEventerFailure(self.deviceAdapter, event_data) if event_data['cmdCode'] in ['21']: return ChargingJNDZReportEvent(self.deviceAdapter, event_data) def analyze_event_data(self, data): cmdCode = data[4:6] #: 上传设备故障 if cmdCode == '0A': port = int(data[8:10], 16) errCode = data[10:12] FaultDict = { 'A0': ['继电器粘连,请到现场排查', FAULT_LEVEL.FATAL], 'A1': ['高温', FAULT_LEVEL.CRITICAL], 'A2': ['低温', FAULT_LEVEL.NORMAL], 'A3': ['空载,充电头脱落、充电器拔出', FAULT_LEVEL.NORMAL], 'A4': ['消防(烟感)', FAULT_LEVEL.CRITICAL], 'A5': ['总功率过载' if port == 255 else '{}号端口过载'.format(port), FAULT_LEVEL.FATAL], 'A6': ['倾斜', FAULT_LEVEL.FATAL], 'A7': ['水压高', FAULT_LEVEL.NORMAL], 'A8': ['水压低', FAULT_LEVEL.NORMAL], 'A9': ['过压', FAULT_LEVEL.NORMAL], 'AA': ['欠压', FAULT_LEVEL.NORMAL], '27': ['水压高恢复', FAULT_LEVEL.NORMAL], } return { 'status': Const.DEV_WORK_STATUS_FAULT, 'statusInfo': FaultDict.get(errCode, ['设备故障', FAULT_LEVEL.NORMAL])[0], 'cmdCode': cmdCode, 'port': port, 'errCode': errCode, 'faultContent': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[0], 'level:': FaultDict.get(errCode, [errCode, FAULT_LEVEL.NORMAL])[1] } elif cmdCode == '21': _result = data[6: 8] port_num = int(data[8:10], 16) port_info_urat_info = data[10:170] port_status_desc = { "01": "端口空闲", "02": "端口正在使用", "03": "端口禁用", "04": "端口故障", } port_info = {} for index in xrange(0, 160, 16): item = port_info_urat_info[index: index + 16] one_port = {} one_port["port"] = int(item[:2], 16) one_port["portStatus"] = item[2:4] # one_port["portStatusDesc"] = port_status_desc.get(item[2:4]) one_port["leftTime"] = int(item[4:8], 16) one_port["power"] = int(item[8:12], 16) one_port["usedElec"] = int(item[12:16], 16) * 0.01 # 转换为度要因为DevicePortReport表中的elec为,乘以 0.01 port_info[str(one_port["port"])] = one_port return {"cmdCode": cmdCode, "result": _result, "portNum": port_num, "portInfo": port_info, "sourceData": data} else: logger.info("receive data <{}>, cmd is invalid".format(data)) class CardEvent(WorkEvent): def do(self): if self.event_data['funCode'] == '10': self._do_get_balance() def _do_get_balance(self): RATIO_CONVERSION = 100 smartbox = self.deviceAdapter # type: ChargingXiaoKeDouBox cardNo = str(int(self.event_data["card_id"], 16)) logger.info('[_do_get_balance] receive cardNo = {}'.format(cardNo)) card = self.update_card_dealer_and_type(cardNo) # type: Card data = {"funCode": '10', "card_id": self.event_data["card_id"], 'coins': 0} # 十六进制卡号 dealer = self.device.owner # 无主卡或者是卡被冻结 if not card or not card.openId or card.frozen or not dealer: logger.info('[_do_get_balance] receive cardNo = {}, card invalid!'.format(cardNo)) data.update({"result": '02'}) return self.send_mqtt(data=data) # 是否存在没有到账的余额 进行充值 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() ongoingList = getattr(card, 'ongoingList', []) # 有冻结未结束的订单 # 先不给做一张卡只能开启一单的限制 if card.balance <= RMB(0) or ongoingList: logger.info('[_do_get_balance] receive cardNo = {}, card balance = {} not enough!'.format(cardNo, card.balance)) data.update({"result": '01'}) return self.send_mqtt(data=data) server_configs = smartbox.get_server_configs() onlineCardMinStartCoins = RMB(server_configs.get('onlineCardMinStartCoins', 0)) onlineCardTime = server_configs.get('onlineCardTime', 720.0) # 单位: 分钟 onlineCardELec = server_configs.get('onlineCardELec', 3.0) # 单位: 度 need_time = int(float(onlineCardTime)) need_elec = int(float(onlineCardELec) * 100) data.update({ 'coins': int(RMB(card.balance) * 10), # 单位: 角 'need_time': need_time, 'need_elec': need_elec, 'attach_paras': {'openId': card.openId} }) if float(self.device['driverVersion'].split('.')[-1]) <= 9: data.update({'account_rule': smartbox.get_account_rule()}) else: data.update({'account_rule': smartbox.get_account_rule_new()}) data['attach_paras'].update({'precision': 3600}) if card.balance > onlineCardMinStartCoins: data.update({"result": '00'}) else: data.update({"result": '01'}) if self.device.bill_as_service_feature.on: serviceFee = int(self.device.bill_as_service_feature.service_charge * RATIO_CONVERSION) elecFee = int(self.device.bill_as_service_feature.elec_charge * RATIO_CONVERSION) data.update({"serviceFee": serviceFee, "elecFee": elecFee, "RATIO_CONVERSION": RATIO_CONVERSION, "billingMethod": "billAsService"}) self.send_mqtt(data=data) def send_mqtt(self, data=None, cmd=DeviceCmdCode.OPERATE_DEV_SYNC, otherData=None): ''' 发送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') class JNDZEventerFailure(FaultEvent): def do(self, **args): faultContent = self.event_data.get('faultContent', '') level = self.event_data.get('level', '') errCode = self.event_data.get('errCode') port = self.event_data.get('port') if port and port != 255: title = u'注意!您的设备{}号端口发出告警!'.format(port) else: title = u'注意!您的设备发出告警!' if errCode in ['A3']: # 空载 无需显示在经销商后台 return # else: # Device.update_dev_control_cache(self.device['devNo'], self.event_data) group = Group.get_group(self.device.groupId) if self.is_notify_dealer: self.notify_dealer( templateName="device_fault", title=title, device=u'{}-{}'.format(self.device.devTypeName, self.device.logicalCode), fault=faultContent, location=u'{address}-{groupName}-{groupNumber}号设备({logicalCode})'.format(address=group["address"], groupName=group["groupName"], groupNumber=self.device[ "groupNumber"], logicalCode=self.device[ "logicalCode"]), notifyTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") ) # 记录错误故障 self.record( title=title, description=faultContent, level=level ) def is_server_refund(billingType, dev, dealer, agent): # type:(str, DeviceDict, Dealer, Agent)->bool if billingType != 'time': if 'jhCardElecRefund' in dealer.features: return False if dev.is_auto_refund: return True else: return False else: if 'huopo_card_time_refund' in agent.features: return True else: support_server_refund = dev.devType.get('features', {}).get( 'support_server_refund', False) if not support_server_refund: return False if dev.is_auto_refund: return True else: return False class StartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason): FINISHED_CHARGE_REASON_MAP = { '00': u'购买的充电时间或者电量已经用完', '01': u'系统判断为异常断电(插头被拔或者松动,或者电瓶已经充满),电瓶车充电器种类繁多,可能存在误差', '02': u'电池已经充满', '03': u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电', '04': u'远程断电', '11': u'设备或是端口出现问题,被迫停止', # 服务器定义的停止事件 '90': u'订单异常,设备使用期间可能存在离线时长超过一小时', '91': u'系统检测到充电已结束, 平台结单', # 主板轮询出来的停止(无结束上报) '92': u'管理员远程关闭订单', '95': u'继电器粘连、短路,停止充电', '96': u'空载,充电头脱落、充电器拔出', '97': u'用户远程手动停止订单', '98': u'您购买的时间已经用完了', '99': u'您购买的电量已经用完了', '100': u'金额已用完了', 'EE': u'经销商强制结束', } 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 'duration' in event_data and event_data['duration'] != 0: duration1 = event_data.get('duration') event_data['duration'] = round(duration1 / 60.0, 1) if 'fts' in event_data and 'sts' in event_data: duration2 = event_data['fts'] - event_data['sts'] if abs(duration2 - duration1) < 300: duration = max(duration1, duration2) event_data['duration'] = round(duration / 60.0, 1) if 'elec' in event_data: elec = event_data.get('elec') event_data['elec'] = round(elec / 3600000.0, 3) if 'need_time' in event_data: event_data['needTime'] = event_data.pop('need_time') / 60.0 if 'need_elec' in event_data: event_data['needElec'] = event_data.pop('need_elec') / 3600000.0 if 'port' in event_data: pass if event_data['status'] == 'finished': event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason', '91')) if 'need_pay' in event_data: precision = event_data.get('attach_paras', {}).get('precision', 1000.0) event_data['needPay'] = VirtualCoin(event_data.get('need_pay', 0) / (100.0 * precision)) # 单位(分 精度(老的设备为1000 新的设备为3600)) if event_data.get('billingMethod') == 'prepaid' or event_data.get('billingMethod') == 'billAsService': if event_data.get('reason') in ['00', '98', '99']: event_data['reasonDesc'] = '订购的套餐已用完' if 'sub' in event_data: pass return event_data class ChargingJNDZReportEvent(WorkEvent): def do(self, **args): ctrInfo = Device.get_dev_control_cache(self.device.devNo) logger.info('ctrInfo:<{}> upload_21:<{}>'.format(ctrInfo, self.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 package = master_order.package needTime, needElec, coins = self.deviceAdapter._check_package(package) start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = {'estimatedTs': int(start_time.timestamp + (needTime + 5) * 60)} if package.get('billingMethod') == CONSUMETYPE.POSTPAID or package.get('billingMethod') == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID: portDict['needKind'] = 'needTime' portDict['needValue'] = needTime portDict['unit'] = u'分钟' portDict['coins'] = 0 portDict['consumeType'] = package.get('billingMethod') else: unit = package.get('unit') portDict['coins'] = coins portDict['consumeType'] = CONSUMETYPE.MOBILE if unit == '度': portDict['needKind'] = 'needElec' portDict['needValue'] = needElec else: portDict['needKind'] = 'needTime' portDict['needValue'] = needTime for sub_order in sub_orders: needTime, needElec, coins = self.deviceAdapter._check_package(sub_order.package) portDict['coins'] += coins if unit == '度': portDict['needValue'] += needElec else: portDict['needValue'] += needTime return portDict # TODO 结束事件处理 def do_finished_event(self, master_order, sub_orders, merge_order_info): # type: (ConsumeRecord, [ConsumeRecord], dict)->None if merge_order_info['consumeType'] == CONSUMETYPE.POSTPAID or merge_order_info['consumeType'] == CONSUMETYPE.BILL_AS_SERVICE_POSTPAID: self.do_postpaid_time_finished(master_order, sub_orders, merge_order_info) else: self.do_prepaid_time_finished(master_order, sub_orders, merge_order_info) @property def is_postpaid(self): return True def do_postpaid_time_finished(self, master_order, sub_orders=None, merge_order_info=None): duration = self.event_data.get('duration', 0) elec = self.event_data.get('elec', 0) # 做一个时间溢出的保护 try: dealer = self.device.owner agent = Agent.objects.get(id=dealer.agentId) features = agent.features if 'time_overflow_protection' in features: if 'needKind' in merge_order_info and merge_order_info['needKind'] == 'needTime': if abs(duration - merge_order_info['needValue']) <= 10: duration = merge_order_info['needValue'] except: pass needPay = self.event_data.get('needPay', 0) consumeDict = { 'reason': self.event_data.get('reasonDesc'), 'chargeIndex': str(master_order.used_port), DEALER_CONSUMPTION_AGG_KIND.DURATION: duration, DEALER_CONSUMPTION_AGG_KIND.ELEC: elec, } if self.device.bill_as_service_feature.on: ratio_conversion = self.event_data.get('ratio_conversion', 1) precision = self.event_data.get('precision', 3600) needPay = VirtualCoin(round(float(needPay) / ratio_conversion / 10, 2)) serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2) elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE:elecFee}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE:serviceFee}) # 没有真正的结束, 等待状态机执行 master_order.servicedInfo.update(consumeDict) logger.debug( 'orderNo = {}, orderType = isPostpaid, usedTime = {}, needPayMoney = {}'.format(master_order.orderNo, duration, needPay)) # 保护时间判断 refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5)) if duration < refundProtectionTime: needPay = VirtualCoin(0) # 先尝试用金币付款 # 1 获取金币汇率 coinRatio = float(self.device.otherConf.get('serverConfigs', {}).get('coinRatio', 1)) # 2 更新订单金额 master_order.coin = (needPay * coinRatio).mongo_amount master_order.money = needPay master_order.save() # 3 使用金币支付 self.pay_order(master_order) master_order.reload() extra = [{u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)}] if self.device.bill_as_service_feature.on: extra.append({ u'电费金额': u'{}(金币)'.format(serviceFee), u'服务费金额': u'{}(金币)'.format(elecFee) }) if master_order.status == ConsumeRecord.Status.FINISHED: if needPay == VirtualCoin(0): desc = '' else: if master_order.paymentInfo.get('via') == 'virtualCard': desc = u'(已使用优惠卡券抵扣本次消费)' master_order.update(coin=VirtualCoin(0), money=RMB(0)) # 结算完了进行退虚拟卡额度处理: try: if "rcdId" in master_order.paymentInfo: consumeRcdId = master_order.paymentInfo['rcdId'] else: consumeRcdId = master_order.paymentInfo['itemId'] vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId) vCard = UserVirtualCard.objects.get(id=vCardConsumeRcd.cardId) vCard.refund_quota(vCardConsumeRcd, duration, 0.0, VirtualCoin(0).mongo_amount) except: pass else: desc = u'(已使用账户余额自动结算本次消费)' extra.append({u'消费金额': u'{}(金币)'.format((needPay * coinRatio).mongo_amount)}) if self.device.bill_as_service_feature.on: extra.append({ u'电费金额': u'{}(金币)'.format(serviceFee), u'服务费金额': u'{}(金币)'.format(elecFee) }) else: desc = u'(您的账户余额已不足以抵扣本次消费,请前往账单中心进行支付)' extra.append({u'消费金额': u'{}(元)'.format(master_order.money)}) if self.device.bill_as_service_feature.on: extra.append({ u'电费金额': u'{}(金币)'.format(serviceFee), u'服务费金额': u'{}(金币)'.format(elecFee) }) extra.append({u'用户余额': u'{}(金币)'.format(master_order.user.calc_currency_balance(self.device.owner, self.device.group))}) self.event_data['reasonDesc'] += desc self.notify_to_user(master_order.user.managerialOpenId, extra) def do_dealer_feature_processing(self, master_order): ''' 处理一些更具经销商特征要求显示或则隐藏的关于订单的信息 ''' pass def notify_to_user(self, openId, extra): group = Group.get_group(self.device['groupId']) self.notify_user_service_complete( service_name='充电', openid=openId, port='', address=group['address'], reason=self.event_data.get('reasonDesc'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def pay_order(self, order): order.status = 'running' order.attachParas['packageId'] = order.package.get('packageId') order.save() order.s_to_e() def do_prepaid_time_finished(self, master_order, sub_orders, merge_order_info): duration, elec = self.event_data.get('duration', 0), self.event_data.get('elec', 0) needKind = merge_order_info['needKind'] needValue = merge_order_info['needValue'] coins = VirtualCoin(merge_order_info['coins']) # 保护时间判断 refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5)) if duration <= refundProtectionTime: usedRatio = 0 else: if not self.device.is_auto_refund: # 没有开启退费开关 直接退费 usedRatio = 1 else: if needKind == 'needTime': # 做一个时间溢出的保护 duration = min(merge_order_info['needValue'], duration) usedRatio = duration / (needValue * 1.0) else: # 做一个电量溢出保护 elec = min(merge_order_info['needValue'], elec) usedRatio = elec / (needValue * 1.0) usedFee = coins * usedRatio backCoins = coins - usedFee logger.debug( 'orderNo = {}, orderType = isPostpaid, usedTime = {},coins = {}, usedFee = {}, backCoins = {}'.format( master_order.orderNo, duration, coins, usedFee, backCoins)) user = master_order.user # type: MyUser extra = [{u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)}] if self.device.bill_as_service_feature.on: ratio_conversion = self.event_data.get('ratio_conversion', 1) precision = self.event_data.get('precision', 3600) serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2) elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2) extra.append({ u'电费金额': u'{}(金币)'.format(serviceFee), u'服务费金额': u'{}(金币)'.format(elecFee) }) if master_order.paymentInfo['via'] == 'free': extra.append({u'消费金额': u'当前设备免费使用'}) elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: all_money = RMB(0) all_refund_money = RMB(0) orders = [master_order] + sub_orders refundCash = 'refundRMB_device_event' in self.device.owner.features for _order in orders[::-1]: consumeDict = { 'reason': self.event_data.get('reasonDesc'), 'chargeIndex': str(master_order.used_port), } need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, _order.coin) rechargeRcdId = _order.attachParas.get('linkedRechargeRecordId', '') if rechargeRcdId != '': rechargeRcd = RechargeRecord.objects.filter(id=rechargeRcdId, isQuickPay=True).first() else: rechargeRcd = None if refundCash and rechargeRcd: # 退现金特征 + 有充值订单 # 退现金部分 user.clear_frozen_balance(str(_order.id), _order.paymentInfo['deduct'], back_coins=VirtualCoin(0), consume_coins=VirtualCoin(_order.coin)) refundRMB = rechargeRcd.money * (float(need_back_coins) / float(_order.coin)) self.refund_net_pay(user, {'rechargeRcdId': rechargeRcdId, 'openId': user.openId}, refundRMB, VirtualCoin(0), consumeDict, True) all_money += RMB(rechargeRcd.money) all_refund_money += RMB(refundRMB) else: 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.orderNo == master_order.orderNo: consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.DURATION: duration, DEALER_CONSUMPTION_AGG_KIND.ELEC: elec, }) if self.device.bill_as_service_feature.on: consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE: elecFee}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee}) _order.update_service_info(consumeDict) if refundCash and all_money > RMB(0): extra.append({u'消费金额': '{}元'.format(all_money - all_refund_money)}) if all_refund_money > RMB(0): extra.append({u'退款金额': '{}元'.format(all_refund_money)}) else: extra.append({u'消费金额': '{}金币'.format(usedFee)}) if (coins - usedFee) > VirtualCoin(0): extra.append({u'退款金额': '{}金币(当充电金币数量大于或等于扫码充电金额时可抵现金,金币不可提现)'.format(coins - usedFee)}) self.notify_to_user(master_order.user.managerialOpenId, extra) else: logger.error('not net pay rather user virtual card pay. something is wrong.') return 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 MyIcStartAckEvent(IcStartAckEvent): def __init__(self, smartBox, event_data): super(MyIcStartAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_after_start(self, order=None): pass def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict consumeModule = self.device.get('otherConf', dict()).get('consumeModule', 0) # 按时间计费 if consumeModule == 0: billingType = 'time' else: billingType = 'elec' start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { 'billingType': billingType } if master_order.paymentInfo['via'] == 'virtualCard': portDict.update({ 'vCardId': master_order.paymentInfo['itemId'] }) all_coins = master_order.coin all_money = master_order.money for sub_order in sub_orders: all_coins += sub_order.coin all_money += sub_order.money portDict['coins'] = str(all_coins) portDict['money'] = str(all_money) portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60) return portDict def do_time_finished(self, card, order, merge_order_info): # type: (Card, ConsumeRecord, dict)->None duration, elec = self.event_data['duration'], self.event_data['elec'] coins = VirtualCoin(merge_order_info['coins']) left = self.event_data['left'] consumeDict = { 'reason': self.event_data['reason'], 'chargeIndex': str(order.used_port), 'duration': duration, 'elec': elec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } # backMoney = VirtualCoin(self.event_data['backMoney']) group = self.device.group # type: GroupDict extra = [{ u'实体卡号': self.event_data['cardNo'] }] consumeDict.update({'balance': str(card.balance)}) order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=card.managerialOpenId if card else '', port=order.used_port, address=group.address, reason=self.event_data['reason'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_elec_finished(self, card, order, merge_order_info): # type:(Card, ConsumeRecord, dict)->None ''' 电川的板子 按电量退费 :return: ''' leftElec = self.event_data.get('left', 0) / 100.0 duration, elec = self.event_data['duration'], self.event_data['elec'] try: consumeDict = { 'reason': self.event_data['reasonDesc'], 'chargeIndex': order.used_port, 'duration': duration, 'elec': elec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } # backMoney = self.event_data.get('backMoney') extra = [{u'实体卡号': self.event_data['cardNo']}] # if self.event_data['cardType'] == 'ID': # if backMoney > 0: # self.refund_money_for_card(RMB(backMoney), card.id) # extra.append({ # u'退款金额': u'{}(金币)'.format(backMoney) # }) # # consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))}) # else: consumeDict.update({'balance': str(card.balance)}) order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=self.get_managerialOpenId_by_openId(order.openId), port=order.used_port, address=self.device.group.address, reason=self.event_data['reason'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) except Exception as e: logger.exception(e) def do_finished_event(self, card, order, merge_order_info): # type:(Card, ConsumeRecord, dict)->None if 'backMoney' in self.event_data and self.event_data['backMoney'] > RMB(0): refund_order = RechargeRecord.objects(orderNo=self.event_data['order_id']).first() if not refund_order: self.refund_money_for_card(RMB(self.event_data['backMoney']), card.id, self.event_data['order_id']) billing_type = merge_order_info['billingType'] if billing_type == 'time': self.do_time_finished(card, order, merge_order_info) else: self.do_elec_finished(card, order, merge_order_info) class MyIcRechargeAckEvent(IcRechargeAckEvent): def __init__(self, smartBox, event_data): super(MyIcRechargeAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) class MyCardRefundAckEvent(CardRefundAckEvent): def __init__(self, smartBox, event_data): super(MyCardRefundAckEvent, self).__init__(smartBox, event_data, CardRefundAckEventPreProcessor()) class CardRefundAckEventPreProcessor(AckEventProcessorIntf): def pre_processing(self, device, event_data): # type:(DeviceDict, dict)->dict if 'cardType' in event_data: if event_data['cardType'] == 'AA33': event_data['cardType'] = 'ID' else: event_data['cardType'] = 'IC' if 'cardNo' in event_data: event_data['cardNo'] = str(event_data['cardNo']) event_data['backMoney'] = (int(event_data['backMoney']) / 10.0) return event_data class IdStartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason): FINISHED_CHARGE_REASON_MAP = { '00': u'购买的充电时间或者电量已经用完', '01': u'系统判断为异常断电(插头被拔或者松动,或者电瓶已经充满),电瓶车充电器种类繁多,可能存在误差', '02': u'电池已经充满', '03': u'警告!您的电池功率超过本机最大限制。为了公共安全,不建议您在该充电桩充电', '04': u'远程断电', '11': u'设备或是端口出现问题,被迫停止', # 服务器定义的停止事件 '90': u'订单异常,设备使用期间可能存在离线时长超过一小时', '91': u'主板异常停止,未知错误', # 主板轮询出来的停止(无结束上报) '92': u'管理员远程关闭订单', '95': u'继电器粘连、短路,停止充电', '96': u'空载,充电头脱落、充电器拔出', '97': u'用户远程手动停止订单', '98': u'您购买的时间已经用完了', '99': u'您购买的电量已经用完了', 'EE': u'经销商强制结束', } 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 'duration' in event_data and event_data['duration'] != 0: duration = event_data.get('duration') event_data['duration'] = round(duration / 60.0, 1) if 'fts' in event_data and 'sts' in event_data: duration = event_data['fts'] - event_data['sts'] event_data['duration'] = round(duration / 60.0, 1) if 'elec' in event_data: elec = event_data.get('elec') event_data['elec'] = round(elec / 3600000.0, 3) if 'need_time' in event_data: event_data['needTime'] = event_data.pop('need_time') / 60.0 if 'need_elec' in event_data: event_data['needElec'] = event_data.pop('need_elec') / 3600000.0 if 'port' in event_data: pass if event_data['status'] == 'finished': event_data['reasonDesc'] = self.analysis_reason(event_data.get('reason', '91')) if 'need_pay' in event_data: precision = event_data.get('attach_paras', {}).get('precision', 1000.0) event_data['needPay'] = VirtualCoin( event_data.get('need_pay', 0) / (100.0 * precision)) # 单位(分 精度(老的设备为1000 新的设备为3600 if 'sub' in event_data: subs = event_data.get('sub', []) for item in subs: if 'need_time' in event_data: event_data['needTime'] = item.pop('need_time') if 'need_elec' in event_data: event_data['needElec'] = item.pop('need_elec') if 'card_id' in event_data: event_data['cardNo'] = str(int(event_data.get('card_id'), 16)) event_data['fee'] = 0.0 return event_data class MyIdStartAckEvent(IdStartAckEvent): def __init__(self, smartBox, event_data): super(MyIdStartAckEvent, self).__init__(smartBox, event_data, IdStartAckEventPreProcessor()) 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_start(self, order=None): # 通知用户,已经扣费 title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.device.group.address)}, {u'设备编号': u'{}'.format(self.device['logicalCode'])}, {u'实体卡': u'{}--No:{}'.format(self.card.cardName or self.card.nickName, self.card.cardNo)}, ]) start_time_stamp = self.event_data.get('sts', time.time()) start_time = datetime.datetime.fromtimestamp(start_time_stamp) self.notify_user( self.card.managerialOpenId, 'dev_start', **{ 'title': title, 'things': u'刷卡启动充电', 'remark': u'感谢您的支持!', 'time': start_time.strftime(Const.DATETIME_FMT) } ) def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict billingType = self.device.get('otherConf', dict()).get('billingType', 'time') start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { 'billingType': billingType, 'consumeType': 'postpaid' } if self.device.bill_as_service_feature.on: billingType = 'elec' portDict.update({ 'billingType': billingType, 'consumeType': "billAsServicePostpaid" }) all_coins = master_order.coin all_money = 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 += sub_order.coin all_money += 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['coins'] = str(all_coins) portDict['money'] = str(all_money) if billingType == 'time': portDict['needKind'] = 'needTime' portDict['needValue'] = all_consume_time portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60) elif billingType == 'elec': portDict['needKind'] = 'needElec' portDict['needValue'] = round(all_consume_elec / 100.0, 2) portDict['unit'] = u'度' portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60) else: pass return portDict def do_time_finished(self, order, merge_order_info): # type: (ConsumeRecord, dict)->None duration, elec = self.event_data['duration'], self.event_data['elec'] coins = VirtualCoin(merge_order_info['coins']) left = self.event_data['leftTime'] group = Group.get_group(self.device['groupId']) dealer = Dealer.objects(id=group['ownerId']).first() if not dealer: logger.error('dealer is not found, dealerId=%s' % group['ownerId']) return agent = Agent.objects(id=dealer.agentId).first() if not agent: logger.error('agent is not found, agentId=%s' % dealer.agentId) return consumeDict = { 'reason': self.event_data['reason'], 'chargeIndex': str(order.used_port), 'duration': duration, 'elec': elec, 'cardNo': self.event_data['cardNo'], 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } auto_refund = self.device.is_auto_refund refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5)) logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format( repr(self.device), str(auto_refund), refundProtectionTime)) if left > merge_order_info['needValue']: left = merge_order_info['needValue'] real_back_coins = coins * Ratio(float(left) / float(merge_order_info['needValue'])) if auto_refund: if duration < refundProtectionTime: backMoney = coins else: backMoney = real_back_coins else: backMoney = VirtualCoin(0) if backMoney and backMoney > RMB(0): if is_server_refund('time', self.device, dealer, agent): logger.info( 'ready to server refund money <{}> for user card <{}> in device<{}>'.format( backMoney, str(self.card.id), self.device.devNo)) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount}) self.refund_money_for_card(backMoney, self.card.id, order.orderNo) self.notify_user(self.card.managerialOpenId, 'refund_coins', **{ 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (self.card.cardNo, self.card.nickName), 'backCount': u'金币:%s' % backMoney, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) extra = [{ u'实体卡号': self.event_data['cardNo'] }] order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=self.card.managerialOpenId if self.card else '', port=order.used_port, address=group.address, reason=self.event_data['reasonDesc'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_elec_finished(self, order, merge_order_info): # type:(ConsumeRecord, dict)->None ''' 电川的板子 按电量退费 :return: ''' leftElec = self.event_data.get('leftElec', 0) duration, elec = self.event_data['duration'], self.event_data['elec'] coins = VirtualCoin(merge_order_info['coins']) group = Group.get_group(self.device['groupId']) dealer = Dealer.objects(id=group['ownerId']).first() if not dealer: logger.error('dealer is not found, dealerId=%s' % group['ownerId']) return agent = Agent.objects(id=dealer.agentId).first() if not agent: logger.error('agent is not found, agentId=%s' % dealer.agentId) return try: consumeDict = { 'reason': self.event_data['reasonDesc'], 'chargeIndex': order.used_port, 'duration': duration, 'elec': elec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } if leftElec > merge_order_info['needValue']: leftElec = merge_order_info['needValue'] backMoney = coins * Ratio(leftElec / merge_order_info['needValue']) if backMoney and backMoney > RMB(0): if is_server_refund('elec', self.device, dealer, agent): logger.info( 'ready to server refund money <{}> for user card <{}> in device<{}>'.format( backMoney, str(self.card.id), self.device.devNo)) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: backMoney.mongo_amount}) self.refund_money_for_card(backMoney, self.card.id, order.orderNo) self.notify_user(self.card.managerialOpenId, 'refund_coins', **{ 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (self.card.cardNo, self.card.nickName), 'backCount': u'金币:%s' % backMoney, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) extra = [{u'实体卡号': self.event_data['cardNo']}] consumeDict.update({'balance': str(self.card.balance)}) order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=self.get_managerialOpenId_by_openId(order.openId), port=order.used_port, address=self.device.group.address, reason=self.event_data['reasonDesc'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) except Exception as e: logger.exception(e) def do_finished_event(self, order, merge_order_info): # type:(ConsumeRecord, dict)->None self.do_postpaid_time_finished(order, merge_order_info) def do_postpaid_time_finished(self, master_order, merge_order_info): extra = [] duration = self.event_data.get('duration', 0) elec = self.event_data.get('elec', 0) needPay = self.event_data.get('needPay', 0) consumeDict = { 'reason': self.event_data.get('reasonDesc'), 'chargeIndex': str(master_order.used_port), DEALER_CONSUMPTION_AGG_KIND.DURATION: duration, DEALER_CONSUMPTION_AGG_KIND.ELEC: elec, } if self.device.bill_as_service_feature.on: ratio_conversion = self.event_data.get('ratio_conversion', 100) precision = self.event_data.get('precision', 3600) needPay = VirtualCoin(round(float(needPay) / ratio_conversion / 10, 2)) serviceFee = round(self.event_data.get("service_fee", 0) / ratio_conversion / (1000.0 * precision), 2) elecFee = round(self.event_data.get("elec_fee", 0) / ratio_conversion / (1000.0 * precision), 2) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.ELECFEE: elecFee}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SERVICEFEE: serviceFee}) extra.append({ u'电费金额': u'{}(金币)'.format(serviceFee), u'服务费金额': u'{}(金币)'.format(elecFee) }) logger.debug( 'orderNo = {}, orderType = isPostpaid, usedTime = {}, needPayMoney = {}'.format(master_order.orderNo, duration, needPay)) # 保护时间判断 refundProtectionTime = int(self.device.otherConf.get('serverConfigs', {}).get('refundProtectionTime', 5)) if duration < refundProtectionTime: needPay = VirtualCoin(0) master_order.coin = needPay master_order.money = needPay master_order.save() # 3 使用金币支付() self.pay_by_card(master_order) self.card.reload() extra.append({u'使用详情': '{}号端口, {}(分钟)'.format(master_order.used_port, duration)}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: master_order.coin.mongo_amount}) master_order.update_service_info(consumeDict) extra.append({u'消费金额': u'{}(金币)'.format(needPay.mongo_amount)}) if self.card.balance >= RMB(0): extra.append({u'卡余额': u'{}(金币)'.format(self.card.balance)}) else: extra.append({u'卡余额': u'已欠费{}(金币)'.format(abs(self.card.balance))}) self.notify_to_user(self.card.managerialOpenId, extra) def notify_to_user(self, openId, extra): group = Group.get_group(self.device['groupId']) self.notify_user_service_complete( service_name='充电', openid=openId, port=self.event_data.get('port'), address=group['address'], reason=self.event_data.get('reasonDesc'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def checkout_order(self, order): #冻结卡不让其他设备使用 self.card.freeze_balance(str(order.id), VirtualCoin(0)) self.card.reload() def pay_by_card(self, order): # 1 接触冻结状态 self.card.clear_frozen_balance(str(order.id), VirtualCoin(0)) # 2 扣除费用 self.card.update(dec__balance=round(order.coin, 2)) class MyVirtualCardStartAckEvent(VirtualCardStartAckEvent): def __init__(self, smartBox, event_data): super(MyVirtualCardStartAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_after_start(self, order=None, card=None): pass def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict billingType = self.device.get('otherConf', dict()).get('billingType', 'time') start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { 'billingType': billingType } all_coins = master_order.coin all_money = master_order.money all_consume_time = self.event_data.get('needTime', 0) all_consume_elec = self.event_data.get('needElec', 0) 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['coins'] = str(all_coins) portDict['money'] = str(all_money) if billingType == 'time': portDict['needKind'] = 'needTime' portDict['needValue'] = all_consume_time portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + all_consume_time * 60) elif billingType == 'elec': portDict['needKind'] = 'needElec' portDict['needValue'] = round(all_consume_elec / 100.0, 2) portDict['unit'] = u'度' portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60) else: pass return portDict def checkout_order(self, order): group = Group.get_group(self.device.groupId) # type:GroupDict freeze_user_balance(self.device, group, order, self.virtualCard) def do_time_finished(self, order, sub_orders, merge_order_info): # type: (ConsumeRecord, list, dict)->None duration, elec = self.event_data['duration'], self.event_data['elec'] consumeDict = { 'reason': self.event_data['reasonDesc'], 'chargeIndex': str(order.used_port), 'duration': duration, 'elec': elec, 'cardNo': self.event_data['cardNo'], 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } auto_refund = self.device.is_auto_refund refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5)) logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format( repr(self.device), str(auto_refund), refundProtectionTime)) if auto_refund: order_need_time = order.package.get('time') if order_need_time >= duration: usedTime = duration duration = 0 else: usedTime = order_need_time duration -= order_need_time success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id), usedTime=usedTime, spendElec=0, backCoins=VirtualCoin( 0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay) for sub_order in sub_orders: order_need_time = sub_order.package.get('time') if order_need_time >= duration: usedTime = duration duration = 0 else: usedTime = order_need_time duration -= order_need_time success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota( transaction_id=str(sub_order.id), usedTime=usedTime, spendElec=0, backCoins=VirtualCoin(0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay) else: order_need_time = order.package.get('time') success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id), usedTime=order_need_time, spendElec=0, backCoins=VirtualCoin( 0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay) for sub_order in sub_orders: order_need_time = sub_order.package.get('time') success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota( transaction_id=str(sub_order.id), usedTime=order_need_time, spendElec=0, backCoins=VirtualCoin(0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay) group = self.device.group # type: GroupDict extra = [{ u'虚拟卡号': self.virtualCard.cardNo }] order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=self.card.managerialOpenId if self.card else '', port=order.used_port, address=group.address, reason=self.event_data['reasonDesc'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_elec_finished(self, order, sub_orders, merge_order_info): # type:(ConsumeRecord, list, dict)->None duration, elec = self.event_data['duration'], self.event_data['elec'] consumeDict = { 'reason': self.event_data['reasonDesc'], 'chargeIndex': str(order.used_port), 'duration': duration, 'elec': elec, 'cardNo': self.event_data['cardNo'], 'elecFee': self.deviceAdapter.calc_elec_fee(elec), } auto_refund = self.device.is_auto_refund refundProtectionTime = int(self.device['otherConf'].get('serverConfigs', {}).get('refundProtectionTime', 5)) logger.debug('{} auto refund enable switch is {}. refund protect time = {}'.format( repr(self.device), str(auto_refund), refundProtectionTime)) if auto_refund: order_need_time = order.package.get('time') if order_need_time >= elec: usedTime = duration duration = 0 else: usedTime = order_need_time elec -= order_need_time success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota(transaction_id=str(order.id), usedTime=usedTime, spendElec=0, backCoins=VirtualCoin( 0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay) for sub_order in sub_orders: order_need_time = order.package.get('time') if order_need_time >= duration: usedTime = duration duration = 0 else: usedTime = order_need_time duration -= order_need_time success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota( transaction_id=str(sub_order.id), usedTime=usedTime, spendElec=0, backCoins=VirtualCoin(0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay) else: order_need_time = order.package.get('time') success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota( transaction_id=str(order.id), usedTime=order_need_time, spendElec=0, backCoins=VirtualCoin( 0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, order, success, consumeTotal, consumeDay) for sub_order in sub_orders: order_need_time = sub_order.package.get('time') success, consumeTotal, consumeDay = self.virtualCard.clear_frozen_quota( transaction_id=str(sub_order.id), usedTime=order_need_time, spendElec=0, backCoins=VirtualCoin(0).mongo_amount) self.insert_vCard_consume_record(self.virtualCard, sub_order, success, consumeTotal, consumeDay) extra = [{ u'虚拟卡号': self.virtualCard.cardNo }] order.update_service_info(consumeDict) self.notify_user_service_complete( service_name='充电', openid=self.get_managerialOpenId_by_openId(order.openId), port=order.used_port, address=self.device.group.address, reason=self.event_data['reason'], finished_time=order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def do_finished_event(self, order, sub_orders, merge_order_info): # type:(ConsumeRecord, list, dict)->None billing_type = merge_order_info['billingType'] if billing_type == 'time': self.do_time_finished(order, sub_orders, merge_order_info) else: self.do_elec_finished(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 = self.device.devTypeCode, devTypeName = self.device.devTypeName, 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 as e: logger.exception(e)