helpers.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import logging
  4. from functools import wraps, partial
  5. import simplejson as json
  6. from django.http import JsonResponse
  7. from django.utils.module_loading import import_string
  8. from typing import Union, Tuple, TYPE_CHECKING, Optional, Mapping
  9. from apps import serviceCache
  10. from apps.web.constant import APP_TYPE, AppPlatformType
  11. from apps.web.core import ROLE
  12. from apps.web.core.auth.ali import AlipayAuthBridge
  13. from apps.web.core.auth.wechat import WechatAuthBridge
  14. from apps.web.core.bridge import WechatClientProxy
  15. from apps.web.core.datastructures import BaseVisitor
  16. from apps.web.utils import is_user, is_dealer, is_agent, is_anonymous
  17. from apps.web.core.models import WechatPayApp, AliApp, SaobeiPayApp, JDAggrePayApp, DlbPayApp, SystemSettings, \
  18. ManualPayApp, SwapPayApp
  19. logger = logging.getLogger(__name__)
  20. if TYPE_CHECKING:
  21. from apps.web.device.models import DeviceDict
  22. from apps.web.core.models import WechatManagerApp, WechatAuthApp, RuralCreditUnionApp
  23. from apps.web.ad.models import Advertiser, Advertisement
  24. from apps.web.user.models import MyUser
  25. from apps.web.common.models import WithdrawRecord
  26. from apps.web.dealer.models import SubAccount
  27. from django.contrib.auth.models import AnonymousUser
  28. from apps.web.dealer.models import Dealer
  29. from apps.web.agent.models import Agent
  30. from apps.web.device.models import Device
  31. from apps.web.management.models import Manager
  32. SourceT = Union[Agent, Dealer, Device, Manager, DeviceDict, WithdrawRecord]
  33. from apps.web.core.payment import WechatMiniPaymentGatewayT
  34. WechatAPP_T = Union[WechatPayApp, WechatManagerApp, WechatAuthApp, JDAggrePayApp, SwapPayApp]
  35. class DeviceTypeVisitor(BaseVisitor):
  36. def entry(self, node):
  37. return node.__class__.__name__
  38. def visit_Agent(self, node):
  39. # type:(Agent)->list
  40. """
  41. 主要的逻辑在于,如果该代理商有以自己为准的设备类型则显示,否则则向上查。因为在某些情况下,同一厂商下的代理商可能会与主代理商的部分起冲突
  42. :param node:
  43. :return:
  44. """
  45. from apps.web.device.models import DeviceType
  46. query_set = DeviceType.objects(agentId = str(node.id)).order_by("-popularity")
  47. if query_set.count() > 0:
  48. return query_set
  49. else:
  50. return DeviceType.objects(agentId = str(node.primary_agent.id)).order_by("-popularity")
  51. def visit_Dealer(self, node):
  52. # type:(Dealer)->list
  53. Agent = import_string('apps.web.agent.models.Agent')
  54. agent = Agent.objects(id = node.agentId).get()
  55. return self.visit_Agent(agent)
  56. def visit_SubAccount(self, node):
  57. # type:(SubAccount)->list
  58. Agent = import_string('apps.web.agent.models.Agent')
  59. agent = Agent.objects(id = node.agentId).get()
  60. return self.visit_Agent(agent)
  61. def visit_Manager(self, node):
  62. # type:(Manager)->list
  63. Agent = import_string('apps.web.agent.models.Agent')
  64. agent = Agent.objects(id = str(node.primeAgentId)).get()
  65. return self.visit_Agent(agent)
  66. def visit_Advertiser(self, node):
  67. # type:(Advertiser)->list
  68. from apps.web.management.models import Manager
  69. manager = Manager.objects(id = str(node.managerId)).first()
  70. return self.visit_Manager(manager)
  71. def visit_Advertisement(self, node):
  72. # type:(Advertisement)->list
  73. from apps.web.management.models import Manager
  74. manager = Manager.objects(id = str(node.managerId)).first()
  75. return self.visit_Manager(manager)
  76. class AgentCustomizedBrandVisitor(BaseVisitor):
  77. """
  78. 该访问者主要是为了帮助不同的资源利用者获得
  79. TODO: 减少请求,可以做缓存
  80. keygen := e.g. redis.set(<visitor-name>:<visitor-via(dealer|device|...)>:<hashed-id>, <id-to-retrieve-gateway|bridge>
  81. 目前自定义授权信息和收入流均为代理商所绑定。
  82. 在这里通过不同的对象分发生成相应的网关和桥。
  83. """
  84. def __init__(self, factory_type, **kwargs):
  85. self.model_cls = import_string('apps.web.agent.models.Agent')
  86. self.factory = self.model_cls.factory(factory_type = factory_type, **kwargs)
  87. def entry(self, node):
  88. return node.__class__.__name__
  89. def visit_Agent(self, node):
  90. return self.factory(node)
  91. def visit_Dealer(self, node):
  92. agent = self.model_cls.objects(id = str(node.agentId)).get()
  93. return self.visit_Agent(agent)
  94. def visit_Device(self, node):
  95. Dealer = import_string('apps.web.dealer.models.Dealer')
  96. Agent = import_string('apps.web.agent.models.Agent')
  97. dealer = Dealer.objects(id = node.ownerId).get()
  98. agent = Agent.objects(id = str(dealer.agentId)).get()
  99. return self.visit_Agent(agent)
  100. def visit_Manager(self, node):
  101. Agent = import_string('apps.web.agent.models.Agent')
  102. agent = Agent.objects(id = str(node.primeAgentId)).get()
  103. return self.visit_Agent(agent)
  104. def visit_DeviceDict(self, node):
  105. # type:(DeviceDict)->None
  106. """默认视为deviceDict"""
  107. Dealer = import_string('apps.web.dealer.models.Dealer')
  108. Agent = import_string('apps.web.agent.models.Agent')
  109. dealer = Dealer.objects(id = str(node['ownerId'])).get()
  110. agent = Agent.objects(id = str(dealer.agentId)).get()
  111. return self.visit_Agent(agent)
  112. def visit_WithdrawRecord(self, node):
  113. # type:(WithdrawRecord)->None
  114. Dealer = import_string('apps.web.dealer.models.Dealer')
  115. Agent = import_string('apps.web.agent.models.Agent')
  116. if node.role == ROLE.dealer:
  117. dealer = Dealer.objects(id = node.ownerId).get()
  118. agent = Agent.objects(id = str(dealer.agentId)).get()
  119. elif node.role == ROLE.agent:
  120. agent = Agent.objects(id = str(node.ownerId)).get()
  121. else:
  122. assert False, 'invalid withdraw record role'
  123. return self.visit_Agent(agent)
  124. def visit_dict(self, node):
  125. return self.visit_DeviceDict(node)
  126. class DealerCustomizedBrandVisitor(BaseVisitor):
  127. def __init__(self, factory_type, **kwargs):
  128. self.model_cls = import_string('apps.web.dealer.models.Dealer')
  129. self.factory = self.model_cls.factory(factory_type = factory_type, **kwargs)
  130. def entry(self, node):
  131. return node.__class__.__name__
  132. def visit_Dealer(self, node):
  133. return self.factory(node)
  134. def is_same_wechat_app(leftApp, rightApp):
  135. # type:(WechatAPP_T, WechatAPP_T)->bool
  136. if leftApp['appid'] == rightApp['appid']:
  137. return True
  138. else:
  139. return False
  140. def get_app(source, app_type, vistor = AgentCustomizedBrandVisitor, **kwargs):
  141. # type:(SourceT, str, Optional[AgentCustomizedBrandVisitor, DealerCustomizedBrandVisitor], Mapping[str, str])->WechatAPP_T
  142. logger.debug('get wechat app. source = {}; app type = {}; vistor = {}; kwargs = {}'.format(
  143. repr(source), app_type, vistor.__name__, str(kwargs)))
  144. return vistor(factory_type = 'app', app_type = app_type, **kwargs).visit(source)
  145. def get_user_manager_agent(source):
  146. # type:(SourceT)->Agent
  147. app = get_app(source = source, app_type = APP_TYPE.WECHAT_USER_MANAGER)
  148. return getattr(app, 'occupant')
  149. def get_ali_auth_bridge(source, app_type):
  150. # type:(SourceT)->AlipayAuthBridge
  151. app = get_app(source, app_type)
  152. return AlipayAuthBridge(app)
  153. def get_wechat_auth_bridge(source, app_type):
  154. # type:(SourceT, str)->WechatAuthBridge
  155. app = get_app(source, app_type)
  156. return WechatAuthBridge(app = app)
  157. def get_wechat_user_messager_app(source):
  158. return get_app(source, APP_TYPE.WECHAT_USER_MESSAGER)
  159. def get_wechat_user_manager_mp_proxy(source):
  160. app = get_app(source, APP_TYPE.WECHAT_USER_MANAGER)
  161. return WechatClientProxy(app)
  162. def get_wechat_user_sub_manager_mp_proxy(source):
  163. app = get_app(source, APP_TYPE.WECHAT_USER_SUBSCRIBE_MANAGER)
  164. return WechatClientProxy(app)
  165. def get_wechat_dealer_sub_manager_mp_proxy(source):
  166. app = get_app(source, APP_TYPE.WECHAT_DEALER_SUBSCRIBE_MANAGER)
  167. return WechatClientProxy(app)
  168. def get_wechat_manager_mp_proxy(source):
  169. app = get_app(source, APP_TYPE.WECHAT_MANAGER)
  170. return WechatClientProxy(app)
  171. def get_inhouse_wechat_manager_mp_proxy():
  172. Agent = import_string('apps.web.agent.models.Agent')
  173. app = Agent.get_platform_wechat_manager_app()
  174. return WechatClientProxy(app)
  175. def get_inhouse_wechat_user_manager_mp_proxy():
  176. Agent = import_string('apps.web.agent.models.Agent')
  177. app = Agent.get_inhouse_wechat_user_manager_app()
  178. return WechatClientProxy(app)
  179. def get_wechat_mini_env_pay_gateway(source, role = None, pay_app_type = None):
  180. # type:(SourceT, str, str)->WechatMiniPaymentGatewayT
  181. app = get_app(source = source,
  182. app_type = APP_TYPE.WECHAT_MINI_ENV_PAY,
  183. role = role,
  184. pay_app_type = pay_app_type) # type: WechatAPP_T
  185. return app.new_gateway(AppPlatformType.WECHAT_MINI)
  186. def get_wechat_env_pay_gateway(source, role = None, pay_app_type = None, vistor = AgentCustomizedBrandVisitor):
  187. app = get_app(source = source,
  188. app_type = APP_TYPE.WECHAT_ENV_PAY,
  189. role = role,
  190. vistor = vistor,
  191. pay_app_type = pay_app_type) # type: Optional[WechatPayApp, JDAggrePayApp, SaobeiPayApp, DlbPayApp, RuralCreditUnionApp]
  192. return app.new_gateway(AppPlatformType.WECHAT)
  193. def get_alipay_env_pay_gateway(source, role = None, pay_app_type = None, vistor = AgentCustomizedBrandVisitor):
  194. app = get_app(source = source,
  195. app_type = APP_TYPE.ALIPAY_ENV_PAY,
  196. role = role,
  197. vistor = vistor,
  198. pay_app_type = pay_app_type) # type: Optional[AliApp, JDAggrePayApp, SaobeiPayApp, DlbPayApp]
  199. return app.new_gateway(AppPlatformType.ALIPAY)
  200. def get_jd_env_pay_gateway(source, role = None, pay_app_type = None, vistor = AgentCustomizedBrandVisitor):
  201. app = get_app(source = source,
  202. app_type = APP_TYPE.JD_ENV_PAY,
  203. role = role,
  204. vistor = vistor,
  205. pay_app_type = pay_app_type) # type: Optional[JDAggrePayApp, DlbPayApp]
  206. return app.new_gateway(AppPlatformType.JD)
  207. def get_inhourse_wechat_env_pay_gateway(role = None, pay_app_type = None):
  208. """
  209. 使用平台商户进行支付. 例如SIM卡充值, API充值以及清静计划充值等
  210. 都在在微信环境在进行支付
  211. :param role:
  212. :param pay_app_type:
  213. :return:
  214. """
  215. Agent = import_string('apps.web.agent.models.Agent')
  216. inhouse_agent = Agent.get_inhouse_prime_agent() # type: Agent
  217. app = get_app(source = inhouse_agent,
  218. app_type = APP_TYPE.WECHAT_ENV_PAY,
  219. role = role,
  220. pay_app_type = pay_app_type) # type: Optional[WechatPayApp, JDAggrePayApp, SaobeiPayApp, DlbPayApp]
  221. return app.new_gateway(AppPlatformType.WECHAT)
  222. def get_platform_promotion_pay_gateway(pay_app_type):
  223. """
  224. 平台推广支付网关. 包括红包支付等, 提现广告记录在平台
  225. :param pay_app_type:
  226. :return:
  227. """
  228. from apps.web.core.models import PlatformPromotionApp
  229. return PlatformPromotionApp.get_app().new_gateway(pay_app_type)
  230. def get_platform_wallet_pay_gateway(pay_app_type):
  231. """
  232. 平台钱包支持. SIM卡自动充值, 代理商分成提现记录在平台
  233. :param pay_app_type:
  234. :return:
  235. """
  236. from apps.web.core.models import PlatformWalletApp
  237. return PlatformWalletApp.get_app().new_gateway(pay_app_type)
  238. def get_platform_reconcile_pay_gateway():
  239. """
  240. 平台对账支付网关. 用于账务错误给客户分账
  241. :return:
  242. """
  243. from apps.web.core.models import PlatformPromotionApp
  244. return PlatformPromotionApp.get_app().new_gateway(AppPlatformType.PLATFORM)
  245. def get_swap_env_pay_gateway(source):
  246. app = get_app(source = source,
  247. app_type = APP_TYPE.SWAP_ENV_PAY,
  248. vistor = DealerCustomizedBrandVisitor) # type: Optional[SwapPayApp]
  249. return app.new_gateway(AppPlatformType.SWAP)
  250. def get_manual_pay_gateway(dealer):
  251. my_app = ManualPayApp.get_null_app()
  252. my_app.occupantId = str(dealer.id)
  253. my_app.occupant = dealer
  254. return my_app.new_gateway(AppPlatformType.PLATFORM)
  255. #############
  256. ### Misc ####
  257. #############
  258. def get_wx_config(user, url):
  259. # type:(Union[Dealer, Agent, MyUser, AnonymousUser], str)->Tuple[int, dict]
  260. try:
  261. if not user or is_anonymous(user):
  262. wechat_mp_proxy = get_inhouse_wechat_manager_mp_proxy()
  263. elif is_user(user):
  264. agent_id = str(user.agentId)
  265. Agent = import_string('apps.web.agent.models.Agent')
  266. agent = Agent.objects(id = agent_id).get()
  267. wechat_mp_proxy = get_wechat_user_manager_mp_proxy(agent)
  268. elif is_dealer(user) or is_agent(user):
  269. wechat_mp_proxy = get_wechat_manager_mp_proxy(user)
  270. else:
  271. wechat_mp_proxy = None
  272. if wechat_mp_proxy:
  273. value = wechat_mp_proxy.generate_js_auth_signature(url = url)
  274. logger.info('get wx config success. wxconfig = %s; proxy = %s' % (json.dumps(value), repr(wechat_mp_proxy)))
  275. else:
  276. value = {}
  277. logger.info('get wx config failure. auth bridge is null. user = %s' % repr(user))
  278. except Exception as e:
  279. value = {}
  280. logger.exception('get wx config failure. error = %s; user = %s' % (e, repr(user)))
  281. return value
  282. def current_platform(gateway_key, current):
  283. if gateway_key == current:
  284. return u'新平台'
  285. else:
  286. return u'老平台'
  287. DEVICE_LOCK_KEY = '{item}-operation-lock'
  288. def device_lock_key(item):
  289. # type:(str)->str
  290. return DEVICE_LOCK_KEY.format(item = item)
  291. START_DEVICE_LOCK_KEY = '{openId}-start-device-lock'
  292. def start_device_lock_key(openId):
  293. return START_DEVICE_LOCK_KEY.format(openId=openId)
  294. class ServerSwitch(object):
  295. # 拦截状态
  296. payment_blocking_status = ['pass', 'blocking', 'alipay', 'wechat']
  297. payment_Type = []
  298. white_list = ['o-VzzwAfpdglJY38Kj7yMvVWlIgw']
  299. @staticmethod
  300. def payment_switch(responseText):
  301. def warpper(func):
  302. @wraps(func)
  303. def inner(request, *args, **kwargs):
  304. status = ServerSwitch.check_payment_blocking_status(request)
  305. if status == True:
  306. logger.info('Blocking function:{}.{}()'.format(inner.__module__, inner.__name__))
  307. return JsonResponse({'result': 0, 'description': responseText, 'payload': {}})
  308. return func(request, *args, **kwargs)
  309. return inner
  310. return warpper
  311. @classmethod
  312. def check_payment_blocking_status(cls, request):
  313. result = False
  314. # 白名单获取
  315. white_list = serviceCache.get('adminWhiteList')
  316. if not white_list:
  317. white_list_obj = SystemSettings.objects.filter(key = 'adminWhiteList').first()
  318. if not white_list_obj:
  319. white_list = []
  320. serviceCache.set('adminWhiteList', white_list)
  321. else:
  322. white_list = white_list_obj.value
  323. serviceCache.set('adminWhiteList', white_list)
  324. if not serviceCache.get('paymentSwitch'):
  325. sys_setting = SystemSettings.objects.filter(key = 'paymentSwitch').first()
  326. if not sys_setting:
  327. logger.info('Can not get server settings,please touch mongodb')
  328. else:
  329. # 白名单(openid)
  330. if str(request.user.openId) in white_list:
  331. logger.info('user in white list user={}'.format(str(request.user.openId)))
  332. result = False
  333. # 全部拦截
  334. elif sys_setting.value == 'blocking':
  335. serviceCache.set('paymentSwitch', sys_setting.value)
  336. result = True
  337. # 拦截微信
  338. elif sys_setting.value == 'wechat':
  339. user_agent = request.META.get('HTTP_USER_AGENT')
  340. if 'MicroMessenger' in user_agent:
  341. serviceCache.set('paymentSwitch', sys_setting.value)
  342. result = True
  343. logger.info('Blocking wechat user request')
  344. # 拦截支付宝
  345. elif sys_setting.value == 'alipay':
  346. user_agent = request.META.get('HTTP_USER_AGENT')
  347. if 'Alipay' in user_agent:
  348. serviceCache.set('paymentSwitch', sys_setting.value)
  349. result = True
  350. logger.info('Blocking alipay user request')
  351. else:
  352. # 白名单(openid)
  353. if str(request.user.openId) in white_list:
  354. logger.info('user in white list user={}'.format(str(request.user.openId)))
  355. result = False
  356. elif serviceCache.get('paymentSwitch') == 'blocking':
  357. result = True
  358. elif serviceCache.get('paymentSwitch') == 'wechat':
  359. user_agent = request.META.get('HTTP_USER_AGENT')
  360. if 'MicroMessenger' in user_agent:
  361. result = True
  362. logger.info('Blocking wechat user request')
  363. elif serviceCache.get('paymentSwitch') == 'alipay':
  364. user_agent = request.META.get('HTTP_USER_AGENT')
  365. if 'Alipay' in user_agent:
  366. result = True
  367. logger.info('Blocking alipay user request')
  368. return result
  369. # 设置支付拦截状态
  370. @classmethod
  371. def set_payment_blocking_switch(cls, status = None):
  372. """
  373. status='pass':不拦截
  374. status='blocking':全部拦截
  375. status='wechat':拦截微信
  376. status='alipay':拦截支付宝
  377. """
  378. payment_blocking_status = ['pass', 'blocking', 'alipay', 'wechat']
  379. sys_setting = SystemSettings.objects.filter(key = 'paymentSwitch').first()
  380. if not sys_setting:
  381. logger.info('Can not get server settings,please touch mongodb')
  382. else:
  383. try:
  384. if not status:
  385. sys_setting.value = 'pass'
  386. sys_setting.save()
  387. serviceCache.delete('paymentSwitch')
  388. serviceCache.set('paymentSwitch', 'pass')
  389. logger.info('Set Payment blocking switch is fail ,status : pass')
  390. elif status not in payment_blocking_status:
  391. logger.info('That is not an option , status : ["pass", "blocking", "alipay", "wechat"]')
  392. else:
  393. sys_setting.value = status
  394. sys_setting.save()
  395. serviceCache.delete('paymentSwitch')
  396. serviceCache.set('paymentSwitch', status)
  397. logger.info('Payment blocking is running ,status : {}'.format(status))
  398. except Exception:
  399. logger.error('Set payment blocking switch is fail, try edit in mongodb')
  400. # 添加删除修改白名单
  401. @classmethod
  402. def edit_payment_while_list(cls, editType = 'add', openIds = []):
  403. white_list_obj = SystemSettings.objects.filter(key = 'adminWhiteList').first() or SystemSettings(
  404. key = 'adminWhiteList', value = [])
  405. white_list = white_list_obj.value
  406. if editType == 'add':
  407. white_list = white_list + openIds
  408. white_list_obj.value = list(set(white_list))
  409. white_list_obj.save()
  410. elif editType == 'update':
  411. white_list = openIds
  412. white_list_obj.value = list(set(white_list))
  413. white_list_obj.save()
  414. elif editType == 'delete':
  415. white_list = set(white_list) - set(openIds)
  416. white_list_obj.value = list(white_list)
  417. white_list_obj.save()
  418. if editType == 'clear':
  419. white_list_obj.delete()
  420. serviceCache.delete('adminWhiteList')
  421. serviceCache.set('adminWhiteList', white_list)
  422. def detect_browser(self, request, browser_name):
  423. user_agent = request.META.get('HTTP_USER_AGENT', 'unknown')
  424. return browser_name in user_agent
  425. detect_alipay_client = partial(detect_browser, browser_name = 'Alipay')
  426. detect_wechat_client = partial(detect_browser, browser_name = 'MicroMessenger')
  427. detect_union_client = partial(detect_browser, browser_name = 'Union')
  428. detect_jdpay_client = partial(detect_browser, browser_name = 'jdapp')
  429. detect_jdjr_client = partial(detect_browser, browser_name = 'JDJR')
  430. def remove_some_desc_for_consume(oldDesc, needRemove):
  431. vList = oldDesc.split(' ')
  432. try:
  433. sIndex = vList.index(needRemove)
  434. except Exception, e:
  435. return oldDesc
  436. vList[sIndex], vList[sIndex + 1], vList[sIndex + 2] = '', '', ''
  437. return ' '.join(vList)