# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import json import logging import time import traceback from typing import TYPE_CHECKING, Optional, Tuple from apilib.monetary import VirtualCoin, RMB, Ratio from apilib.utils_string import make_title_from_dict from apps.web.constant import Const, START_DEVICE_STATUS, DEALER_CONSUMPTION_AGG_KIND from apps.web.core.accounting import Accounting from apps.web.device.models import Device, GroupDict, Group, DeviceDict from apps.web.device.timescale import FluentedEngine from apps.web.eventer.base import WorkEvent from apps.web.exceptions import UnifiedConsumeOrderError from apps.web.user.constant2 import StartDeviceType from apps.web.user.models import ConsumeRecord, UserVirtualCard, MonthlyPackage, ServiceProgress, CardRechargeOrder, \ Card, CardConsumeRecord, RechargeRecord, RefundMoneyRecord, VCardConsumeRecord from apps.web.user.utils2 import generate_card_payment, ConsumeOrderStateEngine, UnifiedCardConsumeOrderManager from apps.web.utils import set_start_key_status logger = logging.getLogger(__name__) 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: pass class WeiFuLeStatusEvent(WorkEvent): def do(self, **args): return self._do_update_charge_status() def __translate_status_from_str(self, status): dictConf = { 'idle': Const.DEV_WORK_STATUS_IDLE, 'busy': Const.DEV_WORK_STATUS_WORKING, 'forbid': Const.DEV_WORK_STATUS_FORBIDDEN, 'fault': Const.DEV_WORK_STATUS_FAULT, 'running': Const.DEV_WORK_STATUS_WORKING, 'link': Const.DEV_WORK_STATUS_CONNECTED, 'estop': Const.DEV_WORK_STATUS_ESTOP, 'ready': Const.DEV_WORK_STATUS_READY, } return dictConf.get(status, Const.DEV_WORK_STATUS_FAULT) def _do_update_charge_status(self): def do_flush(): ctrInfo = Device.get_dev_control_cache(self.device.devNo) for port, status in port_status.items(): if str(port) in ctrInfo: ctrInfo[str(port)]['status'] = self.__translate_status_from_str(status) else: ctrInfo[str(port)] = {'status': self.__translate_status_from_str(status)} Device.update_dev_control_cache(self.device.devNo, ctrInfo) source = self.event_data data = source.get('data', {}) upTime = source.get('time') port_status = self.parse_status(data) if not upTime or not port_status: return import time now = time.time() need_flush = bool((now - upTime) < 10) # 半分钟以内的状态上报为有效上报 if not need_flush: logger.info('Timeout status report!') return do_flush() def parse_status(self, data): return data.get('port_status', {}) class WeiFuLeCommonEvent(object): def pay_apps_start_by_32(self, order, callbackSP=None): """ 此函数会对订单进行扣款(补扣或者抢锁扣款,依据上报的事件) 一般callbackSP为创建服务进程函数 """ consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first() # 检查订单是否完成 if consumeRcd is None or consumeRcd.isNormal: logger.info('weifule({}) pay by startAcion..'.format(order['id'])) return # 创建锁 result = ConsumeRecord.objects.filter(orderNo=order['id'], isNormal=False, attachParas__payBy=None).update( attachParas__payBy='event') # TODO 抢扣 或者 补扣 if result: package = consumeRcd.package attachParas = consumeRcd.attachParas group = self.device.group # type: GroupDict if group.is_free: pass # 先寻找是否可用 包月套餐 monthlyPackage = MonthlyPackage.get_enable_one( openId=consumeRcd.openId, groupId=consumeRcd.groupId, ) if monthlyPackage: monthlyPackage.deduct(consumeRcd) elif package.get('name') == '充满自停' and package.get('coins') == 0 and package.get('price') == 0: # 后付费 consumeRcd.update(isNormal=True, status='') else: # 虚拟卡查找 vCardId = attachParas.get('vCardId') if vCardId: vCard = UserVirtualCard.objects.filter(id=vCardId).first() if not vCard: consumeRcd.update(isNormal=False, status=START_DEVICE_STATUS.FAILURE, errorDesc='没有找到虚拟卡.或虚拟卡已经过期(event)') self.deviceAdapter.stop(order['port']) vCardConsumeOrder = vCard.consume(openId=consumeRcd.openId, group=group, dev=self.device, package=package, attachParas=attachParas, nickname=consumeRcd.nickname, orderNo=consumeRcd.orderNo) if not vCardConsumeOrder: # 扣除虚拟卡失败. 设备又启动了, 直接发明了停止掉 consumeRcd.update(isNormal=False, status=START_DEVICE_STATUS.FAILURE, errorDesc='扣除虚拟卡额度的时候,出现异常,请重试(event)') logger.info('vCardConsumeOrder error , devNo=<{}> stop port({}) now.'.format(self.device.devNo, order['port'], )) return self.deviceAdapter.stop(order['port']) else: user = consumeRcd.user user.update(inc__total_consumed=VirtualCoin(package['coins'])) user.pay(coins=VirtualCoin(package['coins'])) # 允许有负值 # 纠正订单信息 if consumeRcd.status == START_DEVICE_STATUS.FAILURE: errorDesc = consumeRcd.errorDesc + "(<{}补充扣款{}币>)".format( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), VirtualCoin(package['coins']).mongo_amount) if vCardId: errorDesc += '(虚拟卡抵扣)' # TODO 补充扣款只扣除金币 所以如果有充值订单 修正订单为金币支付 consumeRcd.update(isNormal=True, status='', errorDesc=errorDesc, rechargeRcdId='', attachParas__lastRechargeRcdId=consumeRcd.rechargeRcdId) else: consumeRcd.update(isNormal=True, status='') logger.info('weifule({}) pay by event..'.format(order['id'])) if callable(callbackSP): callbackSP(consumeRcd, order) else: logger.info('weifule({}) pay by startAcion..'.format(order['id'])) class WeiFuLePolicyProcess(WorkEvent): def translate_reason(self, order): return '充电结束' @staticmethod def _auto_parser_card_no(card_no): return card_no if card_no.isdigit() and len(card_no) > 8 else '{}'.format(int(card_no, 16)) @staticmethod def _auto_parser_card_type(card_type): if card_type in ['ID', 'online_card']: return 'ID' elif card_type in ['IC', 'offline_card']: return 'IC' else: return '' @staticmethod def _notify_card_title(card, order): # type: (Card, dict) -> dict if card: if card.is_id_card: card_title = u'在线卡片' elif card.is_ic_card: card_title = u'离线卡片' else: card_title = u'实体卡片' return {card_title: u'{}--No.({})'.format(card.cardName or card.nickName, card.cardNo)} else: return {} def notify_to_user(self, openId, extra): group = Group.get_group(self.device['groupId']) self.notify_user_service_complete( service_name='充电', openid=openId, port='', address=group['address'], reason=self.consumeDict.get('reason'), finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), url=self.device.deviceAdapter.custom_push_url(self.consumeRcd, self.consumeRcd.user), extra=extra) 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 == False: progress.finished_time = 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 record_consume_for_card(self, card, money, desc=u'', servicedInfo=None, sid=None, attachParas=None): # type: (Card, RMB, unicode, Optional[dict], Optional[str], Optional[dict])->Tuple[ConsumeRecord, CardConsumeRecord] group = Group.get_group(self.device['groupId']) address = group['address'] group_number = self.device['groupNumber'] now = datetime.datetime.now() if not servicedInfo: servicedInfo = {} if not attachParas: attachParas = {} new_record = ConsumeRecord.objects.create(**{ 'orderNo': sid, 'time': now.strftime('%Y-%m-%d %H:%M:%S'), 'dateTimeAdded': now, 'openId': card.openId, 'nickname': card.nickName, 'ownerId': self.device['ownerId'], 'coin': money.mongo_amount, 'money': money.mongo_amount, 'devNo': self.device['devNo'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'devTypeCode': self.device.devTypeCode, 'devTypeName': self.device.devTypeName, 'isNormal': True, 'remarks': u'刷卡消费', 'errorDesc': '', 'sequanceNo': '', 'desc': desc, 'attachParas': attachParas, 'servicedInfo': servicedInfo }) # 刷卡消费也记录一条数据 new_card_record = CardConsumeRecord.objects.create(**{ 'orderNo': sid, 'openId': card.openId, 'cardId': str(card.id), 'money': money.mongo_amount, 'balance': card.balance.mongo_amount, 'devNo': self.device['devNo'], 'devType': self.device['devType']['name'], 'logicalCode': self.device['logicalCode'], 'groupId': self.device['groupId'], 'address': address, 'groupNumber': group_number, 'groupName': group['groupName'], 'result': 'success', 'remarks': u'刷卡消费', 'sequanceNo': '', 'dateTimeAdded': datetime.datetime.now(), 'desc': desc, 'servicedInfo': servicedInfo, 'linkedConsumeRcdOrderNo': str(new_record['orderNo']) }) return new_record, new_card_record def response_id_card(self): """ 回复ID卡余额查询 """ card_no = self.event_data['card_no'] cardNo = self._auto_parser_card_no(card_no) Card.record_dev_card_no(self.device.devNo, cardNo) card = self.update_card_dealer_and_type(cardNo) resp_message = {'fun_code': self.event_data['fun_code'], 'card_no': card_no, 'balance': 0, 'policy': {}} # 如果没有卡,直接返回 if not card or not card.isBinded: resp_message.update({'result': card_not_in_db}) else: if card.frozen: resp_message.update({'result': card_is_forzen}) else: # 遇到没有充值的订单 重新充值一次 card_recharge_order = CardRechargeOrder.get_last_to_do_one(str(card.id)) if card_recharge_order: self.recharge_id_card(card=card, rechargeType='append', order=card_recharge_order) card.reload() oper = self.event_data.get('reduce') if oper: result, policy, showBalance = self.generate_Idcard_rules(card) resp_message.update({'result': result, 'balance': RMB.yuan_to_fen(showBalance), 'policy': policy}) else: resp_message.update({'result': card_is_normal, 'balance': RMB.yuan_to_fen(card.balance)}) return self.deviceAdapter.send_mqtt(resp_message) def generate_Idcard_rules(self, card): _policyTemp = self.device.policyTemp.get('forIdcard', self.deviceAdapter.INIT_POLICY['forIdcard']) policyType = _policyTemp.get('policyType') billingMethod = _policyTemp.get('billingMethod') # 策略计费 policy = { 'type': policyType, 'mode': 'price', 'auto_stop': True, 'over_money_stop': True, 'open_id': card.openId, 'billingMethod': _policyTemp['billingMethod'], 'rule': {} } if policyType == 'time': # 格式化规则 prices = sorted(_policyTemp.get('rule', {}).get('prices', []), key=lambda _: _['power']) for item in prices: item['value'] = int(float(item.pop('price')) * 100) item['power'] = int(item['power']) policy['rule'] = {'time': 'full_stop', 'prices': prices} elif policyType == 'elec': price = RMB.yuan_to_fen(_policyTemp.get('price', 1)) policy['rule'] = {'time': 'full_stop', 'price': price} else: pass # 付费时间的不同 导致下发的不同的参数 if billingMethod == 'prepaid': money = _policyTemp['money'] # 预支付的启动金额不足 if RMB(money) > RMB(card.balance): result = card_less_balance showBalance = RMB(card.balance) # 预支付的启动金额足够 else: result = card_is_normal showBalance = RMB(card.balance) - RMB(money) # 获取预支付模式下的自动退款 auto_refund = _policyTemp.get('autoRefund') policy.update({ 'auto_refund': auto_refund, 'money': int(float(money) * 100), }) # 后支付的模式 elif billingMethod == 'postpaid': showBalance = card.balance # 对比后支付的最小启动金额 minAfterStartCoins = _policyTemp.get('minAfterStartCoins') if RMB(minAfterStartCoins) > RMB(card.balance): result = card_less_balance # 余额不足 else: result = card_is_normal policy.update({ 'auto_refund': False, 'money': int(float(card.balance) * 100) }) else: result = card_not_in_db showBalance = RMB(0) return result, policy, showBalance def _do_ack_order(self, order): funCode = self.event_data['fun_code'] if funCode in [32, 34]: try: self.deviceAdapter.ack_event(order['id'], funCode) except Exception as e: logger.info('ack event devNo=%s err=%s' % (self.device.devNo, e)) def _do_created_order_32(self, order): if order['order_type'] == 'apps_start': # 扫码 self.pay_apps_start_by_32(order) elif order['order_type'] == 'card_start': self.pay_card_start_by_32(order) elif order['order_type'] == 'coin_start': self.pay_coin_start_by_32(order) else: pass logger.info('finished 32 !!order=<{}>'.format(order)) def _do_exec_order_33(self, order): if 'exec_time' in order: now = datetime.datetime.fromtimestamp(order['exec_time']) elif 'execute_time' in order: now = datetime.datetime.fromtimestamp(order['execute_time']) else: now = datetime.datetime.now() ConsumeRecord.objects.filter(orderNo=order['id']).update(startTime=now) logger.info('now to exec next order <{}>'.format(order['id'])) def _do_finished_order_34(self, order): # 如果是离线卡的余额回收,修改下余额,然后直接返回即可 if order['order_type'] == 'card_refund': return self.end_for_card_refund(order) self.common_verify_order(order) if not self.consumeRcd: return try: # 初始化订单的类型(预付费, 后付费) self._init_common_info(order) self.billingMethod = self.consumeRcd.servicedInfo.get('billingMethod') if self.billingMethod == 'postpaid': self.finishe_postpaid_process(order) elif self.billingMethod == 'prepaid': self.finish_prepaid_process(order) self.consumeRcd.update_service_info(self.consumeDict) else: logger.info('No such process.. orderNo={}'.format(self.consumeRcd.orderNo)) self.notify_to_user(self.consumeRcd.user.managerialOpenId, self.extra) logger.info('order is finished.. orderNo={}'.format(self.consumeRcd.orderNo)) except Exception as e: logger.exception('some exception happed,devNo=%s,e=%s' % (self.device.devNo, e)) finally: ctrInfo = Device.get_dev_control_cache(self.device.devNo) lineInfo = ctrInfo.get(str(order['port']), {}) # 如果缓存中要结束的端口是本单的 就清空缓存 否则不处 if order['id'] == lineInfo.get('orderNo'): Device.clear_port_control_cache(self.device.devNo, order['port']) def common_verify_order(self, order): self.consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id'], status__ne='finished').first() # type:ConsumeRecord if not self.consumeRcd: logger.info('Repeat processing!! orderNo<{}>'.format(order['id'])) return else: self.consumeRcd.status = 'finished' self.consumeRcd.finishedTime = datetime.datetime.now() self.consumeRcd.servicedInfo['source'] = json.dumps(order) self.consumeRcd.save() if 'card_no' in order: cardNo = self._auto_parser_card_no(order['card_no']) self.card = self.update_card_dealer_and_type(cardNo=cardNo) progress = ServiceProgress.objects.filter(device_imei=self.device.devNo, port=int(order['port']), devTypeCode=self.device.devTypeCode, consumes__contains=order['id']).first() if progress is None: pass else: progress.consumes.remove(order['id']) if len(progress.consumes) > 0: # 还有其他的单未处理完成 progress.save() else: progress.status = 'finished' # 此时为本次服务的最后一单 progress.isFinished = True progress.save() def end_for_card_refund(self, order): return self.update_card_dealer_and_type(cardNo=order['card_no'], cardType='IC', balance=RMB(order['balance'] / 100.0)) def _init_common_info(self, order): """ 初始化结束要用到的数据 """ self.refundProtectionTime = int(self.device['otherConf'].get('refundProtectionTime', 5)) self.consumeDict = self.consumeRcd.servicedInfo self.duration = round(order['time'] / 60.0, 1) self.elec = round(order['elec'] / 1000000.0, 3) self.power = round(order.get('power', 0), 1) order['reason'] = self.translate_reason(order) self.consumeDict.update({'reason': order['reason'], 'duration': self.duration, 'elec': self.elec}) # 添加一个分档功率 if self.power: self.consumeDict.update({'maxPower': self.power}) self.extra = [ {u'使用详情': u'{}分钟(端口: {})'.format(self.duration, self.consumeDict['chargeIndex'])} ] # 32启动补扣或者刷卡建单 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* def pay_apps_start_by_32(self, order, callbackSP=None): """ 此函数会对订单进行扣款(补扣或者抢锁扣款,依据上报的事件) 一般callbackSP为创建服务进程函数 """ consumeRcd = ConsumeRecord.objects.filter(orderNo=order['id']).first() # type: ConsumeRecord # 检查订单是否完成 if consumeRcd is None or consumeRcd.isNormal: logger.info('weifule({}) pay by startAction..'.format(order['id'])) return # 创建锁 result = ConsumeRecord.objects.filter( orderNo=order['id'], isNormal=False, attachParas__payBy=None).update( attachParas__payBy='event' ) if not result: return # TODO 在线支付的抢扣或者补扣 # 订单为免费的情况 更换状态即可 if consumeRcd.isFree: logger.info("[pay_apps_start_by_32] free order = {}, event = {}".format(consumeRcd, self.event_data)) consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING) return # 后支付的情况 更换订单状态即可 if consumeRcd.package.isPostpaid: logger.info("[pay_apps_start_by_32] post paid order = {}, event = {}".format(consumeRcd, self.event_data)) consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING) # 预支付的情况 考虑要补扣金额 else: consumeRcd.frozen_payer_balance() # 纠正订单信息 if consumeRcd.status == START_DEVICE_STATUS.FAILURE: errorDesc = consumeRcd.description + "(<{}补充扣款>)".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING, errorDesc=errorDesc) else: consumeRcd.update(isNormal=True, status=START_DEVICE_STATUS.RUNNING) logger.info('weifule({}) pay by event..'.format(order['id'])) set_start_key_status(start_key=consumeRcd.startKey, state=START_DEVICE_STATUS.FINISHED, order_id=str(consumeRcd.id)) if callable(callbackSP): callbackSP(consumeRcd, order) self.create_progress_for_socket_order(consumeRcd, order) def pay_card_start_by_32(self, order): # 查找卡 logger.debug("[pay_card_start_by_32] order = {}".format(order)) cardNo, cardType = self._auto_parser_card_no(order['card_no']), self._auto_parser_card_type(order['card_type']) card = self.update_card_dealer_and_type(cardNo, cardType) # type: Card # 非法卡 if not card or not card.isBinded or card.frozen: logger.info("[pay_card_start_by_32], not find normal card! order = {}".format(order)) if card and card.is_id_card: self.device.deviceAdapter.stop_charging_port(order["port"]) return # extra = [ # {u'设备地址': u'{}'.format(self.device.group.address)}, # {u'设备编号': u'{}'.format(self.device.logicalCode)}, # self._notify_card_title(card, order) # ] # # 创建订单参数 data = { 'devInfo': {'logicalCode': self.device.logicalCode}, 'userInfo': {'cardId': str(card.id)}, } # 从policy中构建出套餐参数 startInfo = { 'sequenceNo': order['id'], 'startType': StartDeviceType.CARD, 'port': order['port'], 'packageId': '', "name": "刷卡套餐", "policyType": order['policy']['type'], "autoRefund": order['policy']['auto_refund'], "autoStop": order['policy']['auto_stop'], "billingMethod": order['policy']['billingMethod'], "rules": order['policy']["rule"] } # 预支付的情况 套餐的金额等于付款的金额 if startInfo["billingMethod"] == "prepaid": startInfo["price"] = RMB.fen_to_yuan(order['policy']['money']) # type:RMB # 后支付的情况 套餐金额暂时不变 else: startInfo["price"] = RMB(0) # type:RMB data["startInfo"] = startInfo # 根据参数组装订单 try: with UnifiedCardConsumeOrderManager(**data) as manager: consumeRcd = manager.buildOrder() # type: ConsumeRecord except UnifiedConsumeOrderError as ue: logger.error("[pay_card_start_by_32] order create order error = {}, order = {}".format(ue, order)) self.device.deviceAdapter.stop_charging_port(order["port"]) return # 预支付的情况下支付 if card.is_id_card: # 预支付的情况下 进行扣款操作 if not consumeRcd.isPostPaid: try: payment = generate_card_payment(consumeRcd) if payment: consumeRcd.update_payment(payment) consumeRcd.frozen_payer_balance() if not consumeRcd.isPaid: raise ValueError('') except Exception as e: self.device.deviceAdapter.stop_charging_port(order["port"]) logger.exception(e) result = { "deviceStartTime": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(order["create_time"])) } ConsumeOrderStateEngine(consumeRcd).to_running(result) start_time_stamp = order.get('create_time') start_time = datetime.datetime.fromtimestamp(start_time_stamp) ctrInfo = Device.get_dev_control_cache(self.device.devNo) lineInfo = ctrInfo.get(str(order['port']), {}) if not lineInfo or lineInfo.get('status') == Const.DEV_WORK_STATUS_IDLE: lineInfo = { 'port': str(order['port']), 'status': Const.DEV_WORK_STATUS_WORKING, 'order_type': 'card_start', 'startTime': start_time.strftime('%Y-%m-%d %H:%M:%S'), 'orderNo': consumeRcd.orderNo, } Device.update_port_control_cache(self.device.devNo, lineInfo) # 预付费流程 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* def finish_prepaid_process(self, order): # 中间留着写业务 self._prepaid_analyze_order_info(order) self._prepaid_calc_refund_coins(order) # 扫码的退费处理 if order['order_type'] == 'apps_start': self.prepaid_end_for_app_start(order) # 刷卡的退费处理 elif order['order_type'] == 'card_start' and order['card_no']: self.prepaid_end_for_card_start(order) self._prepaid_analyze_notify_info(order) def _prepaid_analyze_order_info(self, order): try: if order['order_type'] == 'apps_start': pass elif order['order_type'] == 'card_start': pass except: logger.info(traceback.format_exc()) def _prepaid_calc_refund_coins(self, order): auto_refund = order['policy'].get('auto_refund', False) self.backCoins = RMB(0) money = VirtualCoin(self.consumeRcd.money) minFee = VirtualCoin(self.consumeRcd.package.get('minFee') or 0) # 免费地址: if self.consumeRcd.is_free: pass else: if self._auto_parser_card_type(order.get('card_type')) == 'IC': logger.info( 'IC card is not refundable orderNo=<{}> duration=<{}>'.format(self.consumeRcd.orderNo, self.duration)) return if self.duration < self.refundProtectionTime: self.backCoins = money else: usedFee = min(VirtualCoin(order['money'] * 0.01), money) # 做一个使用费用的保护 防止使用金额溢出 usedFee = max(usedFee, minFee) # 和最小消费做比较 取大的 if auto_refund: self.backCoins = money - usedFee logger.info( 'REFUND STATUS orderNo=<{}>, auto_refund={}, refundProtectionTime=<{}>, duration=<{}>, backCoins=<{}> minFee=<{}>'.format( self.consumeRcd.orderNo, auto_refund, self.refundProtectionTime, self.duration, self.backCoins, minFee )) def _prepaid_analyze_notify_info(self, order): # 消费部分 if order['order_type'] == 'apps_start': if self.consumeDict['policyType'] == 'time': self.extra.extend([ {u'订购套餐': self.consumeDict['needTime']}, ]) elif self.consumeDict['policyType'] == 'elec': self.extra.extend([ {u'订购套餐': self.consumeDict['needElec']}, {u'消耗电量': self.consumeDict['elec']}, ]) else: pass elif order['order_type'] == 'card_start': self.extra.append(self._notify_card_title(order)) else: pass # 金额部分 if self.consumeRcd.is_free: self.extra.append({u'付费金额': u'免费使用!'}) else: if self.backCoins > RMB(0): self.extra.append({u'使用明细': u'支付{}元,退费{}元'.format(self.consumeRcd.money, self.backCoins)}) else: self.extra.append({u'使用明细': u'支付{}元'.format(self.consumeRcd.money, self.backCoins)}) if order['order_type'] == 'card_start': self.extra.append({u'卡片余额': u'{}元'.format(self.card.balance)}) def prepaid_end_for_app_start(self, order): # 如果需要退款,计算退款数据. if self.backCoins > RMB(0): openId = self.consumeRcd.user.openId lineInfo = {'openId': openId} refundRMB = RMB(0) is_temp_package = self.consumeRcd.is_temp_package if is_temp_package and self.consumeRcd.recharge_record_id: rechargeRcd = RechargeRecord.objects.filter(id=self.consumeRcd.recharge_record_id, openId=openId, devNo=self.device.devNo, isQuickPay=True, groupId=self.device.groupId).first() refundRMB = rechargeRcd.money * (self.backCoins.amount / self.consumeRcd.coin.amount) lineInfo.update({'rechargeRcdId': self.consumeRcd.recharge_record_id}) self.refund_net_pay(self.consumeRcd.user, lineInfo, refundRMB, self.backCoins, self.consumeDict, is_temp_package) def prepaid_end_for_card_start(self, order): if not self.card: # 离线卡没有绑定或者在线卡被解绑了 return # 不需要退款,直接返回.在线卡需要退费,离线卡只登记使用记录就OK if self.backCoins > RMB(0) and self.card.cardType == 'ID': self.refund_money_for_card(self.backCoins, self.card.id) self.consumeDict.update({ DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: self.backCoins.mongo_amount }) # 最后维护一次 订单上显示的卡余额 self.card.reload() self.consumeDict.update({ 'cardBalance': self.card.balance.mongo_amount }) # 后付费流程*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* def finishe_postpaid_process(self, order): self._postpaid_analyze_order_info(order) self._postpaid_calc_used_fee(order) # 扫码扣除余额 if order['order_type'] == 'apps_start': self.postpay_end_for_app_start(order) # 刷卡的扣除余额 elif order['order_type'] == 'card_start' and order['card_no']: self.postpay_end_for_card_start(order) self._postpaid_analyze_notify_info(order) def _postpaid_analyze_order_info(self, order): pass def _postpaid_calc_used_fee(self, order): self.usedFee = VirtualCoin(order['money'] * 0.01) # 最小金额处理 minFee = VirtualCoin(self.consumeRcd.package.get('minFee') or 0) # 免费地址: if self.consumeRcd.is_free: self.usedFee = VirtualCoin(0) else: # 使用金额不能超过本单的卡余额 if self.duration < self.refundProtectionTime: self.usedFee = VirtualCoin(0) else: if order['order_type'] == 'apps_start': self.usedFee = max(self.usedFee, minFee) elif order['order_type'] == 'card_start': pass logger.info( 'REFUND STATUS orderNo=<{}>, isFree={} usedFee={}, refundProtectionTime=<{}>, duration=<{}> minFee=<{}>'.format( self.consumeRcd.orderNo, self.consumeRcd.is_free, self.usedFee, self.refundProtectionTime, self.duration, minFee )) def _postpaid_analyze_notify_info(self, order): # 消费部分 if order['order_type'] == 'apps_start': if self.consumeDict['policyType'] == 'time': self.extra.extend([ {u'订购套餐': u'{}'.format(self.consumeDict['needTime'])}, ]) elif self.consumeDict['policyType'] == 'elec': self.extra.extend([ {u'订购套餐': u'{}度'.format(self.consumeDict['needElec'])}, {u'消耗电量': u'{}度'.format(self.consumeDict['elec'])}, ]) # 金额部分 if self.consumeRcd.is_free: self.extra.append({u'扣费金额': u'免费使用!'}) else: if self.consumeRcd.status == ConsumeRecord.Status.FINISHED: if self.consumeRcd.paymentInfo.get('via') == 'virtualCard': desc = u'(已使用优惠卡券抵扣本次消费)' else: desc = u'(已使用账户余额自动结算本次消费)' if self.usedFee > VirtualCoin(0) else '' else: desc = u'(您的账户余额已不足以抵扣本次消费,请前往账单中心进行支付)' self.consumeDict['reason'] += desc self.extra.append({u'消费金额': u'{}元'.format(self.usedFee)}) self.extra.append( {u'用户余额': u'{}元'.format( self.consumeRcd.user.calc_currency_balance(self.device.owner, self.device.group))}) if order['order_type'] == 'card_start': self.extra.append(self._notify_card_title(order)) if self.consumeDict['policyType'] == 'time': pass elif self.consumeDict['policyType'] == 'elec': self.extra.extend([ {u'消耗电量': '{}度'.format(self.consumeDict['elec'])} ]) else: pass self.extra.append({u'扣费金额': u'{}元'.format(self.usedFee)}) self.extra.append({u'卡片余额': u'{}元'.format(self.card.balance)}) def postpay_end_for_app_start(self, order): def pay_order(): try: self.consumeRcd.status = 'running' self.consumeRcd.attachParas['packageId'] = self.consumeRcd.package.get('packageId') self.consumeRcd.save() self.consumeRcd.s_to_e() except: pass # 1 更新订单金额 self.consumeRcd.coin = self.usedFee self.consumeRcd.money = self.usedFee self.consumeRcd.servicedInfo = self.consumeDict self.consumeRcd.save() # 2 去结算 if self.consumeRcd.is_free: pass else: pay_order() self.consumeRcd.reload() # 如果结算成功 if self.consumeRcd.status == ConsumeRecord.Status.FINISHED: # 如果结算使用的是虚拟卡 if self.consumeRcd.paymentInfo.get('via') == 'virtualCard': # 虚拟卡抵扣 更新订单金额为0 self.consumeRcd.update(coin=VirtualCoin(0), money=RMB(0)) try: if "rcdId" in self.consumeRcd.paymentInfo: consumeRcdId = self.consumeRcd.paymentInfo['rcdId'] else: consumeRcdId = self.consumeRcd.paymentInfo['itemId'] vCardConsumeRcd = VCardConsumeRecord.objects.get(id=consumeRcdId) vCard = UserVirtualCard.objects.get(id=vCardConsumeRcd.cardId) vCard.refund_quota(vCardConsumeRcd, self.duration, 0.0, VirtualCoin(0).mongo_amount) except: pass else: pass def postpay_end_for_card_start(self, order): if not self.card: # 离线卡没有绑定或者在线卡被解绑了 return if self.card.cardType == 'ID': self.card.update(dec__balance=self.usedFee) self.card.reload() # 维护 订单上显示的卡余额 self.consumeDict.update({ 'cardBalance': self.card.balance.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: self.usedFee.mongo_amount, }) # 维护一次订单 self.consumeRcd.update(coin=self.usedFee.mongo_amount, money=self.usedFee.mongo_amount) CardConsumeRecord.objects.filter(orderNo=self.consumeRcd.orderNo).update( money=self.usedFee.mongo_amount, balance=self.card.balance.mongo_amount, finishedTime=self.consumeRcd.finishedTime, servicedInfo=self.consumeDict ) else: # 异常情况 不处理 pass def pay_coin_start_by_32(self, order): policy = order.get('policy', {}) coin = int(policy['money'] * 0.01) if coin > 0: Accounting.syncOfflineCoin( self.device, datetime.datetime.fromtimestamp(order['create_time']).strftime('%Y-%m-%d'), coin) FluentedEngine().in_put_coins_udp(devNo=self.device.devNo, ts=int(time.time()), coins=coin, mode='uart') logger.info('devNo=<{}> This coin=<{}> consumption has been recorded!'.format(self.device.devNo, coin)) class WeiFuLeCarProcess(object): desc_map = { 0: '订购已用完', 1: '管理员停止', 2: '设备故障', 3: '充电枪拔除', 4: '紧急停止按钮', 5: '计量故障', 6: '功率过载', 7: '电压过载', 8: '电流过载', 9: '超过单次充电最大时长', 10: '设备启动失败', 11: '继电器故障', 12: '刷卡停止', 13: '用户远程停止', 14: '经销商远程停止', 15: '温度超限' } CREATED = 'created' RUNNING = 'running' WAITING = 'wait' FINISHED = 'finished' END = 'end' TIMEOUT = 'timeout' FAILURE = 'failure' def __init__(self, smartbox): self.devNo = smartbox.device.devNo self.fun_code = smartbox.event_data.get('fun_code') self.order_info = smartbox.event_data.get('order') self.dev = smartbox.device # type: DeviceDict self.adapter = smartbox.deviceAdapter self.smartbox = smartbox def _exec_order_33(self): start_time = self.order_info.get('exec_time') start_time = datetime.datetime.fromtimestamp(start_time) order_id = self.order_info.get('id') consumeOrder = ConsumeRecord.objects.filter(orderNo=order_id, status__ne=ConsumeRecord.Status.FINISHED).first() if not consumeOrder: logger.info('no this order') return consumeOrder.update(startTime=start_time) def apps_start(self): """ 'order_type' : 'apps_start', 'money' : 1.2388653318201, 'id' : '86743505437857520201225084915001', 'left_money' : 98.76113466818, 'last_clock' : 607.31, 'create_time' : 1608857355, 'time' : 475.525, 'amount' : 100, 'elec' : 12388.653318201, 'exec_time' : 1608857355, 'status' : 'running', 'last_ecnt' : 16777128 """ dev_stat = self.order_info.get('status') order = ConsumeRecord.objects.filter(orderNo=self.order_info['id']).first() if not order: logger.info('no this order') return if dev_stat == 'running': self._apps_start_do_record_running(dev_stat, order) elif dev_stat == 'waiting': self._apps_start_do_record_waiting(dev_stat, order) elif dev_stat == 'finished': self._apps_start_do_record_finished(dev_stat, order) def _apps_start_do_record_running(self, dev_stat, order): rechargeRcdId = order.rechargeRcdId or '' if order.status == self.CREATED: # 让adapter处理 if 'adapter' in self.order_info: # 服务器过来直接处理 order.update(status=self.RUNNING) else: if (time.time() - self.order_info.get('create_time', 0)) > 300: ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True) elif order.status == self.RUNNING: # 设备上报 start_time = self.order_info.get('exec_time') start_time = datetime.datetime.fromtimestamp(start_time) order.update( status=self.RUNNING, startTime=start_time ) elif order.status == self.WAITING: # 33 上来的wait单变running start_time = self.order_info.get('exec_time') start_time = datetime.datetime.fromtimestamp(start_time) order.update( status=self.RUNNING, startTime=start_time, ) elif order.status in [self.TIMEOUT, self.FAILURE]: # 此时已经退费,标记为后支付,不做处理 start_time = self.order_info.get('exec_time') or self.order_info.get('create_time') port = self.order_info.get('port') start_time = datetime.datetime.fromtimestamp(start_time) order.update( status=self.RUNNING, coin=VirtualCoin(0), money=RMB(0), startTime=start_time, attachParas__payAfterUse=True, rechargeRcdId='', attachParas__last_refund_rechargeRcdId=rechargeRcdId, isNormal=True, errorDesc='设备启动已退费, 此单标记为后支付'.format(dev_stat), ) consumeOrder = { 'orderNo': order.orderNo, 'coin': 0, 'consumeType': 'postpaid', } self.adapter.register_service_progress(order.openId, order.orderNo, port, consumeOrder, order.attachParas) title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.dev.group.address)}, {u'设备编号': u'{}'.format(self.dev['logicalCode'])}, ]) self.smartbox.notify_user( order.user.managerialOpenId, 'dev_start', **{ 'title': title, 'things': u'您的汽车充电服务已启动', 'remark': u'感谢您的支持!', 'time': start_time.strftime(Const.DATETIME_FMT) } ) elif order.status in [self.FINISHED, self.END]: pass else: logger.info('error order status devStat=<{}>, order=<{}>'.format(dev_stat, order.orderNo)) def _apps_start_do_record_waiting(self, dev_stat, order): # 兼容系统 全改为running单 rechargeRcdId = order.rechargeRcdId or '' if order.status == self.CREATED: # 启动过程服务器中断停留在 if (time.time() - self.order_info.get('create_time', 0)) > 300: ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True) elif order.status == self.RUNNING: # 服务器成功启动置为running order.update( status=self.RUNNING ) elif order.status == self.WAITING: # 重复 pass elif order.status in [self.TIMEOUT, self.FAILURE]: # 此时已经退费,标记为后支付,不做处理 order.update( status=self.RUNNING, coin=VirtualCoin(0), money=RMB(0), attachParas__payAfterUse=True, rechargeRcdId='', attachParas__last_refund_rechargeRcdId=rechargeRcdId, isNormal=True, errorDesc='设备启动已退费, 此单标记为后支付'.format(dev_stat), ) consumeOrder = { 'orderNo': order.orderNo, 'coin': 0, 'consumeType': 'postpaid', } self.adapter.register_service_progress(order.openId, order.orderNo, consumeOrder, order.attachParas) elif order.status in [self.FINISHED, self.END]: pass def _apps_start_do_record_finished(self, dev_stat, order): if order.status == self.CREATED: # 启动过程中服务器出错 ConsumeRecord.objects.filter(id=order.id, status=self.CREATED).update(status=self.RUNNING, isNormal=True) order.reload() self.pre_pay(order) elif order.status in [self.RUNNING, self.WAITING]: # 正常状态的 或者修正状态的单 payAfterUse = order.attachParas.get('payAfterUse') if payAfterUse: self.after_pay(order) else: self.pre_pay(order) elif order.status == self.TIMEOUT: # 设备启动后,100事件先上(该状态为running),210回复改为timeout,后付费处理 order.update( status=self.RUNNING, coin=VirtualCoin(0), money=RMB(0), attachParas__payAfterUse=True, rechargeRcdId='', attachParas__last_refund_rechargeRcdId=order.rechargeRcdId, isNormal=True, errorDesc='设备启动已退费, 此单标记为后支付'.format(dev_stat), ) order.reload() self.after_pay(order) elif order.status == self.FAILURE: # 状态位置不处理 pass elif order.status in [self.FINISHED, self.END]: logger.info('do apps finished --> no order found or repeated submit orderNo:{}'.format(order.orderNo)) return logger.info('apps start event orderNo:{} is over'.format(order)) def pre_pay(self, order): openId = order.openId # leftMoney = RMB.fen_to_yuan(self.order_info.get('left_money', 0)) # 传过来的这个值有问题 amount = RMB.fen_to_yuan(self.order_info.get('amount', 0)) usedMoney = RMB.fen_to_yuan(self.order_info.get('money')) leftMoney = amount - usedMoney used_time = round(self.order_info.get('time') / 60.0, 2) used_elec = self.order_info.get('elec') / 10.0 ** 6 reason = self.desc_map.get(self.order_info.get('cause')) execTimeStamp = self.order_info.get('exec_time') finishedTimeStamp = self.order_info.get('over_time') port = self.order_info.get('port') finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp) if not execTimeStamp: startTime = finishedTime else: startTime = datetime.datetime.fromtimestamp(execTimeStamp) status = self.order_info.get('status') consumeDict = { DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time, DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec, DEALER_CONSUMPTION_AGG_KIND.COIN: amount.mongo_amount, 'reason': reason } order.status = status order.startTime = startTime order.finishedTime = finishedTime order.save() user = order.user group = Group.get_group(self.dev['groupId']) extra = [ # {u'订单号': '{}'.format(order.orderNo)}, {u'本次使用时长': '{}(分钟)'.format(used_time)}, ] self.smartbox.notify_user_service_complete( service_name='充电', openid=user.managerialOpenId if user else '', port=port, address=group['address'], reason=reason, finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra ) if order.attachParas.get('is_auto_refund', False): recharge_record = None if order.rechargeRcdId: recharge_record = RechargeRecord.objects( id=order.rechargeRcdId, isQuickPay=True).first() # type: RechargeRecord if recharge_record: refundMoneyRecord = RefundMoneyRecord.objects.filter(openId=openId, rechargeObjId=recharge_record.id).first() if not refundMoneyRecord: self._do_refund_money(recharge_record, order, leftMoney, consumeDict) else: self._do_refund_coin(order, leftMoney, consumeDict) order.update_service_info(consumeDict) ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update( isFinished=True, finished_time=int(time.time()) ) self.adapter.async_update_portinfo_from_dev() def after_pay(self, order): """ 只针对设备下发之后,没有回复的故障单为后付费 :return: """ used_money = RMB.fen_to_yuan(self.order_info.get('money', 0)) amount = RMB.fen_to_yuan(self.order_info.get('amount', 0)) used_time = round(self.order_info.get('time') / 60.0, 2) used_elec = self.order_info.get('elec') / 10.0 ** 6 reason = self.desc_map.get(self.order_info.get('cause')) execTimeStamp = self.order_info.get('exec_time') finishedTimeStamp = self.order_info.get('over_time') port = self.order_info.get('port') finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp) if not execTimeStamp: startTime = finishedTime else: startTime = datetime.datetime.fromtimestamp(execTimeStamp) consumeDict = { DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time, DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec, 'reason': reason, } order.startTime = startTime order.finishedTime = finishedTime coins = VirtualCoin(order.package.get('coins', 0)) price = RMB(order.package.get('price')) ratio = Ratio(float(used_money) / float(amount)) order.coin = coins * ratio order.money = price * ratio if order.money == RMB(0): order.coin = VirtualCoin(0) order.save() extra = [ # {u'订单号': '{}'.format(order.orderNo)}, {u'本次使用时长': '{}(分钟)'.format(used_time)}, ] if order.money > RMB(0): self._pay_by_coins(order) else: order.update(status=self.FINISHED) order.reload() if order.status == self.FINISHED: extra.append({u'本单消费金额': '{}金币(已使用账户余额自动结算本次消费)'.format(order.coin.mongo_amount)}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.COIN: order.coin.mongo_amount}) else: extra.append({u'本单消费金额': '{}元((您的账户余额不足以抵扣本次消费,请前往账单中心进行支付))'.format(order.money.mongo_amount)}) consumeDict.update({DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: order.money.mongo_amount}) self.smartbox.notify_user_service_complete( service_name='充电结束', openid=order.user.managerialOpenId, port=str(port), address=self.dev.group['address'], reason=reason, finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra ) order.update_service_info(consumeDict) ServiceProgress.objects.filter(device_imei=self.devNo, weifuleOrderNo=order.orderNo).update( isFinished=True, finished_time=int(time.time()) ) def _pay_by_coins(self, order): try: order.s_to_e() except Exception: return False def _do_refund_money(self, recharge_record, consumeOrder, leftMoney, consumeDict): logger.info('now do refund money!') coins = consumeOrder.coin openId = consumeOrder.openId price = recharge_record.money user = consumeOrder.user refundMoney = RMB(price) * Ratio(float(leftMoney) / float(coins)) if refundMoney > RMB(price): refundMoney = RMB(price) if refundMoney > RMB(0): self.smartbox.refund_net_pay(user, {'rechargeRcdId': str(recharge_record.id), 'openId': user.openId}, refundMoney, VirtualCoin(0), consumeDict, True) title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.dev.group.address)}, {u'设备编号': u'{}'.format(self.dev['logicalCode'])}, ]) self.smartbox.notify_user(self.smartbox.get_managerialOpenId_by_openId(openId), 'refund_coins', **{ 'title': title, 'backCount': u'金额:%s' % refundMoney, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) logger.info('refund cash is successful refundMoney:{}'.format(refundMoney)) def _do_refund_coin(self, consumeOrder, leftMoney, consumeDict): logger.info('now do refund coin !') coins = consumeOrder.coin openId = consumeOrder.openId user = consumeOrder.user backCoins = VirtualCoin(coins) * Ratio(float(leftMoney) / float(coins)) if backCoins > VirtualCoin(coins): backCoins = VirtualCoin(coins) if backCoins > RMB(0): self.smartbox.refund_net_pay(user, {'openId': openId}, RMB(0), backCoins, consumeDict, False) title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.dev.group.address)}, {u'设备编号': u'{}'.format(self.dev['logicalCode'])}, ]) self.smartbox.notify_user(self.smartbox.get_managerialOpenId_by_openId(openId), 'refund_coins', **{ 'title': title, 'backCount': u'金币:%s' % backCoins, 'finishTime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}) # ---------------------刷卡处理------------------------- def card_start(self): status = self.order_info.get('status') if self.fun_code == 32: self._card_start_do_record_running_32() elif self.fun_code == 34: self._card_start_do_record_finished_34() elif self.fun_code == 33: self._exec_order_33() def card_refund(self): """ 离线卡:上报退费 做一次卡余额同步 """ status = self.order_info.get('status') if self.fun_code == 32: if status == 'finished': self._card_refund_do_record_finished() else: pass elif self.fun_code == 34: if status == 'finished': pass # self._card_refund_do_record_finished() else: pass def _card_start_do_record_running_32(self): # used_money = RMB.fen_to_yuan(self.order_info.get('money', 0)) # left_money = RMB.fen_to_yuan(self.order_info.get('left_money')) amount = RMB.fen_to_yuan(self.order_info.get('amount', 0)) card_balance = RMB.fen_to_yuan(self.order_info.get('balance')) start_time_stamp = self.order_info.get('create_time') start_time = datetime.datetime.fromtimestamp(start_time_stamp) # used_time = self.order_info.get('time') / 60 # used_elec = self.order_info.get('elec') / 10 ** 6 # status = self.order_info.get('status') order_id = self.order_info.get('id') card_no = self.order_info.get('card_no') port = self.order_info.get('port') card = Card.objects.filter(cardNo=card_no, cardType='IC', dealerId=self.dev.ownerId).first() if not card: logger.info('no this card cardNo:{}'.format(card_no)) return # 做一次订单号去重 order = ConsumeRecord.objects.filter(orderNo=order_id).first() if order: logger.info('order already exists cardNo:{}, order_id'.format(card_no, order_id)) return servicedInfo = { 'cardNo': card_no, 'port': port, 'cardBalance': card_balance.mongo_amount } attachParas = { 'orderNo': order_id, 'chargeIndex': str(port) } # 记录卡消费记录以及消费记录 orderNo, cardOrderNo = self.smartbox.record_consume_for_card(card, amount, servicedInfo=servicedInfo, attachParas=attachParas) consumeOrder = { 'orderNo': orderNo, 'cardOrderNo': cardOrderNo, 'coin': str(amount), 'consumeType': 'card', } self._register_card_service_progress(card, port, consumeOrder, order_id) title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.dev.group.address)}, {u'设备编号': u'{}'.format(self.dev['logicalCode'])}, {u'实体卡': u'{}--No:{}'.format(card.cardName or card.nickName, card.cardNo)}, {u'本次消费': u'{} 元'.format(amount)}, {u'卡余额': u'{} 元'.format(card_balance)}, ]) self.smartbox.notify_user( card.managerialOpenId, 'dev_start', **{ 'title': title, 'things': u'刷卡消费', 'remark': u'感谢您的支持!', 'time': start_time.strftime(Const.DATETIME_FMT) } ) def _card_start_do_record_finished_34(self): used_time = round(self.order_info.get('time') / 60.0, 2) finishedTimeStamp = self.order_info.get('over_time') finishedTime = datetime.datetime.fromtimestamp(finishedTimeStamp) execTimeStamp = self.order_info.get('exec_time') if not execTimeStamp: startTime = finishedTime else: startTime = datetime.datetime.fromtimestamp(execTimeStamp) reason = self.desc_map.get(self.order_info.get('cause')) card_no = self.order_info.get('card_no') used_elec = self.order_info.get('elec') / 10 ** 6 card_balance = RMB.fen_to_yuan(self.order_info.get('balance')) amount = RMB.fen_to_yuan(self.order_info.get('amount')) port = self.order_info.get('port') order_id = self.order_info.get('id') # left_money = RMB.fen_to_yuan(self.order_info.get('left_money')) #这个值有问题 used_money = RMB.fen_to_yuan(self.order_info.get('money')) left_money = amount - used_money consumeOrder = ConsumeRecord.objects.filter(devNo=self.devNo, orderNo=order_id, status__ne=ConsumeRecord.Status.FINISHED).first() if not consumeOrder: logger.info('do card finished --> no order found or repeated submit orderNo:{}'.format(order_id)) return openId = consumeOrder.openId consumeOrder.servicedInfo.update({ DEALER_CONSUMPTION_AGG_KIND.REFUND_CARD: left_money.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.CONSUME_CARD: amount.mongo_amount, DEALER_CONSUMPTION_AGG_KIND.SPEND_MONEY: (amount - left_money).mongo_amount, DEALER_CONSUMPTION_AGG_KIND.DURATION: '%d' % used_time, DEALER_CONSUMPTION_AGG_KIND.ELEC: '%.2f' % used_elec, 'cardBalance': card_balance.mongo_amount, 'reason': reason, 'cardNo': card_no }) consumeOrder.status = self.FINISHED consumeOrder.startTime = startTime consumeOrder.finishedTime = finishedTime consumeOrder.save() ServiceProgress.update_progress_and_consume_rcd( self.dev['ownerId'], { 'open_id': openId, 'device_imei': self.devNo, 'isFinished': False, 'weifuleOrderNo': order_id, }, consumeOrder.servicedInfo, progressDict={'isFinished': True, 'finished_time': finishedTimeStamp} ) user = consumeOrder.user group = Group.get_group(self.dev['groupId']) extra = [ # {u'订单号': '{}'.format(consumeOrder.orderNo)}, {u'本次使用时长': '{}(分钟)'.format(used_time)}, {u'卡片余额': '{}(元)'.format(card_balance)}, ] self.smartbox.notify_user_service_complete( service_name='刷卡充电', openid=user.managerialOpenId if user else '', port=str(port), address=group['address'], reason=reason, finished_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), extra=extra ) logger.info('card start event orderNo:{} is over'.format(order_id)) self.adapter.async_update_portinfo_from_dev() def _register_card_service_progress(self, card, port, consumeOrder, weifuleOrderNo=None, attachParas=None): return ServiceProgress.objects.create( open_id=card.openId, device_imei=self.devNo, devTypeCode=self.dev.devTypeCode, port=port, cardId=str(card.id), attachParas=attachParas if attachParas else {}, start_time=int(time.time()), finished_time=int(time.time() + 24 * 60 * 60), consumeOrder=consumeOrder if consumeOrder else {}, weifuleOrderNo=weifuleOrderNo, ).id def _card_refund_do_record_finished(self): card_no = self.order_info.get('card_no') card_balance = RMB.fen_to_yuan(self.order_info.get('balance')) refundMoney = RMB.fen_to_yuan(self.order_info.get('refund')) finishedTime = self.order_info.get('over_time') finishedTime = datetime.datetime.fromtimestamp(finishedTime).strftime('%Y-%m-%d %H:%M:%S') card = self.smartbox.update_card_dealer_and_type(card_no, cardType='IC', balance=card_balance) if card: logger.info('Card balance sync completed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format( card_no, refundMoney.mongo_amount, finishedTime)) title = make_title_from_dict([ {u'设备地址': u'{}'.format(self.dev.group.address)}, {u'设备编号': u'{}'.format(self.dev['logicalCode'])}, {u'实体卡': u'{}--No:{}'.format(card.cardName, card.cardNo)}, {u'卡余额': u'{} 元'.format(card_balance)}, ]) self.smartbox.notify_user( card.managerialOpenId, 'refund_coins', **{ 'title': title, 'backCount': u'%s 元' % refundMoney, 'finishTime': finishedTime } ) else: logger.info( 'Card balance sync failed, cardNo:{} , refundMoney:{} ,finishedTime:{}'.format( card_no, refundMoney.mongo_amount, finishedTime))