# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging from decimal import Decimal from apilib.monetary import RMB from apilib.utils_datetime import to_datetime from apilib.utils_sys import memcache_lock from apps.web.device.models import Device, Group from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent from apps.web.user.models import ServiceProgress, 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 device_event['cmd'] == 100: return HangXinEvent(self.deviceAdapter, event_data) class HangXinEvent(WorkEvent): def __init__(self, smartBox, event_data): super(HangXinEvent, self).__init__(smartBox, event_data) @staticmethod def __get_cache_lock(devNo, portStr, openId): return "{devNo}-{portStr}-{openId}".format(devNo = devNo, portStr = portStr, openId = openId) def do(self, **args): # 获取设备相关信息 devNo = self.device["devNo"] portStr = self.event_data.pop("portStr") portInfo = Device.get_dev_control_cache(devNo).get(portStr, {}) openId = portInfo.get("openId") vCardId = portInfo.get("vCardId", None) # 此设备对于事件上报会有5次,第一次事件上报之后 获取设备的IMEI 操作者openId 以及端口号 加锁操作 # 后续将此设备端口信息清空 释放锁 if not openId: logger.info("event has been handled! ") return user = MyUser.objects(openId = openId, groupId = self.device['groupId']).first() group = Group.get_group(self.device["groupId"]) # 计算返费金额 money = RMB(portInfo['coins']) leftElec = self.event_data["leftElec"] needElec = portInfo["needElec"] backCoins = RMB(money.amount * Decimal(leftElec) / Decimal(needElec)) now_time = str(datetime.datetime.now())[:19] startTime = portInfo.get("startTime") lock_id = self.__get_cache_lock(devNo, portStr, openId) with memcache_lock(lock_id, value = '1', expire = 10) as acquired: if acquired: # 通知用户服务结束 try: consumeDict = { "chargeIndex": portStr, "reason": portInfo.get("desc"), "finishedTime": now_time, "duration": (to_datetime(now_time) - to_datetime(startTime)).total_seconds() / 60 } if self.device.is_auto_refund and float(backCoins) > 0 and vCardId is None: refund_money(self.device, backCoins, openId) consumeDict.update({"refundedMoney": backCoins.mongo_amount}) self.notify_user( user.managerialOpenId if user else '', 'refund_coins', **{ 'title': u"退款通知", 'backCount': u'金币:%s' % backCoins, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') } ) self.notify_user( user.managerialOpenId if user else "", 'service_complete', **{ 'title': self.event_data.get("desc"), 'service': u'充电服务(设备编号:%s,分机:%s,地址:%s)' % ( self.device['logicalCode'], portStr, group['address']), 'finishTime': datetime.datetime.now().strftime('%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 ) except Exception as e: logger.exception('deal with hx devNo=%s event e=%s' % (devNo, e)) finally: # 清空端口信息 Device.clear_port_control_cache(devNo, portStr) # 电量告警计算 self.elec_warning(float(needElec) - float(leftElec)) else: logger.warning("has no acquired") def elec_warning(self, useElec): """ 电量报警 :param useElec: 设备上报的使用的电量 :return: """ otherConf = self.device.get("otherConf", dict()) quotaElec = otherConf.get("quotaElec") warningElec = otherConf.get("warningElec") if not all([quotaElec, warningElec]): return # 真实使用电量和上报使用电量之间的比例关系 realUseElec = float(useElec) / 11.1 quotaElec = float("%.1f" % (float(quotaElec) - realUseElec)) if quotaElec <= float(warningElec): group = Group.get_group(self.device["groupId"]) 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"当前设备电量为{}, 小于告警设备电量{},请及时为设备买电(此消息仅供电量参考)".format(quotaElec, warningElec), notifyTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") ) # 更新新的电量额度 otherConf.update({"quotaElec": quotaElec}) Device.objects.filter(devNo = self.device["devNo"]).update(otherConf = otherConf) Device.invalid_device_cache(self.device["devNo"])