# -*- coding: utf-8 -*- # !/usr/bin/env python import contextlib import inspect import logging import uuid from copy import deepcopy from functools import wraps, partial import simplejson as json from django.conf import settings from django.http import JsonResponse from django.utils.module_loading import import_string from mongoengine.base import BaseField from typing import Union, Tuple, TYPE_CHECKING, Optional, Mapping from apps import serviceCache from apps.web.constant import APP_TYPE, AppPlatformType from apps.web.core import ROLE from apps.web.core.auth.ali import AlipayAuthBridge from apps.web.core.auth.wechat import WechatAuthBridge from apps.web.core.bridge.wechat import WechatClientProxy from apps.web.core.datastructures import BaseVisitor from apps.web.core.models import SystemSettings, ManualPayApp, AliApp from apps.web.core.payment.ali import AliPayISVGateway from apps.web.models import MongoTempTable from apps.web.utils import is_user, is_dealer, is_agent, is_anonymous logger = logging.getLogger(__name__) if TYPE_CHECKING: from apps.web.device.models import DeviceDict from apps.web.core.models import WechatManagerApp, WechatAuthApp from apps.web.ad.models import Advertiser, Advertisement from apps.web.user.models import MyUser from apps.web.common.models import WithdrawRecord from apps.web.dealer.models import SubAccount from django.contrib.auth.models import AnonymousUser from apps.web.dealer.models import Dealer from apps.web.agent.models import Agent from apps.web.device.models import Device from apps.web.management.models import Manager SourceT = Union[Agent, Dealer, Device, Manager, DeviceDict, WithdrawRecord] from apps.web.core.models import WechatPayApp WechatAPP_T = Union[WechatPayApp, WechatManagerApp, WechatAuthApp] PayApp_T = Union[WechatPayApp, AliApp] AuthApp_T = Union[WechatManagerApp, WechatAuthApp] class DeviceTypeVisitor(BaseVisitor): def entry(self, node): return node.__class__.__name__ def visit_Agent(self, node): # type:(Agent)->list """ 主要的逻辑在于,如果该代理商有以自己为准的设备类型则显示,否则则向上查。因为在某些情况下,同一厂商下的代理商可能会与主代理商的部分起冲突 :param node: :return: """ from apps.web.device.models import DeviceType query_set = DeviceType.objects(agentId = str(node.id)).order_by("-popularity") if query_set.count() > 0: return query_set else: return DeviceType.objects(agentId = str(node.primary_agent.id)).order_by("-popularity") def visit_Dealer(self, node): # type:(Dealer)->list Agent = import_string('apps.web.agent.models.Agent') agent = Agent.objects(id = node.agentId).get() return self.visit_Agent(agent) def visit_SubAccount(self, node): # type:(SubAccount)->list Agent = import_string('apps.web.agent.models.Agent') agent = Agent.objects(id = node.agentId).get() return self.visit_Agent(agent) def visit_Manager(self, node): # type:(Manager)->list Agent = import_string('apps.web.agent.models.Agent') agent = Agent.objects(id = str(node.primeAgentId)).get() return self.visit_Agent(agent) def visit_Advertiser(self, node): # type:(Advertiser)->list from apps.web.management.models import Manager manager = Manager.objects(id = str(node.managerId)).first() return self.visit_Manager(manager) def visit_Advertisement(self, node): # type:(Advertisement)->list from apps.web.management.models import Manager manager = Manager.objects(id = str(node.managerId)).first() return self.visit_Manager(manager) class AgentCustomizedBrandVisitor(BaseVisitor): """ 该访问者主要是为了帮助不同的资源利用者获得 TODO: 减少请求,可以做缓存 keygen := e.g. redis.set(::, 目前自定义授权信息和收入流均为代理商所绑定。 在这里通过不同的对象分发生成相应的网关和桥。 """ def __init__(self, factory_type, **kwargs): self.model_cls = import_string('apps.web.agent.models.Agent') self.factory = self.model_cls.factory(factory_type = factory_type, **kwargs) def entry(self, node): return node.__class__.__name__ def visit_Agent(self, node): return self.factory(node) def visit_Dealer(self, node): agent = self.model_cls.objects(id = str(node.agentId)).get() return self.visit_Agent(agent) def visit_Device(self, node): Dealer = import_string('apps.web.dealer.models.Dealer') Agent = import_string('apps.web.agent.models.Agent') dealer = Dealer.objects(id = node.ownerId).get() agent = Agent.objects(id = str(dealer.agentId)).get() return self.visit_Agent(agent) def visit_Manager(self, node): Agent = import_string('apps.web.agent.models.Agent') agent = Agent.objects(id = str(node.primeAgentId)).get() return self.visit_Agent(agent) def visit_DeviceDict(self, node): # type:(DeviceDict)->None """默认视为deviceDict""" Dealer = import_string('apps.web.dealer.models.Dealer') Agent = import_string('apps.web.agent.models.Agent') dealer = Dealer.objects(id = str(node['ownerId'])).get() agent = Agent.objects(id = str(dealer.agentId)).get() return self.visit_Agent(agent) def visit_WithdrawRecord(self, node): # type:(WithdrawRecord)->None Dealer = import_string('apps.web.dealer.models.Dealer') Agent = import_string('apps.web.agent.models.Agent') if node.role == ROLE.dealer: dealer = Dealer.objects(id = node.ownerId).get() agent = Agent.objects(id = str(dealer.agentId)).get() elif node.role == ROLE.agent: agent = Agent.objects(id = str(node.ownerId)).get() else: assert False, 'invalid withdraw record role' return self.visit_Agent(agent) def visit_dict(self, node): return self.visit_DeviceDict(node) class DealerCustomizedBrandVisitor(BaseVisitor): def __init__(self, factory_type, **kwargs): self.model_cls = import_string('apps.web.dealer.models.Dealer') self.factory = self.model_cls.factory(factory_type = factory_type, **kwargs) def entry(self, node): return node.__class__.__name__ def visit_Dealer(self, node): return self.factory(node) def is_same_wechat_app(leftApp, rightApp): # type:(WechatAPP_T, WechatAPP_T)->bool if leftApp['appid'] == rightApp['appid']: return True else: return False def get_app(source, app_type, vistor = AgentCustomizedBrandVisitor, **kwargs): # type:(SourceT, str, Optional[AgentCustomizedBrandVisitor, DealerCustomizedBrandVisitor], Mapping[str, str])->WechatAPP_T logger.debug('get wechat app. source = {}; app type = {}; vistor = {}; kwargs = {}'.format( repr(source), app_type, vistor.__name__, str(kwargs))) return vistor(factory_type = 'app', app_type = app_type, **kwargs).visit(source) def get_user_manager_agent(source): # type:(SourceT)->Agent app = get_app(source = source, app_type = APP_TYPE.WECHAT_USER_MANAGER) return getattr(app, 'occupant') def get_ali_auth_bridge(source, app_type): # type:(SourceT)->AlipayAuthBridge app = get_app(source, app_type) return AlipayAuthBridge(app) def get_wechat_auth_bridge(source, app_type): # type:(SourceT, str)->WechatAuthBridge app = get_app(source, app_type) return WechatAuthBridge(app = app) def get_wechat_user_messager_app(source): return get_app(source, APP_TYPE.WECHAT_USER_MESSAGER) def get_wechat_user_manager_mp_proxy(source): app = get_app(source, APP_TYPE.WECHAT_USER_MANAGER) return WechatClientProxy(app) def get_wechat_user_sub_manager_mp_proxy(source): app = get_app(source, APP_TYPE.WECHAT_USER_SUBSCRIBE_MANAGER) return WechatClientProxy(app) def get_wechat_dealer_sub_manager_mp_proxy(source): app = get_app(source, APP_TYPE.WECHAT_DEALER_SUBSCRIBE_MANAGER) return WechatClientProxy(app) def get_wechat_manager_mp_proxy(source): app = get_app(source, APP_TYPE.WECHAT_MANAGER) return WechatClientProxy(app) def get_inhouse_wechat_manager_mp_proxy(): Agent = import_string('apps.web.agent.models.Agent') app = Agent.get_platform_wechat_manager_app() return WechatClientProxy(app) def get_inhouse_wechat_user_manager_mp_proxy(): Agent = import_string('apps.web.agent.models.Agent') app = Agent.get_inhouse_wechat_user_manager_app() return WechatClientProxy(app) def get_wechat_mini_env_pay_gateway(source, role = None, pay_app_type = None): # type:(SourceT, str, str)->WechatMiniPaymentGatewayT app = get_app(source = source, app_type = APP_TYPE.WECHAT_MINI_ENV_PAY, role = role, pay_app_type = pay_app_type) # type: WechatAPP_T return app.new_gateway(AppPlatformType.WECHAT_MINI) def get_wechat_env_pay_gateway(source, role = None, pay_app_type = None, vistor = AgentCustomizedBrandVisitor): app = get_app(source = source, app_type = APP_TYPE.WECHAT_ENV_PAY, role = role, vistor = vistor, pay_app_type = pay_app_type) # type: Optional[WechatPayApp] return app.new_gateway(AppPlatformType.WECHAT) def get_alipay_env_pay_gateway(source, role = None, pay_app_type = None, vistor = AgentCustomizedBrandVisitor): app = get_app(source = source, app_type = APP_TYPE.ALIPAY_ENV_PAY, role = role, vistor = vistor, pay_app_type = pay_app_type) # type: Optional[AliApp] return app.new_gateway(AppPlatformType.ALIPAY) def get_inhourse_wechat_env_pay_gateway(role = None, pay_app_type = None): """ 使用平台商户进行支付. 例如SIM卡充值, API充值以及清静计划充值等 都在在微信环境在进行支付 :param role: :param pay_app_type: :return: """ Agent = import_string('apps.web.agent.models.Agent') inhouse_agent = Agent.get_inhouse_prime_agent() # type: Agent app = get_app(source = inhouse_agent, app_type = APP_TYPE.WECHAT_ENV_PAY, role = role, pay_app_type = pay_app_type) # type: Optional[WechatPayApp] return app.new_gateway(AppPlatformType.WECHAT) def get_platform_promotion_pay_gateway(pay_app_type): """ 平台推广支付网关. 包括红包支付等, 提现广告记录在平台 :param pay_app_type: :return: """ from apps.web.core.models import PlatformPromotionApp return PlatformPromotionApp.get_app().new_gateway(pay_app_type) def get_platform_wallet_pay_gateway(pay_app_type): """ 平台钱包支持. SIM卡自动充值, 代理商分成提现记录在平台 :param pay_app_type: :return: """ from apps.web.core.models import PlatformWalletApp return PlatformWalletApp.get_app().new_gateway(pay_app_type) def get_platform_reconcile_pay_gateway(): """ 平台对账支付网关. 用于账务错误给客户分账 :return: """ from apps.web.core.models import PlatformPromotionApp return PlatformPromotionApp.get_app().new_gateway(AppPlatformType.PLATFORM) def get_manual_pay_gateway(dealer): my_app = ManualPayApp.get_null_app() my_app.occupantId = str(dealer.id) my_app.occupant = dealer return my_app.new_gateway(AppPlatformType.PLATFORM) class PlatformPayGatewayUtil(object): @classmethod def get_ali_isv_pay_gateway(cls): app = AliApp.objects(appid = settings.ALI_ISV_APP_ID).first() return AliPayISVGateway(app) ############# ### Misc #### ############# def get_wx_config(user, url): # type:(Union[Dealer, Agent, MyUser, AnonymousUser], str)->Tuple[int, dict] try: if not user or is_anonymous(user): wechat_mp_proxy = get_inhouse_wechat_manager_mp_proxy() elif is_user(user): agent_id = str(user.agentId) Agent = import_string('apps.web.agent.models.Agent') agent = Agent.objects(id = agent_id).get() wechat_mp_proxy = get_wechat_user_manager_mp_proxy(agent) elif is_dealer(user) or is_agent(user): wechat_mp_proxy = get_wechat_manager_mp_proxy(user) else: wechat_mp_proxy = None if wechat_mp_proxy: value = wechat_mp_proxy.generate_js_auth_signature(url = url) logger.info('get wx config success. wxconfig = %s; proxy = %s' % (json.dumps(value), repr(wechat_mp_proxy))) else: value = {} logger.info('get wx config failure. auth bridge is null. user = %s' % repr(user)) except Exception as e: value = {} logger.exception('get wx config failure. error = %s; user = %s' % (e, repr(user))) return value def current_platform(gateway_key, current): if gateway_key == current: return u'新平台' else: return u'老平台' DEVICE_LOCK_KEY = '{item}-operation-lock' def device_lock_key(item): # type:(str)->str return DEVICE_LOCK_KEY.format(item = item) START_DEVICE_LOCK_KEY = '{openId}-start-device-lock' def start_device_lock_key(openId): return START_DEVICE_LOCK_KEY.format(openId=openId) class ServerSwitch(object): # 拦截状态 payment_blocking_status = ['pass', 'blocking', 'alipay', 'wechat'] payment_Type = [] white_list = ['o-VzzwAfpdglJY38Kj7yMvVWlIgw'] @staticmethod def payment_switch(responseText): def warpper(func): @wraps(func) def inner(request, *args, **kwargs): status = ServerSwitch.check_payment_blocking_status(request) if status == True: logger.info('Blocking function:{}.{}()'.format(inner.__module__, inner.__name__)) return JsonResponse({'result': 0, 'description': responseText, 'payload': {}}) return func(request, *args, **kwargs) return inner return warpper @classmethod def check_payment_blocking_status(cls, request): result = False # 白名单获取 white_list = serviceCache.get('adminWhiteList') if not white_list: white_list_obj = SystemSettings.objects.filter(key = 'adminWhiteList').first() if not white_list_obj: white_list = [] serviceCache.set('adminWhiteList', white_list) else: white_list = white_list_obj.value serviceCache.set('adminWhiteList', white_list) if not serviceCache.get('paymentSwitch'): sys_setting = SystemSettings.objects.filter(key = 'paymentSwitch').first() if not sys_setting: logger.info('Can not get server settings,please touch mongodb') else: # 白名单(openid) if str(request.user.openId) in white_list: logger.info('user in white list user={}'.format(str(request.user.openId))) result = False # 全部拦截 elif sys_setting.value == 'blocking': serviceCache.set('paymentSwitch', sys_setting.value) result = True # 拦截微信 elif sys_setting.value == 'wechat': user_agent = request.META.get('HTTP_USER_AGENT') if 'MicroMessenger' in user_agent: serviceCache.set('paymentSwitch', sys_setting.value) result = True logger.info('Blocking wechat user request') # 拦截支付宝 elif sys_setting.value == 'alipay': user_agent = request.META.get('HTTP_USER_AGENT') if 'Alipay' in user_agent: serviceCache.set('paymentSwitch', sys_setting.value) result = True logger.info('Blocking alipay user request') else: # 白名单(openid) if str(request.user.openId) in white_list: logger.info('user in white list user={}'.format(str(request.user.openId))) result = False elif serviceCache.get('paymentSwitch') == 'blocking': result = True elif serviceCache.get('paymentSwitch') == 'wechat': user_agent = request.META.get('HTTP_USER_AGENT') if 'MicroMessenger' in user_agent: result = True logger.info('Blocking wechat user request') elif serviceCache.get('paymentSwitch') == 'alipay': user_agent = request.META.get('HTTP_USER_AGENT') if 'Alipay' in user_agent: result = True logger.info('Blocking alipay user request') return result # 设置支付拦截状态 @classmethod def set_payment_blocking_switch(cls, status = None): """ status='pass':不拦截 status='blocking':全部拦截 status='wechat':拦截微信 status='alipay':拦截支付宝 """ payment_blocking_status = ['pass', 'blocking', 'alipay', 'wechat'] sys_setting = SystemSettings.objects.filter(key = 'paymentSwitch').first() if not sys_setting: logger.info('Can not get server settings,please touch mongodb') else: try: if not status: sys_setting.value = 'pass' sys_setting.save() serviceCache.delete('paymentSwitch') serviceCache.set('paymentSwitch', 'pass') logger.info('Set Payment blocking switch is fail ,status : pass') elif status not in payment_blocking_status: logger.info('That is not an option , status : ["pass", "blocking", "alipay", "wechat"]') else: sys_setting.value = status sys_setting.save() serviceCache.delete('paymentSwitch') serviceCache.set('paymentSwitch', status) logger.info('Payment blocking is running ,status : {}'.format(status)) except Exception: logger.error('Set payment blocking switch is fail, try edit in mongodb') # 添加删除修改白名单 @classmethod def edit_payment_while_list(cls, editType = 'add', openIds = []): white_list_obj = SystemSettings.objects.filter(key = 'adminWhiteList').first() or SystemSettings( key = 'adminWhiteList', value = []) white_list = white_list_obj.value if editType == 'add': white_list = white_list + openIds white_list_obj.value = list(set(white_list)) white_list_obj.save() elif editType == 'update': white_list = openIds white_list_obj.value = list(set(white_list)) white_list_obj.save() elif editType == 'delete': white_list = set(white_list) - set(openIds) white_list_obj.value = list(white_list) white_list_obj.save() if editType == 'clear': white_list_obj.delete() serviceCache.delete('adminWhiteList') serviceCache.set('adminWhiteList', white_list) def detect_browser(self, request, browser_name): user_agent = request.META.get('HTTP_USER_AGENT', 'unknown') return browser_name in user_agent detect_alipay_client = partial(detect_browser, browser_name = 'Alipay') detect_wechat_client = partial(detect_browser, browser_name = 'MicroMessenger') detect_union_client = partial(detect_browser, browser_name = 'Union') detect_jdpay_client = partial(detect_browser, browser_name = 'jdapp') detect_jdjr_client = partial(detect_browser, browser_name = 'JDJR') def remove_some_desc_for_consume(oldDesc, needRemove): vList = oldDesc.split(' ') try: sIndex = vList.index(needRemove) except Exception, e: return oldDesc vList[sIndex], vList[sIndex + 1], vList[sIndex + 2] = '', '', '' return ' '.join(vList) def copy_temp_document_classes(clazz, new_model_name): exclude = ['_fields', '_db_field_map', '_reverse_db_field_map', '_fields_ordered', '_is_document', 'MultipleObjectsReturned', '_superclasses', '_subclasses', '_types', '_class_name', '_meta', '__doc__', '__module__', '_collection', '_is_base_cls', '_auto_id_field', 'id', 'DoesNotExist', 'objects', '_cached_reference_fields'] dicts = {'meta': deepcopy(clazz._origin_meta)} dicts['meta'].pop('indexes', None) dicts['meta']['index_background'] = True dicts['meta']['auto_create_index'] = False dicts['__module__'] = clazz.__module__ new_cls = type(new_model_name, clazz.__bases__, dicts) for name, field in clazz.__dict__.iteritems(): if name in exclude: continue else: field = getattr(clazz, name) if isinstance(field, BaseField): setattr(new_cls, name, field) else: if inspect.ismethod(field): if not field.im_self: setattr(new_cls, name, field.im_func) else: setattr(new_cls, name, classmethod(field.im_func)) elif inspect.isfunction(field): setattr(new_cls, name, staticmethod(field)) else: setattr(new_cls, name, field) return new_cls @contextlib.contextmanager def mongo_temp_table(): tempModel = copy_temp_document_classes( MongoTempTable, '{}_{}'.format(MongoTempTable.__name__, str(uuid.uuid1()))) yield tempModel try: tempModel.get_collection().drop() except Exception: pass