# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import random import time from arrow import Arrow from django.conf import settings from typing import TYPE_CHECKING, List from apilib.monetary import sum_rmb, RMB, VirtualCoin, Ratio from apilib.utils_datetime import to_datetime from apps.web.south_intf.liangxi_fire import LiangXiXiaoFang from apps.web.south_intf.yuhuan_fire import YuhuanNorther from apps.web.common.models import District from apps.web.constant import Const, FAULT_CODE, FAULT_LEVEL, APP_TYPE, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.helpers import ActionDeviceBuilder from apps.web.core.accounting import Accounting from apps.web.core.device_define.dianchuan import FINISHED_CHARGE_REASON_MAP from apps.web.dealer.models import Dealer from apps.web.device.models import PortReport, OfflineCoinStatistics, Part, Group, Device from apps.web.eventer.base import FaultEvent, WorkEvent, ComNetPayAckEvent, IcStartAckEvent, \ AckEventProcessorIntf, IcRechargeAckEvent, CardRefundAckEvent from apps.web.eventer import EventBuilder from apps.web.helpers import get_wechat_auth_bridge from apps.web.south_intf.platform import notify_event_to_north, notify_event_to_north_v2 from apps.web.south_intf.zhejiang_fire import send_event_to_zhejiang from apps.web.user.models import VCardConsumeRecord, ServiceProgress, UserVirtualCard, CardRechargeOrder, MyUser, \ ConsumeRecord, RechargeRecord if TYPE_CHECKING: from apps.web.user.models import Card from apps.web.device.models import GroupDict 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': return MyIcRechargeAckEvent(self.deviceAdapter, device_event) if device_event['order_type'] == 'ic_start': return MyIcStartAckEvent(self.deviceAdapter, device_event) if device_event['order_type'] == 'card_refund': return MyCardRefundAckEvent(self.deviceAdapter, device_event) else: if 'data' not in device_event: return None # 100228 互感器事件处理 if 'type' in device_event: if device_event['type'] == 'alert': return InteroperatorAlertEvent(self.deviceAdapter, device_event) if device_event['type'] == 'report': return InteroperatorReport(self.deviceAdapter, device_event) else: event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None or 'cmdCode' not in event_data: return None if 'duration' in device_event: event_data.update({'duration': device_event['duration']}) if 'elec' in device_event: event_data.update({'elec': device_event['elec']}) if 'v' in device_event: event_data.update({'v': device_event['v']}) if event_data.get('cmdCode') in ['03', '05', '11', '12', '17', '22']: return ChargingZHIXIA2WorkEvent(self.deviceAdapter, event_data) if event_data.get('cmdCode') == '0D': return ZHIXIA2FaultEvent(self.deviceAdapter, event_data) if event_data.get('cmdCode') == '35' or event_data.get('cmdCode') == '41': return ZHIXIA2InductorEvent(self.deviceAdapter, event_data) return None class ZHIXIA2FaultEvent(FaultEvent): LX_FAULE_CODE_MAP = { "01": "07", "02": "03", "03": "05" } def do_norther(self): """ 上报其他平台的,都在这个地方处理 可迁移至异步任务 :return: """ # 玉环的消防对接 YuhuanNorther.send_dev_event(self.device, self.event_data['FaultCode'], self.event_data['port']) # 梁溪消防局的对接 faultContent = self.event_data["statusInfo"] faultCode = self.LX_FAULE_CODE_MAP.get(self.event_data["FaultCode"], "") districtInfo = District.get_district(self.device.group["districtId"]) self.device.update({"districtInfo": districtInfo, "groupAddr": self.device.group["address"]}) LiangXiXiaoFang.send_to_xf_fault(self.device, faultCode, faultContent, self.event_data["port"]) def do(self, **args): # 将告警的消息打入相应的缓存 port = self.event_data["port"] # 0 表示整机 if port == 0xFF: 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(ZHIXIA2FaultEvent, self).do() class ChargingZHIXIA2WorkEvent(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 + 59) / 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 def do(self, **args): logger.info('zhixiakeji2 charging event detected, devNo=%s,info=%s' % (self.device.devNo, self.event_data)) devNo = self.device.devNo if self.event_data['cmdCode'] == '03': # 电川的无法记录投币的端口数据 Accounting.recordOfflineCoin(device=self.device, report_ts=int(time.time()), coins=int(self.event_data['coins']), port=self.event_data.get('port', None)) if self.device.ownerId is not None and self.device.ownerId != '': dealer = Dealer.objects(id = self.device.ownerId).first() if dealer is not None and 'show_device_offline_coins' in dealer.features: OfflineCoinStatistics.recordCoinEvent( self.device['logicalCode'], self.device.devNo, int(self.event_data.get('coins', 1)), self.device['groupId'] ) else: logger.error('undefined dealer id=%s' % self.device.ownerId) # 如果是投币,直接把端口状态刷新下 try: self.deviceAdapter.get_port_status_from_dev() except Exception, e: logger.info('some err=%s' % e) YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1) elif self.event_data['cmdCode'] == '22': # ID在线卡刷卡查询余额 cardNo = self.event_data['cardNo'] fee = RMB(self.event_data['fee']) cardType = self.event_data['cardType'] card = self.update_card_dealer_and_type(cardNo) # 首先检查订单,并进行充值 # 不存在卡的情况下 直接 if not card: res = "03" elif not card.openId: res = "03" elif card.frozen: res = "04" # 有绑定的卡 并且卡没被冻结的情况下 else: # 刷新卡的金额 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) result = self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() logger.info('cardNo=%s,openId=%s,result=%s,fee=%s, info=%s' % (cardNo, card.openId, result, fee, self.event_data)) res = "01" if result["balance"] >= fee else "02" balance = RMB(0) if not card else card.balance try: logger.info("card is <{}> fee is <{}> response res is <{}>".format(cardNo, fee, res)) result = self.deviceAdapter.response_card_status(cardNo, balance, res) except Exception, e: logger.info('resp back error=%s' % e) return # 设备回复扣款成功,服务器就需要正式扣掉钱 if result['status'] == '01' and res == '01': virtual_card = card.bound_virtual_card if virtual_card is not None: group = Group.get_group(self.device['groupId']) VCardConsumeRecord( orderNo = VCardConsumeRecord.make_no(self.device.logicalCode), openId = card.openId, cardId = str(virtual_card.id), dealerId = card.dealerId, devNo = self.device.devNo, devTypeCode = self.device.devTypeCode, devTypeName = self.device.devTypeName, logicalCode = self.device['logicalCode'], groupId = group['groupId'], address = group['address'], groupNumber = self.device['groupNumber'], groupName = group['groupName'], ).save() else: self.consume_money_for_card(card, fee) # 记录卡消费记录以及消费记录 orderNo, cardOrderNo = self.record_consume_for_card(card, fee) # 记录当前服务的progress.没有上报端口号,所以先用-1标记,表示端口未知。端口等消费的时候会报上来 ServiceProgress.register_card_service( self.device, -1, card, { 'orderNo': orderNo, 'money': self.event_data['fee'], 'coin': self.event_data['fee'], 'needTime': 0, 'cardOrderNo': cardOrderNo } ) # 通知微信,已经扣费 self.notify_balance_has_consume_for_card(card, fee) elif self.event_data['cmdCode'] == '11' and self.event_data['status'] == '01': # 开始启动某个端口 # 按下端口,开始使用。IC卡和ID卡都会上报这条信息。IC卡只记录消费记录信息,ID卡主要是上报了具体的某一个端口.ID卡和劲能的类似,主要是表示开始某一个端口以及回收余额 cardType = self.event_data['cardType'] card = self.update_card_dealer_and_type(self.event_data['cardNo'], cardType) if not card: logger.info('no find card<{}>'.format(self.event_data['cardNo'])) return # 这个地方将计费方式更新到 portDict 刷卡退费的时候会将刷卡的退费金额上报过来 结束事件需要这个字段 主要的是要更新consumeDict的字段 # 0 按时间收费 # 1 按度计费 consumeModule = self.device.get("otherConf", dict()).get("consumeModule", 0) if consumeModule == 0: self.event_data["billingType"] = "time" else: self.event_data["billingType"] = "elec" if cardType == 'ID': consumeDict = {'chargeIndex': self.event_data['port']} queryDict = {'device_imei': self.device.devNo, 'port': -1, 'isFinished': False, 'cardId': str(card.id), 'start_time': {'$gte': int(time.time()) - 3600}} progressDict = {'port': self.event_data['port']} ServiceProgress.update_progress_and_consume_rcd(ownerId = self.device.ownerId, queryDict = queryDict, consumeDict = consumeDict, updateConsume = True, progressDict = progressDict) # 找出对应的卡的ID记录到端口内存数据 queryDict.update(progressDict) rcds = ServiceProgress.get_collection().find(queryDict, {'consumeOrder': 1}, sort = [('start_time', -1)]) allCoins = sum_rmb([rcd['consumeOrder']['coin'] for rcd in rcds]) try: d = Device.objects(devNo=self.device.devNo).first() billingType = d.otherConf.get('billingType', 'time') actionBox = ActionDeviceBuilder.create_action_device(Device.get_dev(self.device.devNo)) cardTime = actionBox.get_freemode_volume_andsoon_config()['card1Time'] icMoney = int(actionBox.get_IC_coin_power_config()['icMoney']) / 10 allCardTime = int(int(allCoins) / icMoney) * int(cardTime) except Exception as e: logger.error('allCardTime error: %s' % e) allCardTime = 0 billingType = 'time' self.event_data.update({'cardOrderTime': str(allCardTime)}) self.event_data.update({'cardId': str(card.id)}) self.event_data.update({'openId': card.openId}) self.event_data.update({'coins': str(allCoins)}) self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING}) self.event_data.update({'billingType': billingType}) Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId else: # IC卡,需要记录消费记录 fee = RMB(self.event_data['fee']) card.balance = RMB(self.event_data['balance']) try: card.save() except Exception, e: logger.exception(e) orderNo, cardOrderNo = self.record_consume_for_card(card, fee) ServiceProgress.register_card_service(self.device, self.event_data['port'], card, {'orderNo': orderNo, 'money': self.event_data['fee'], 'coin': self.event_data['fee'], 'cardOrderNo': cardOrderNo}) # 通知微信,已经扣费 self.notify_balance_has_consume_for_card(card, fee) self.event_data.update({'cardId': str(card.id)}) self.event_data.update({'openId': card.openId}) self.event_data.update({'coins': self.event_data['fee']}) self.event_data.update({'startTime': datetime.datetime.now().strftime(Const.DATETIME_FMT)}) self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING}) Device.update_port_control_cache(self.device.devNo, self.event_data) # 记录该端口累计需要的时间和钱,cardId YuhuanNorther.send_dev_status(self.device, self.event_data['port'], 1) elif self.event_data['cmdCode'] == '17': # 余额回收 card = self.update_card_dealer_and_type(self.event_data['cardNo'], 'IC') # type: Card self.refund_money_for_card(RMB(self.event_data['backMoney']), card.id) elif self.event_data['cmdCode'] == '12': cardNo = self.event_data['cardNo'] preBalance = RMB(self.event_data['balance']) card = self.update_card_dealer_and_type(cardNo, 'IC', balance = preBalance) # type: Card if not card: return if card.frozen: logger.debug('{} has been frozen.'.format(repr(card))) return card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) # type: CardRechargeOrder if not card_recharge_order: logger.debug('{} has no recharge order.'.format(repr(card))) return if self.device.support_reliable: self.recharge_ic_card_realiable(card = card, preBalance = preBalance, rechargeType = 'overwrite', order = card_recharge_order) else: self.recharge_ic_card(card = card, preBalance = preBalance, rechargeType = 'overwrite', order = card_recharge_order) elif self.event_data['cmdCode'] == '05': # 新版本的结束事件,比就版本多刷卡ID卡的在线退费 devNo = self.device.devNo port = str(self.event_data['port']) try: # 获取充电的模式 ctrInfo = Device.get_dev_control_cache(devNo) lineInfo = ctrInfo.get(port) if not lineInfo: logger.debug('get null control cache from {}'.format(repr(self.device))) return else: logger.debug('port cache is: {}'.format(str(lineInfo))) billingType = lineInfo.get("billingType", "time") if billingType == "elec": return self.do_elec_finish(devNo, port, lineInfo, self.event_data) else: return self.do_time_finish(devNo, port, lineInfo, self.event_data) finally: Device.clear_port_control_cache(devNo, str(port)) notify_event_to_north_v2(self.device["devNo"], self.event_data) notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL, desc = self.event_data['reason']) send_event_to_zhejiang(self.dealer, self.device, self.event_data) if self.event_data['reasonCode'] == '04': # 功率过载导致的断电,一条告警,一条恢复告警 YuhuanNorther.send_dev_event(self.device, '97', port) YuhuanNorther.send_dev_event(self.device, '98', port) # 发送一条端口使用结束的告警 YuhuanNorther.send_dev_status(self.device, port, 2) def do_elec_finish(self, devNo, port, lineInfo, msgDict): """ 电川的板子 按电量退费 :return: """ dealer = self.device.owner if not dealer: logger.error('dealer is not found, dealerId=%s' % self.device['ownerId']) return leftElec = self.event_data.get("leftTime", 0) / 100.0 reasonCode = self.event_data.get("endType") reasonStr = self.event_data.get("reason") cardNo = str(self.event_data.get("cardNo")) if self.event_data.get("cardNo") else None cardType = self.event_data.get("cardType") price = RMB(lineInfo.get('price', 0)) startTime = lineInfo.get("startTime") # 投币事件 if not startTime: return startTime = to_datetime(startTime) nowTime = datetime.datetime.now() deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data) try: consumeDict = { "reason": reasonStr, "chargeIndex": port, "uartData": self.event_data.get('uartData', '') } # 计算充电的时间 if deviceDuration and deviceDuration > 0: usedTime = deviceDuration else: if startTime > nowTime: usedTime = 0 else: usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0))) consumeDict.update({"duration": usedTime}) # 获取组信息 group = Group.get_group(self.device["groupId"]) # 扫码的结束 if not cardNo: needElec = lineInfo.get("needElec") if leftElec == int("FFFF", 16): leftElec = needElec elif leftElec > needElec: logger.error('left elec is bigger than need elec. something is wrong') leftElec = 0 spendElec = needElec - leftElec coins = VirtualCoin(lineInfo.get("coins")) refundCoins = VirtualCoin(0) refundedMoney = RMB(0) consumeDict.update({ "reason": reasonStr, "chargeIndex": port }) refundSwitch = self.device.is_auto_refund if refundSwitch: refundCoins = VirtualCoin(coins) * Ratio(leftElec / needElec) refundedMoney = RMB(price) * Ratio(leftElec / needElec) if 'consumeRcdId' in lineInfo and lineInfo['consumeRcdId']: # 虚拟卡的结束 consumeRcdId = lineInfo['consumeRcdId'] vCardId = lineInfo.get("vCardId", "") vCard = UserVirtualCard.objects(id = vCardId).first() 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 refundCoins > VirtualCoin(0): vCardConsumeRcd = VCardConsumeRecord.objects.get(id = consumeRcdId) vCard.refund_quota(vCardConsumeRcd, needElec - spendElec, spendElec, refundCoins.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} ] ) elif 'openId' in lineInfo and lineInfo['openId']: # 扫码使用金币启动 user = MyUser.objects(openId = lineInfo['openId'], groupId = self.device.groupId).first() 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 refundCoins > VirtualCoin(0): consumeDict.update( {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount}) self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, 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 = [] billAsService = self.device.bill_as_service_feature billAsServiceSwitch = billAsService.on 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(price) - real_refund)}) extra.append({u'退款金额': '{}(元)'.format(real_refund)}) if billAsServiceSwitch: elecExpense = round(billAsService.elec_charge * spendElec, 2) serviceExpense = round(billAsService.service_charge * spendElec, 2) extra.append({u'电费': '{}(元)'.format(elecExpense)}) extra.append({u'服务费': '{}(元)'.format(serviceExpense)}) extra.append({u'说明': '目前只能按电量充电,不能按时间充电'}) else: extra.append({u'消费金额': '{}(元)'.format(price)}) if billAsServiceSwitch: elecExpense = round(billAsService.elec_charge * spendElec, 2) serviceExpense = round(billAsService.service_charge * spendElec, 2) extra.append({u'电费': '{}(元)'.format(elecExpense)}) extra.append({u'服务费': '{}(元)'.format(serviceExpense)}) extra.append({u'说明': '目前只能按电量充电,不能按时间充电'}) 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)}) if billAsServiceSwitch: elecExpense = round(billAsService.elec_charge * spendElec, 2) serviceExpense = round(billAsService.service_charge * spendElec, 2) extra.append({u'电费': '{}(金币)'.format(elecExpense)}) extra.append({u'服务费': '{}(金币)'.format(serviceExpense)}) extra.append({u'说明': '目前只能按电量充电,不能按时间充电'}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) if billAsServiceSwitch: elecExpense = round(billAsService.elec_charge * spendElec, 2) serviceExpense = round(billAsService.service_charge * spendElec, 2) extra.append({u'电费': '{}(金币)'.format(elecExpense)}) extra.append({u'服务费': '{}(金币)'.format(serviceExpense)}) extra.append({u'说明': '目前只能按电量充电,不能按时间充电'}) else: extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(coins))}) if billAsServiceSwitch: elecExpense = round(billAsService.elec_charge * spendElec, 2) serviceExpense = round(billAsService.service_charge * spendElec, 2) extra.append({u'电费': '{}(金币)'.format(elecExpense)}) extra.append({u'服务费': '{}(金币)'.format(serviceExpense)}) extra.append({u'说明': '目前只能按电量充电,不能按时间充电'}) 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 = extra) else: logger.error('not net pay rather user virtual card pay. something is wrong.') else: backMoney = self.event_data.get("backMoney") # 刷卡的结束 如果没有开启刷卡退费 则结束上报过来的backMoney = 0 if cardType == "IC": # IC卡结束 只有刷卡结束的IC卡才能够刷新IC卡余额 card = self.update_card_dealer_and_type(cardNo, "IC") if reasonCode == "05" and backMoney > 0: # 原因为05表示刷卡结束充电,会把退费金额报上来. 否则只能通过11指令报上来 self.refund_money_for_card(VirtualCoin(backMoney), card.id) consumeDict.update({'balance': str(card.balance + VirtualCoin(backMoney))}) else: consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来 elif cardType == "ID": # ID卡结束 直接退费就行了 card = self.update_card_dealer_and_type(cardNo, "ID") if backMoney > 0: self.refund_money_for_card(RMB(backMoney), str(card.id)) consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))}) else: logger.error("invalid card type event data is <{}>".format(self.event_data)) ServiceProgress.update_progress_and_consume_rcd( self.device.ownerId, { 'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished': False }, consumeDict ) extra = [{u'实体卡号': cardNo}] if backMoney > 0: extra.append({ u'退款金额': u'{}(金币)'.format(backMoney) }) self.notify_user_service_complete( service_name = u'充电', openid = self.get_managerialOpenId_by_openId(lineInfo['openId']), port = port, address = group['address'], reason = self.event_data['reason'], finished_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = extra ) except Exception as e: logger.exception(e) def do_time_finish(self, devNo, port, lineInfo, msgDict): try: dealer = Dealer.objects(id=self.device['ownerId']).first() if not dealer: logger.error('dealer is not found, dealerId=%s' % self.device['ownerId']) return price = RMB(lineInfo.get('price', 0)) refundProtectionTime = lineInfo.get('refundProtectionTime', 5) deviceDuration, deviceElec = self.__parse_device_finished_data(self.event_data) if lineInfo is not None and 'startTime' in lineInfo: startTime = to_datetime(lineInfo['startTime']) nowTime = datetime.datetime.now() if startTime > nowTime: logger.error('start time is bigger than now time,devNo={}'.format(devNo)) serverDuration = -1 else: serverDuration = int(round((((nowTime - startTime).total_seconds() + 59) / 60.0))) else: logger.info('lineinfo has not startTime,devNo=%s' % devNo) serverDuration = -1 if deviceDuration > 0: usedTime = deviceDuration elif serverDuration >= 0: usedTime = serverDuration else: usedTime = -1 leftTime = self.event_data['leftTime'] if leftTime != 65535: leftTimeStr = leftTime if usedTime == -1: actualNeedTime = -1 else: actualNeedTime = usedTime + leftTime else: leftTimeStr = u'端口未使用' actualNeedTime = 0 group = self.device.group # type: GroupDict if actualNeedTime == -1: if 'cardNo' in self.event_data and self.event_data['cardNo']: card = Card.objects(cardNo = str(self.event_data['cardNo']), agentId = self.dealer.agentId).first() if card is not None: # todo 生成模板所需要的所有参数, 后续有客户自定义的话, 直接在这个字典里面添加, 不需要删掉之前的. # todo 这个变量仅仅用于下面generate_service_complete_title_by_devType()函数的参. 做成配置项更好, 下次不用改代码. templateMap = { 'cardCoins': 0, 'cardOrderTime': 0, 'actualNeedTime': 0, 'usedTime': 0, 'backCoins': 0, 'reason': u'没有实际预定时间, 忽略此次消费事件' } # 先去获取devType上面的模板, 如果没有就走正常的流程 title = self.generate_service_complete_title_by_devType(self.device['devType']['id'], templateMap) if title != '': self.notify_user( card.managerialOpenId if card else '', 'service_complete', **{ 'title': title, 'service': u'充电服务', 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'remark': u'动态功率计算时间是指按照你的电动车功率大小进行折算出来的实际充电时间。' }) else: 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=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=[{ u'实体卡号': self.event_data['cardNo'] }] ) logger.debug('has no actual need time. ignore this event.') return consumeDict = { 'reason': self.event_data['reason'], 'leftTime': leftTimeStr, 'chargeIndex': port, 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime, 'duration': usedTime, 'deviceDuration': deviceDuration, 'serverDuration': serverDuration, 'uartData': self.event_data.get('uartData', '') } if deviceElec != -1: consumeDict.update({'elec': deviceElec}) else: # deviceObj = Device.objects(devNo = self.device.devNo).first() groupObj = Group.objects(id=self.device.groupId).first() if groupObj.otherConf.get('zhuxing', None) is not None: spendElec = round((float(random.randint(15, 19)) / 100) * (float(usedTime) / 60), 3) consumeDict.update({'elec': spendElec}) consumeDict.update({'elecFee': self.deviceAdapter.calc_elec_fee(spendElec)}) else: spendElec = 0.0 consumeDict.update({'elec': 0.0}) consumeDict.update({'elecFee': RMB(0.0).mongo_amount}) # 涉及到卡的退费 不要以缓存为准 以设备上报的为准 cardNo = self.event_data.get("cardNo") if cardNo: # 如果是刷卡的,直接更新消费记录,发通知消息,支持ID卡的退费。 # IC卡的退费, 如果结束原因是刷卡退费(05), 则会把退费信息返回; # 否则不会把退费信息传过来,会通过11号命令返回 cardType = self.event_data.get("cardType") if cardType == 'ID': card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="ID") consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))}) if self.event_data.has_key('backMoney') and self.event_data['backMoney'] > 0: self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id) # 通知微信,已经退费 self.notify_user(card.managerialOpenId, 'refund_coins', **{ 'title': u'退币完成!您的卡号是%s,卡别名:%s' % (card.cardNo, card.nickName), 'backCount': u'金币:%s' % VirtualCoin(self.event_data['backMoney']), 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) elif cardType == "IC": card = self.update_card_dealer_and_type(cardNo=cardNo, cardType="IC") # IC 并且结束code是05 表示卡贴上去了 if not card: logger.info('no found card=<{}>'.format(cardNo)) return if self.event_data['endType'] == '05': # 刷卡退费结束的时候,才能够刷新IC卡的余额 self.refund_money_for_card(VirtualCoin(self.event_data['backMoney']), card.id) consumeDict.update({'balance': str(card.balance + VirtualCoin(self.event_data['backMoney']))}) else: consumeDict.update({'balance': str(card.balance)}) # IC卡必须下次刷卡的时候回收上来 else: logger.error("invalid card type event data is <{}>".format(self.event_data)) ServiceProgress.update_progress_and_consume_rcd(self.device.ownerId, {'open_id': lineInfo['openId'], 'port': int(port), 'device_imei': self.device.devNo, 'isFinished': False}, consumeDict) extra = [{ u'实体卡号': lineInfo['cardNo'] }] if VirtualCoin(self.event_data['backMoney']) > VirtualCoin(0): extra.append({ u'退费金额': u'{}(金币)'.format(self.event_data['backMoney']) }) templateMap = { 'cardCoins': lineInfo.get('coins', 0), 'cardOrderTime': lineInfo.get('cardOrderTime', 0), 'actualNeedTime': actualNeedTime, 'usedTime': usedTime, 'backCoins': self.event_data['backMoney'], 'reason': self.event_data['reason'] } title = self.generate_service_complete_title_by_devType(self.device['devType']['id'], templateMap) if title != '': # u"\\n\\n刷卡扣费金额:\\t\\t{cardCoins}\\n\\n刷卡订购时间:\\t\\t{cardOrderTime}\\n\\n动态功率计算时间:\\t\\t{actualNeedTime}\\n\\n使用时间:\\t\\t{usedTime}\\n\\n退款金额:\\t\\t{backCoins}\\n\\n结束原因:\\t\\t{reason}", self.notify_user( card.managerialOpenId if card else '', 'service_complete', **{ 'title': title, 'service': u'充电服务', 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'remark': u'动态功率计算时间是指按照你的电动车功率大小进行折算出来的实际充电时间。' }) else: 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 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = extra) else: if 'coins' not in lineInfo: logger.warning('line info has no coins.'.format(self.device.devNo)) return # 计算退费信息 coins = VirtualCoin(lineInfo['coins']) refundCoins = VirtualCoin(0) refundedMoney = RMB(0) if self.device.is_auto_refund: if leftTime != 65535: refundCoins = coins * (float(leftTime) / float(actualNeedTime)) refundedMoney = RMB(price * (float(leftTime) / float(actualNeedTime))) else: refundCoins = coins refundedMoney = RMB(price) if usedTime < refundProtectionTime and refundCoins != VirtualCoin(0): refundCoins = coins refundedMoney = RMB(price) if refundCoins > coins: refundCoins = coins if refundedMoney > RMB(price): refundedMoney = RMB(price) # 退费保护不受金币退款开关影响, 照样退费 elif usedTime < refundProtectionTime: refundCoins = coins refundedMoney = RMB(price) if refundCoins > coins: refundCoins = coins if refundedMoney > RMB(price): refundedMoney = RMB(price) else: pass logger.debug( 'lefttime = {}, usedTime = {}, need time = {}, back coins = {}'.format(leftTime, usedTime, actualNeedTime, refundCoins)) if '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 refundCoins > VirtualCoin(0): vCardConsumeRcd = VCardConsumeRecord.objects.get(id = lineInfo['consumeRcdId']) vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, refundCoins.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} ] ) elif 'openId' in lineInfo and lineInfo['openId']: user = MyUser.objects(openId = lineInfo['openId'], groupId = self.device.groupId).first() 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 refundCoins > VirtualCoin(0): consumeDict.update( {DEALER_CONSUMPTION_AGG_KIND.COIN: (VirtualCoin(coins) - refundCoins).mongo_amount}) self.refund_net_pay(user, lineInfo, refundedMoney, refundCoins, 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(price) - real_refund)}) extra.append({u'退款金额': '{}(元)'.format(real_refund)}) else: extra.append({u'消费金额': '{}(元)'.format(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 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra = extra) else: logger.error('not net pay rather user virtual card pay. something is wrong.') except Exception as e: logger.exception(e) class ZHIXIA2InductorEvent(FaultEvent): def do(self, **args): # 处理数据 voltage = self.event_data.get('voltage', -1) temperature = self.event_data.get('temperature', -1) smokeWarning = self.event_data.get('smokeWarning', False) try: otherConf = Device.objects.get(devNo = self.device.devNo).otherConf maxVoltage = otherConf.get('voltageThre', 230) if voltage >= maxVoltage: desc = u'当前电压%s伏超过了门限值:%s伏' % (voltage, maxVoltage) self.record(faultCode = FAULT_CODE.OVER_VOLTAGE, description = desc, title = u'主机电压过高', detail = None, level = FAULT_LEVEL.CRITICAL) maxTemperature = otherConf.get('temThre', 60) if temperature > maxTemperature: desc = u'当前主机温度%s度超过了门限值:%s度' % (temperature, maxTemperature) self.record(faultCode = FAULT_CODE.OVER_TEMPERATURE, description = desc, title = u'主机温度过高', detail = None, level = FAULT_LEVEL.CRITICAL) if smokeWarning: desc = u'当前主机出现冒烟,请第一时间确定是否有起火。此告警十万火急,请迅速联系物业、消防相关部门!' self.record(faultCode = FAULT_CODE.SMOKE, description = desc, title = u'主机出现冒烟', detail = None, level = FAULT_LEVEL.FATAL) except Exception, e: logger.error('some error=%s' % e) return class InteroperatorAlertEvent(FaultEvent): def __Analyze_alert_data(self, data): alertInfo = {'cmdCode': data['cmd'], 'logicalCode': self.device['logicalCode']} address = Group.get_group(self.device['groupId'])['address'] # 这里判断数据格式 if 'status' not in data: logger.error('Data arrays have no keywords status') return # 这里做漏电告警处理 if '5' in data['status']: electricityNum = str(int(data['values'][0:4], 16)) + 'mA' alertInfo['electricity'] = {'electricityNum': electricityNum, 'address': address, 'reasonCode': '12', 'reason': u'在{}编号为{}发生漏电,漏电量为{}' .format(address, self.device['logicalCode'], electricityNum)} # 这里做高温告警处理 if '6' in data['status']: temperatureAccess = [index for index, acces in enumerate(data['status'], 1) if acces == '6'] temperatureAlertList = [] for i in temperatureAccess: temperatureValue = str(int(data['values'][(i - 1) * 4:(i - 1) * 4 + 4], 16)) temperatureAlertList.append( {'temperatureValue': temperatureValue, 'address': address, 'reasonCode': '11', 'reason': u'在{}编号为{}的设备有高温预警,当前温度为{}摄氏度' .format(address, self.device['logicalCode'], temperatureValue)}) alertInfo['temperature'] = temperatureAlertList return alertInfo def do(self, **args): # 判断不存在的设备网上报 if not self.device.ownerId: logger.error('This device cant find a dealer') return # 是否存在温感和电感 temperaturePart = Part.objects(logicalCode = self.device['logicalCode'], partType = '3001') electricityPart = Part.objects(logicalCode = self.device['logicalCode'], partType = '3002') if not temperaturePart.count() or not electricityPart.count(): logger.error( 'There are no transformers in the locigalcode {} equipment'.format(self.device['logicalCode'])) return # 处理数据 eventInfo = self.__Analyze_alert_data(self.event_data['data']) try: # 先处理高温情况 if 'temperature' in eventInfo: for InfoDetail in eventInfo['temperature']: send_event_to_zhejiang(self.dealer, self.device, InfoDetail, partId = temperaturePart[0].id) # 提示用户 group = Group.get_group(self.device['groupId']) self.notify_dealer('device_fault', **{ 'title': u'注意!注意!您的设备发生故障', 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']), 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']), 'fault': InfoDetail['reason'], 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) # 上报高温至消防 # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"): group = Group.get_group(self.device['groupId']) districtInfo = District.get_district(group["districtId"]) self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]}) LiangXiXiaoFang.send_to_xf_fault(self.device, "01", u"设备温度过高") # 处理漏电情况 elif 'electricity' in eventInfo: # 获取漏电告警插件 send_event_to_zhejiang(self.dealer, self.device, eventInfo['electricity'], partId = electricityPart[0].id) # 提示用户 group = Group.get_group(self.device['groupId']) self.notify_dealer('device_fault', **{ 'title': u'注意!注意!您的设备发生故障', 'device': u'组号::%s, 二维码编号:%s' % (self.device['groupNumber'], self.device['logicalCode']), 'location': u'组名称:%s, 地址:%s' % (group['groupName'], group['address']), 'fault': eventInfo['electricity']['reason'], 'notifyTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') }) # 上报漏电至消防 # if self.device["ownerId"] in ("5b4ed32e8732d67bd0626528", "5b6c29388732d669f3ae6f94"): group = Group.get_group(self.device['groupId']) districtInfo = District.get_district(group["districtId"]) self.device.update({"districtInfo": districtInfo, "groupAddr": group["address"]}) LiangXiXiaoFang.send_to_xf_fault(self.device, "04", u"设备发生漏电") except: logger.error('Array {} nonspecification'.format(eventInfo)) return self.record(detail = eventInfo) class InteroperatorReport(WorkEvent): def do(self, **args): if 'type' not in self.event_data: logger.error('Array {} is not format,lose a key named "type"'.format(self.event_data)) if self.event_data.get('type') == 'report': devReportDict = {'logicalCode': 'logicalCode', 'time': self.event_data['time_stamp'], 'portInfo': {}} temperature = '' voltage = 220 try: # 拿到个数判断是不是第一次 reportNum = PortReport.get_collection().find({ 'logicalCode': self.device['logicalCode'] }).sort('time', -1).count() if reportNum: # 获取上一次存储的信息 reportLast = PortReport.get_collection().find({ 'logicalCode': self.device['logicalCode'] }).sort('time', -1)[0] for ii in range(10): power = self.__saveDate(1, msgDict = self.event_data, ii = ii) if power: electricity = float(power) / voltage / 10 else: electricity = reportLast['portInfo'][str(ii + 1)]['electricity'] temperatureR = self.__saveDate(2, msgDict = self.event_data, ii = ii, electricity = electricity, devReportDict = devReportDict) if temperatureR: temperature = temperatureR devReportDict.update({'temperature': temperature}) # 查看现在的跟以前差距多少 timeInterval = devReportDict['time'] - reportLast['time'] if timeInterval > 2: PortReportNewList = [ {"logicalCode": self.device['logicalCode'], "temperature": reportLast['temperature'], 'portInfo': reportLast['portInfo'], 'time': reportLast['time'] + (v + 1) * 2} for v in range(int(timeInterval / 2) - 1)] PortReport.get_collection().insert_many(PortReportNewList) # 首存的情况 else: for ii in range(10): power = self.__saveDate(1, msgDict = self.event_data, ii = ii) electricity = float(power) / voltage / 10 temperatureR = self.__saveDate(2, msgDict = self.event_data, ii = ii, electricity = electricity, devReportDict = devReportDict) if temperatureR: temperature = temperatureR devReportDict.update({'temperature': temperature}) except Exception, e: logger.error('solve dev=%s device report has an error e=%s' % (self.device.devNo, e)) finally: newInfo = PortReport( logicalCode = self.device['logicalCode'], temperature = devReportDict['temperature'], time = devReportDict['time'], portInfo = devReportDict['portInfo'] ) newInfo.save() def __saveDate(self, data, msgDict, ii, electricity = None, devReportDict = None): # 存储数据库 if data == 1: powerData = msgDict['data']['power_data'][0 + 4 * ii:4 + 4 * ii] power = int(powerData, 16) return power if data == 2: temperature = '' status = 'idle' if electricity == 0 else 'busy' devReportDict['portInfo'].update( {str(ii + 1): {'electricity': round(electricity, 3), 'status': status}}) if ii < 4 and msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii] != '0000': temperatureNum = msgDict['data']['temp_data'][0 + 4 * ii:4 + 4 * ii] temperature = int(temperatureNum, 16) return temperature class StartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason): return FINISHED_CHARGE_REASON_MAP.get(reason, reason) def pre_processing(self, device, event_data): # type:(DeviceDict, dict)->dict if 'duration' in event_data: duration = event_data.get('duration') event_data['duration'] = ((duration + 59) / 60) if 'elec' in event_data: elec = event_data.get('elec') event_data['elec'] = round(elec / (10000.0 * 3600.0), 3) 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']) if 'reason' in event_data: event_data['reasonDesc'] = self.analysis_reason(event_data['reason']) if 'fee' in event_data: event_data['fee'] = (RMB(event_data['fee']) * Ratio("0.1")) if 'balance' in event_data: event_data['balance'] = (RMB(event_data['balance']) * Ratio("0.1")) if 'backMoney' in event_data: event_data['backMoney'] = (RMB(event_data['backMoney']) * Ratio("0.1")) if 'sub' in event_data: subs = event_data.get('sub', []) for item in subs: if 'fee' in item: item['fee'] = (RMB(item['fee']) * Ratio('0.1')) if 'balance' in item: item['balance'] = (RMB(item['balance']) * Ratio('0.1')) if 'left' in event_data: event_data['left'] = event_data.pop('left') else: event_data['left'] = 0 return event_data 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 MyComNetPayAckEvent(ComNetPayAckEvent): def __init__(self, smartBox, event_data): super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_after_start(self, order = None): port = order.used_port YuhuanNorther.send_dev_status(self.device, int(port), 1) def post_after_finish(self, order = None): port = order.used_port if self.event_data['reason'] < 0: logger.debug('reason<{}> is not to report.'.format(self.event_data['reason'])) return notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL, desc = self.event_data.get('reasonDesc', '')) send_event_to_zhejiang(self.dealer, self.device, self.event_data) if self.event_data['reason'] == '04': YuhuanNorther.send_dev_event(self.device, '97', port) YuhuanNorther.send_dev_event(self.device, '98', port) # 发送一条端口使用结束的告警 YuhuanNorther.send_dev_status(self.device, port, 2) def merge_order(self, master_order, sub_orders): # type:(ConsumeRecord, list)->dict order_value, unit, billingType = self.deviceAdapter._check_package(master_order.package) 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.package['coins'] all_price = master_order.package['price'] all_consume_value = order_value for sub_order in sub_orders: all_coins += sub_order.package['coins'] all_price += sub_order.package['price'] sub_consume_value, _, _ = self.deviceAdapter._check_package(sub_order.package) all_consume_value += sub_consume_value portDict['coins'] = str(all_coins) portDict['price'] = str(all_price) if billingType == 'time': portDict['needKind'] = 'needTime' portDict['needValue'] = all_consume_value portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + all_consume_value * 60) else: portDict['needKind'] = 'needElec' portDict['needValue'] = all_consume_value / 100.0 portDict['unit'] = u'度' portDict['estimatedTs'] = int(start_time.timestamp + 12 * 60 * 60) return portDict def do_time_finished(self, master_order, sub_orders, merge_order_info): # type: (ConsumeRecord, List[ConsumeRecord], dict)->(dict) user = MyUser.objects(openId = master_order.openId, groupId = master_order.groupId).first() # type: MyUser if not user: logger.error( 'user is not exist. openId = {}, groupId = {}'.format(master_order.openId, master_order.groupId)) return coins = VirtualCoin(merge_order_info['coins']) left = self.event_data['left'] if left == 65535: consumeDict = { 'reason': self.event_data['reason'], 'leftTime': '端口故障', 'chargeIndex': str(master_order.used_port), 'actualNeedTime': u'动态功率计算为0分钟', 'duration': 0, 'elec': 0, 'elecFee': 0 } real_back_coins = coins back_coins = coins else: duration, elec = self.event_data['duration'], self.event_data['elec'] actual_time = duration + left if (actual_time == 0) or (actual_time > merge_order_info['needValue'] * 60): actual_time = merge_order_info['needValue'] * 60 real_back_coins = coins * (float(left) / float(actual_time)) consumeDict = { 'reason': self.event_data['reason'], 'leftTime': str(left), 'chargeIndex': str(master_order.used_port), 'actualNeedTime': u'动态功率计算为%s分钟' % actual_time, 'duration': duration, 'elec': elec, 'elecFee': self.deviceAdapter.calc_elec_fee(elec) } if master_order.paymentInfo['via'] == 'free': back_coins = VirtualCoin(0) else: refundProtection = self.device.get('otherConf', {}).get('refundProtection', 0) refundProtectionTime = self.device.get('otherConf', {}).get('refundProtectionTime', 5) auto_refund = self.device.is_auto_refund logger.debug('{} auto refund enable switch is {}. refund protect = {}, refund protect time = {}'.format( repr(self.device), str(auto_refund), refundProtection, refundProtectionTime)) if auto_refund: if (refundProtection == 1 and duration < refundProtectionTime): back_coins = coins else: back_coins = real_back_coins else: back_coins = VirtualCoin(0) if back_coins > coins: back_coins = coins logger.debug( 'lefttime = {}, usedTime = {}, need time = {}, real back coins = {}, back coins = {}'.format( left, duration, actual_time, real_back_coins, back_coins)) extra = [] if master_order.paymentInfo['via'] == 'free': extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(0))}) elif master_order.paymentInfo['via'] == 'virtualCard': vCard = UserVirtualCard.objects(id = master_order.virtual_card_id).first() # type: UserVirtualCard if not vCard: logger.info('can not find the vCard id = {}'.format(master_order.virtual_card_id)) return if back_coins > VirtualCoin(0): success, consumeTotal, consumeDay = vCard.clear_frozen_quota(str(master_order.id), duration, 0.0, back_coins.mongo_amount) try: if success and consumeDay['count'] > 0: record = VCardConsumeRecord( orderNo = VCardConsumeRecord.make_no(master_order.logicalCode), openId = master_order.openId, nickname = master_order.nickname, cardId = str(vCard.id), dealerId = vCard.dealerId, devNo = master_order.devNo, devTypeCode = master_order.devTypeCode, devTypeName = master_order.dev_type_name, logicalCode = master_order.logicalCode, groupId = master_order.groupId, address = master_order.address, groupNumber = master_order.groupNumber, groupName = master_order.groupName, attachParas = master_order.attachParas, consumeData = consumeTotal, consumeDayData = consumeDay ) record.save() except Exception, e: logger.exception(e) extra.append({u'虚拟卡号': vCard.cardNo}) elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: user.clear_frozen_balance(str(master_order.id), master_order.paymentInfo['deduct'], back_coins) for sub_order in sub_orders: user.clear_frozen_balance(str(sub_order.id), sub_order.paymentInfo['deduct'], VirtualCoin(0)) if back_coins > VirtualCoin(0): consumeDict['refundedMoney'] = str(back_coins) extra.append({u'消费金额': '{}(金币)'.format(coins - back_coins)}) extra.append({u'退款金额': '{}(金币)'.format(back_coins)}) else: extra.append({u'消费金额': '{}(金币)'.format(coins)}) else: logger.error('not net pay rather user virtual card pay. something is wrong.') return master_order.update_service_info(consumeDict) auth_bridge = get_wechat_auth_bridge(source = self.device, app_type = APP_TYPE.WECHAT_USER_MANAGER) self.notify_user_service_complete( service_name = u'充电', openid = user.get_bound_pay_openid(auth_bridge.bound_openid_key), port = str(master_order.used_port), address = master_order.address, reason = self.event_data.get('reasonDesc'), finished_time = master_order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra = extra) def do_elec_finished(self, master_order, sub_orders, merge_order_info): # type: (ConsumeRecord, List[ConsumeRecord], dict)->(dict) """ 电川的板子 按电量退费 :return: """ user = MyUser.objects(openId = master_order.openId, groupId = self.device.groupId).first() # type: MyUser if not user: logger.error('not find user'.format(master_order.openId, self.device.groupId)) 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": master_order.used_port, "duration": self.event_data['duration'] } # 获取组信息 group = Group.get_group(self.device.groupId) # type: GroupDict # 扫码的结束 needElec = merge_order_info['needValue'] if leftElec == int("FFFF", 16): # 端口故障 left = needElec elif leftElec > needElec: logger.error('left elec is bigger than need elec. something is wrong') left = 0 spendElec = needElec - leftElec coins = VirtualCoin(merge_order_info['coins']) back_coins = VirtualCoin(0) if master_order.paymentInfo['via'] != 'free': auto_refund = self.device.is_auto_refund if auto_refund: back_coins = VirtualCoin(coins) * Ratio(leftElec / needElec) logger.debug( 'leftElec = {}, spendElec = {}, usedTime = {}, back coins = {}, auto refund = {}'.format( left, spendElec, duration, back_coins, auto_refund)) else: logger.debug( 'leftElec = {}, spendElec = {}, usedTime = {}, back coins = {}, free = True'.format( left, spendElec, duration, back_coins)) extra = [] if master_order.paymentInfo['via'] == 'free': extra.append({u'消费金额': '{}(金币)'.format(VirtualCoin(0))}) elif master_order.paymentInfo['via'] == 'virtualCard': vCard = UserVirtualCard.objects(id = master_order.virtual_card_id).first() if not vCard: logger.info('can not find the vCard id = %s' % master_order.virtual_card_id) return extra.append({u'虚拟卡号': vCard.cardNo}) if back_coins > VirtualCoin(0): success, consumeTotal, consumeDay = vCard.clear_frozen_quota(str(master_order.id), needElec - spendElec, spendElec, back_coins) try: if success and consumeDay['count'] > 0: record = VCardConsumeRecord( orderNo = VCardConsumeRecord.make_no(master_order.logicalCode), openId = master_order.openId, nickname = master_order.nickname, cardId = str(vCard.id), dealerId = vCard.dealerId, devNo = master_order.devNo, devTypeCode = master_order.devTypeCode, devTypeName = master_order.dev_type_name, logicalCode = master_order.logicalCode, groupId = master_order.groupId, address = master_order.address, groupNumber = master_order.groupNumber, groupName = master_order.groupName, attachParas = master_order.attachParas, consumeData = consumeTotal, consumeDayData = consumeDay ) record.save() except Exception, e: logger.exception(e) elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: user.clear_frozen_balance(str(master_order.id), master_order.paymentInfo['deduct'], back_coins) for sub_order in sub_orders: user.clear_frozen_balance(str(sub_order.id), sub_order.paymentInfo['deduct'], VirtualCoin(0)) if back_coins > VirtualCoin(0): consumeDict.update({"refundedMoney": str(back_coins)}) extra.append({u'消费金额': '{}(金币)'.format(coins - back_coins)}) extra.append({u'退款金额': '{}(金币)'.format(back_coins)}) else: extra.append({u'消费金额': '{}(金币)'.format(coins)}) else: logger.error('not net pay rather user virtual card pay. something is wrong.') return for order in [master_order].extend(sub_orders): order.update_service_info(consumeDict) auth_bridge = get_wechat_auth_bridge(source = self.device, app_type = APP_TYPE.WECHAT_USER_MANAGER) self.notify_user_service_complete( service_name = u'充电', openid = user.get_bound_pay_openid(auth_bridge.bound_openid_key), port = str(master_order.used_port), address = master_order.address, reason = self.event_data.get('reasonDesc'), finished_time = master_order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra = extra) except Exception as e: logger.exception(e) def do_finished_event(self, master_order, sub_orders, merge_order_info): # type: (ConsumeRecord, List[ConsumeRecord], dict)->None billing_type = merge_order_info['billingType'] if billing_type == 'time': self.do_time_finished(master_order, sub_orders, merge_order_info) else: self.do_elec_finished(master_order, sub_orders, merge_order_info) class MyIcStartAckEvent(IcStartAckEvent): def __init__(self, smartBox, event_data): super(MyIcStartAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_after_start(self, order = None): port = order.used_port YuhuanNorther.send_dev_status(self.device, int(port), 1) def post_after_finish(self, order = None): port = order.used_port if self.event_data['reason'] < 0: logger.debug('reason<{}> is not to report.'.format(self.event_data['reason'])) return notify_event_to_north(self.dealer, self.device, level = Const.EVENT_NORMAL, desc = self.event_data.get('reasonDesc', '')) send_event_to_zhejiang(self.dealer, self.device, self.event_data) if self.event_data['reason'] == '04': YuhuanNorther.send_dev_event(self.device, '97', port) YuhuanNorther.send_dev_event(self.device, '98', port) # 发送一条端口使用结束的告警 YuhuanNorther.send_dev_status(self.device, port, 2) 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 += master_order.coin all_money += master_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)->(dict) 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 = u'充电', 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 = u'充电', 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())