# -*- coding: utf-8 -*- #!/usr/bin/env python import datetime import logging from apilib.monetary import RMB, VirtualCoin, Ratio from apilib.utils_datetime import to_datetime from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND from apps.web.device.models import Device, Group from apps.web.eventer.base import WorkEvent from apps.web.eventer import EventBuilder from apps.web.user.models import ServiceProgress, UserVirtualCard, VCardConsumeRecord, MyUser, ConsumeRecord logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): if device_event['cmd'] == 100: event_data = self.deviceAdapter.analyze_event_data(device_event['data']) return DeKangEvent(self.deviceAdapter, event_data) return None class DeKangEvent(WorkEvent): def __init__(self, smartBox, event_data): super(DeKangEvent, self).__init__(smartBox, event_data) def do(self, **args): """ 状态: 00 : 未插 空闲 AA5524019B010000000008CA000000 00 000000000000000000000000000000000000000000B409BB 11 : 插上 检测负载 AA5524019B010000000008CA000000 11 0000000000000000000000000000000000000000002AD9BB 12 : 付款 继电器连接 AA5524019B01000000000000043B3B 12 1400000000000000000000000000000000000000003401BB 13 : 付款 继电器断开 AA5524019B010000000025D404330B 13 1400000000000000000000000000000000000000005529BB 52 : 启动 使用 AA5524019B01000000000000043B3A 52 14000000000000000000000000000000000000000065B1BB :param args: :return: """ devNo = self.device['devNo'] portStatus = self.event_data.get('portStatus') portStr = self.event_data.pop('portStr') if portStatus in ['0000', '0001', '0100']: # 0000:未插空闲 (拔插头) #0001 负载接入状态 #0100 充电完成 self.do_finished_or_update_cache(portStr) elif portStatus in ['0010', '0011']: portInfo = Device.get_dev_control_cache(devNo).get(portStr, {}) needTime = portInfo.get('needTime') # 第一次上报初始化一个最大时间 if not needTime: portInfo.update({'needTime': self.event_data.get('leftTime')}) portInfo.update(self.event_data) Device.update_dev_control_cache(devNo, {portStr: portInfo}) else: logger.error('undefine port status! dev: %s port: %s status: %s' % (devNo, portStr, portStatus)) def do_finished_or_update_cache(self, portStr): devNo = self.device.devNo lineInfo = Device.get_dev_control_cache(devNo).get(portStr, {}) orderNo = lineInfo.get('orderNo') if not self.check_order_status(orderNo): # 主板切换到状态 却没有 缓存信息 说明是 启动用户 否则则为 结束用户 Device.clear_port_control_cache(devNo, portStr) Device.update_port_control_cache(devNo, self.event_data) logger.info('devNo=<{}> port=<{}> data={}> now do Cache updating'.format(devNo, portStr, self.event_data)) return openId = lineInfo.get('openId') coins = VirtualCoin(lineInfo['coins']) price = RMB(lineInfo['price']) actualNeedTime = lineInfo.get('needTime') # 总共需要时间 if not actualNeedTime: Device.clear_port_control_cache(devNo, portStr) Device.update_port_control_cache(devNo, self.event_data) return startTime = to_datetime(lineInfo['startTime']) nowTime = datetime.datetime.now() usedTime = ((nowTime - startTime).total_seconds() + 59) / 60 if usedTime > actualNeedTime: usedTime = actualNeedTime leftTime = actualNeedTime - usedTime refundProtectionTime = self.device['otherConf'].get('refundProtectionTime', 5) if usedTime <= refundProtectionTime: backCoins = VirtualCoin(coins) refundRMB = RMB(price) else: if self.device.is_auto_refund: backCoins = coins * Ratio(float(leftTime) / float(actualNeedTime)) refundRMB = price * Ratio(float(leftTime) / float(actualNeedTime)) else: backCoins = VirtualCoin(0) refundRMB = RMB(0) try: title = u'您的充电服务已结束,端口已经断开连接,感谢您的使用! ' vCardId = lineInfo.get('vCardId', None) user = MyUser.objects(openId=openId, groupId=self.device['groupId']).first() group = Group.get_group(self.device['groupId']) self.notify_user(user.managerialOpenId, 'service_complete', **{ 'title': title, 'service': u'充电服务(设备编号:%s,分机:%s,地址:%s)' % ( self.device['logicalCode'], portStr, group['address']), 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'remark': u'谢谢您的支持' }) logger.info('notify user completed!') # 处理退费!! # 扫码支付 if not vCardId: # 服务结束通知 consumeDict = { 'reason': u'充电结束', 'needTime': actualNeedTime, DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime, DEALER_CONSUMPTION_AGG_KIND.COIN: coins.mongo_amount } logger.info('refund money start!') if backCoins > VirtualCoin(0) or refundRMB > RMB(0): payInfo = lineInfo.get('payInfo') refundCash = 'refundRMB_device_event' in self.device.owner.features extra = {'title': u'退款通知', 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} if refundCash and payInfo: self.refund_net_pay(user, lineInfo, refundRMB, VirtualCoin(0), consumeDict, True) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (price - refundRMB).mongo_amount}) extra.update({'backCount': u'金额:%s' % refundRMB}) else: self.refund_net_pay(user, lineInfo, RMB(0), backCoins, consumeDict, False) extra.update({'backCount': u'金币:%s' % backCoins}) # 退款通知 self.notify_user(user.managerialOpenId, 'refund_coins',**extra) logger.info('server complete, no money refund!') # 虚拟卡支付 else: # 通知充电完成 vCard = UserVirtualCard.objects.filter(id=vCardId).first() if not vCard: logger.info('devNo=<{}> port=<{}> vCardId=<{}> no this vCard'.format(devNo, portStr, vCardId)) consumeDict = { 'reason': u'充电结束', DEALER_CONSUMPTION_AGG_KIND.DURATION: usedTime, 'actualNeedTime': actualNeedTime, } consumeRcdId = lineInfo.get('consumeRcdId', None) if consumeRcdId is None: logger.info('can not find consume rcd id') return vCardConsumeRcd = VCardConsumeRecord.objects.filter(id=consumeRcdId).first() if not vCardConsumeRcd: logger.info('can not find the consume rcd id = %s' % consumeRcdId) return # 消费单位都为次 直接用算好的退币进行退款 vCard.refund_quota(vCardConsumeRcd, 0, 0, backCoins.mongo_amount) 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: import traceback logger.info(traceback.format_exc()) finally: Device.clear_port_control_cache(devNo, portStr) Device.update_port_control_cache(devNo, self.event_data) def check_order_status(self, orderNo): """ 防重入 和 校验信息 :param orderNo: :return: """ if not orderNo: return False order = ConsumeRecord.objects.filter(orderNo=orderNo).first() if not order: return False if order.status == 'finished': return False else: order.update(status='finished', finishedTime=datetime.datetime.now()) return True