# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import random import time import simplejson as json from bson import ObjectId from mongoengine import DoesNotExist from apilib.monetary import RMB, VirtualCoin, Ratio from apilib.utils_datetime import to_datetime from apilib.utils_sys import memcache_lock from apps import serviceCache from apps.web.agent.models import Agent from apps.web.common.models import TempValues from apps.web.constant import FAULT_CODE, FAULT_LEVEL, Const, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.accounting import Accounting from apps.web.core.device_define.changyuan import CYCardMixin, CYConstant from apps.web.dealer.models import Dealer from apps.web.device.models import Group, Device from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent, FaultEvent from apps.web.user.models import CardRechargeOrder, Card, RechargeRecord, CardRechargeRecord, ServiceProgress, \ ConsumeRecord, CardConsumeRecord, MyUser, UserVirtualCard logger = logging.getLogger(__name__) class PayTypeEnum(object): COUNT_CARD = '00' NET_PAY = '01' OFFLINE_COIN = '02' PREPAID_CARD = '03' ONLINE_CARD = '04' class builder(EventBuilder): def __getEvent__(self, device_event): if 'data' not in device_event: return None eventData = self.deviceAdapter.analyze_event_data(device_event.get('data')) if eventData is None or 'cmdCode' not in eventData: return None if "ack_id" in device_event: eventData["ack_id"] = device_event["ack_id"] if eventData['cmdCode'] == 'D7': return ChangYuanFaultEventer(self.deviceAdapter, eventData) else: return ChangYuanWorkEventer(self.deviceAdapter, eventData) class ChangYuanFaultEventer(FaultEvent): def do(self, **args): if "ack_id" in self.event_data: self.deviceAdapter._ack(self.event_data["ack_id"]) self.event_data.update({ 'faultName': u'设备火灾预警', 'faultCode': FAULT_CODE.FIRE_ALARM, 'level': FAULT_LEVEL.CRITICAL, 'desc': u'主板上报设备火警' }) warningData = { "warningStatus": 2, "warningDesc": "设备温度超限警告", "warningTime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } # 整机告警 Device.update_dev_warning_cache(self.device.devNo, {"0": warningData}) super(ChangYuanFaultEventer, self).do(**args) class ChangYuanWorkEventer(CYCardMixin, WorkEvent): def do(self, **args): if "ack_id" in self.event_data: self.deviceAdapter._ack(self.event_data["ack_id"]) cmdCode = self.event_data.get('cmdCode') if cmdCode == 'D8': return self._do_report_device_status() cacheKey = '{}-{}-event'.format(self.device.devNo, cmdCode) with memcache_lock(key = cacheKey, value = '1', expire = 120) as acquired: if acquired: if cmdCode == 'D3': self._do_recharge_card_result() elif cmdCode == 'D4': self._do_recharge_card() elif cmdCode == 'D9': self._do_start_charge() elif cmdCode == 'DA': self._do_finish() elif cmdCode == 'DE': self._do_card_refund() elif cmdCode == 'F0': self._do_prepare_online_card_start() else: logger.warning( 'cyp_event is doing!, dev is <{}>, cacheKey is <{}>, cmd is <{}>, data is <{}>'.format( self.device.devNo, cacheKey, cmdCode, self.event_data ) ) def _do_card_refund(self): beforeRefund = self.event_data.get('beforeRefund') refund = self.event_data.get('refund') afterRefund = self.event_data.get('afterRefund') cardNo = str(self.event_data.get('cardNo')) # 指令上传错误 if VirtualCoin(beforeRefund) + VirtualCoin(refund) != VirtualCoin(afterRefund): logger.info('bad refund event, beforeRefund is {}, refund is {} afterRefund is {}'.format( beforeRefund, refund, afterRefund )) return # 查找卡的消息 一定要是预付费卡 card = self._update_card_dealer_and_type(cardNo, "01") if not card: return # 查看返费前的金额是否是相等的 if VirtualCoin(card.balance) != VirtualCoin(beforeRefund): logger.info( 'beforeRefund is not equal card balance, cardNo is {}, beforeRefund is {}, card balance is {}'.format( cardNo, beforeRefund, card.balance )) return # 退费 self.refund_money_for_card(RMB(refund), str(card.id)) # 通知用户 self.notify_user( card.managerialOpenId or "", 'refund_coins', **{ 'title': u'预付卡充电桩返费', 'backCount': u'%s' % refund, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } ) @staticmethod def _do_update_ConsumeRecord_CardConsumeRecord_service_progress(portCache, updateMoney): portCache['payCount'] = portCache.get('payCount') + 1 portCache['allPayMoney'] = updateMoney portCache['coins'] = updateMoney orderNo = portCache.get('orderNo') cardOrderNo = portCache.get('cardOrderNo') try: order = ConsumeRecord.objects.get(orderNo=orderNo) card_order = CardConsumeRecord.objects.get(orderNo=cardOrderNo) service_pogress = ServiceProgress.objects(__raw__={'consumeOrder.orderNo': orderNo}).first() order.coin = updateMoney order.money = updateMoney card_order.money = updateMoney service_pogress.consumeOrder['money'] = updateMoney service_pogress.consumeOrder['coin'] = updateMoney order.save() card_order.save() service_pogress.save() except Exception as e: logger.debug('%s' % e) finally: pass def _do_recharge_card(self): """ 同步卡的请求 :return: """ cardNo = self.event_data.get('cardNo') cardBalance = self.event_data.get('cardBalance') cardType = self.event_data.get('cardType') # 查询卡 包括卡的类型 card = self._update_card_dealer_and_type(cardNo, billing_method=cardType) if not card: return if card.cardType == "ID": return # 更新卡的余额 以设备侧的为准 if RMB(cardBalance) != card.balance: logger.info('Card<{}> balance<{}> needs to be sync !!!'.format(cardNo, card.balance)) card.balance = RMB(cardBalance) card = card.save() # orderNos 表示充值的订单 cardOrderNos 表示卡充值的订单 money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id)) # 一旦卡被冻结 立即下发 创建一张为 负数的金额订单 将卡的余额清空 if card.frozen: group = Group.get_group(card.groupId) CardRechargeOrder.new_one( openId=card.openId, cardId=str(card.id), cardNo=card.cardNo, money=RMB(0), coins=VirtualCoin(0) - VirtualCoin(coins) - VirtualCoin(card.balance), group=group, rechargeId=ObjectId(), rechargeType=u"sendCoin" ) money, coins, orderNos, cardOrderNos = CardRechargeOrder.get_to_do_list(str(card.id)) # 没有需要同步的订单的情况下 直接退出 if not orderNos: logger.info('Not card recharge record!, card is <{}>'.format(cardNo)) # self._do_offline_recharge_by_dealer(cardNo, card, cardType) return orderNos = [str(item) for item in orderNos] sid = str(random.randint(0, 0xFFFF)) TempValues.set('%s-%s' % (self.device.devNo, sid), value=json.dumps(orderNos)) TempValues.set('%s-%s-%s' % (self.device.devNo, sid, cardNo), value=json.dumps(cardOrderNos)) TempValues.set('%s-%s' % (self.device.devNo, cardNo), value=str(sid)) # 计算总的需要同步的金额 asyncMoney = RMB(cardBalance) + RMB(coins) logger.info('ready to recharge card, card is <{}>, money is <{}>'.format(cardNo, coins)) # 已经下发金额后,先将订单的状态改为结束。如果主板上报失败,再将订单状态迁移回来否则就不再变化订单状态了 try: CardRechargeOrder.update_card_order_has_finished(str(card.id)) except Exception as e: logger.debug('%s' % e) else: self.deviceAdapter._async_card_balance(cardType, cardNo, asyncMoney) def _do_recharge_card_result(self): """ 确认卡的同步结果 :return: """ asyncStatus = self.event_data.get('result') answerSid = self.event_data.get('sid') cardBalance = self.event_data.get('cardBalance') cardNo = str(self.event_data.get('cardNo')) cardType = self.event_data.get('cardType') # 先找卡 卡都不存在的话 直接推掉 card = self._update_card_dealer_and_type(cardNo, cardType) if not card: return sid = TempValues.get('{}-{}'.format(self.device.devNo, cardNo)) if sid != answerSid: logger.error('answer sid is not equal sid <{}>-<{}>'.format(sid, answerSid)) return # 根据sid 以及卡号 查询出此次充值的所有的订单 orderNos = TempValues.get('{}-{}'.format(self.device.devNo, sid)) cardOrderNos = TempValues.get('{}-{}-{}'.format(self.device.devNo, sid, cardNo)) # 明确接收到了主板同步失败的情况下 直接退出 不在转换充值订单的问题 防止出错 if not asyncStatus: logger.error("card async not success! event data is <{}>".format(self.event_data)) return # 下面的都不会更改订单的状态 最多走售后 money, coins = RMB(0), VirtualCoin(0) # 校验金额是否是相等的 orderNos = [ObjectId(item) for item in orderNos] rds = RechargeRecord.objects.filter(ownerId=card.dealerId, id__in=orderNos) for item in rds: money += item.money coins += item.coins # 这个地方就是异常值处理了 if VirtualCoin(coins + card.balance) != VirtualCoin(cardBalance): logger.error('card pre balance not equal now balance! event is <{}>'.format(self.event_data)) return # 依次更改卡充值的订单 并创建充值订单 cardOrders = CardRechargeOrder.objects.filter(orderNo__in=cardOrderNos) for _order in cardOrders: # type: CardRechargeOrder _order.update_after_recharge_ic_card( device=self.device, sendMoney=RMB(_order.coins), preBalance=card.balance ) preBalance = card.balance card.balance = card.balance + _order.coins # 创建充值记录 CardRechargeRecord.add_record( card=card, group=Group.get_group(_order.groupId), order=_order, device=self.device ) # 保存 card.save() # 完成之后将TempValue 的key value 清空 TempValues.remove('%s-%s' % (self.device['devNo'], sid)) TempValues.remove('%s-%s' % (self.device['devNo'], cardNo)) TempValues.remove("%s-%s-%s" % (self.device["devNo"], sid, cardNo)) def _do_report_device_status(self): ''' 同步设备状态 :return: ''' portInfo = self.event_data.get('portInfo') temperature = self.event_data.get('temperature') # 首先依据上报的状态获取已使用未使用端口数量 allPorts, usedPorts, usePorts = self.deviceAdapter.get_port_static_info(portInfo) # 端口信息更新 devCache = Device.get_dev_control_cache(self.device.devNo) or dict() for portStr, value in devCache.items(): if not portStr.isdigit() or not isinstance(value, dict): continue # 更新每个端口的信息 tempPortInfo = portInfo.get(portStr, dict()) value.update(tempPortInfo) devCache[portStr] = value devCache['temperature'] = temperature devCache['allPorts'] = allPorts devCache['usedPorts'] = usedPorts devCache['usePorts'] = usePorts Device.update_dev_control_cache(self.device.devNo, devCache) def _do_start_charge(self): ''' 充电开始指令, 主要是更新各种缓存等等, 对于扫码的启动基本在adapter缓存已经处理完成,主要需要处理的是刷卡的启动 根据 需求方要求 刷卡的可以做合单处理 :return: ''' logger.info( 'do_start_charge() is run!! event_data:{}'.format(json.dumps(self.event_data, ensure_ascii=False))) portStr = str(self.event_data.get('port')) ctrInfo = Device.get_dev_control_cache(self.device.devNo) lineInfo = ctrInfo.get(portStr, {'port': portStr}) payType = self.event_data.get('payType') if payType == PayTypeEnum.NET_PAY: # 线上支付 if lineInfo.get('status', Const.DEV_WORK_STATUS_IDLE) == Const.DEV_WORK_STATUS_IDLE: lineInfo['power'] = self.event_data.get('power') lineInfo['payType'] = self.event_data.get('payType') lineInfo['needTime'] = self.event_data.get('needTime') logger.info("NET_PAY is ok!!") else: lineInfo['power'] = self.event_data.get('power') lineInfo['needTime'] = self.event_data.get('needTime') logger.info("NET_PAY is ok!!") Device.update_port_control_cache(self.device.devNo, lineInfo) return elif payType == PayTypeEnum.OFFLINE_COIN: Accounting.recordOfflineCoin(self.device, int(time.time()), int(self.event_data['coins'])) orderNo = self.record_consume_for_coin(RMB(self.event_data["coins"])) Device.update_port_control_cache( self.device["devNo"], {'port':portStr, "isStart": True, "coins": self.event_data["coins"], "needTime": self.event_data["needTime"], "power": self.event_data["power"], "payType": self.event_data["payType"], "startTime": datetime.datetime.now().strftime(Const.DATETIME_FMT), "status": Const.DEV_WORK_STATUS_WORKING, "consumeOrderNo": orderNo, "payInfo": [{ "coins": float(self.event_data["coins"]), "price": float(self.event_data["coins"]), "needTIme": self.event_data["needTime"], "consumeOrderNo": orderNo }] }) else: cardNo = self.event_data.get('cardNo') if cardNo == '00000000': return cardBalance = RMB(self.event_data.get('cardBalance')) coins = self.event_data.get('coins') needTime = self.event_data.get('needTime') # 判断卡 card = self._update_card_dealer_and_type(cardNo, billing_method=self.event_data["cardType"]) if not self.check_card_can_use(card): logger.info("[ChangYuanWorkEventer _do_start_charge] check card not can use devNo = {}".format(self.device.devNo)) Device.clear_port_control_cache(self.device.devNo, portStr) self.notify_invalid_card_to_dealer(self.event_data["cardNo"], card) return self.deviceAdapter.stop_charging_port(portStr) if not card: Device.update_dev_control_cache(self.device.devNo, {portStr: self.event_data}) return if payType == PayTypeEnum.ONLINE_CARD: oldBalance = card.balance newBalance = oldBalance - RMB(coins) self.update_card_balance(card, newBalance) else: # 离线卡 以设备上报过来的数据为准 self.update_card_balance(card, RMB(cardBalance)) if lineInfo.get('status', Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE and cardNo == lineInfo.get('cardNo'): logger.info('Card<{}> Swipe to pay again!!'.format(cardNo)) orderNo = lineInfo.get('orderNo') cardOrderNo = lineInfo.get('cardOrderNo') lineInfo['coins'] += coins lineInfo['payCount'] += 1 if not needTime: needTime = 0 lineInfo['needTime'] = needTime ConsumeRecord.objects.filter(orderNo=orderNo).update_one(inc__coin=coins, inc__money=coins) CardConsumeRecord.objects.filter(orderNo=cardOrderNo).update_one(inc__money=coins, dec__balance=coins) ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(portStr), consumeOrder__orderNo=orderNo, isFinished=False).update_one( inc__consumeOrder__coin=coins, inc__finished_time=(time.time()+needTime * 60)) # 第一次刷卡 else: logger.info('Card<{}> start charge the once!!'.format(cardNo)) # 记录ID卡的消费 attachParas = { 'chargeIndex': portStr } servicedInfo = { 'cardNo': cardNo, 'chargeIndex': portStr } needTime = self.event_data.get('needTime') orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(coins), servicedInfo=servicedInfo, attachParas=attachParas) # 记录缓存信息 可以在个人中心以及经销商的端口管理显示 lineInfo = { 'port': portStr, 'cardNo': cardNo, 'openId': card.openId, 'payCount': 1, 'coins': coins, 'status': Const.DEV_WORK_STATUS_WORKING, 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'orderNo': orderNo, 'cardOrderNo': cardOrderNo, 'payType': payType, 'consumeType': 'card', 'needTime':needTime, } finishedTime = None if needTime: finishedTime = int(time.time()) + needTime * 60 # lineInfo['needTime'] = needTime ServiceProgress.register_card_service( self.device, int(portStr), card, { 'orderNo': orderNo, 'coin': VirtualCoin(coins).mongo_amount, 'cardOrderNo': cardOrderNo }, finishedTime) logger.info('port:{} is running, order:{} cardOrder:{}'.format(portStr, orderNo, cardOrderNo)) # 更新端口缓存 Device.update_port_control_cache(self.device.devNo, lineInfo) # 通知卡消费成功 self.notify_balance_has_consume_for_card(card, coins) def _do_finish(self): ''' 充电结束的指令 :return: ''' portStr = str(self.event_data.get('port')) FINISHED_COUNT_KEY = 'device_port_finish_{}_{}'.format(self.device.devNo, portStr) count = serviceCache.get(FINISHED_COUNT_KEY, 0) ctrInfo = Device.get_dev_control_cache(self.device.devNo) lineInfo = ctrInfo.get(portStr) if not lineInfo or lineInfo.get('payType') is None: logger.info('cache is missing ,DA is over') return if "startTime" in lineInfo: duration = ((datetime.datetime.now() - to_datetime(lineInfo.get('startTime'))).total_seconds() + 59) // 60 if duration < 3: count += 1 serviceCache.set(FINISHED_COUNT_KEY, count, 60 * 60 * 24) if count >= 3: self.notify_fault_to_dealer() serviceCache.delete(FINISHED_COUNT_KEY) payType = lineInfo.get('payType') try: if payType == PayTypeEnum.NET_PAY: self._do_netpay_finish(portStr, lineInfo) elif payType == PayTypeEnum.OFFLINE_COIN: self._do_offline_coin_finish(portStr, lineInfo) else: self._do_card_finish(portStr, lineInfo) except Exception as e: logger.error('DA is over error:{}'.format(e)) Device.clear_port_control_cache(self.device.devNo, portStr) def _do_card_finish(self, portStr, lineInfo): logger.info('do card finished lineInfo={} ||| event_data={}'.format(lineInfo, self.event_data)) extra = [] coins = lineInfo.get('coins') cardNo = lineInfo.get('cardNo') card = self.update_card_dealer_and_type(cardNo, cardType='IC') if not card: logger.debug('server no find card!!! cardNo=<{}>'.format(cardNo)) return extra.append({u'使用卡片': u'{}--{}'.format(card.cardName, cardNo)}) consumeDict = { 'port': portStr, 'cardNo': cardNo, 'elec': self.event_data.get('usedElec'), 'reason': self.event_data.get('reason'), } payType = lineInfo.get('payType') if payType == PayTypeEnum.COUNT_CARD: leftTime = self.event_data.get('left') now = datetime.datetime.now() consumeDict['leftTime'] = leftTime consumeDict['duration'] = ((now - to_datetime(lineInfo.get('startTime'))).total_seconds() + 59) // 60 consumeDict['actualNeedTime'] = '动态计算时间为{}分钟'.format(consumeDict['duration'] + leftTime) extra.append({u"使用详情": u"本次使用{}分钟,剩余{}分钟".format(consumeDict['duration'], consumeDict['leftTime'])}) elif payType == PayTypeEnum.PREPAID_CARD: leftMoney = self.event_data.get('left') * 0.01 if leftMoney: if leftMoney > coins: leftMoney = coins refundedMoney = round(leftMoney, 2) consumeDict['spendMoney'] = round(coins - leftMoney, 2) extra.append( {u"消费明细": u"消费{}金币,剩余{}金币(等待靠卡退费)".format(consumeDict['spendMoney'], refundedMoney)}) else: consumeDict['spendMoney'] = round(coins, 2) extra.append( {u"消费明细": u"消费{}金币".format(coins)}) elif payType == PayTypeEnum.ONLINE_CARD: usedElec = self.event_data.get('usedElec') reason = self.event_data.get('reason') leftTime = self.event_data.get('left') extra = [] openId = lineInfo.get('openId') coins = VirtualCoin(lineInfo.get('coins', 0)) money = RMB(lineInfo.get('price', 0)) payInfo = lineInfo.get('payInfo') vCardId = lineInfo.get('vCardId') needTime = lineInfo.get('needTime') duration = needTime - leftTime backCoins = coins * Ratio(float(leftTime) / float(needTime)) refundMoney = money * Ratio(float(leftTime) / float(needTime)) if duration <= CYConstant.REFUND_PRODUCTION_TIME: refundMoney = money backCoins = coins # 如果没有openId则为经销商远程上分 if not openId: logger.info('Remote activation by dealer') return else: user = MyUser.objects.filter(openId=openId, groupId=self.device['groupId']).first() consumeDict = { 'port': portStr, 'elec': usedElec, 'duration': duration, 'reason': reason, 'needTime': needTime, } logger.info( 'ChangYuanPower net pay finish and start to notify user! {}-{}'.format(self.device['devNo'], portStr)) logger.info( 'ChangYuanPower net pay finish refund data {}-backCoins:{}-refundMoney:{}'.format(self.device['devNo'], str(backCoins), str(refundMoney))) if duration > 5: extra.append({u'充电时长': u'{}分钟'.format(duration)}) else: extra.append({u'充电时长': u'0分钟'}) if self.device.is_auto_refund or duration <= 5: if payInfo: # 现金退款 self.refund_money_for_card(refundMoney, str(card.id)) extra.append({u'消费明细': u'支付{}元,退款{}元'.format(money, refundMoney)}) elif vCardId: try: vCard = UserVirtualCard.objects.get(id=vCardId) extra.append({u'虚拟卡券': u'{}--{}'.format(vCard.cardName, vCard.cardNo)}) except DoesNotExist: logger.info('can not find the vCard id = %s' % vCardId) pass pass else: self.refund_money_for_card(backCoins, str(card.id)) extra.append({u'消费明细': u'消费{}金币,退款{}金币'.format(coins-backCoins, backCoins)}) consumeDict['spendMoney'] = round(coins-backCoins, 2) else: extra.append({u'消费明细': u'消费{}金币'.format(coins)}) consumeDict['spendMoney'] = round(coins, 2) if payInfo: for item in payInfo: self.do_ledger(item['rechargeRcdId']) ServiceProgress.update_progress_and_consume_rcd( self.device['ownerId'], { 'device_imei': self.device['devNo'], 'cardId': str(card.id), 'port': int(portStr), 'isFinished': False }, consumeDict ) self.notify_to_user(card.managerialOpenId, extra) logger.info('do_card_finish is finished!!') def _do_netpay_finish(self, portStr, lineInfo): logger.info('[ChangYuanWorkEventer _do_netpay_finish] devNo = {}, event data = {}'.format(self.device.devNo, self.event_data)) usedElec = self.event_data.get('usedElec') reason = self.event_data.get('reason') leftTime = self.event_data.get('left') openId = lineInfo.get('openId') coins = VirtualCoin(lineInfo.get('coins', 0)) money = RMB(lineInfo.get('price', 0)) payInfo = lineInfo.get('payInfo') vCardId = lineInfo.get('vCardId') needTime = lineInfo.get("needTime", leftTime) # 如果没有openId则为经销商远程上分 if not openId: logger.info('[ChangYuanWorkEventer _do_netpay_finish] Remote activation by dealer, devNo = {}, lineInfo = {}'.format(self.device.devNo, lineInfo)) return user = MyUser.objects.filter(openId=openId).first() now = datetime.datetime.now() usedTime = needTime - leftTime duration = (now - to_datetime(lineInfo.get('startTime'))).total_seconds() // 60 actualNeedTime = duration + leftTime backCoins = coins * Ratio(leftTime / actualNeedTime) refundMoney = money * Ratio(leftTime / actualNeedTime) extra = [ {u"订购时长": u"{} 分钟".format(needTime)}, {u"使用时长": u"{} 分钟".format(usedTime)} ] consumeDict = { 'port': portStr, 'elec': usedElec, 'duration': usedTime, 'reason': reason, 'needTime': needTime, 'actualNeedTime': u'动态功率计算为%s分钟' % actualNeedTime, } # 大于充电保护时长 if usedTime <= CYConstant.REFUND_PRODUCTION_TIME: usedTime = 0 backCoins = coins refundMoney = money self.event_data.update({"reason": u"异常结束,如非自己停止,可能是插头松动"}) if usedTime < 0: usedTime = 0 backCoins = VirtualCoin(0) refundMoney = RMB(0) if (self.device.is_auto_refund and self.event_data.get( "reasonCode") in CYConstant.NEED_REFUND) or usedTime <= CYConstant.REFUND_PRODUCTION_TIME: if payInfo: # 先做现金分账 for item in payInfo: self.do_ledger(item['rechargeRcdId']) # 现金退款 self.refund_net_pay(user, lineInfo, refundedMoney=refundMoney, refundCoins=VirtualCoin(0), consumeDict=consumeDict, is_cash=True) extra.append({u'消费明细': u'支付{}元,退款{}元'.format(money, refundMoney)}) elif vCardId: try: vCard = UserVirtualCard.objects.get(id=vCardId) extra.append({u'虚拟卡券': u'{}--{}'.format(vCard.cardName, vCard.cardNo)}) except DoesNotExist: logger.error('can not find the vCard id = %s' % vCardId) else: self.refund_net_pay(user, lineInfo, refundedMoney=RMB(0), refundCoins=backCoins, consumeDict=consumeDict, is_cash=False) extra.append({u'消费明细': u'支付{}金币,退款{}金币'.format(coins, backCoins)}) else: if payInfo: for item in payInfo: self.do_ledger(item['rechargeRcdId']) ServiceProgress.update_progress_and_consume_rcd( self.device['ownerId'], { 'open_id': openId, 'device_imei': self.device['devNo'], 'port': int(portStr), 'isFinished': False }, consumeDict ) self.notify_to_user(openId=user.managerialOpenId, extra=extra) def _update_card_dealer_and_type(self, cardNo, billing_method): """ 昌原的都是IC卡 并且需要标注卡类型 :param cardNo: 卡号 :param billing_method: 卡类型 :return: """ if billing_method in ['0A','02']: card = self.update_card_dealer_and_type(cardNo, "ID") card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: self.recharge_id_card(card = card, rechargeType = 'append', order = card_recharge_order) card.reload() else: card = self.update_card_dealer_and_type(cardNo, "IC") # type: Card if not card: logger.info("not found card <{}>, event is <{}>".format(cardNo, self.event_data)) return None # 没有卡的计费类型的 说明是经销商新添加的卡 添加上计费类型 有了卡计费类型的 需要比较卡计费类型是否正确 如果不正确的 说明卡不正确 # if not card.billing_method: # card.billing_method = billing_method # 只要检测到卡上报的卡类型和服务器存储的卡类型不同,则以上报的卡类型为主 card.billing_method = billing_method if not card.billing_method_equal(billing_method): logger.error("card <{}> billing_method not equal!, event is <{}>".format(cardNo, self.event_data)) return None return card def update_card_dealer_and_type(self, cardNo, cardType='ID', isHaveBalance=True, balance=None): """ 更新卡的状态 重写目的 如果卡不存在 立即下发停止充电指令 而不是新建一张卡 :param cardNo: :param cardType: :param isHaveBalance: :param balance: :return: """ dealer = Dealer.objects.get(id=self.device['ownerId']) agent = Agent.objects.get(id=dealer.agentId) if not agent: logger.error('agent is not found, agentId=%s' % dealer.agentId) return try: card = Card.objects.filter(cardNo=cardNo, agentId=dealer.agentId).first() if not card: return None # 如果卡没有被绑定,这个时候检查下绑定关系。如果卡已经被某个经销商认领了,就不要刷新,不要动了 if card.cardType and card.dealerId and card.devNo and (card.dealerId == self.device['ownerId']): card.cardType = cardType return card else: # card.dealerId = self.device['ownerId'] card.devNo = self.device['devNo'] card.cardType = cardType card.devTypeCode = self.device['devType']['code'] card.isHaveBalance = isHaveBalance card.save() return card except Exception as e: logger.exception(e) return 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['port'], address=group['address'], reason=self.event_data.get('reason'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def _do_offline_coin_finish(self, portStr, lineInfo): CoinConsumeRcd = ConsumeRecord.objects.get(orderNo=lineInfo.get('consumeOrderNo')) startTime = lineInfo.get('startTime') startTime = to_datetime(startTime) nowTime = datetime.datetime.now() if startTime > nowTime: usedTime = 0 else: usedTime = int(round(((nowTime - startTime).total_seconds() / 60.0))) consumeDict = { 'chargeIndex': portStr, 'reason': self.event_data.get('reason'), 'leftTime': self.event_data.get('left', 0), 'actualNeedTime': usedTime + self.event_data.get('left', 0), DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime, DEALER_CONSUMPTION_AGG_KIND.ELEC: self.event_data.get('usedElec'), DEALER_CONSUMPTION_AGG_KIND.TOTAL_COUNT: lineInfo.get('coins'), } CoinConsumeRcd.update_service_info(consumeDict) CoinConsumeRcd.update(status='finished',finishedTime=nowTime) def notify_fault_to_dealer(self): group = self.device.group self.notify_dealer( templateName='device_fault', title=u'端口连续停止超过三次', device=u'{groupNumber}组-{logicalCode}-端口{port}'.format(groupNumber=self.device['groupNumber'], logicalCode=self.device['logicalCode'],port=self.event_data.get('port')), location=u'{address}-{groupName}'.format(address=group['address'], groupName=group['groupName']), fault=u'端口连续停止超过三次', notifyTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') ) def anonymous_user_start(self, lineInfo): coins = self.event_data.get('coins') cardNo = self.event_data.get('cardNo') port = self.event_data.get('port') needTime = self.event_data.get('needTime') cardBalance = self.event_data.get('cardBalance') payType = self.event_data.get('payType') if lineInfo.get('status', Const.DEV_WORK_STATUS_IDLE) != Const.DEV_WORK_STATUS_IDLE and cardNo == lineInfo.get( 'cardNo'): logger.info('anonymous_user card<{}> Swipe to pay again!!'.format(cardNo)) orderNo = lineInfo.get('orderNo') cardOrderNo = lineInfo.get('cardOrderNo') lineInfo['coins'] += coins lineInfo['payCount'] += 1 if not needTime: needTime = 0 # lineInfo['needTime'] = needTime ConsumeRecord.objects.filter(orderNo=orderNo).update_one(inc__coin=coins, inc__money=coins) CardConsumeRecord.objects.filter(orderNo=cardOrderNo).update_one(inc__money=coins, dec__balance=coins) ServiceProgress.objects.filter(device_imei=self.device.devNo, port=port, consumeOrder__orderNo=orderNo, isFinished=False).update_one( inc__consumeOrder__coin=coins, inc__finished_time=(time.time() + needTime * 60)) # 第一次刷卡 else: logger.info('anonymous_user card<{}> start charge the once!!'.format(cardNo)) card = Card( cardNo=cardNo, dealerId=self.device.ownerId, agentId=self.device.owner.agentId, groupId=self.device.groupId, openId=Const.DEFAULT_CARD_OPENID, balance=RMB(cardBalance), ).save() # 记录ID卡的消费 attachParas = { 'chargeIndex': port } servicedInfo = { 'cardNo': cardNo, 'chargeIndex': port, } orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(coins), servicedInfo=servicedInfo, attachParas=attachParas) # 记录缓存信息 可以在个人中心以及经销商的端口管理显示 lineInfo = { 'port': port, 'cardNo': cardNo, 'openId': card.openId, 'payCount': 1, 'coins': coins, 'status': Const.DEV_WORK_STATUS_WORKING, 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'orderNo': orderNo, 'cardOrderNo': cardOrderNo, 'payType': payType, 'consumeType': 'card', } finishedTime = None if needTime: finishedTime = int(time.time()) + (needTime+5) * 60 # lineInfo['needTime'] = needTime ServiceProgress.register_card_service( self.device, port, card, { 'orderNo': orderNo, 'coin': VirtualCoin(coins).mongo_amount, 'cardOrderNo': cardOrderNo }, finishedTime) logger.info('port:{} is running, order:{} cardOrder:{}'.format(port, orderNo, cardOrderNo)) # 更新端口缓存 Device.update_port_control_cache(self.device.devNo, lineInfo) def _do_prepare_online_card_start(self): cardType = self.event_data['cardType'] cardNo = self.event_data['cardNo'] # card = Card.objects.get(cardNo=cardNo) card = self._update_card_dealer_and_type(cardNo,cardType) # type:Card balance = card.balance port = self.event_data['port'] self.deviceAdapter._response_F0(cardType,cardNo,port,balance)