+ return {
+ 'nickname': self.nickname,
+ 'avatar': self.avatar,
+ }
@@ -0,0 +1,7 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+from __future__ import unicode_literals |
|
3 |
+ |
|
4 |
+from django.test import TestCase |
|
5 |
+ |
|
6 |
+ |
|
7 |
+# Create your tests here. |
@@ -0,0 +1,7 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+from __future__ import unicode_literals |
|
3 |
+ |
|
4 |
+from django.shortcuts import render |
|
5 |
+ |
|
6 |
+ |
|
7 |
+# Create your views here. |
@@ -0,0 +1,27 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+ |
|
3 |
+import os |
|
4 |
+ |
|
5 |
+ |
|
6 |
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
7 |
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) |
|
8 |
+ |
|
9 |
+TEMPLATES = [ |
|
10 |
+ { |
|
11 |
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates', |
|
12 |
+ 'DIRS': [os.path.join(BASE_DIR, 'templates')], |
|
13 |
+ # 'APP_DIRS': True, |
|
14 |
+ 'OPTIONS': { |
|
15 |
+ 'context_processors': [ |
|
16 |
+ 'django.template.context_processors.debug', |
|
17 |
+ 'django.template.context_processors.request', |
|
18 |
+ 'django.contrib.auth.context_processors.auth', |
|
19 |
+ 'django.contrib.messages.context_processors.messages', |
|
20 |
+ ], |
|
21 |
+ 'loaders': [ |
|
22 |
+ 'django.template.loaders.filesystem.Loader', |
|
23 |
+ 'django.template.loaders.app_directories.Loader', |
|
24 |
+ ], |
|
25 |
+ }, |
|
26 |
+ }, |
|
27 |
+] |
@@ -0,0 +1,27 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+ |
|
3 |
+import os |
|
4 |
+ |
|
5 |
+ |
|
6 |
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
7 |
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) |
|
8 |
+ |
|
9 |
+TEMPLATES = [ |
|
10 |
+ { |
|
11 |
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates', |
|
12 |
+ 'DIRS': [os.path.join(BASE_DIR, 'templates')], |
|
13 |
+ # 'APP_DIRS': True, |
|
14 |
+ 'OPTIONS': { |
|
15 |
+ 'context_processors': [ |
|
16 |
+ 'django.template.context_processors.debug', |
|
17 |
+ 'django.template.context_processors.request', |
|
18 |
+ 'django.contrib.auth.context_processors.auth', |
|
19 |
+ 'django.contrib.messages.context_processors.messages', |
|
20 |
+ ], |
|
21 |
+ 'loaders': [ |
|
22 |
+ 'django.template.loaders.filesystem.Loader', |
|
23 |
+ 'django.template.loaders.app_directories.Loader', |
|
24 |
+ ], |
|
25 |
+ }, |
|
26 |
+ }, |
|
27 |
+] |
@@ -54,6 +54,7 @@ INSTALLED_APPS = ( |
||
54 | 54 |
'box', |
55 | 55 |
'commands', |
56 | 56 |
'group', |
57 |
+ 'guideline', |
|
57 | 58 |
'integral', |
58 | 59 |
'logs', |
59 | 60 |
'mch', |
@@ -390,11 +391,20 @@ KODO_DEFAULT_BRAND_NAME = '' |
||
390 | 391 |
KODO_DEFAULT_BRAND_DOMAIN = '' |
391 | 392 |
|
392 | 393 |
KODO_CLERK_AUTH_URL = 'http://pai.ai/w/o?r=http%3A%2F%2Fkodo.xfoto.com.cn%2Fp%2Fclerk%3Fbrand_id%3D{0}' |
394 |
+KODO_SCREEN_AUTH_URL = 'http://pai.ai/w/o?r=http%3A%2F%2Fkodo.xfoto.com.cn%2Fp%2Fscreen%2Fadmin%2Foauth%3Fbrand_id%3D{0}' |
|
395 |
+KODO_SCREEN_LOGIN_URL = 'http://pai.ai/w/o?s=snsapi_base&r=http%3A%2F%2Fkodo.xfoto.com.cn%2Fp%2Fscreen%2Fadmin%2Flogin/%3Fbrand_id%3D{0}%26token%3D{1}' |
|
393 | 396 |
|
394 | 397 |
# 经纬度 |
395 | 398 |
GIS_2_ADMINISTRATIVE_DIVISION = 'http://116.196.105.215:1234/gis?auth_user=freevip&latitude={0}&longitude={1}' |
396 | 399 |
PHONE_2_ADMINISTRATIVE_DIVISION = 'https://www.baifubao.com/callback?cmd=1059&callback=phone&phone={0}' |
397 | 400 |
|
401 |
+# 开发调试相关配置 |
|
402 |
+if DEBUG: |
|
403 |
+ try: |
|
404 |
+ from local_settings_dev import * |
|
405 |
+ except ImportError: |
|
406 |
+ pass |
|
407 |
+ |
|
398 | 408 |
try: |
399 | 409 |
from local_settings import * |
400 | 410 |
except ImportError: |
@@ -0,0 +1,79 @@ |
||
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+ |
|
3 |
+import shortuuid |
|
4 |
+from django.conf import settings |
|
5 |
+from django_response import response |
|
6 |
+from json_render import json_render |
|
7 |
+ |
|
8 |
+from guideline.models import ScreenAdminInfo |
|
9 |
+from utils.error.errno_utils import PermissionStatusCode |
|
10 |
+from utils.redis.connect import r |
|
11 |
+from utils.redis.rkeys import KODO_SCREEN_ADMIN_LOGIN |
|
12 |
+ |
|
13 |
+ |
|
14 |
+def screen_admin_oauthqr(request): |
|
15 |
+ brand_id = request.GET.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
16 |
+ |
|
17 |
+ return json_render(request, 'page/screen_admin_oauth_qrcode.html', unjsondumpsdict={ |
|
18 |
+ 'qr': settings.KODO_SCREEN_AUTH_URL.format(brand_id) |
|
19 |
+ }) |
|
20 |
+ |
|
21 |
+ |
|
22 |
+def screen_admin_oauth(request): |
|
23 |
+ brand_id = request.GET.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
24 |
+ unionid = request.GET.get('unionid', '') |
|
25 |
+ openid = request.GET.get('openid', '') |
|
26 |
+ |
|
27 |
+ ScreenAdminInfo.objects.update_or_create(brand_id=brand_id, unionid=unionid, openid=openid, defaults={ |
|
28 |
+ 'nickname': request.GET.get('nickname', ''), |
|
29 |
+ 'avatar': request.GET.get('avatar', ''), |
|
30 |
+ 'user_status': ScreenAdminInfo.ACTIVATED, |
|
31 |
+ }) |
|
32 |
+ |
|
33 |
+ return json_render(request, 'page/screen_admin_oauth_success.html', unjsondumpsdict={ |
|
34 |
+ }) |
|
35 |
+ |
|
36 |
+ |
|
37 |
+def screen_admin_loginqr(request): |
|
38 |
+ brand_id = request.GET.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
39 |
+ |
|
40 |
+ token = shortuuid.uuid() |
|
41 |
+ |
|
42 |
+ return response(200, { |
|
43 |
+ 'qr': settings.KODO_SCREEN_LOGIN_URL.format(brand_id, token), |
|
44 |
+ 'token': token, |
|
45 |
+ }) |
|
46 |
+ |
|
47 |
+ |
|
48 |
+def screen_admin_login(request): |
|
49 |
+ brand_id = request.GET.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
50 |
+ unionid = request.GET.get('unionid', '') |
|
51 |
+ openid = request.GET.get('openid', '') |
|
52 |
+ token = request.GET.get('token', '') |
|
53 |
+ |
|
54 |
+ try: |
|
55 |
+ admin = ScreenAdminInfo.objects.get(unionid=unionid, user_status=ScreenAdminInfo.ACTIVATED) |
|
56 |
+ except ScreenAdminInfo.DoesNotExist: |
|
57 |
+ return json_render(request, 'page/screen_admin_login_fail.html', unjsondumpsdict={ |
|
58 |
+ }) |
|
59 |
+ |
|
60 |
+ r.setex(KODO_SCREEN_ADMIN_LOGIN % (brand_id, token), r.REDIS_EXPIRED_HALF_HOUR, unionid) |
|
61 |
+ |
|
62 |
+ return json_render(request, 'page/screen_admin_login_success.html', unjsondumpsdict={ |
|
63 |
+ }) |
|
64 |
+ |
|
65 |
+ |
|
66 |
+def screen_admin_loginrst(request): |
|
67 |
+ brand_id = request.GET.get('brand_id', settings.KODO_DEFAULT_BRAND_ID) |
|
68 |
+ token = request.GET.get('token', '') |
|
69 |
+ |
|
70 |
+ unionid = r.get(KODO_SCREEN_ADMIN_LOGIN % (brand_id, token)) |
|
71 |
+ |
|
72 |
+ try: |
|
73 |
+ admin = ScreenAdminInfo.objects.get(unionid=unionid, user_status=ScreenAdminInfo.ACTIVATED) |
|
74 |
+ except ScreenAdminInfo.DoesNotExist: |
|
75 |
+ return response(PermissionStatusCode.PERMISSION_DENIED) |
|
76 |
+ |
|
77 |
+ return response(200, { |
|
78 |
+ 'info': admin.data, |
|
79 |
+ }) |
@@ -0,0 +1,12 @@ |
||
1 |
+<!DOCTYPE html> |
|
2 |
+<html lang="en"> |
|
3 |
+<head> |
|
4 |
+ <meta charset="UTF-8"> |
|
5 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
6 |
+ <meta http-equiv="X-UA-Compatible" content="ie=edge"> |
|
7 |
+ <title>登录失败</title> |
|
8 |
+</head> |
|
9 |
+<body> |
|
10 |
+ <div class="" style="text-align:center;margin-top:100px;font-size:25px;">登录失败</div> |
|
11 |
+</body> |
|
12 |
+</html> |
@@ -0,0 +1,12 @@ |
||
1 |
+<!DOCTYPE html> |
|
2 |
+<html lang="en"> |
|
3 |
+<head> |
|
4 |
+ <meta charset="UTF-8"> |
|
5 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
6 |
+ <meta http-equiv="X-UA-Compatible" content="ie=edge"> |
|
7 |
+ <title>登录成功</title> |
|
8 |
+</head> |
|
9 |
+<body> |
|
10 |
+ <div class="" style="text-align:center;margin-top:100px;font-size:25px;">登录成功</div> |
|
11 |
+</body> |
|
12 |
+</html> |
@@ -0,0 +1,64 @@ |
||
1 |
+{% load staticfiles %} |
|
2 |
+ |
|
3 |
+<!DOCTYPE html> |
|
4 |
+<html lang="zh-CN"> |
|
5 |
+ <head> |
|
6 |
+ <meta charset="utf-8"> |
|
7 |
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
|
8 |
+ <meta name="format-detection" content="telephone=no,email=no,address=no"> |
|
9 |
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"> |
|
10 |
+ <title>授权登录二维码</title> |
|
11 |
+ |
|
12 |
+ <link href="//res.wx.qq.com/open/libs/weui/0.4.3/weui.min.css" rel="stylesheet" type="text/css" /> |
|
13 |
+ |
|
14 |
+ <style> |
|
15 |
+ input:required:invalid { |
|
16 |
+ color: #E64340; |
|
17 |
+ } |
|
18 |
+ input:required:valid { |
|
19 |
+ color: rgb(0, 0, 0); |
|
20 |
+ } |
|
21 |
+ .hidden { |
|
22 |
+ display: none; |
|
23 |
+ } |
|
24 |
+ .qr { |
|
25 |
+ position: fixed; |
|
26 |
+ left: 50%; |
|
27 |
+ top: 50%; |
|
28 |
+ margin-left: -100px; |
|
29 |
+ margin-top: -100px; |
|
30 |
+ padding: 5px 5px 0 5px; |
|
31 |
+ border: 1px solid #000; |
|
32 |
+ border-radius: 5px; |
|
33 |
+ } |
|
34 |
+ </style> |
|
35 |
+ </head> |
|
36 |
+ <body> |
|
37 |
+ <div class="container" > |
|
38 |
+ <img id="qr_logo" class="hidden" src="{% static 'kodo/img/paiai_96_96.png' %}"> |
|
39 |
+ <div id="qr" class="qr"></div> |
|
40 |
+ </div> |
|
41 |
+ |
|
42 |
+ <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> |
|
43 |
+ <script src="//cdnjs.cloudflare.com/ajax/libs/lrsjng.jquery-qrcode/0.14.0/jquery-qrcode.min.js"></script> |
|
44 |
+ <script> |
|
45 |
+ $("#qr").empty().qrcode({ |
|
46 |
+ render: 'canvas', |
|
47 |
+ mode: 0, |
|
48 |
+ text: '{{ qr }}' |
|
49 |
+ }); |
|
50 |
+ </script> |
|
51 |
+ <script type="text/javascript" src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> |
|
52 |
+ <script type="text/javascript" src="{% static 'kodo/js/jswe-0.0.4.js' %}"></script> |
|
53 |
+ <script> |
|
54 |
+ V.initWxData({ |
|
55 |
+ imgUrl: "http://pai.ai/static/kodo/img/paiai_96_96.png", |
|
56 |
+ link: 'http://pai.ai/w/o?r=http%3A%2F%2Fpai.ai%2Fp%2Floginqr', |
|
57 |
+ desc: "授权登录", |
|
58 |
+ title: "授权登录", |
|
59 |
+ timeLine: "" |
|
60 |
+ }, true); |
|
61 |
+ V.hideOptionMenu(); |
|
62 |
+ </script> |
|
63 |
+ </body> |
|
64 |
+</html> |
@@ -0,0 +1,13 @@ |
||
1 |
+<!DOCTYPE html> |
|
2 |
+<html lang="en"> |
|
3 |
+<head> |
|
4 |
+ <meta charset="UTF-8"> |
|
5 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
6 |
+ <meta http-equiv="X-UA-Compatible" content="ie=edge"> |
|
7 |
+ <title>授权成功</title> |
|
8 |
+</head> |
|
9 |
+<body> |
|
10 |
+ <div class="" style="text-align:center;margin-top:100px;font-size:25px;">授权成功</div> |
|
11 |
+ <div class="" style="text-align:center;font-size:15px;">请扫描统揽大屏二维码开启大屏</div> |
|
12 |
+</body> |
|
13 |
+</html> |
@@ -4,7 +4,7 @@ from django.conf.urls import url |
||
4 | 4 |
|
5 | 5 |
from account import tourguide_views |
6 | 6 |
from group import lensman_views |
7 |
-from page import info_views, oauth_views, page_views, sale_views |
|
7 |
+from page import info_views, oauth_views, page_views, sale_views, screen_views |
|
8 | 8 |
|
9 | 9 |
|
10 | 10 |
urlpatterns = [ |
@@ -33,3 +33,9 @@ urlpatterns = [ |
||
33 | 33 |
url(r'^clerk$', oauth_views.clerk_oauth, name='clerk_oauth'), # 店员授权页面 |
34 | 34 |
url(r'^clerk/info$', info_views.clerk_info_oauth, name='clerk_info_oauth'), # 店员信息授权页面 |
35 | 35 |
] |
36 |
+ |
|
37 |
+urlpatterns += [ |
|
38 |
+ url(r'^qr$', screen_views.screen_admin_oauthqr, name='oauthqr'), |
|
39 |
+ url(r'^screen/admin/oauth$', screen_views.screen_admin_oauth, name='screen_admin_oauth'), |
|
40 |
+ url(r'^screen/admin/login$', screen_views.screen_admin_login, name='screen_admin_login'), |
|
41 |
+] |
@@ -2,7 +2,7 @@ Django==1.11.16 |
||
2 | 2 |
django-admin==1.3.2 |
3 | 3 |
django-cors-headers==2.4.0 |
4 | 4 |
django-curtail-uuid==1.0.4 |
5 |
-django-detect==1.0.6 |
|
5 |
+django-detect==1.0.8 |
|
6 | 6 |
django-file-md5==1.0.2 |
7 | 7 |
django-file-upload==1.1.0 |
8 | 8 |
django-ip==1.0.2 |
@@ -187,3 +187,8 @@ class TokenStatusCode(BaseStatusCode): |
||
187 | 187 |
""" 票据相关错误码 4090xx """ |
188 | 188 |
TOKEN_NOT_FOUND = StatusCodeField(409901, 'Token Not Found', description=u'票据不存在') |
189 | 189 |
TOKEN_HAS_EXPIRED = StatusCodeField(409911, 'Token Has Expired', description=u'票据过期,请刷新重扫二维码') |
190 |
+ |
|
191 |
+ |
|
192 |
+class PermissionStatusCode(BaseStatusCode): |
|
193 |
+ """ 4099xx 权限相关错误码 """ |
|
194 |
+ PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description=u'权限不足') |
@@ -69,3 +69,5 @@ BOX_PROGRAM_VERSION_INFO = 'box:program:version:info' # STRING,BOX 程序版 |
||
69 | 69 |
|
70 | 70 |
# 小程序相关 |
71 | 71 |
MINI_PROGRAM_GIS_LIST = 'tamron:miniprogram:gis:list' |
72 |
+ |
|
73 |
+KODO_SCREEN_ADMIN_LOGIN = 'kodo:screen:admin:login:%s:%s' # brand_id, token |