@@ -8,7 +8,7 @@ from mch.models import ConsumeInfoSubmitLogInfo  | 
            ||
| 8 | 8 | 
                 | 
            
| 9 | 9 | 
                 | 
            
| 10 | 10 | 
                class UserInfoAdmin(ChangeOnlyModelAdmin, admin.ModelAdmin):  | 
            
| 11 | 
                -    list_display = ('user_id', 'username', 'unionid', 'openid', 'openid_miniapp', 'name', 'sex', 'nickname', 'phone', 'location', 'balance', 'user_status', 'test_user', 'status', 'created_at', 'updated_at')
               | 
            |
| 11 | 
                +    list_display = ('user_id', 'unionid', 'openid', 'nickname', 'phone', 'location', 'balance', 'user_status', 'test_user', 'status', 'created_at', 'updated_at')
               | 
            |
| 12 | 12 | 
                     list_filter = ('test_user', 'sex', 'user_status', 'status')
               | 
            
| 13 | 13 | 
                     readonly_fields = ('user_id', )
               | 
            
| 14 | 14 | 
                     search_fields = ('user_id', 'username', 'unionid', 'openid', 'openid_miniapp', 'name', 'phone', 'location')
               | 
            
                @@ -14,7 +14,7 @@ from group import views as group_views  | 
            ||
| 14 | 14 | 
                from message import views as message_views  | 
            
| 15 | 15 | 
                from miniapp import views as mini_views  | 
            
| 16 | 16 | 
                from operation import views as op_views  | 
            
| 17 | 
                -from page import oauth_views, sale_views  | 
            |
| 17 | 
                +from page import oauth_views, sale_views, screen_views  | 
            |
| 18 | 18 | 
                from pay import views as pay_views  | 
            
| 19 | 19 | 
                from photo import views as photo_views  | 
            
| 20 | 20 | 
                from server import server_views  | 
            
                @@ -269,3 +269,8 @@ urlpatterns += [  | 
            ||
| 269 | 269 | 
                url(r'^clerk/update$', clerk_views.clerk_update, name='clerk_update'),  | 
            
| 270 | 270 | 
                url(r'^clerk/list$', clerk_views.clerk_list, name='clerk_list'),  | 
            
| 271 | 271 | 
                ]  | 
            
| 272 | 
                +  | 
            |
| 273 | 
                +urlpatterns += [  | 
            |
| 274 | 
                + url(r'^screen/admin/loginqr$', screen_views.screen_admin_loginqr, name='screen_admin_loginqr'),  | 
            |
| 275 | 
                + url(r'^screen/admin/loginrst$', screen_views.screen_admin_loginrst, name='screen_admin_loginrst'),  | 
            |
| 276 | 
                +]  | 
            
                @@ -0,0 +1,15 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.contrib import admin  | 
            |
| 4 | 
                +from django_admin import ChangeOnlyModelAdmin, ReadOnlyModelAdmin  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +from guideline.models import ScreenAdminInfo  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +  | 
            |
| 9 | 
                +class ScreenAdminInfoAdmin(ChangeOnlyModelAdmin, admin.ModelAdmin):  | 
            |
| 10 | 
                +    list_display = ('admin_id', 'unionid', 'openid', 'name', 'sex', 'nickname', 'phone', 'location', 'status', 'created_at', 'updated_at')
               | 
            |
| 11 | 
                +    readonly_fields = ('admin_id',)
               | 
            |
| 12 | 
                +    search_fields = ('admin_id', 'unionid', 'openid', 'name', 'phone', 'location')
               | 
            |
| 13 | 
                +  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                +admin.site.register(ScreenAdminInfo, ScreenAdminInfoAdmin)  | 
            
                @@ -0,0 +1,8 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +from __future__ import unicode_literals  | 
            |
| 3 | 
                +  | 
            |
| 4 | 
                +from django.apps import AppConfig  | 
            |
| 5 | 
                +  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +class GuidelineConfig(AppConfig):  | 
            |
| 8 | 
                + name = 'guideline'  | 
            
                @@ -0,0 +1,48 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +# Generated by Django 1.11.16 on 2018-11-14 20:44  | 
            |
| 3 | 
                +from __future__ import unicode_literals  | 
            |
| 4 | 
                +  | 
            |
| 5 | 
                +from django.db import migrations, models  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class Migration(migrations.Migration):  | 
            |
| 9 | 
                +  | 
            |
| 10 | 
                + initial = True  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + dependencies = [  | 
            |
| 13 | 
                + ]  | 
            |
| 14 | 
                +  | 
            |
