# -*- coding: utf-8 -*- # !/usr/bin/env python import datetime import logging from mongoengine import DoesNotExist from typing import TYPE_CHECKING from apilib.monetary import RMB from apilib.utils_sys import MemcachedLock from apps.web.constant import START_DEVICE_STATUS, USER_RECHARGE_TYPE, Const from apps.web.core.exceptions import ServiceException from apps.web.core.payment import WithdrawGateway from apps.web.core.services import StartDeviceEngine from apps.web.exceptions import DuplicatedOperationError from apps.web.helpers import get_user_manager_agent from apps.web.report.ledger import Ledger from apps.web.user.models import MyUser, ConsumeRecord, CardRechargeOrder, Card, CardRechargeRecord, RechargeRecord from apps.web.user.utils import get_consume_order from apps.web.user.utils2 import ConsumeOrderStateEngine, generate_net_payment from apps.web.utils import set_start_key_status logger = logging.getLogger(__name__) if TYPE_CHECKING: from apps.web.device.models import GroupDict def is_after_ledger(recharge_record): # type: (RechargeRecord)->bool if not WithdrawGateway.is_ledger(recharge_record.withdraw_source_key): return False after_ledger = False if recharge_record.device.devTypeCode in ( Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY, Const.DEVICE_TYPE_CODE_CHANGING_CY4, Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY_V2, Const.DEVICE_TYPE_CODE_CHANGING_CY_POWER, Const.DEVICE_TYPE_CODE_DUIBIJI, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE_ONLINECARD, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_SIX): after_ledger = True return after_ledger def is_refund_cash(recharge_record, device): _refund_cash = False if recharge_record.is_temp_package: _refund_cash = True elif device.devTypeCode in [ Const.DEVICE_TYPE_CODE_CHANGING_WEIFULE, Const.DEVICE_TYPE_CODE_CHANGING_WEIFULE2, Const.DEVICE_TYPE_CODE_WEIFULE_MINI, Const.DEVICE_TYPE_CODE_WEIFULE_TOUCH_PAD, Const.DEVICE_TYPE_CODE_WEIFULE_ANJIAN, Const.DEVICE_TYPE_CODE_CHANGING_SOCKET, Const.DEVICE_TYPE_CODE_WEIFULE_WASHER ]: _refund_cash = False elif ('refundRMB_device_event' in recharge_record.owner.features) or \ device.devTypeCode in ( Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY, Const.DEVICE_TYPE_CODE_CHANGING_CY4, Const.DEVICE_TYPE_CODE_CAR_CHARGING_CY_V2, Const.DEVICE_TYPE_CODE_CHANGING_CY_POWER, Const.DEVICE_TYPE_CODE_DUIBIJI, Const.DEVICE_TYPE_CODE_CAR_CHARGING_JN, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_FIVE_ONLINECARD, Const.DEVICE_TYPE_CODE_CHARGING_CHANGYUAN_SIX ): _refund_cash = True return _refund_cash def recharge(user, recharge_record): # type: (MyUser, RechargeRecord)->None """ 优惠充值流程 :param user: :param recharge_record: :return: """ # 经销商分账 ledger = Ledger(USER_RECHARGE_TYPE.RECHARGE, recharge_record) ledger.execute(stats=True) # 用户充值 user.recharge(money=RMB(recharge_record.money), bestowMoney=RMB(recharge_record.coins - recharge_record.money)) user.account_recharge(recharge_record) def recharge_start_device(user, recharge_record): # type:(MyUser, RechargeRecord) -> None """ 充值启动设备 充值并启动设备 实际上还是先充值到用户的余额 然后利用用户的余额直接启动设备 """ # 首先还是先给用户充值 recharge(user, recharge_record) orderNo = recharge_record.attachParas.get("orderNo") if not orderNo: logger.error('[recharge_start_device] recharge order <{}> has no start orderNo'.format(repr(recharge_record))) return order = get_consume_order(orderNo) # type: ConsumeRecord if not order: logger.error("[recharge_start_device] recharge order <{}> {} has not find order".format(repr(recharge_record), orderNo)) # 鉴别不支持启动设备的订单 直接返回 locker = MemcachedLock(key=order.startLockKey, value='1', expire=180) if not locker.acquire(): logger.error( '[_start_device ERROR] cannot get device lock<{}>, openId={}, devNo={}, port={}'.format(order.startLockKey, order.openId, order.devNo, order.port) ) raise DuplicatedOperationError(u'订单运行中 请刷新界面查看订单详情') try: proxy = StartDeviceEngine(order) # type: StartDeviceEngine # 检查订单的支付情况 更多的是余额检验 检验完成之后 根据订单的支付选择 决定是否生成支付信息并添加 paymentInfo = proxy.get_payment_info() paymentInfo and order.update_payment(paymentInfo) except ServiceException as se: ConsumeOrderStateEngine(order).to_failure(se.result["description"]) set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.TIMEOUT) return # 这个地方只负责将设备的启动传递到新的线程 对于设备启动的结果实际上是未知的 release_locker = True try: proxy.start() except ServiceException as e: logger.info("[_start_device ERROR] service exception = {}".format(e.result)) set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.FAILURE) except Exception as e: logger.exception('[_start_device ERROR](order={}) error={}'.format(order, e)) set_start_key_status(start_key=order.orderNo, state=START_DEVICE_STATUS.FAILURE) finally: logger.debug('release_locker = {}'.format(release_locker)) locker.release() def recharge_for_order(user, recharge_record): # type: (MyUser, RechargeRecord)->None recharge(user, recharge_record) try: order = get_consume_order(recharge_record.attachParas.get("orderNo")) if not order: logger.error("[recharge_for_order] no find the order which paid for!!!!!!!") return # 执行一次订单的扣款 payment = generate_net_payment(order) order.update_payment(payment) order.frozen_payer_balance() order.clear_payer_frozen() ConsumeOrderStateEngine(order).to_finished() except Exception as e: logger.exception(e) def recharge_for_card(user, recharge_record): # 经销商分账 ledger = Ledger(USER_RECHARGE_TYPE.RECHARGE, recharge_record) ledger.execute(stats=True) # 查找卡 cardId, cardNo = recharge_record.attachParas["cardId"], recharge_record.attachParas["cardNo"] # 创建充值记录 cardOrder = CardRechargeOrder.new_one( openId=recharge_record.openId, cardId=cardId, cardNo=cardNo, money=recharge_record.money, coins=recharge_record.coins, group=recharge_record.group, rechargeId=recharge_record.id ) if cardOrder is None: logger.error("[recharge_for_card] create card order error, recharge order = {}".format(recharge_record)) return try: card = Card.objects.get(id=cardId) except DoesNotExist: logger.warning("[recharge_for_card] not find card, card ={}, recharge order = {}".format(cardId, recharge_record)) return # ID卡的 直接充值到卡里面 if not card.is_id_card: return status = Card.get_card_status(str(card.id)) if status == "busy": logger.warning("[recharge_for_card] card recharge, card ={}, recharge order = {}".format(cardId, recharge_record)) return Card.set_card_status(str(card.id), 'busy') try: cardOrder.update_after_recharge_id_card( device=recharge_record.device, balance=card.balance+cardOrder.coins, preBalance=card.balance ) CardRechargeRecord.add_record( card=card, group=recharge_record.group, order=cardOrder ) # 卡充值 card.recharge(cardOrder.chargeAmount, cardOrder.bestowAmount) # 统计卡的余额变化 card.account_recharge(recharge_record) except Exception as e: logger.exception("[recharge_for_card] recharge id card error, recharge order = {}, error = {}".format(recharge_record, e)) finally: Card.set_card_status(str(card.id), "idle") def post_pay(record): # type:(RechargeRecord)->None try: logger.info('post pay for record({})'.format(repr(record))) record.reload() if not record.owner: logger.warning('{} check failure. owner is not exist.'.format(repr(record), record.ownerId)) return group = record.group # type: GroupDict if not group: logger.error('group(%s) is not exist' % record.groupId) return if group.ownerId != record.ownerId: logger.warning('{} check failure. ownerId of record<{}> != ownerId of group<{}>.'.format( repr(record), record.ownerId, record.group.ownerId)) return if not record.openId: logger.error('openid is null. is not valid record.') return user = MyUser.objects(openId=record.openId, groupId=record.groupId).first() if not user: agentId = record.owner.agentId product_agent = get_user_manager_agent(record.owner) user = MyUser.get_or_create( app_platform_type=record.gateway, open_id=record.openId, group_id=record.groupId, **{ 'agentId': agentId, 'productAgentId': str(product_agent.id) }) diff_ts = (datetime.datetime.now() - record.dateTimeAdded).total_seconds() logger.debug( '{} in ({}) via={} attachParas={} record={} diff={}'.format( repr(user), record.gateway, record.via, record.attachParas, repr(record), diff_ts)) if record.via == USER_RECHARGE_TYPE.RECHARGE: return recharge(user, record) elif record.via == USER_RECHARGE_TYPE.START_DEVICE: return recharge_start_device(user, record) elif record.via == USER_RECHARGE_TYPE.RECHARGE_CASH: return recharge_for_order(user, record) elif record.via == USER_RECHARGE_TYPE.RECHARGE_CARD: return recharge_for_card(user, record) else: logger.error('invalid pay type. recharge record id = %s' % str(record.id)) except Exception as e: logger.exception(e)