views.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import datetime
  4. import hashlib
  5. import logging
  6. import re
  7. import ssl
  8. import subprocess
  9. import time
  10. import urllib2
  11. import simplejson as json
  12. import xmltodict
  13. from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
  14. from django.views.decorators.gzip import gzip_page
  15. from django.views.generic.base import View
  16. from pymongo.results import UpdateResult
  17. from typing import TYPE_CHECKING, Iterable, Dict
  18. from apilib.monetary import RMB
  19. from apilib.utils import generate_RSA_key_pairs
  20. from apilib.utils_datetime import to_datetime
  21. from apilib.utils_string import cn
  22. from apps import serviceCache
  23. from apps.web import district
  24. from apps.web.ad.models import Advertisement, AdStatistics
  25. from apps.web.agent.models import MoniApp
  26. from apps.web.common.models import AddressType, WithdrawRecord, Banks
  27. from apps.web.common.models import FrontendLog, FAQ
  28. from apps.web.common.transaction.pay.alipay import AliPayWithdrawNotifier
  29. from apps.web.common.utils import WechatMessage, WechatText, WechatMenu, WechatSubscribe, WechatUnSubscribe, \
  30. WechatScanEvent, MessageHandler
  31. from apps.web.constant import Const, AppPlatformType, AdSpace, USER_RECHARGE_TYPE
  32. from apps.web.core import ROLE
  33. from apps.web.core.exceptions import InvalidFileSize, InvalidFileName
  34. from apps.web.core.file import AliOssFileUploader, WechatSubscriptionAccountVerifyFileUploader
  35. from apps.web.core.models import WechatPayApp, AliApp, BankCard
  36. from apps.web.core.payment.ali import AliPayGateway
  37. from apps.web.core.utils import DefaultJsonErrorResponse, JsonOkResponse, JsonErrorResponse
  38. from apps.web.dealer.models import Dealer
  39. from apps.web.device.models import Device
  40. from apps.web.device.timescale import PowerManager
  41. from apps.web.user.models import MoniUser, AskRobot
  42. from apps.web.user.models import MyUser
  43. from apps.web.user.utils import get_consume_order
  44. from apps.web.utils import error_tolerate, permission_required, concat_dealer_main_page_url, \
  45. concat_front_end_url, concat_user_center_url, concat_user_login_entry_url
  46. if TYPE_CHECKING:
  47. from apps.web.core.db import RoleBaseDocument
  48. from django.core.handlers.wsgi import WSGIRequest
  49. from apps.web.device.models import DeviceDict
  50. from apps.web.core.adapter.bolai_gateway import ChargingGatewayBox
  51. logger = logging.getLogger(__name__)
  52. def loadDistrictData(request):
  53. if not request.user.is_authenticated():
  54. return HttpResponse('Unauthorized', status = 401)
  55. return JsonResponse({'result': 1, 'description': None, 'payload': district.DISTRICT})
  56. @error_tolerate(nil = lambda: JsonResponse({'result': 0, 'description': u'系统错误'}), logger = logger)
  57. def logFrontend(request):
  58. """
  59. 收集来自前端的日志
  60. :param request:
  61. :return:
  62. """
  63. if not request.body:
  64. return JsonResponse({'result': 1, 'description': u'BODY为空'})
  65. payload = json.loads(request.body)
  66. if 'onlyLog' in payload and payload['onlyLog']:
  67. logger.debug('--[BLUETOOTH]onlyLog--' + str(payload) + '--eof onlyLog--')
  68. else:
  69. FrontendLog(**payload).save()
  70. return JsonResponse({'result': 1, 'description': u'记录成功'})
  71. def getFAQ(request):
  72. """
  73. 使用的常见说明
  74. :param request:
  75. :return:
  76. """
  77. current_user = request.user # type: RoleBaseDocument
  78. faqs = FAQ.get_by_role(current_user.role)
  79. return JsonResponse({
  80. "result": 1,
  81. "description": None,
  82. "payload": {'dataList': [faq.to_dict() for faq in faqs], 'total': len(faqs)}
  83. })
  84. def getNotifications(request):
  85. """
  86. 做一个临时的用户通知 如果可以使用京东引流 就通知经销商
  87. :param request:
  88. :return:
  89. """
  90. return JsonResponse(
  91. {
  92. "result": 1,
  93. "description": None,
  94. "payload": {
  95. "total": 0,
  96. "dataList": []
  97. }
  98. }
  99. )
  100. # @trace_call()
  101. # def wx_biz_dispatch(request):
  102. # def response_xml(payload):
  103. # xmlData = dicttoxml.dicttoxml(payload, attr_type = False, custom_root = 'xml', cdata = True)
  104. # return HttpResponse(xmlData)
  105. #
  106. # logger.info('has receive wechat dispatch message')
  107. #
  108. # def check_signature(signature, timestamp, nonce, token):
  109. # args = [token, timestamp, nonce]
  110. # args.sort()
  111. # return hashlib.sha1(''.join(args).encode('utf-8')).hexdigest() == signature
  112. #
  113. # if request.method == 'GET':
  114. # signature = request.GET['signature']
  115. # timestamp = request.GET['timestamp']
  116. # nonce = request.GET['nonce']
  117. # echostr = request.GET['echostr']
  118. # if check_signature(signature, timestamp, nonce, settings.MY_WECHAT_USER_TOKEN):
  119. # return HttpResponse(str(echostr))
  120. # else:
  121. # return HttpResponse('')
  122. # else:
  123. # replyData = event_reply(request)
  124. # logger.info('calc replaydata = %s' % replyData)
  125. # if replyData:
  126. # return response_xml(replyData)
  127. #
  128. # return HttpResponse('success')
  129. @error_tolerate(nil = lambda: JsonResponse({'result': 0, 'description': u'文件操作有误'}), logger = logger)
  130. @permission_required(ROLE.dealer, ROLE.manager, ROLE.myuser, ROLE.advertiser, ROLE.supermanager, ROLE.subaccount)
  131. def uploadFile(request):
  132. """
  133. 公共的文件上传接口
  134. TODO 需要注意安全选项!!
  135. ..added 2018/03/21 添加参数判断安全路径
  136. :param request:
  137. :return:
  138. """
  139. UPLOADER_MAP = {
  140. 'wechatValidate': WechatSubscriptionAccountVerifyFileUploader,
  141. 'public': AliOssFileUploader
  142. }
  143. Uploader = UPLOADER_MAP.get(request.GET.get('category', 'public'))
  144. uploader = Uploader(inputFile = request.FILES.getlist('file')[0], uploadType = request.GET.get('type', 'others'))
  145. logger.info('[upload] %s is being used' % (repr(uploader),))
  146. try:
  147. outputUrl = uploader.upload()
  148. return JsonResponse({'result': 1, 'description': '', 'payload': outputUrl})
  149. except InvalidFileSize, e:
  150. logger.info(
  151. '%s(id=%s)\'s uploaded file reached limit' % (request.user.__class__.__name__, str(request.user.id),))
  152. return JsonResponse({'result': 0, 'description': e.message, 'payload': {}})
  153. except InvalidFileName, e:
  154. logger.info(
  155. '%s(id=%s)\'s uploaded file name is not legal' % (request.user.__class__.__name__, str(request.user.id),))
  156. return JsonResponse({'result': 0, 'description': e.message, 'payload': {}})
  157. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  158. @permission_required(ROLE.dealer, ROLE.agent, ROLE.subaccount)
  159. def getCardBankNameType(request):
  160. cardNo = request.GET.get('cardNo', '')
  161. if not cardNo:
  162. return JsonResponse({"result": 0, "description": u"银行卡号不能为空", 'payload': {}})
  163. try:
  164. bank = Banks.get_bank_info(cardNo)
  165. logger.debug(str(bank))
  166. if not bank:
  167. return JsonResponse({"result": -1, "description": "不支持该银行绑定,目前仅支持列表中银行", 'payload': {}})
  168. else:
  169. bank_name = bank.get('bankName', '')
  170. if not bank_name:
  171. return JsonResponse({"result": -1, "description": "不支持该银行绑定,目前仅支持列表中银行", 'payload': {}})
  172. else:
  173. return JsonResponse({
  174. "result": 1,
  175. "description": None,
  176. 'payload': {
  177. 'bankName': bank_name,
  178. 'cardType': bank['cardType']
  179. }
  180. })
  181. except Exception as e:
  182. return JsonResponse({"result": 0, "description": u"查询银行卡信息失败,请刷新", 'payload': {}})
  183. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  184. @permission_required(ROLE.manager, ROLE.advertisement, ROLE.advertiser, ROLE.dealer, ROLE.agent, ROLE.subaccount)
  185. def getAddressType(request):
  186. # type: (WSGIRequest)->JsonResponse
  187. """
  188. 获取地址类型
  189. :param request:
  190. :return:
  191. """
  192. addressTypes = AddressType.objects.all().order_by('-showWeight') # type: Iterable[AddressType]
  193. dataList = [{'value': addressType.value, 'label': addressType.label} for addressType in addressTypes]
  194. return JsonResponse(
  195. {
  196. 'result': 1,
  197. 'description': '',
  198. 'payload':
  199. {
  200. 'dataList': dataList,
  201. 'total': len(dataList)
  202. }
  203. }
  204. )
  205. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  206. def getVersion(request):
  207. version = subprocess.check_output(["git", "log", "-1", "--format=%cd"]).strip()
  208. return JsonOkResponse(payload = {'version': version})
  209. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  210. @permission_required(ROLE.supermanager, ROLE.manager)
  211. def generateNewAppKeyPair(request):
  212. # type: (WSGIRequest)->JsonResponse
  213. """
  214. 生成新的应用私钥公匙对, 返回给前台私钥的地址和公钥的内容 (方便前台去粘贴给支付宝)
  215. :param request:
  216. :return:
  217. """
  218. payload = json.loads(request.body)
  219. appid = payload.get('appid')
  220. if not appid:
  221. return JsonErrorResponse(description = u'appid未传入')
  222. key = 'alipay_app_private_{appid}_{timestamp}'.format(appid = appid, timestamp = int(time.time() * 1000))
  223. pair = generate_RSA_key_pairs()
  224. serviceCache.set(key, pair.private, 3600)
  225. appPublicKeyContent = re.sub(r'-.*', '', pair.public)
  226. return JsonOkResponse(description = u'生成成功', payload = {'path': key, 'appPublicKeyContent': appPublicKeyContent})
  227. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  228. @permission_required(ROLE.supermanager, ROLE.manager)
  229. def verifyAppKeyPairs(request):
  230. # type: (WSGIRequest)->JsonResponse
  231. """
  232. :param request:
  233. :return:
  234. """
  235. return JsonErrorResponse(description = u'暂不开放')
  236. @error_tolerate(nil = DefaultJsonErrorResponse)
  237. @permission_required(ROLE.supermanager)
  238. def getAliAppList(request):
  239. # type: (WSGIRequest)->JsonResponse
  240. """
  241. 获取阿里APP列表
  242. :param request:
  243. :return:
  244. """
  245. page_index = int(request.GET.get('pageIndex', 1))
  246. page_size = int(request.GET.get('pageSize', 10))
  247. search_key = request.GET.get('searchKey', None)
  248. total, items = AliApp.list(page_index = page_index, page_size = page_size, search_key = search_key)
  249. return JsonResponse({'result': 1,
  250. 'description': None,
  251. 'payload': {
  252. 'total': total,
  253. 'dataList': items
  254. }
  255. })
  256. @error_tolerate(nil = DefaultJsonErrorResponse)
  257. @permission_required(ROLE.supermanager)
  258. def editAliApp(request):
  259. # type: (WSGIRequest)->JsonResponse
  260. """
  261. 编辑阿里APP
  262. :param request:
  263. :return:
  264. """
  265. payload = json.loads(request.body) # type: dict
  266. id = payload.pop('id', None)
  267. update = payload
  268. if id:
  269. query = {'_id': id}
  270. else:
  271. query = {'appid': payload.get('appid')}
  272. result = AliApp.get_collection().update_one(query, update, upsert = True) # type: UpdateResult
  273. return JsonResponse({
  274. 'result': 1 if (result.modified_count == 1 or result.upserted_id) else 0,
  275. 'description': u'修改成功' if (result.modified_count == 1 or result.upserted_id) else u'修改失败',
  276. 'payload': {
  277. 'id': str(result.upserted_id) if result.upserted_id else id
  278. }})
  279. @error_tolerate(nil = DefaultJsonErrorResponse, logger = logger)
  280. @permission_required(ROLE.supermanager, ROLE.manager)
  281. def getWechatFundApp(request):
  282. # type: (WSGIRequest)->JsonResponse
  283. """
  284. 更改微信资金平台APP信息
  285. :param request:
  286. :return:
  287. """
  288. appid = request.GET.get('appid')
  289. mchid = request.GET.get('mchid')
  290. app = WechatPayApp.objects(appid = appid, mchid = mchid).first() # type: WechatPayApp
  291. if not app:
  292. return JsonOkResponse(payload = {})
  293. else:
  294. return JsonOkResponse(payload = app.to_dict())
  295. @error_tolerate(nil = DefaultJsonErrorResponse)
  296. @permission_required(ROLE.supermanager, ROLE.manager)
  297. def editWechatFundApp(request):
  298. # type: (WSGIRequest)->JsonResponse
  299. """
  300. 编辑阿里APP
  301. :param request:
  302. :return:
  303. """
  304. payload = json.loads(request.body) # type: dict
  305. id = payload.pop('id', None)
  306. update = payload
  307. if id:
  308. query = {'_id': id}
  309. else:
  310. query = {'appid': payload.get('appid')}
  311. result = WechatPayApp.get_collection().update_one(query, update, upsert = True) # type: UpdateResult
  312. return JsonResponse({
  313. 'result': 1 if (result.modified_count == 1 or result.upserted_id) else 0,
  314. 'description': u'修改成功' if (result.modified_count == 1 or result.upserted_id) else u'修改失败',
  315. 'payload': {
  316. 'id': str(result.upserted_id) if result.upserted_id else id
  317. }})
  318. @error_tolerate(nil = DefaultJsonErrorResponse)
  319. @permission_required(ROLE.supermanager, ROLE.manager)
  320. def getWechatAppList(request):
  321. # type: (WSGIRequest)->JsonResponse
  322. """
  323. 获取微信支付APP列表
  324. :param request:
  325. :return:
  326. """
  327. page_index = int(request.GET.get('pageIndex', 1))
  328. page_size = int(request.GET.get('pageSize', 10))
  329. search_key = request.GET.get('searchKey', None)
  330. total, items = WechatPayApp.list(page_index = page_index, page_size = page_size, search_key = search_key)
  331. return JsonResponse({'result': 1,
  332. 'description': None,
  333. 'payload': {
  334. 'total': total,
  335. 'dataList': items
  336. }
  337. })
  338. def event_reply(request):
  339. logger.info('has receive event_reply dispatch message = %s' % request.body)
  340. raw = xmltodict.parse(request.body)['xml']
  341. msgType = raw.get('MsgType', '')
  342. event = raw.get('Event', '')
  343. if msgType == 'event' and event in ['subscribe', 'unsubscribe']:
  344. moniOpenId = raw['FromUserName']
  345. rawAppId = raw['ToUserName']
  346. try:
  347. moniApp = MoniApp.objects.get(rawAppId = rawAppId)
  348. except Exception, e:
  349. return ''
  350. try:
  351. moniUser = MoniUser.objects.filter(moniAppId = moniApp.appId, moniOpenId = moniOpenId).first()
  352. if moniUser:
  353. if event == 'subscribe':
  354. moniUser.subTime = datetime.datetime.now()
  355. moniUser.isSubscribe = True
  356. AskRobot.reply(event, moniOpenId, '', moniApp.appId, moniApp.secret, openId = moniUser.openId)
  357. else:
  358. moniUser.unsubTime = datetime.datetime.now()
  359. moniUser.isSubscribe = False
  360. moniUser.save()
  361. else:
  362. user = MyUser.objects.filter(managerialOpenId = raw['FromUserName']).first()
  363. if user is None:
  364. newUser = MoniUser(moniAppId = moniApp.appId, moniOpenId = moniOpenId,
  365. subTime = datetime.datetime.now(), isSubscribe = True)
  366. newUser.save()
  367. else:
  368. AskRobot.reply(event, moniOpenId, '', moniApp.appId, moniApp.secret, openId = user.openId,
  369. agentId = user.agentId)
  370. except Exception, e:
  371. logger.exception('event_reply error=%s' % e)
  372. return ''
  373. elif msgType == 'text':
  374. try:
  375. # 如果是监督号,就直接回复。如果不是监督号,可能是用户的管理app,这个时候,需要走另外的流程。用户付费后关注
  376. moniApp = MoniApp.objects.filter(rawAppId = raw['ToUserName']).first()
  377. if moniApp:
  378. try:
  379. logger.info('111')
  380. moniUser = MoniUser.objects.filter(moniAppId = moniApp.appId,
  381. moniOpenId = raw['FromUserName']).first()
  382. if moniUser and moniUser.openId and moniUser.subAgentId:
  383. logger.info('222')
  384. logger.info('content = %s,openId=%s' % (raw['Content'], moniUser.openId))
  385. AskRobot.reply(event, raw['FromUserName'], raw['Content'], moniApp.appId, moniApp.secret,
  386. openId = moniUser.openId, agentId = moniUser.subAgentId)
  387. else:
  388. logger.info('333')
  389. user = MyUser.objects.filter(managerialOpenId = raw['FromUserName']).first()
  390. if user is None:
  391. logger.info('444')
  392. return {'MsgType': 'text', 'FromUserName': raw['ToUserName'],
  393. 'ToUserName': raw['FromUserName'], 'Content': u'没有找到您在平台扫码使用设备的任何信息',
  394. 'CreateTime': int(time.time())}
  395. AskRobot.reply(event, raw['FromUserName'], raw['Content'], moniApp.appId, moniApp.secret,
  396. openId = user.openId, agentId = user.agentId)
  397. except Exception, e:
  398. logger.exception('event_reply error=%s' % e)
  399. return ''
  400. logger.info('555')
  401. return ''
  402. except Exception, e:
  403. return ''
  404. return ''
  405. @error_tolerate(logger = logger, nil = JsonErrorResponse(u'故障部件图片存储失败,请重新上传'))
  406. @permission_required(ROLE.dealer, ROLE.subaccount)
  407. def uploadFaultPartPic(request):
  408. # type: (WSGIRequest)->JsonResponse
  409. files = request.FILES.getlist('file')
  410. if not len(files):
  411. return JsonResponse({'result': 0, 'description': u'故障部件图片存储失败,请重新上传', 'payload': ''})
  412. uploader = AliOssFileUploader(inputFile = request.FILES.getlist('file')[0], uploadType = 'fault')
  413. logger.info('[uploadFaultPartPic] %s is being used' % (repr(uploader),))
  414. try:
  415. outputUrl = uploader.upload()
  416. return JsonResponse({'result': 1, 'description': '', 'payload': outputUrl})
  417. except InvalidFileSize, e:
  418. logger.info(
  419. '%s(id=%s)\'s uploaded file reached limit' % (request.user.__class__.__name__, str(request.user.id),))
  420. return JsonResponse({'result': 0, 'description': e.message, 'payload': {}})
  421. except InvalidFileName, e:
  422. logger.info(
  423. '%s(id=%s)\'s uploaded file name is not legal' % (request.user.__class__.__name__, str(request.user.id),))
  424. return JsonResponse({'result': 0, 'description': e.message, 'payload': {}})
  425. def queryExpressInfo(request):
  426. orderNo = request.GET.get('orderNo')
  427. bodys = {}
  428. url = 'https://kuaidid.market.alicloudapi.com/danhao'
  429. bodys['com'] = ''
  430. bodys['src'] = orderNo
  431. from six.moves.urllib import parse
  432. post_data = parse.urlencode(bodys)
  433. request = urllib2.Request(url, post_data)
  434. request.add_header('Authorization', 'APPCODE ' + Const.KUAIDI_API_APPCODE)
  435. request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
  436. ctx = ssl.create_default_context()
  437. ctx.check_hostname = False
  438. ctx.verify_mode = ssl.CERT_NONE
  439. response = urllib2.urlopen(request, timeout = 15, context = ctx)
  440. content = response.read()
  441. if (content):
  442. result = json.loads(content)
  443. if result['status'] == 200:
  444. resultList = []
  445. for info in result['msg']['context']:
  446. resultList.append({'time': datetime.datetime.fromtimestamp(info['time']).strftime(Const.DATETIME_FMT),
  447. 'desc': info['desc']})
  448. return JsonOkResponse(payload = {'dataList': resultList})
  449. else:
  450. return JsonErrorResponse(description = u'无法获取到订单详细信息')
  451. else:
  452. return JsonErrorResponse(description = u'无法获取到订单详细信息')
  453. @error_tolerate(logger = logger)
  454. def aliNotify(request):
  455. payload = request.POST.dict() # type: Dict
  456. logger.info('ali common notify: %s' % str(payload))
  457. if 'service' in payload and payload['service'] == 'alipay.service.check':
  458. biz_content = xmltodict.parse(payload['biz_content'])['XML']
  459. app = AliApp.objects(appid = biz_content['AppId']).first() # type: AliApp
  460. gateway = app.new_gateway(app_platform_type = AppPlatformType.ALIPAY) # type: AliPayGateway
  461. if gateway.check(data = payload, pop_sign_type = False):
  462. sign = gateway.client._sign('<success>true</success>')
  463. return HttpResponse(
  464. '<?xml version="1.0" encoding="GBK"?><alipay><response><success>true</success></response><app_cert_sn>{}</app_cert_sn><sign>{}</sign><sign_type>RSA2</sign_type></alipay>'.format(
  465. gateway.client.app_cert_sn, sign))
  466. if payload['msg_method'] == 'alipay.fund.trans.order.changed':
  467. payload['biz_content'] = json.loads(payload['biz_content'])
  468. order_no = payload['biz_content']['out_biz_no']
  469. if WithdrawRecord.is_my(order_no):
  470. return AliPayWithdrawNotifier(record_cls = WithdrawRecord).do(payload)
  471. else:
  472. logger.warn('not support this order. orderNo = {}'.format(order_no))
  473. else:
  474. logger.warn('not support this method = {}'.format(payload['msg_method']))
  475. return HttpResponse('success')
  476. @error_tolerate(logger=logger, nil=JsonErrorResponse(description=u"获取订单失败,请刷新"))
  477. def getReceipt(request): # type: (WSGIRequest)->JsonResponse
  478. """
  479. 获取支付结果
  480. 消费单的结果直接跳转回去
  481. """
  482. user = request.user # type: RoleBaseDocument
  483. order_type = request.GET.get('order_type', 'recharge')
  484. out_trade_no = request.GET.get('out_trade_no')
  485. check_code = request.GET.get('check_code', None)
  486. from apps.web.user.models import RechargeRecord, ConsumeRecord
  487. if order_type == 'consume':
  488. record = get_consume_order(out_trade_no) # type: ConsumeRecord
  489. payload = record.receiptDesc
  490. else:
  491. record = RechargeRecord.objects(__raw__={
  492. '$or': [
  493. {
  494. 'wxOrderNo': out_trade_no
  495. },
  496. {
  497. 'orderNo': out_trade_no
  498. }
  499. ]}).first() # type: RechargeRecord
  500. if not record:
  501. from apps.web.dealer.models import DealerRechargeRecord
  502. record = DealerRechargeRecord.objects(__raw__={
  503. '$or': [
  504. {
  505. 'wxOrderNo': out_trade_no
  506. },
  507. {
  508. 'orderNo': out_trade_no
  509. }
  510. ]}).first() # type: DealerRechargeRecord
  511. if not record:
  512. return JsonErrorResponse(description = u'订单获取失败,请重新获取')
  513. else:
  514. payload = {
  515. 'itemName': record.subject,
  516. 'orderNo': record.orderNo,
  517. 'payment': RMB.fen_to_yuan(record.totalFee),
  518. 'homeLink': concat_dealer_main_page_url(),
  519. 'finishedTime': record.finishedTime,
  520. 'adInfo': {
  521. 'adShow': 'noshow'
  522. }
  523. }
  524. if record.product == DealerRechargeRecord.ProductType.SimCard:
  525. payload.update(
  526. {'detailLink': concat_front_end_url(
  527. uri = '/app/payOrderDetail.html?orderNo={}'.format(record.orderNo))})
  528. return JsonOkResponse(payload = payload)
  529. else:
  530. if record.result != RechargeRecord.PayResult.SUCCESS:
  531. return JsonErrorResponse(description = u'订单回调还未调用')
  532. payload = {
  533. 'orderNo': record.orderNo,
  534. 'payment': record.money,
  535. 'finishedTime': record.finished_time
  536. }
  537. from apps.web.device.models import Device
  538. # 是否是启动设备的
  539. start_key = record.startKey
  540. if start_key:
  541. payload.update({
  542. 'startKey': start_key,
  543. 'devNo': record.devNo,
  544. 'logicalCode': record.logicalCode,
  545. 'homeLink': concat_user_login_entry_url(l = record.logicalCode),
  546. 'serviceLink': concat_user_center_url(l = record.logicalCode)
  547. })
  548. else:
  549. payload.update({
  550. 'serviceLink': concat_user_center_url(),
  551. 'homeLink': concat_user_center_url(),
  552. 'detailLink': concat_front_end_url(
  553. uri = '/user/index.html#/user/chargeRecordDetail?ownerId={}&id={}'.format(record.ownerId,
  554. str(record.id)))
  555. })
  556. if isinstance(record, RechargeRecord):
  557. payload['itemName'] = record.subject
  558. if record.devNo:
  559. device = Device.get_dev(record.devNo)
  560. else:
  561. device = None
  562. else:
  563. device = Device.get_dev(record.devNo)
  564. if record.port:
  565. payload['itemName'] = cn(u'{major_type}/{logicalCode}-端口{port}'.format(
  566. major_type = device.majorDeviceType[0:8], logicalCode = record.logicalCode,
  567. port = record.port))
  568. else:
  569. payload['itemName'] = cn(u'{major_type}/{logicalCode}'.format(
  570. major_type = device.majorDeviceType[0:8], logicalCode = record.logicalCode))
  571. if not check_code:
  572. owner = Dealer.objects(id=record.ownerId).first() # type: Dealer
  573. ad_dict = Advertisement.fetch_payafter_ad(
  574. ua=request.META.get('HTTP_USER_AGENT', ''), user=request.user, dealer=owner, device=device)
  575. logger.debug('ad dict is {}'.format(ad_dict))
  576. payload.update({'adInfo': ad_dict})
  577. else:
  578. AdStatistics.inc_make_count(adSpace=AdSpace.PAYAFTER, showType='payAfter_dianjin', gateway='wechat')
  579. return JsonOkResponse(payload=payload)
  580. @error_tolerate(logger = logger, nil = JsonErrorResponse(description = u"获取数据失败,请刷新"))
  581. # @permission_required(ROLE.dealer, ROLE.agent, ROLE.supermanager)
  582. @gzip_page
  583. def supportedBanks(request):
  584. # type: (WSGIRequest)->JsonResponse
  585. account_type = request.GET.get('accountType')
  586. if account_type == BankCard.AccountType.PUBLIC:
  587. return JsonOkResponse(payload = Banks.get_public_banks())
  588. else:
  589. return JsonOkResponse(payload = Banks.get_personal_banks())
  590. def bolaiEvent(request):
  591. payload = json.loads(request.body)
  592. logger.debug('received bolai event: {}'.format(payload))
  593. gateId = payload.get('gateway_id')
  594. gateway_dev = Device.get_dev(gateId) # type: DeviceDict
  595. if gateway_dev is None: # 可能是一体化设备
  596. rcds = Device.get_collection().find({'gateImei': gateId})
  597. if rcds.count() == 0:
  598. return JsonOkResponse()
  599. gateway_dev = Device.get_dev(rcds[0]['devNo'])
  600. logger.info('this is gatewayplug device')
  601. if gateway_dev is None:
  602. return JsonOkResponse()
  603. if 'data' in payload and 'node_index' in payload['data']:
  604. if payload['data']['node_index'] != 0:
  605. logger.info('this is nodeplug device')
  606. gateway_adapter = gateway_dev.deviceAdapter # type: ChargingGatewayBox
  607. node_devno = gateway_adapter.get_node_devNo(str(payload['data']['node_index']))
  608. node_dev = Device.get_dev(node_devno) # type: DeviceDict
  609. if not node_dev:
  610. return JsonOkResponse()
  611. payload.update({
  612. 'nodeType': 'node'
  613. })
  614. event = node_dev.eventer.getEvent(payload)
  615. event.do()
  616. else:
  617. logger.info('this is gateway device')
  618. payload.update({
  619. 'nodeType': 'node'
  620. })
  621. event = gateway_dev.eventer.getEvent(payload)
  622. event.do()
  623. else:
  624. payload.update({
  625. 'nodeType': 'gateway'
  626. })
  627. event = gateway_dev.eventer.getEvent(payload)
  628. event.do()
  629. return JsonOkResponse()
  630. def bolaitenEvent(request):
  631. payload = json.loads(request.body)
  632. logger.debug('received bolai event: {}'.format(payload))
  633. devNo = payload.get('gateway_id')
  634. dev = Device.get_dev(devNo)
  635. if dev is None:
  636. return JsonOkResponse()
  637. if 'data' in payload:
  638. event = dev.eventer.getEvent(payload)
  639. event.do()
  640. return JsonOkResponse()
  641. @error_tolerate(nil = DefaultJsonErrorResponse)
  642. @permission_required(ROLE.dealer, ROLE.supermanager, ROLE.myuser)
  643. def getPowerGraphByDevice(request):
  644. startTime = to_datetime(request.GET.get('startTime'))
  645. endTime = to_datetime(request.GET.get('endTime'))
  646. port = str(request.GET.get('port', '1'))
  647. device = Device.get_dev_by_logicalCode(request.GET.get('logicalCode')) # type: DeviceDict
  648. if device.devTypeCode in [Const.DEVICE_TYPE_CODE_CHANGING_YUEWANGTONG]:
  649. interval = 600
  650. else:
  651. interval = device.cycle
  652. items = PowerManager.instence().get(devNo = device.devNo, port = port, sTime = startTime, eTime = endTime,
  653. interval = interval)
  654. return JsonOkResponse(payload = {'dataList': items})
  655. class WechatFollowView(View):
  656. def __init__(self, **kwargs):
  657. super(WechatFollowView, self).__init__(**kwargs)
  658. self.app = None
  659. @staticmethod
  660. def auth_request(request, app): # type:(WSGIRequest, MoniApp) -> bool
  661. """
  662. 对微信的请求进行验签
  663. """
  664. token = app.appToken
  665. signature = request.GET.get('signature')
  666. timestamp = request.GET.get('timestamp')
  667. nonce = request.GET.get('nonce')
  668. params = [token, timestamp, nonce]
  669. params.sort()
  670. return hashlib.sha1("".join(params)).hexdigest() == signature
  671. @staticmethod
  672. def get_followed_app(rawAppId):
  673. return MoniApp.get_app_by_raw(rawAppId)
  674. def dispatch(self, request, *args, **kwargs):
  675. rawAppId = kwargs.pop("rawAppId", "")
  676. app = self.get_followed_app(rawAppId)
  677. # 查找是否是入库的app
  678. if not app:
  679. logger.warning("[WechatFollowView dispatch], not find app! rawAppId = {}".format(rawAppId))
  680. return HttpResponseBadRequest()
  681. # 查找是否通过校验
  682. if not self.auth_request(request, app):
  683. logger.warning("[WechatFollowView dispatch], verify server error! params = {}".format(request.GET))
  684. return HttpResponseBadRequest()
  685. self.app = app
  686. return super(WechatFollowView, self).dispatch(request)
  687. def get(self, request):
  688. """
  689. 微信公众号服务器验证token使用 配置的时候只会访问一次
  690. """
  691. logger.info("[WechatFollowView GET], verify server success! params = {}, app = {}".format(request.GET, self.app))
  692. echoStr = request.GET["echostr"]
  693. return HttpResponse(echoStr)
  694. def post(self, request):
  695. """
  696. 业务请求都在这里
  697. 大致分为两类
  698. 1.业务事件(关注、订阅、点击菜单)
  699. 2.用户互动文字
  700. """
  701. logger.info("[WechatFollowView POST], payload = {}, app = {}".format(request.body, self.app))
  702. message = WechatMessage(request.body)
  703. if message.isText:
  704. response = WechatText(message, self.app).handle()
  705. elif message.isEvent:
  706. if message.isScanEvent:
  707. response = WechatScanEvent(message, self.app).handle()
  708. elif message.isSubscribeEvent:
  709. response = WechatSubscribe(message, self.app).handle()
  710. elif message.isUnSubscribeEvent:
  711. response = WechatUnSubscribe(message, self.app).handle()
  712. elif message.isMenuEvent:
  713. response = WechatMenu(message, self.app).handle()
  714. else:
  715. response = MessageHandler.defaultResponse()
  716. else:
  717. response = MessageHandler.defaultResponse()
  718. return response