# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging from apilib.monetary import RMB, VirtualCoin from apilib.utils_sys import memcache_lock from apps.web.constant import Const from apps.web.core.device_define.yongxin import REASON_MAP from apps.web.device.models import Group, Device from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent from apps.web.helpers import device_lock_key from apps.web.user.models import ServiceProgress, MyUser, Card, ConsumeRecord, CardConsumeRecord logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): event_data = self.deviceAdapter.analyze_event_data(device_event['data']) if event_data is None or 'cmdCode' not in event_data: return None if event_data['cmdCode'] in ['17', '19', '1D', '41', '11']: return YongxinWorkEvent(self.deviceAdapter, event_data) return None class YongxinWorkEvent(WorkEvent): def do(self, **args): devNo = self.device['devNo'] logger.info('yong xin car charging event detected, devNo=%s,curInfo=%s' % (devNo, self.event_data)) if self.event_data["cmdCode"] == "11": self.do_login() elif self.event_data["cmdCode"] == "41": self.do_elec_price() elif self.event_data['cmdCode'] == '17': # 刷卡消费上报,需要记录下 self.do_card_start() elif self.event_data['cmdCode'] == '19': # 充电实时数据;分扫码扣费,刷卡扣费 self.do_report() elif self.event_data['cmdCode'] == '1D': # 订单上传,这里需要过滤掉,防止多次报文上报上来 self.do_finish() else: logger.error("error cmd, cmd = <{}>, dev = <{}> , event data is <{}>".format( self.event_data["cmdCode"], self.device.devNo, self.event_data )) return def do_card_start(self): """ 处理账单上传 """ sequanceNo = self.event_data["sequanceNo"] with memcache_lock(key = device_lock_key(self.event_data['cardNo']), value = sequanceNo, expire = 360) as acquired: if not acquired: return cardNo = self.event_data['cardNo'] portStr = str(self.event_data["port"]) balance = RMB(self.event_data["balance"]) card = self.update_card_dealer_and_type(cardNo, 'IC', True) self.update_card_balance(card, RMB(self.event_data['balance'])) order = ConsumeRecord.objects(sequanceNo=sequanceNo).first() # type: ConsumeRecord # 存在订单的情况下 直接回复就好了 可能是重复性报文 if order: lineInfo = Device.get_port_control_cache(self.device.devNo, self.event_data['port']) old_orderNo = lineInfo.get('orderNo', '') if lineInfo.get('cardId', '') == str(card.id) and old_orderNo == order.orderNo: return self.device.deviceAdapter._response_card_start(portStr, sequanceNo) else: orderNo, cardOrderNo = self.add_record_consume_for_card(card, sequanceNo, balance) # 记录当前服务的progress ServiceProgress.register_card_service(self.device, self.event_data['port'], card, {'orderNo': orderNo, 'cardOrderNo': cardOrderNo}) rv = { 'cardId': str(card.id), 'startTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING, 'useType': 'card', 'orderNo': orderNo, 'sequanceNo': sequanceNo, 'openId': card.openId, 'cardNo': self.event_data['cardNo'], 'balance': self.event_data['balance'] } Device.update_dev_control_cache(self.device.devNo, {portStr: rv}) return self.device.deviceAdapter._response_card_start(portStr, sequanceNo) def do_report(self): """ 处理 充电数据上传 """ lineInfo = Device.get_port_control_cache(self.device.devNo, str(self.event_data["port"])) lineInfo.update(self.event_data) if not lineInfo.has_key('useType'): # 有可能实时数据先于刷卡数据过来 return if lineInfo['useType'] == 'card': # 刷卡,直接返回就Ok servicedInfo = { 'outputVoltage': lineInfo['outputVoltage'], 'outputElec': lineInfo['outputElec'], 'elec': lineInfo['elec'], 'spendMoney': lineInfo['spendMoney'], 'duration': lineInfo['duration'] } self.update_card_consume_record( orderNo=lineInfo['orderNo'], spendMoney=lineInfo['spendMoney'], desc=u'已经花费%s元,已充%s分钟,%s度电' % ( lineInfo['spendMoney'], lineInfo['duration'], lineInfo['elec']), servicedInfo=servicedInfo ) else: # 更新消费信息即可 leftMoney = str(VirtualCoin(lineInfo.get("coins", 0)) - VirtualCoin(lineInfo["spendMoney"])) lineInfo["leftMoney"] = leftMoney Device.update_dev_control_cache(self.device.devNo, {str(self.event_data["port"]): lineInfo}) def do_finish(self): """ 处理 结算账单上传 """ sequanceNo = self.event_data['sequanceNo'] portStr = str(self.event_data["port"]) with memcache_lock(key = device_lock_key(sequanceNo), value = sequanceNo, expire = 360) as acquired: if not acquired: return self.device.deviceAdapter._response_finished(portStr, sequanceNo, 0) order = ConsumeRecord.objects(sequanceNo=sequanceNo).first() # type: ConsumeRecord if not order: logger.warning('order is not exists.'.format(sequanceNo)) return if order.remarks == u'刷卡消费': servicedInfo = { 'elec': self.event_data['elec'], 'spendMoney': self.event_data['spendMoney'], 'duration': self.event_data['duration'] } _, cardConsumeRecord = self.update_card_consume_record( orderNo=order.orderNo, spendMoney=self.event_data['spendMoney'], desc=u'一共花费%s元,共充电%s分钟,消耗%s度电' % ( self.event_data['spendMoney'], self.event_data['duration'], self.event_data['elec']), servicedInfo=servicedInfo ) try: cardId = cardConsumeRecord.cardId card = Card.objects.get(id=cardId) card.balance = RMB(self.event_data['balance']) card.save() except Exception as e: logger.exception('some error=%s' % e) else: devCache = Device.get_dev_control_cache(self.device.devNo) portCache = devCache.get(portStr, {}) payMoney = VirtualCoin(portCache["coins"]) spendMoney = VirtualCoin(self.event_data["spendMoney"]) refundMoney = payMoney - spendMoney logger.info("[YONG XIN device <{}> refund money is <{}>]".format(self.device.devNo, refundMoney)) # 退款大于起始 的情况下 走售后 if refundMoney > payMoney: refundMoney = VirtualCoin(0) group = Group.get_group(self.device['groupId']) user = order.user # 更新用户的余额以及此次的消费金额 user.balance += RMB(refundMoney) order.coin = VirtualCoin(spendMoney) order.save() user.save() consumeDict = { 'chargeIndex': self.event_data['port'], 'elec': self.event_data['elec'], 'spendMoney': self.event_data['spendMoney'], 'duration': self.event_data['duration'], 'reason': REASON_MAP.get(self.event_data["reason"], "") } ServiceProgress.update_progress_and_consume_rcd( self.device['ownerId'], { 'open_id': order.openId, 'device_imei': self.device['devNo'], 'port': self.event_data['port'], 'isFinished': False }, consumeDict ) self.notify_user( managerialOpenId=user.managerialOpenId if user else "", templateName="service_complete", title=u"\\n\\n结束原因:\\t\\t{reason}\\n\\n设备编号:\\t\\t{logicalCode}-{port}\\n\\n服务地址:\\t\\t{group}\\n\\n充电时间:\\t\\t{duration}分钟\\n\\n消耗电量:\\t\\t{elec}度".format( reason=REASON_MAP.get(self.event_data["reason"], ""), logicalCode=self.device["logicalCode"], port=self.event_data["port"], group=group["address"], duration=self.event_data['duration'], elec=self.event_data['elec'] ), service=u"充电桩充电服务", finishTime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), remark=u'谢谢您的支持' ) Device.clear_port_control_cache(self.device.devNo, portStr) def do_login(self): """ 处理登录请求 """ self.device.deviceAdapter._response_login(self.event_data["devType"]) def do_elec_price(self): """ 处理电价查询 """ self.device.deviceAdapter._response_elec_price() def add_record_consume_for_card(self, card, sequanceNo, balance): servicedInfo = {} group = Group.get_group(self.device['groupId']) address = group['address'] group_number = self.device['groupNumber'] now = datetime.datetime.now() orderNo = ConsumeRecord.make_no() new_record = { 'orderNo': orderNo, 'dateTimeAdded': now, 'openId': card.openId, 'ownerId': self.device['ownerId'], 'coin': VirtualCoin('0.00').mongo_amount, 'money': RMB('0.00').mongo_amount, 'devNo': self.device['devNo'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'isNormal': True, 'status': ConsumeRecord.Status.RUNNING, 'remarks': u'刷卡消费', 'errorDesc': '', 'sequanceNo': sequanceNo, 'desc': "", 'attachParas': {}, 'servicedInfo': {} } ConsumeRecord.get_collection().insert_one(new_record) # 刷卡消费也记录一条数据 cardOrderNo = CardConsumeRecord.make_no() if not CardConsumeRecord.objects.filter(devNo = self.device['devNo'], orderNo=orderNo).count(): new_card_record = { 'orderNo': cardOrderNo, 'openId': card.openId, 'cardId': str(card.id), 'money': RMB('0.00').mongo_amount, 'balance': balance.mongo_amount, 'devNo': self.device['devNo'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'result': 'success', 'remarks': u'刷卡消费', 'dateTimeAdded': datetime.datetime.now(), 'desc': "", 'servicedInfo': servicedInfo } CardConsumeRecord.get_collection().insert_one(new_card_record) return orderNo, cardOrderNo @staticmethod def update_card_consume_record(orderNo, spendMoney, desc, servicedInfo): consumeRcd = ConsumeRecord.objects(orderNo=orderNo).first() if not consumeRcd: logger.info('can not find the record') return None, None consumeRcd.coin = VirtualCoin(spendMoney) consumeRcd.money = RMB(spendMoney) consumeRcd.desc = desc consumeRcd.servicedInfo = servicedInfo consumeRcd.save() cardConsumeRcd = CardConsumeRecord.objects.get(orderNo = consumeRcd.orderNo) cardConsumeRcd.money = RMB(spendMoney) cardConsumeRcd.desc = desc cardConsumeRcd.servicedInfo = servicedInfo cardConsumeRcd.save() return consumeRcd, cardConsumeRcd