# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging import time from mongoengine import DoesNotExist from apilib.monetary import RMB, VirtualCoin from apilib.utils_sys import memcache_lock from apps.web.agent.models import Agent from apps.web.constant import Const, DeviceCmdCode 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 ServiceProgress, UserVirtualCard, VCardConsumeRecord, Card, MyUser from apps.web.user.transaction_deprecated import refund_money 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 ["08", "0C", "0E"]: if event_data['cmdCode'] == "08": event_data.update({{ 'answer': device_event['data'][42:-4] }}) return ChargingNanjiguangWorkEvent(self.deviceAdapter, event_data) if event_data["cmdCode"] in ["12"]: return ChargingNanjiguangFaultEvent(self.deviceAdapter, event_data) class ChargingNanjiguangWorkEvent(WorkEvent): def do(self, **args): cmdCode = self.event_data.get("cmdCode") devNo = self.device["devNo"] with memcache_lock(key = "{devNo}.{cmdCode}.event".format(devNo = devNo, cmdCode = cmdCode), value = '1') as acquired: if acquired: # 扫码结束充电 if cmdCode == "08": sendData = self.event_data['answer'] self.answer_device(funcCode="09", sendData=sendData) self.do_finish_charging() # 刷卡开始 elif cmdCode == "0C": sendData = self.event_data.get("portHex") + self.event_data.get("timeStampHex") self.answer_device(funcCode="0D", sendData=sendData) self.do_start_card_charging() # 刷卡结束 elif cmdCode == "0E": sendData = self.event_data.get("portHex") + self.event_data.get("timeStampHex") self.answer_device(funcCode="0F", sendData=sendData) self.do_finish_card_charging() else: logger.error("undefined cmd") def answer_device(self, funcCode, sendData): self.deviceAdapter._send_data(funcCode=funcCode, sendData=sendData, cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) def do_finish_charging(self): """ 扫码结束充电 :return: """ portStr = self.event_data.get("port") devCache = Device.get_dev_control_cache(self.device["devNo"]) portCache = devCache.get(portStr) openId = portCache.get("openId") vCardId = portCache.get("vCardId") coins = portCache.get("coins") needTime = portCache.get("needTime") spendTime = self.event_data.get("spendTime") nowTime = datetime.datetime.now() group = Group.get_group(self.device["groupId"]) dealer = Dealer.objects.filter(id=group["ownerId"]).first() if not openId: logger.error("open id is null") if not dealer: logger.error('dealer is not found, dealerId=%s' % group['ownerId']) return agent = Agent.objects(id=dealer.agentId).first() if not agent: logger.error('agent is not found, agentId=%s' % dealer.agentId) return user = MyUser.objects.filter(openId=openId, groupId=self.device["groupId"]).first() # 计算退费 leftTime = needTime * 60 - spendTime if needTime * 60 > spendTime else 0 refundCoins = VirtualCoin((leftTime / (needTime * 60)) * coins) self.notify_user( managerialOpenId=user.managerialOpenId, 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{needTime}分钟\\n\\n剩余时间:\\t\\t{leftTime}分钟".format( reason=u"充电结束", logicalCode=self.device["logicalCode"], port=self.event_data["port"], group=group["address"], needTime=needTime, leftTime=int(leftTime / 60) ), service=u"充电桩充电服务", finishTime=datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), remark = u'谢谢您的支持' ) consumeDict = { 'chargeIndex': portStr, 'reason': u"充电结束", 'actualNeedTime': needTime, 'duration': int(spendTime / 60), 'elec': round(self.event_data["spendElec"], 2), 'elecFee': self.calc_elec_fee(self.event_data["spendElec"]), 'leftTime': int(leftTime / 60), 'needTime': u'扫码订购%s分钟' % needTime } if int(refundCoins) > 0 and self.device.is_auto_refund: if vCardId is None: refund_money(self.device, refundCoins, openId) else: # 虚拟卡退费 try: vCard = UserVirtualCard.objects.get(id=vCardId) except DoesNotExist: logger.info('can not find the vCard id = %s' % vCardId) return consumeRecordId = portCache.get("consumeRcdId") if consumeRecordId is None: logger.info('can not find consume rcd id') return try: vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRecordId) except DoesNotExist, e: logger.info('can not find the consume rcd id = %s' % consumeRecordId) return vCard.refund_quota( vCardConsumeRcd, int(spendTime/60), self.event_data["spendElec"], refundCoins.mongo_amount ) consumeDict.update({"refundCoins": refundCoins.mongo_amount}) refundTitle = u'您使用的%s号端口充电,共付款:%s元,充电预定时间为:%s分钟,剩余时间:%s分钟,给您退款:%s元' % (portStr, coins, int(needTime/60), int(leftTime/60), refundCoins) self.notify_user(user.managerialOpenId if user else '', 'refund_coins', **{ 'title': refundTitle, 'backCount': u'金币:%s' % refundCoins, 'finishTime': datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S") }) ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "open_id": openId, "device_imei": self.device["devNo"], "port": int(portStr), "isFinished": False }, consumeDict ) # 事件结束之后清空端口的信息 更新设备的服务信息 Device.clear_port_control_cache(self.device["devNo"], portStr) def do_start_card_charging(self): """ 刷卡开始 :return: """ portStr = self.event_data.get("port") timeStamp = self.event_data.get("timeStamp") cardNo = self.event_data.get("cardNo") coins = self.event_data.get("coins") startTime = time.strftime("%Y-%m-%d %H:%M", time.localtime(timeStamp)) card = self.update_card_dealer_and_type(cardNo, cardType="IC", isHaveBalance=False) if not card.openId: logger.error("card has not openId, cardNo is %s" % card.cardNo) return orderNo, cardOrderNo = self.record_consume_for_card(card, RMB(self.event_data["coins"])) # 记录当前的卡消费的 progress fee = RMB(coins) ServiceProgress.register_card_service( self.device, int(portStr), card, { "orderNo": orderNo, "money": fee.mongo_amount, "coin": fee.mongo_amount, "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': int(fee)}) self.event_data.update({'startTime': startTime}) self.event_data.update({'isStart': True, 'status': Const.DEV_WORK_STATUS_WORKING}) Device.update_dev_control_cache(self.device['devNo'], {portStr :self.event_data}) def do_finish_card_charging(self): """ # 刷卡结束上报 :return: """ portStr = self.event_data.get("port") spendTime = self.event_data.get("spendTime") spendElec = self.event_data.get("spendElec") devCache = Device.get_dev_control_cache(self.device["devNo"]) portCache = devCache.get(portStr) if not portCache: logger.error("not port cache") return cardId = portCache.get("cardId") try: card = Card.objects.get(id=cardId) except DoesNotExist: logger.error("card don't exist") return openId = card.openId group = Group.get_group(self.device["groupId"]) user = MyUser.objects.filter(openId=openId, groupId=self.device["groupId"]).first() consumeDict = { 'chargeIndex': portStr, 'reason': u"刷卡充电结束", 'duration': int(spendTime / 60), 'elec': round(spendElec, 2), 'elecFee': self.calc_elec_fee(self.event_data["spendElec"]), } nowTime = datetime.datetime.now() self.notify_user( managerialOpenId=user.managerialOpenId, 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{coins}".format( reason=u"刷卡充电结束", logicalCode=self.device["logicalCode"], port=self.event_data["port"], group=group["address"], coins=portCache.get("coin") ), service=u"充电桩充电服务", finishTime=datetime.datetime.strftime(nowTime, "%Y-%m-%d %H:%M:%S"), remark = u'谢谢您的支持' ) ServiceProgress.update_progress_and_consume_rcd( self.device["ownerId"], { "open_id": openId, "device_imei": self.device["devNo"], "port": int(portStr), "isFinished": False }, consumeDict ) # 事件结束之后清空端口的信息 更新设备的服务信息 Device.clear_port_control_cache(self.device["devNo"], portStr) class ChargingNanjiguangFaultEvent(FaultEvent): def do(self, **args): """ 烟感报警 :param **args: :return: """ # 首先发送确认消息 self.deviceAdapter._send_data(funCode=13, sendData=self.event_data.get("timeStampHex"), cmd=DeviceCmdCode.OPERATE_DEV_NO_RESPONSE) group = Group.get_group(self.device["groupId"]) eventTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.event_data.get("timeStamp"))) self.notify_dealer( templateName="device_fault", title="注意!设备烟感报警!", device=u"{groupNumber}组-{logicalCode}".format(groupNumber=self.device["groupNumber"], logicalCode=self.device["logicalCode"]), location=u"{address}-{groupName}".format(address=group["address"], groupName=group["groupName"]), fault=u"设备当前触发烟感,可能发生了火警!", notifyTime=eventTime )