# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import json import random from typing import TYPE_CHECKING from apilib.monetary import RMB, VirtualCoin from apilib.utils_datetime import to_datetime from apps.web.device.models import Device from apps.web.eventer.base import WorkEvent, logger from apps.web.eventer import EventBuilder from apps.web.user.transaction_deprecated import refund_money from apps.web.user.models import Card, UserVirtualCard, ServiceProgress, VCardConsumeRecord, MyUser if TYPE_CHECKING: from apps.web.device.models import GroupDict class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event.get("data")) logger.info("lz_reve event eventData:{}".format(json.dumps(event_data, ensure_ascii=False))) return CFJWorkEvent(self.deviceAdapter, event_data) class CFJWorkEvent(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): cmd = self.event_data.get("cmd", "E") if cmd == "E": logger.info("cfj_no this event!!!! event_data: {}".format(json.dumps(self.event_data, ensure_ascii=False))) return elif cmd == "05": logger.info("cfj_is finished event event_data: {}".format(json.dumps(self.event_data, ensure_ascii=False))) devNo = self.device.devNo port = str(self.event_data.get('port','1'),) try: ctrInfo = Device.get_dev_control_cache(devNo) lineInfo = ctrInfo.get(port) if not lineInfo: logger.debug('cfj_get null control cache from {}'.format(repr(self.device))) return billingType = lineInfo.get("billingType", "time") if billingType == "time": self._do_time_finish(devNo, port, lineInfo, self.event_data) except Exception as e: logger.error("cfj_do finished event is error!!! e={} event_data: {}".format(e, json.dumps(self.event_data, ensure_ascii=False))) finally: Device.clear_port_control_cache(devNo, str(port)) def _do_time_finish(self, devNo, port, lineInfo, msgDict): refundProtectionTime = lineInfo.get('refundProtectionTime', 0) 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 = int(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: 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 } if deviceElec != -1: consumeDict.update({'elec': deviceElec}) else: deviceObj = Device.objects(devNo=self.device.devNo).first() if deviceObj.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}) if 'cardNo' in lineInfo and lineInfo['cardNo']: # 如果是刷卡的,直接更新消费记录,发通知消息,支持ID卡的退费。 # IC卡的退费, 如果结束原因是刷卡退费(05), 则会把退费信息返回; # 否则不会把退费信息传过来,会通过11号命令返回 card = Card.objects(cardNo=lineInfo['cardNo'], agentId=self.dealer.agentId).first() if not card: logger.error('not exist this card no.') return if self.event_data['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') }) else: # IC 并且结束code是05 表示卡贴上去了 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卡必须下次刷卡的时候回收上来 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']) }) 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: # 计算退费信息 coins = VirtualCoin(lineInfo['coins']) backCoins = VirtualCoin(0) if self.device.is_auto_refund: if leftTime != 65535: backCoins = coins * (float(leftTime) / float(actualNeedTime)) else: backCoins = coins # if (refundProtection == 1 and usedTime < refundProtectionTime) and backCoins != VirtualCoin(0): if usedTime < refundProtectionTime and backCoins != VirtualCoin(0): backCoins = coins if backCoins > coins: backCoins = coins logger.debug( 'lefttime = {}, usedTime = {}, need time = {}, back coins = {}'.format(leftTime, usedTime, actualNeedTime, backCoins)) 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 backCoins > VirtualCoin(0): vCardConsumeRcd = VCardConsumeRecord.objects.get(id=lineInfo['consumeRcdId']) vCard.refund_quota(vCardConsumeRcd, usedTime, 0.0, backCoins.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']: try: if backCoins > VirtualCoin(0): consumeDict.update({'refundedMoney': str(backCoins)}) refund_money(self.device, backCoins, lineInfo['openId']) 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 backCoins > VirtualCoin(0): extra.append({u'消费金额': '{}(金币)'.format(coins - backCoins)}) extra.append({u'退款金额': '{}(金币)'.format(backCoins)}) else: extra.append({u'消费金额': '{}(金币)'.format(coins)}) 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=extra ) else: logger.error('not net pay rather user virtual card pay. something is wrong.')