# -*- coding: utf-8 -*- # !/usr/bin/env python import logging import arrow from arrow import Arrow from django.conf import settings from apilib.monetary import RMB from apps.web.constant import DEALER_CONSUMPTION_AGG_KIND, APP_TYPE from apps.web.eventer import EventBuilder from apps.web.eventer.base import WorkEvent, ComNetPayAckEvent, AckEventProcessorIntf from apps.web.helpers import get_wechat_auth_bridge from apps.web.user.models import MyUser logger = logging.getLogger(__name__) class builder(EventBuilder): def __getEvent__(self, device_event): if 'order_id' in device_event: if device_event['order_type'] == 'com_start': return MyComNetPayAckEvent(self.deviceAdapter, device_event) if 'event_type' in device_event: return class FaseChargeEvent(WorkEvent): def do(self, **args): pass class StartAckEventPreProcessor(AckEventProcessorIntf): def analysis_reason(self, reason): FINISHED_CHARGE_REASON_MAP = { '00': u'人工终止', '01': u'自动充满', '02': u'后台终止', '03': u'故障终止', # 服务器定义的停止事件 -2: u'检测到设备未在充电工作状态,结束本次充电', } return FINISHED_CHARGE_REASON_MAP.get(reason, reason) def analysis_charge_method(self,charge_method): MAP = { '00': '自动充满', '01': '定额充电', '02': '定量充电', '03': '定时充电', '04': '备用', '05': '手动充电', } return MAP.get(charge_method, charge_method) def pre_processing(self, device, event_data): def cleaning_data(filed, new_filed=None, ratio=1.0,to_int=False): if filed in event_data: if not new_filed: new_filed = filed event_data[new_filed] = round(float(event_data.pop(filed, 0))*ratio, 2) if to_int: event_data[new_filed] = int(event_data[new_filed]) else: logger.info('{} not in event_data '.format(filed)) cleaning_data('port', 'port', to_int=True) cleaning_data('start_soc', 'startSoc', to_int=True) cleaning_data('finished_soc', 'finishedSoc', to_int=True) cleaning_data('duration', to_int=True) cleaning_data('used_money', 'usedMoney', ratio=0.01) cleaning_data('start_money', 'startMoney', ratio=0.01) cleaning_data('start_elec', 'startElec', ratio=0.01) cleaning_data('finished_elec', 'finishedElec', ratio=0.01) cleaning_data('used_elec', 'usedElec', ratio=0.01) if 'reason' in event_data: event_data['reasonDesc'] = self.analysis_reason(event_data['reason']) if 'charge_method' in event_data: event_data['chargeMethodDesc'] = self.analysis_charge_method(event_data['charge_method']) try: if 'start_time' in event_data: event_data['sts'] = arrow.get(event_data['start_time'], 'YYYY-MM-DD HH:mm:ss', tzinfo = settings.TIME_ZONE).timestamp if 'finished_time' in event_data: event_data['fts'] = arrow.get(event_data['finished_time'], 'YYYY-MM-DD HH:mm:ss', tzinfo = settings.TIME_ZONE).timestamp except Exception: pass return event_data def cleaning_data(self, filed, ratio=1.0): pass class MyComNetPayAckEvent(ComNetPayAckEvent): def __init__(self, smartBox, event_data): super(MyComNetPayAckEvent, self).__init__(smartBox, event_data, StartAckEventPreProcessor()) def post_after_start(self, order=None): pass def post_after_finish(self, order=None): pass def merge_order(self, master_order, sub_orders): portDict = {} billingMethod = master_order.package.get('billingMethod', 'coins') portDict['consumeType'] = billingMethod needTime, needElec = self.deviceAdapter._check_package(master_order.package) start_time = Arrow.fromdatetime(master_order.startTime, tzinfo=settings.TIME_ZONE) portDict = { } if master_order.paymentInfo['via'] == 'virtualCard': portDict.update({ 'vCardId': master_order.virtual_card_id }) all_coins = master_order.package['coins'] all_price = master_order.package['price'] all_consume_time_value = needTime all_consume_elec_value = needElec for sub_order in sub_orders: all_coins += sub_order.package['coins'] all_price += sub_order.package['price'] sub_needTime, sub_needElec = self.deviceAdapter._check_package(sub_order.package) all_consume_time_value += sub_needTime all_consume_elec_value += sub_needElec portDict['coins'] = str(0) portDict['price'] = str(0) portDict['all_consume_time_value'] = str(all_consume_time_value) portDict['all_consume_elec_value'] = str(all_consume_elec_value) portDict['needKind'] = 'needTime' portDict['needValue'] = 999 # 显示充满自停 portDict['unit'] = u'分钟' portDict['estimatedTs'] = int(start_time.timestamp + 720 * 60) return portDict def do_finished_event(self, master_order, sub_orders, merge_order_info): startElec, finishedElec, usedElec = self.event_data.get('startElec'), self.event_data.get('finishedElec'), self.event_data.get('usedElec'), startMoney, usedMoney = self.event_data.get('startElec'), self.event_data.get('startElec'), duration = self.event_data.get('duration') coins = RMB(merge_order_info.get('coins', 0)) usedFee = RMB(usedMoney) consumeDict = { 'reason': self.event_data['reasonDesc'], 'chargeIndex': str(master_order.used_port), DEALER_CONSUMPTION_AGG_KIND.DURATION: duration, DEALER_CONSUMPTION_AGG_KIND.ELEC: usedElec, DEALER_CONSUMPTION_AGG_KIND.ELECFEE: self.deviceAdapter.calc_elec_fee(usedElec), } auto_refund = self.device.is_auto_refund refundProtectionTime = self.device.get('otherConf', {}).get('deviceConfigs', {}).get('refundProtectionTime',5) user = MyUser.objects(openId=master_order.openId, groupId=master_order.groupId).first() # type: MyUser backCoins = RMB(0) if duration < refundProtectionTime: backCoins = coins usedFee = RMB(0) else: if auto_refund: if coins < usedFee: backCoins = coins else: backCoins = coins - usedFee else: usedFee = coins logger.debug('{} auto refund enable switch is {}, refund protect time = {} backMoney={}'.format( repr(self.device), str(auto_refund), refundProtectionTime, backCoins)) extra = [] extra.append({u'本次使用时长': u'{}(分钟)'.format(duration)}) if master_order.paymentInfo['via'] == 'free': extra.append({u'消费金额': u'当前设备免费使用'}) elif master_order.paymentInfo['via'] in ['netPay', 'coins', 'cash', 'coin']: extra.append({u'消费金额': '{}(金币)'.format(usedFee)}) if backCoins > RMB(0): extra.append({u'退款金额': '{}(金币)'.format(backCoins)}) for sub_order in sub_orders[::-1]: need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, sub_order.coin) user.clear_frozen_balance(str(sub_order.id), sub_order.paymentInfo['deduct'], need_back_coins) sub_order.update_service_info({ DEALER_CONSUMPTION_AGG_KIND.COIN: sub_order.coin.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: need_consume_coins.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount}) need_back_coins, need_consume_coins, backCoins = self._calc_refund_info(backCoins, master_order.coin) user.clear_frozen_balance(str(master_order.id), master_order.paymentInfo['deduct'], need_back_coins) consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.COIN: master_order.coin.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: need_consume_coins.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.REFUNDED_COINS: need_back_coins.mongo_amount }) master_order.update_service_info(consumeDict) else: logger.error('not net pay rather user virtual card pay. something is wrong.') return auth_bridge = get_wechat_auth_bridge(source=self.device, app_type=APP_TYPE.WECHAT_USER_MANAGER) self.notify_user_service_complete( service_name='充电', openid=user.get_bound_pay_openid(auth_bridge.bound_openid_key), port=str(master_order.used_port), address=master_order.address, reason=self.event_data.get('reasonDesc'), finished_time=master_order.finishedTime.strftime('%Y-%m-%d %H:%M:%S'), extra=extra) def _calc_refund_info(self, backCoins, orderCoin): if backCoins >= orderCoin: need_back_coins = orderCoin need_consume_coins = RMB(0) backCoins -= orderCoin else: need_back_coins = backCoins need_consume_coins = orderCoin - need_back_coins backCoins = RMB(0) return need_back_coins, need_consume_coins, backCoins