123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- # -*- coding: utf-8 -*-
- # !/usr/bin/env python
- import datetime
- import logging
- import typing
- from typing import TYPE_CHECKING
- from apilib.monetary import RMB
- from apilib.utils_sys import memcache_lock
- from apps.web.constant import ErrorCode, START_DEVICE_STATUS, DEALER_CONSUMPTION_AGG_KIND
- from apps.web.device.models import Group, Device
- from apps.web.eventer import EventBuilder
- from apps.web.eventer.weifuleCommon import WeiFuLeStatusEvent, WeiFuLePolicyProcess
- from apps.web.exceptions import UserServerException
- from apps.web.user.constant2 import ConsumeOrderServiceItem
- from apps.web.user.models import ServiceProgress, Card, CardRechargeOrder, CardRechargeRecord, UserVirtualCard
- from apps.web.user.utils import get_consume_order
- from apps.web.user.utils2 import ConsumeOrderStateEngine, generate_net_payment, generate_card_payment, generate_refund
- from apps.web.utils import set_start_key_status
- if typing.TYPE_CHECKING:
- from apps.web.user.models import ConsumeRecord
- logger = logging.getLogger(__name__)
- created_order_32 = 32
- exec_order_33 = 33
- finished_order_34 = 34
- id_card_request_35 = 35
- card_recharge_order_36 = 36
- status_change_event_44 = 44
- ic_consume_event_48 = 48 # ic卡花钱的时候,会上报此事件
- part_sn_event = 49 # 组件上报相关信息
- card_is_normal = 1
- card_not_in_db = 2
- card_is_forzen = 3
- card_has_not_order = 4 # IC卡适用
- card_less_balance = 5 # ID卡适用
- card_type_is_ic = 6 # IC卡不允许当做ID卡使用
- no_load_code = 7
- if TYPE_CHECKING:
- from apps.web.core.adapter.base import SmartBox
- from apps.web.core.adapter.weifule_policy import POLICYBox
- class builder(EventBuilder):
- def __getEvent__(self, device_event):
- event_data = self.deviceAdapter.analyze_event_data(device_event['data'])
- if event_data is None:
- return None
- if event_data['fun_code'] in [
- created_order_32,
- exec_order_33,
- finished_order_34,
- id_card_request_35,
- card_recharge_order_36
- ]:
- return ChargingSocketWorkEvent(self.deviceAdapter, event_data)
- if event_data['fun_code'] in [status_change_event_44]:
- return WeiFuLeStatusEvent(self.deviceAdapter, device_event)
- class ChargingSocketWorkEvent(WeiFuLePolicyProcess):
- def __init__(self, smartBox, event_data): # type:(SmartBox,dict)->None
- super(ChargingSocketWorkEvent, self).__init__(smartBox, event_data)
- self.deviceAdapter = smartBox # type: POLICYBox
- def create_progress_for_socket_order(self, consumeRcd, devInfo):
- try:
- port = int(devInfo['port'])
- progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=port,
- devTypeCode=self.device.devTypeCode).first()
- if not progress:
- progress = ServiceProgress(device_imei=self.device.devNo, port=port,
- devTypeCode=self.device.devTypeCode)
- progress.start_time = devInfo['create_time']
- progress.finished_time = devInfo['create_time'] + 60 * 60 * 12
- progress.isFinished = False
- else:
- if devInfo['id'] in progress.consumes:
- return
- if progress.isFinished is False:
- progress.finished_time += 60 * 60 * 12
- else:
- progress.consumes = []
- progress.start_time = devInfo['create_time']
- progress.finished_time = devInfo['create_time'] + devInfo.get('amount_time', 60 * 60 * 12)
- progress.isFinished = False
- progress.open_id = consumeRcd.openId
- progress.consumes.append(devInfo['id'])
- progress.status = 'running'
- progress.save()
- except Exception as e:
- logger.exception(e)
- def do(self, **args):
- devNo = self.device['devNo']
- funCode = self.event_data.get('fun_code')
- order = self.event_data.get('order', {})
- logger.info('weifule charging event detected, devNo={}, event = {}'.format(devNo, self.event_data))
- # 刷卡查询余额的上报
- if funCode == id_card_request_35:
- return self.response_id_card()
- with memcache_lock(key='%s-%s-%s-finished' % (devNo, order.get('id'), funCode), value='1', expire=120) as acquired:
- if not acquired:
- logger.info('weifule charging event is doing !!!, devNo={}'.format(devNo))
- return
- self._do_ack_order(order)
- try:
- if funCode == created_order_32:
- self._do_created_order_32(order)
- elif funCode == exec_order_33:
- self._do_exec_order_33(order)
- elif funCode == finished_order_34:
- if order["order_type"] == 'card_charge':
- self.update_card_recharge_for_success_event(order['id'], RMB(order['balance'] / 100.0))
- elif order["order_type"] == 'card_refund':
- self.end_for_card_refund(order)
- else:
- self._do_finished_order_34(order)
- elif funCode == card_recharge_order_36: # 如果是卡充值,直接回复订单
- self.deal_with_ic_charge_event()
- except Exception as e:
- logger.exception(e)
- logger.info('deal order is finished < funCode: {} order: {} >'.format(funCode, order.get('id')))
- def translate_reason(self, order):
- cause = order.get('cause')
- if cause == 1:
- if self.duration < self.refundProtectionTime:
- return u'充电已结束,如有异常情况,请联系本台设备经销商。'
- else:
- return u'订购的套餐已用完。'
- elif cause == 2:
- return u'用户远程停止。'
- elif cause == 3:
- return u'管理员操作停止。'
- elif cause == 4:
- return u'经销商远程停止。'
- elif cause == 5:
- return u'用户拔掉充电器,或者插座脱落。'
- elif cause == 6:
- return u'端口功率过载,系统为了安全,关闭此充电端口。'
- elif cause == 7:
- return u'整机电压过载,系统为了安全,关闭所有充电端口。'
- elif cause == 8:
- return u'端口电流过载,系统为了安全,关闭此充电端口。'
- elif cause == 9:
- return u'整机功率超限,系统为了安全,关闭所有充电端口。'
- elif cause == 10:
- return u'检测到温度超限,系统为了安全,关闭所有充电端口。'
- elif cause == 11:
- return u'恭喜您电池已经充满'
- elif cause == 12:
- if self.duration < self.refundProtectionTime:
- return u'充电已结束,如有异常情况,请联系设备经销商。'
- else:
- return u'订购的时间已用完'
- elif cause == 13:
- if self.duration < self.refundProtectionTime:
- return u'充电已结束,如有异常情况,请联系本台设备经销商。'
- else:
- return u'订购的电量已用完'
- elif cause == 0x0E:
- return u"当前充电功率超过套餐允许最大功率"
- elif cause == 20:
- return u'端口功率过小。可能是电池已经充满,也可能是所接负载功率太小'
- return u'充电结束。'
- def deal_with_ic_charge_event(self):
- cardNo = self.event_data['card_no']
- card = self.update_card_dealer_and_type(cardNo, 'IC')
- if not card:
- return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_not_in_db)
- elif card.frozen:
- return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_is_forzen)
- preBalance = card.balance
- rechargeOrder = CardRechargeOrder.get_last_to_do_one(str(card.id))
- if rechargeOrder:
- self.recharge_ic_card(card=card,
- preBalance=preBalance,
- rechargeType='append',
- order=rechargeOrder,
- need_verify=False)
- else:
- return self.deviceAdapter.response_card_charge_result(self.event_data['card_no'], card_has_not_order)
- def recharge_ic_card(self, card, preBalance, rechargeType, order, need_verify=True):
- # type:(Card, RMB, str, CardRechargeOrder, bool)->bool
- """
- # rechargeType有两种,一种是用直接覆写overwrite的方式,一种是append追加钱的方式。
- # 不同的的设备,充值的方式还不一样.注意:money是实际用户付的钱,coins是给用户充值的钱,比如付10块(money),充15(coins)。
- :param card:
- :param preBalance:
- :param rechargeType:
- :param order:
- :return:
- """
- if not order or order.coins == RMB(0):
- return False
- status = Card.get_card_status(str(card.id))
- if status == 'busy':
- return False
- Card.set_card_status(str(card.id), 'busy')
- try:
- # IC卡需要下发到设备,设备写卡,把余额打入卡中
- if rechargeType == 'overwrite':
- sendMoney = preBalance + order.coins
- else:
- sendMoney = order.coins
- # 先判断order最近一次充值是否OK. 满足三个条件才认为上次充值成功:
- # 1、操作时间已经超过三天
- # 2、操作结果是串口超时, 即result == 1
- # 3、当前余额大于最后一次充值操作的充值前余额
- if need_verify and len(order.operationLog) > 0:
- log = order.operationLog[-1]
- result = log['result']
- time_delta = (datetime.datetime.now() - log['time']).total_seconds()
- last_pre_balance = RMB(log['preBalance'])
- if (result == ErrorCode.DEVICE_CONN_FAIL or result == ErrorCode.BOARD_UART_TIMEOUT) \
- and (time_delta > 3 * 24 * 3600 or preBalance > last_pre_balance):
- logger.debug('{} recharge verify result is finished.'.format(repr(card)))
- order.update_after_recharge_ic_card(device=self.device,
- sendMoney=sendMoney,
- preBalance=preBalance,
- result=ErrorCode.SUCCESS,
- description=u'充值校验结束')
- CardRechargeRecord.add_record(
- card=card,
- group=Group.get_group(order.groupId),
- order=order,
- device=self.device)
- return False
- try:
- operation_result, balance = self.deviceAdapter.recharge_card(card.cardNo, sendMoney,
- orderNo=str(order.id))
- order.update_after_recharge_ic_card(device=self.device,
- sendMoney=sendMoney,
- preBalance=preBalance,
- syncBalance=balance,
- result=operation_result['result'],
- description=operation_result['description'])
- if operation_result['result'] != ErrorCode.SUCCESS:
- return False
- if not balance:
- balance = preBalance + order.coins
- CardRechargeRecord.add_record(
- card=card,
- group=Group.get_group(order.groupId),
- order=order,
- device=self.device)
- # 刷新卡里面的余额
- card.balance = balance
- card.lastMaxBalance = balance
- card.save()
- return True
- except Exception as e:
- order.update_after_recharge_ic_card(device=self.device,
- sendMoney=sendMoney,
- preBalance=preBalance,
- syncBalance=balance,
- result=ErrorCode.EXCEPTION,
- description=e.message)
- return False
- except Exception as e:
- logger.exception(e)
- return False
- finally:
- Card.set_card_status(str(card.id), 'idle')
- def prepaid_end_for_app_start(self, order):
- """
- :param order: 主板上报的 object order 信息
- """
- # 虚拟卡支付的
- if self.consumeRcd and self.consumeRcd.virtual_card_id:
- ue = order["elec"] / 1000 * 1000
- ut = order["time"] / 60.0
- vcardRcd = UserVirtualCard.objects.get(id=self.consumeRcd.virtual_card_id) # type: UserVirtualCard
- modified, consumeTotal, consumeDay = vcardRcd.clear_frozen_quota(str(self.consumeRcd.id), ut, ue, 0)
- # 支付完成之后 穿件一笔消费记录
- if not modified:
- return
- vcardRcd.new_consume_record(self.device, self.consumeRcd.user, consumeTotal)
- else:
- super(ChargingSocketWorkEvent, self).prepaid_end_for_app_start(order)
- # ---------------------------------------- 继承重写 防止误判之前的逻辑 --------------------------------------------
- def pay_apps_start_by_32(self, order, callbackSP=None):
- """
- 此函数会对订单进行扣款(补扣或者抢锁扣款,依据上报的事件)
- 一般callbackSP为创建服务进程函数
- """
- consumeRcd = get_consume_order(orderNo=order["id"]) # type: ConsumeRecord
- # 检查订单是否完成
- if consumeRcd is None:
- logger.info('[pay_apps_start_by_32] weifule do 32, not find order = {}, devNo = {}'.format(order['id'], self.device.devNo))
- return
- # 检测订单是否是合法状态
- if consumeRcd.status not in [
- consumeRcd.Status.WAIT_CONF,
- consumeRcd.Status.UNKNOWN,
- consumeRcd.Status.TIMEOUT
- ]:
- logger.info('[pay_apps_start_by_32] weifule do 32, order <{}> status = '.format(consumeRcd.orderNo, consumeRcd.orderNo))
- return
- # 走到此处 一般是订单状态还在异常状态 此时直接执行
- createTime = order["create_time"]
- deviceStartTime = datetime.datetime.fromtimestamp(createTime).strftime("%Y-%m-%d %H:%M:%S")
- ConsumeOrderStateEngine(consumeRcd).to_running({"deviceStartTime": deviceStartTime})
- set_start_key_status(start_key=consumeRcd.orderNo, state=START_DEVICE_STATUS.FINISHED, order_id=str(consumeRcd.id))
- consumeRcd.frozen_payer_balance()
- def _do_finished_order_34(self, order):
- """
- 处理订单的结束消息
- """
- # 查找订单
- consumeRcd = get_consume_order(orderNo=order['id']) # type: ConsumeRecord
- if not consumeRcd:
- logger.info('[_do_finished_order_34], not find order = {}, devNo = {}'.format(order['id'], self.device.devNo))
- return
- # 检查订单的状态
- if consumeRcd.status not in [consumeRcd.Status.RUNNING]:
- logger.info('[_do_finished_order_34], order <{}> status not normal'.format(order['id']))
- return
- # 组建订单的消费信息
- duration = round(order['time'] / 60.0, 1)
- elec = round(order['elec'] / 1000000.0, 3)
- power = round(order.get('power', 0), 1)
- reason = self._get_reason(order.get("cause"), duration, consumeRcd)
- consumeDict = {
- ConsumeOrderServiceItem.ELEC: elec,
- ConsumeOrderServiceItem.DURATION: duration,
- ConsumeOrderServiceItem.MAX_POWER: power,
- ConsumeOrderServiceItem.REASON: reason,
- ConsumeOrderServiceItem.END_TIME: datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
- ConsumeOrderServiceItem.SPEND: RMB(self.event_data["order"]['money'] * 0.01)
- }
- # 将订单的状态置为 结束 并记录服务信息
- ConsumeOrderStateEngine(consumeRcd).to_end(consumeDict)
- Device.clear_port_control_cache(self.device.devNo, str(order["port"]))
- # 以下步骤 实际上可以从event里面剥离开 直接进入订单处理中心排队完成 时间关系 暂时先放在event里面
- consumeRcd.reload()
- # 后支付的情况 需要将订单的金额算出来 并且生成支付信息 然后进行相应的支付
- if consumeRcd.isPostPaid:
- # 为后付费的订单添加订单的金额
- if consumeRcd.isFree:
- consumeRcd.price = RMB(0)
- else:
- if consumeRcd.service.duration <= consumeRcd.package.refundProtectTime:
- consumeRcd.price = RMB(0)
- else:
- # 最小消费金额 和 实际花费金额中选一个
- consumeRcd.price = max(consumeRcd.service.spendMoney, consumeRcd.package.minFee)
- consumeRcd.save()
- # 根据启动方式的不同 为后付费的订单生成相应的支付条件
- try:
- if consumeRcd.isStartNetPay:
- # 网络支付的生成
- payment = generate_net_payment(consumeRcd)
- elif consumeRcd.isStartCardPay:
- # 实体卡 支付的支付
- payment = generate_card_payment(consumeRcd)
- else:
- logger.error("[_do_finished_order_34], postPaid order <{}> error start type".format(order['id']))
- return
- except UserServerException:
- payment = None
- # 尝试一次支付
- if payment:
- consumeRcd.update_payment(payment)
- consumeRcd.frozen_payer_balance()
- consumeRcd.clear_payer_frozen()
- else:
- # 预支付 保护时间内 全额退费
- if consumeRcd.service.duration <= consumeRcd.package.refundProtectTime:
- refundMoney = consumeRcd.price
- else:
- if consumeRcd.package.autoRefund:
- refundMoney = consumeRcd.price - consumeRcd.service.spendMoney
- else:
- refundMoney = RMB(0)
- consumeRcd.clear_payer_frozen(refundMoney)
- # 尝试变更订单的状态 同时
- consumeRcd.reload()
- ConsumeOrderStateEngine(consumeRcd).to_finished()
- def _do_finished_order(self):
- pass
- @staticmethod
- def _get_reason(cause, duration, order): # type: (int, float, ConsumeRecord) -> basestring
- if cause == 1:
- if duration < order.package.refundProtectTime:
- return u'充电已结束,如有异常情况,请联系本台设备经销商。'
- else:
- return u'订购的套餐已用完。'
- elif cause == 2:
- return u'用户远程停止。'
- elif cause == 3:
- return u'管理员操作停止。'
- elif cause == 4:
- return u'经销商远程停止。'
- elif cause == 5:
- return u'用户拔掉充电器,或者插座脱落。'
- elif cause == 6:
- return u'端口功率过载,系统为了安全,关闭此充电端口。'
- elif cause == 7:
- return u'整机电压过载,系统为了安全,关闭所有充电端口。'
- elif cause == 8:
- return u'端口电流过载,系统为了安全,关闭此充电端口。'
- elif cause == 9:
- return u'整机功率超限,系统为了安全,关闭所有充电端口。'
- elif cause == 10:
- return u'检测到温度超限,系统为了安全,关闭所有充电端口。'
- elif cause == 11:
- return u'恭喜您电池已经充满'
- elif cause == 12:
- if duration < order.package.refundProtectTime:
- return u'充电已结束,如有异常情况,请联系设备经销商。'
- else:
- return u'订购的时间已用完'
- elif cause == 13:
- if duration < order.package.refundProtectTime:
- return u'充电已结束,如有异常情况,请联系本台设备经销商。'
- else:
- return u'订购的电量已用完'
- elif cause == 0x0E:
- return u"当前充电功率超过套餐允许最大功率"
- elif cause == 20:
- return u'端口功率过小。可能是电池已经充满,也可能是所接负载功率太小'
- return u'充电结束。'
|