123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- # -*- 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.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
- 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, AliApp
- 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(<visitor-name>:<visitor-via(dealer|device|...)>:<hashed-id>, <id-to-retrieve-gateway|bridge>
- 目前自定义授权信息和收入流均为代理商所绑定。
- 在这里通过不同的对象分发生成相应的网关和桥。
- """
- 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)
- #############
- ### 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
|