| 15 | 
                + operations = [  | 
            |
| 16 | 
                + migrations.CreateModel(  | 
            |
| 17 | 
                + name='ScreenAdminInfo',  | 
            |
| 18 | 
                + fields=[  | 
            |
| 19 | 
                +                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
               | 
            |
| 20 | 
                +                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
               | 
            |
| 21 | 
                +                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
               | 
            |
| 22 | 
                +                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
               | 
            |
| 23 | 
                +                ('admin_id', models.CharField(blank=True, db_index=True, help_text='\u5927\u5c4f\u7ba1\u7406\u5458\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, unique=True, verbose_name='admin_id')),
               | 
            |
| 24 | 
                +                ('unionid', models.CharField(blank=True, db_index=True, help_text='\u5fae\u4fe1 Union ID', max_length=32, null=True, verbose_name='unionid')),
               | 
            |
| 25 | 
                +                ('openid', models.CharField(blank=True, db_index=True, help_text='\u5fae\u4fe1 Open ID', max_length=32, null=True, verbose_name='openid')),
               | 
            |
| 26 | 
                +                ('name', models.CharField(blank=True, help_text='\u7528\u6237\u59d3\u540d', max_length=8, null=True, verbose_name='name')),
               | 
            |
| 27 | 
                +                ('sex', models.IntegerField(choices=[(1, '\u7537'), (0, '\u5973')], default=1, help_text='\u7528\u6237\u6027\u522b', verbose_name='sex')),
               | 
            |
| 28 | 
                +                ('nickname', models.CharField(blank=True, help_text='\u7528\u6237\u6635\u79f0', max_length=255, null=True, verbose_name='nickname')),
               | 
            |
| 29 | 
                +                ('avatar', models.CharField(blank=True, help_text='\u7528\u6237\u5934\u50cf', max_length=255, null=True, verbose_name='avatar')),
               | 
            |
| 30 | 
                +                ('phone', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u7535\u8bdd', max_length=11, null=True, verbose_name='phone')),
               | 
            |
| 31 | 
                +                ('country', models.CharField(blank=True, help_text='\u7528\u6237\u56fd\u5bb6', max_length=16, null=True, verbose_name='country')),
               | 
            |
| 32 | 
                +                ('province', models.CharField(blank=True, help_text='\u7528\u6237\u7701\u4efd', max_length=16, null=True, verbose_name='province')),
               | 
            |
| 33 | 
                +                ('city', models.CharField(blank=True, help_text='\u7528\u6237\u57ce\u5e02', max_length=16, null=True, verbose_name='city')),
               | 
            |
| 34 | 
                +                ('location', models.CharField(blank=True, help_text='\u7528\u6237\u5730\u5740', max_length=255, null=True, verbose_name='location')),
               | 
            |
| 35 | 
                +                ('brand_id', models.CharField(blank=True, db_index=True, help_text='\u54c1\u724c\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='brand_id')),
               | 
            |
| 36 | 
                +                ('brand_name', models.CharField(blank=True, help_text='\u54c1\u724c\u540d\u79f0', max_length=255, null=True, verbose_name='brand_name')),
               | 
            |
| 37 | 
                +                ('user_status', models.IntegerField(choices=[(1, '\u5df2\u6fc0\u6d3b'), (2, '\u5df2\u7981\u7528'), (3, '\u5df2\u5220\u9664')], db_index=True, default=1, help_text='\u7ba1\u7406\u5458\u72b6\u6001', verbose_name='user_status')),
               | 
            |
| 38 | 
                + ],  | 
            |
| 39 | 
                +            options={
               | 
            |
| 40 | 
                + 'verbose_name': 'ScreenAdminInfo',  | 
            |
| 41 | 
                + 'verbose_name_plural': 'ScreenAdminInfo',  | 
            |
| 42 | 
                + },  | 
            |
| 43 | 
                + ),  | 
            |
| 44 | 
                + migrations.AlterUniqueTogether(  | 
            |
| 45 | 
                + name='screenadmininfo',  | 
            |
| 46 | 
                +            unique_together=set([('brand_id', 'unionid', 'openid')]),
               | 
            |
| 47 | 
                + ),  | 
            |
| 48 | 
                + ]  | 
            
                @@ -0,0 +1,63 @@  | 
            ||
| 1 | 
                +# -*- coding: utf-8 -*-  | 
            |
| 2 | 
                +  | 
            |
| 3 | 
                +from django.db import models  | 
            |
| 4 | 
                +from django.utils.translation import ugettext_lazy as _  | 
            |
| 5 | 
                +from django_models_ext import BaseModelMixin  | 
            |
| 6 | 
                +  | 
            |
| 7 | 
                +  | 
            |
| 8 | 
                +class ScreenAdminInfo(BaseModelMixin):  | 
            |
| 9 | 
                + MALE = 1  | 
            |
| 10 | 
                + FEMALE = 0  | 
            |
| 11 | 
                +  | 
            |
| 12 | 
                + SEX_TYPE_TUPLE = (  | 
            |
| 13 | 
                + (MALE, u'男'),  | 
            |
| 14 | 
                + (FEMALE, u'女'),  | 
            |
| 15 | 
                + )  | 
            |
| 16 | 
                +  | 
            |
| 17 | 
                + ACTIVATED = 1  | 
            |
| 18 | 
                + DISABLED = 2  | 
            |
| 19 | 
                + DELETED = 3  | 
            |
| 20 | 
                +  | 
            |
| 21 | 
                + USER_STATUS_TUPLE = (  | 
            |
| 22 | 
                + (ACTIVATED, u'已激活'),  | 
            |
| 23 | 
                + (DISABLED, u'已禁用'),  | 
            |
| 24 | 
                + (DELETED, u'已删除'),  | 
            |
| 25 | 
                + )  | 
            |
| 26 | 
                +  | 
            |
| 27 | 
                + admin_id = models.CharField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'大屏管理员唯一标识', db_index=True, unique=True)  | 
            |
| 28 | 
                +  | 
            |
| 29 | 
                + unionid = models.CharField(_(u'unionid'), max_length=32, blank=True, null=True, help_text=u'微信 Union ID', db_index=True)  | 
            |
| 30 | 
                + openid = models.CharField(_(u'openid'), max_length=32, blank=True, null=True, help_text=u'微信 Open ID', db_index=True)  | 
            |
| 31 | 
                +  | 
            |
| 32 | 
                + name = models.CharField(_(u'name'), max_length=8, blank=True, null=True, help_text=u'用户姓名')  | 
            |
| 33 | 
                + sex = models.IntegerField(_(u'sex'), choices=SEX_TYPE_TUPLE, default=MALE, help_text=u'用户性别')  | 
            |
| 34 | 
                + nickname = models.CharField(_(u'nickname'), max_length=255, blank=True, null=True, help_text=u'用户昵称')  | 
            |
| 35 | 
                + avatar = models.CharField(_(u'avatar'), max_length=255, blank=True, null=True, help_text=u'用户头像')  | 
            |
| 36 | 
                + phone = models.CharField(_(u'phone'), max_length=11, blank=True, null=True, help_text=u'用户电话', db_index=True)  | 
            |
| 37 | 
                + country = models.CharField(_(u'country'), max_length=16, blank=True, null=True, help_text=u'用户国家')  | 
            |
| 38 | 
                + province = models.CharField(_(u'province'), max_length=16, blank=True, null=True, help_text=u'用户省份')  | 
            |
| 39 | 
                + city = models.CharField(_(u'city'), max_length=16, blank=True, null=True, help_text=u'用户城市')  | 
            |
| 40 | 
                + location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'用户地址')  | 
            |
| 41 | 
                +  | 
            |
| 42 | 
                + brand_id = models.CharField(_(u'brand_id'), max_length=32, blank=True, null=True, help_text=u'品牌唯一标识', db_index=True)  | 
            |
| 43 | 
                + brand_name = models.CharField(_(u'brand_name'), max_length=255, blank=True, null=True, help_text=u'品牌名称')  | 
            |
| 44 | 
                +  | 
            |
| 45 | 
                + user_status = models.IntegerField(_(u'user_status'), choices=USER_STATUS_TUPLE, default=ACTIVATED, help_text=u'管理员状态', db_index=True)  | 
            |
| 46 | 
                +  | 
            |
| 47 | 
                + class Meta:  | 
            |
| 48 | 
                + verbose_name = _(u'ScreenAdminInfo')  | 
            |
| 49 | 
                + verbose_name_plural = _(u'ScreenAdminInfo')  | 
            |
| 50 | 
                +  | 
            |
| 51 | 
                + unique_together = (  | 
            |
| 52 | 
                +            ('brand_id', 'unionid', 'openid'),
               | 
            |
| 53 | 
                + )  | 
            |
| 54 | 
                +  | 
            |
| 55 | 
                + def __unicode__(self):  | 
            |
| 56 | 
                + return unicode(self.pk)  | 
            |
| 57 | 
                +  | 
            |
| 58 | 
                + @property  | 
            |
| 59 | 
                + def data(self):  | 
            |
| 60 | 
                +        return {
               | 
            |
| 61 | 
                + 'nickname': self.nickname,  | 
            |
| 62 | 
                + 'avatar': self.avatar,  | 
            |
| 63 | 
                + }  | 
            
                @@ -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  |