-new"> 17
+        migrations.CreateModel(
18
+            name='UserInfo',
19
+            fields=[
20
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
22
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
23
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
24
+                ('user_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=22, unique=True)),
25
+                ('unionid', models.CharField(blank=True, db_index=True, help_text='\u5fae\u4fe1 Unionid', max_length=255, null=True, unique=True, verbose_name='unionid')),
26
+                ('openid', models.CharField(blank=True, db_index=True, help_text='\u5fae\u4fe1 Openid', max_length=255, null=True, unique=True, verbose_name='openid')),
27
+                ('name', models.CharField(blank=True, help_text='\u7528\u6237\u59d3\u540d', max_length=255, null=True, verbose_name='name')),
28
+                ('sex', models.IntegerField(choices=[(1, '\u7537'), (0, '\u5973')], default=1, help_text='\u7528\u6237\u6027\u522b', verbose_name='sex')),
29
+                ('nickname', models.CharField(blank=True, help_text='\u7528\u6237\u6635\u79f0', max_length=255, null=True, verbose_name='nickname')),
30
+                ('avatar', models.CharField(blank=True, help_text='\u7528\u6237\u5934\u50cf', max_length=255, null=True, verbose_name='avatar')),
31
+                ('phone', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u7535\u8bdd', max_length=255, null=True, unique=True, verbose_name='phone')),
32
+                ('country', models.CharField(blank=True, help_text='\u7528\u6237\u56fd\u5bb6', max_length=255, null=True, verbose_name='country')),
33
+                ('province', models.CharField(blank=True, help_text='\u7528\u6237\u7701\u4efd', max_length=255, null=True, verbose_name='province')),
34
+                ('city', models.CharField(blank=True, help_text='\u7528\u6237\u57ce\u5e02', max_length=255, null=True, verbose_name='city')),
35
+                ('location', models.CharField(blank=True, help_text='\u7528\u6237\u5730\u5740', max_length=255, null=True, verbose_name='location')),
36
+                ('user_status', models.IntegerField(choices=[(0, '\u672a\u9a8c\u8bc1'), (1, '\u5df2\u6fc0\u6d3b'), (2, '\u5df2\u7981\u7528'), (3, '\u5df2\u5220\u9664')], default=0, verbose_name='user_status')),
37
+            ],
38
+            options={
39
+                'verbose_name': 'userinfo',
40
+                'verbose_name_plural': 'userinfo',
41
+            },
42
+        ),
43
+    ]

+ 0 - 0
account/migrations/__init__.py


+ 62 - 0
account/models.py

@@ -0,0 +1,62 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+from shortuuidfield import ShortUUIDField
6
+
7
+from manual.basemodels import CreateUpdateMixin
8
+
9
+
10
+class UserInfo(CreateUpdateMixin):
11
+    UNVERIFIED = 0
12
+    ACTIVATED = 1
13
+    DISABLED = 2
14
+    DELETED = 3
15
+
16
+    USER_STATUS = (
17
+        (UNVERIFIED, u'未验证'),
18
+        (ACTIVATED, u'已激活'),
19
+        (DISABLED, u'已禁用'),
20
+        (DELETED, u'已删除'),
21
+    )
22
+
23
+    MALE = 1
24
+    FEMALE = 0
25
+
26
+    SEX_TYPE = (
27
+        (MALE, u'男'),
28
+        (FEMALE, u'女'),
29
+    )
30
+
31
+    user_id = ShortUUIDField(_(u'user_id'), max_length=255, help_text=u'用户唯一标识', db_index=True, unique=True)
32
+
33
+    # 微信授权用户
34
+    unionid = models.CharField(_(u'unionid'), max_length=255, blank=True, null=True, help_text=u'微信 Unionid', db_index=True, unique=True)
35
+    openid = models.CharField(_(u'openid'), max_length=255, blank=True, null=True, help_text=u'微信 Openid', db_index=True, unique=True)
36
+    # 用户基本信息
37
+    name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'用户姓名')
38
+    sex = models.IntegerField(_(u'sex'), choices=SEX_TYPE, default=MALE, help_text=u'用户性别')
39
+    nickname = models.CharField(_(u'nickname'), max_length=255, blank=True, null=True, help_text=u'用户昵称')
40
+    avatar = models.CharField(_(u'avatar'), max_length=255, blank=True, null=True, help_text=u'用户头像')
41
+    phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'用户电话', db_index=True, unique=True)
42
+    country = models.CharField(_(u'country'), max_length=255, blank=True, null=True, help_text=u'用户国家')
43
+    province = models.CharField(_(u'province'), max_length=255, blank=True, null=True, help_text=u'用户省份')
44
+    city = models.CharField(_(u'city'), max_length=255, blank=True, null=True, help_text=u'用户城市')
45
+    location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'用户地址')
46
+
47
+    user_status = models.IntegerField(_(u'user_status'), choices=USER_STATUS, default=UNVERIFIED)
48
+
49
+    class Meta:
50
+        verbose_name = _(u'userinfo')
51
+        verbose_name_plural = _(u'userinfo')
52
+
53
+    def __unicode__(self):
54
+        return unicode(self.pk)
55
+
56
+    @property
57
+    def data(self):
58
+        return {
59
+            'user_id': self.user_id,
60
+            'nickname': self.nickname,
61
+            'avatar': self.avatar,
62
+        }

+ 29 - 0
account/oauth_views.py

@@ -0,0 +1,29 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import transaction
4
+from logit import logit
5
+
6
+from account.models import UserInfo
7
+from utils.error.response_utils import response
8
+
9
+
10
+@logit
11
+@transaction.atomic
12
+def user_wx_authorize_api(request):
13
+    # Get or Create User
14
+    user, created = UserInfo.objects.select_for_update().get_or_create(unionid=request.POST.get('unionid', ''))
15
+
16
+    # Set User Key's Value
17
+    user.openid = request.POST.get('openid', '')
18
+    user.sex = request.POST.get('sex', 0)
19
+    user.nickname = request.POST.get('nickname', '') or request.POST.get('screen_name', '')
20
+    user.avatar = request.POST.get('headimgurl', '') or request.POST.get('profile_image_url', '')
21
+    user.country = request.POST.get('country', '')
22
+    user.province = request.POST.get('province', '')
23
+    user.city = request.POST.get('city', '')
24
+    user.user_status = UserInfo.ACTIVATED
25
+    user.save()
26
+
27
+    return response(200, 'User Login Success', u'用户端登录成功', {
28
+        'userinfo': user.data,
29
+    })

+ 7 - 0
account/tests.py

@@ -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.

+ 7 - 0
account/views.py

@@ -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
api/__init__.py


+ 7 - 0
api/admin.py

@@ -0,0 +1,7 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.contrib import admin
5
+
6
+
7
+# Register your models here.

+ 8 - 0
api/apps.py

@@ -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 ApiConfig(AppConfig):
8
+    name = 'api'

+ 0 - 0
api/migrations/__init__.py


+ 7 - 0
api/models.py

@@ -0,0 +1,7 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.db import models
5
+
6
+
7
+# Create your models here.

+ 7 - 0
api/tests.py

@@ -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.

+ 36 - 0
api/urls.py

@@ -0,0 +1,36 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf.urls import url
4
+
5
+from account import oauth_views
6
+from config import hot_views
7
+from intro import fav_views, intro_views
8
+from message import msg_views
9
+from support import views as support_views
10
+
11
+
12
+urlpatterns = [
13
+    url(r'^wx/authorize$', oauth_views.user_wx_authorize_api, name='user_wx_authorize_api'),  # 微信用户授权
14
+]
15
+
16
+urlpatterns += [
17
+    url(r'^intro/list$', intro_views.intro_list_api, name='intro_list_api'),  # 说明书列表
18
+    url(r'^intro/query$', intro_views.intro_query_api, name='intro_query_api'),  # 说明书搜索
19
+
20
+    url(r'^intro/fav$', fav_views.intro_fav_api, name='intro_fav_api'),  # 说明书收藏
21
+    url(r'^intro/fav/list$', fav_views.intro_fav_list_api, name='intro_fav_list_api'),  # 说明书收藏列表
22
+]
23
+
24
+urlpatterns += [
25
+    url(r'^msg/list$', msg_views.msg_list_api, name='msg_list_api'),  # 消息列表
26
+]
27
+
28
+urlpatterns += [
29
+    url(r'^hot/recommend/list$', hot_views.hot_recommend_list_api, name='hot_recommend_list_api'),  # 热门推荐列表
30
+    url(r'^hot/query/list$', hot_views.hot_query_list_api, name='hot_query_list_api'),  # 热门搜索列表
31
+]
32
+
33
+urlpatterns += [
34
+    url(r'^support/info$', support_views.support_info_api, name='support_info_api'),  # 支持预约信息
35
+    url(r'^support/prebook$', support_views.support_prebook_submit_api, name='support_prebook_submit_api'),  # 支持预约提交
36
+]

+ 4 - 0
api/views.py

@@ -0,0 +1,4 @@
1
+from django.shortcuts import render
2
+
3
+
4
+# Create your views here.

+ 9 - 0
check.sh

@@ -0,0 +1,9 @@
1
+#!/bin/bash
2
+
3
+echo '>> iSort'
4
+./isort.sh
5
+echo
6
+
7
+echo '>> PEP8'
8
+./pep8.sh
9
+echo

+ 0 - 0
config/__init__.py


+ 19 - 0
config/admin.py

@@ -0,0 +1,19 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from config.models import HotQueryInfo, HotRecommendInfo
6
+
7
+
8
+class HotQueryInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('query', 'status', 'created_at', 'updated_at')
10
+    list_filter = ('status', )
11
+
12
+
13
+class HotRecommendInfoAdmin(admin.ModelAdmin):
14
+    list_display = ('title', 'image', 'status', 'created_at', 'updated_at')
15
+    list_filter = ('status', )
16
+
17
+
18
+admin.site.register(HotQueryInfo, HotQueryInfoAdmin)
19
+admin.site.register(HotRecommendInfo, HotRecommendInfoAdmin)

+ 8 - 0
config/apps.py

@@ -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 ConfigConfig(AppConfig):
8
+    name = 'config'

+ 27 - 0
config/hot_views.py

@@ -0,0 +1,27 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from logit import logit
5
+
6
+from config.models import HotQueryInfo, HotRecommendInfo
7
+from utils.error.response_utils import response
8
+
9
+
10
+@logit
11
+def hot_recommend_list_api(request):
12
+    hots = HotRecommendInfo.objects.filter(status=True)
13
+    hots = [hot.data for hot in hots]
14
+
15
+    return response(200, 'Get Hot Recommend List Success', u'获取热门推荐列表成功', {
16
+        'hots': hots,
17
+    })
18
+
19
+
20
+@logit
21
+def hot_query_list_api(request):
22
+    querys = HotQueryInfo.objects.filter(status=True)
23
+    querys = [query.data for query in querys]
24
+
25
+    return response(200, 'Get Hot Query List Success', u'获取热门搜索列表成功', {
26
+        'querys': querys,
27
+    })

+ 46 - 0
config/migrations/0001_initial.py

@@ -0,0 +1,46 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:03
3
+from __future__ import unicode_literals
4
+
5
+import config.models
6
+from django.db import migrations, models
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    initial = True
12
+
13
+    dependencies = [
14
+    ]
15
+
16
+    operations = [
17
+        migrations.CreateModel(
18
+            name='HotQueryInfo',
19
+            fields=[
20
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
22
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
23
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
24
+                ('query', models.CharField(blank=True, help_text='\u641c\u7d22\u5173\u952e\u8bcd', max_length=255, null=True, unique=True, verbose_name='query')),
25
+            ],
26
+            options={
27
+                'verbose_name': '\u70ed\u95e8\u641c\u7d22',
28
+                'verbose_name_plural': '\u70ed\u95e8\u641c\u7d22',
29
+            },
30
+        ),
31
+        migrations.CreateModel(
32
+            name='HotRecommendInfo',
33
+            fields=[
34
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
35
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
36
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
37
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
38
+                ('title', models.CharField(blank=True, help_text='\u70ed\u95e8\u6807\u9898', max_length=255, null=True, unique=True, verbose_name='title')),
39
+                ('image', models.ImageField(blank=True, help_text='\u70ed\u95e8\u56fe\u7247', null=True, upload_to=config.models.upload_path, verbose_name='image')),
40
+            ],
41
+            options={
42
+                'verbose_name': '\u70ed\u95e8\u63a8\u8350',
43
+                'verbose_name_plural': '\u70ed\u95e8\u63a8\u8350',
44
+            },
45
+        ),
46
+    ]

+ 0 - 0
config/migrations/__init__.py


+ 58 - 0
config/models.py

@@ -0,0 +1,58 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+from django.db import models
6
+from django.utils.translation import ugettext_lazy as _
7
+from TimeConvert import TimeConvert as tc
8
+
9
+from manual.basemodels import CreateUpdateMixin
10
+from utils.url_utils import upload_file_url
11
+
12
+
13
+def upload_path(instance, old_filename):
14
+    return 'file/{ym}/{stamp}{ext}'.format(
15
+        ym=tc.local_string(format='%Y%m'),
16
+        stamp=tc.local_timestamp(ms=True),
17
+        ext=os.path.splitext(old_filename)[1].lower(),
18
+    )
19
+
20
+
21
+class HotQueryInfo(CreateUpdateMixin):
22
+    query = models.CharField(_(u'query'), max_length=255, blank=True, null=True, help_text=u'搜索关键词', unique=True)
23
+
24
+    class Meta:
25
+        verbose_name = _(u'热门搜索')
26
+        verbose_name_plural = _(u'热门搜索')
27
+
28
+    def __unicode__(self):
29
+        return unicode(self.pk)
30
+
31
+    @property
32
+    def data(self):
33
+        return {
34
+            'query': self.query,
35
+        }
36
+
37
+
38
+class HotRecommendInfo(CreateUpdateMixin):
39
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'热门标题', unique=True)
40
+    image = models.ImageField(_(u'image'), upload_to=upload_path, blank=True, null=True, help_text=u'热门图片')
41
+
42
+    class Meta:
43
+        verbose_name = _(u'热门推荐')
44
+        verbose_name_plural = _(u'热门推荐')
45
+
46
+    def __unicode__(self):
47
+        return unicode(self.pk)
48
+
49
+    @property
50
+    def image_url(self):
51
+        return upload_file_url(self.image)
52
+
53
+    @property
54
+    def data(self):
55
+        return {
56
+            'title': self.title,
57
+            'image_url': self.image_url,
58
+        }

+ 7 - 0
config/tests.py

@@ -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.

+ 7 - 0
config/views.py

@@ -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
intro/__init__.py


+ 41 - 0
intro/admin.py

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from intro.models import IntroCatalogInfo, IntroCategoryInfo, IntroContentInfo, IntroFavoriteInfo, IntroNameInfo
6
+
7
+
8
+class IntroCategoryInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('category', 'position', 'status', 'created_at', 'updated_at')
10
+    list_filter = ('status', )
11
+    search_fields = ('category', )
12
+
13
+
14
+class IntroNameInfoAdmin(admin.ModelAdmin):
15
+    list_display = ('name', 'category', 'position', 'status', 'created_at', 'updated_at')
16
+    list_filter = ('category', 'status')
17
+    search_fields = ('name', 'category__category')
18
+
19
+
20
+class IntroCatalogInfoAdmin(admin.ModelAdmin):
21
+    list_display = ('catalog', 'name', 'position', 'status', 'created_at', 'updated_at')
22
+    list_filter = ('name', 'status')
23
+    search_fields = ('catalog', 'name__name')
24
+
25
+
26
+class IntroContentInfoAdmin(admin.ModelAdmin):
27
+    list_display = ('title', 'content', 'pdf', 'catalog', 'position', 'status', 'created_at', 'updated_at')
28
+    list_filter = ('catalog', 'status')
29
+    search_fields = ('title', 'content', 'catalog__catalog')
30
+
31
+
32
+class IntroFavoriteInfoAdmin(admin.ModelAdmin):
33
+    list_display = ('user_id', 'content', 'status', 'created_at', 'updated_at')
34
+    list_filter = ('status', )
35
+
36
+
37
+admin.site.register(IntroCategoryInfo, IntroCategoryInfoAdmin)
38
+admin.site.register(IntroNameInfo, IntroNameInfoAdmin)
39
+admin.site.register(IntroCatalogInfo, IntroCatalogInfoAdmin)
40
+admin.site.register(IntroContentInfo, IntroContentInfoAdmin)
41
+admin.site.register(IntroFavoriteInfo, IntroFavoriteInfoAdmin)

+ 8 - 0
intro/apps.py

@@ -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 IntroConfig(AppConfig):
8
+    name = 'intro'

+ 45 - 0
intro/fav_views.py

@@ -0,0 +1,45 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from logit import logit
5
+from paginator import pagination
6
+
7
+from account.models import UserInfo
8
+from intro.models import IntroFavoriteInfo
9
+from utils.error.errno_utils import ProfileStatusCode
10
+from utils.error.response_utils import response
11
+
12
+
13
+@logit
14
+def intro_fav_api(request):
15
+    user_id = request.POST.get('user_id', '')
16
+    pk = int(request.POST.get('pk', 0))
17
+
18
+    try:
19
+        UserInfo.objects.get(user_id=user_id)
20
+    except UserInfo.DoesNotExist:
21
+        return response(ProfileStatusCode.PROFILE_NOT_FOUND)
22
+
23
+    IntroFavoriteInfo.objects.get_or_create(
24
+        user_id=user_id,
25
+        content=pk,
26
+    )
27
+
28
+    return response(200, 'Intro Fav Success', u'说明书收藏成功')
29
+
30
+
31
+@logit
32
+def intro_fav_list_api(request):
33
+    user_id = request.POST.get('user_id', '')
34
+    page = int(request.POST.get('page', 1))
35
+    num = int(request.POST.get('num', 10))
36
+
37
+    favs = IntroFavoriteInfo.objects.filter(user_id=user_id, status=True)
38
+    favs, left = pagination(favs, page, num)
39
+    favs = [fav.data for fav in favs]
40
+    favs = [fav for fav in favs if fav]
41
+
42
+    return response(200, 'Get Intro Fav List Success', u'获取说明书收藏列表成功', {
43
+        'favs': favs,
44
+        'left': left,
45
+    })

+ 29 - 0
intro/intro_views.py

@@ -0,0 +1,29 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from logit import logit
5
+
6
+from intro.models import IntroCategoryInfo, IntroContentInfo
7
+from utils.error.response_utils import response
8
+
9
+
10
+@logit
11
+def intro_list_api(request):
12
+    cates = IntroCategoryInfo.objects.filter(status=True).order_by('position')
13
+    cates = [cate.data for cate in cates]
14
+
15
+    return response(200, 'Get Intro List Success', u'获取说明书列表成功', {
16
+        'cates': cates,
17
+    })
18
+
19
+
20
+@logit
21
+def intro_query_api(request):
22
+    query = request.POST.get('query', '')
23
+
24
+    cates = IntroContentInfo.objects.filter(content__contains=query, status=True).order_by('position')
25
+    cates = [cate.data2 for cate in cates]
26
+
27
+    return response(200, 'Intro Query Success', u'说明书检索成功', {
28
+        'cates': cates,
29
+    })

+ 102 - 0
intro/migrations/0001_initial.py

@@ -0,0 +1,102 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:03
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import django.db.models.deletion
7
+import intro.models
8
+
9
+
10
+class Migration(migrations.Migration):
11
+
12
+    initial = True
13
+
14
+    dependencies = [
15
+    ]
16
+
17
+    operations = [
18
+        migrations.CreateModel(
19
+            name='IntroCatalogInfo',
20
+            fields=[
21
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
23
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
24
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
25
+                ('catalog', models.CharField(blank=True, help_text='\u76ee\u5f55', max_length=255, null=True, verbose_name='catalog')),
26
+                ('position', models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position')),
27
+            ],
28
+            options={
29
+                'verbose_name': '\u8bf4\u660e\u4e66\u76ee\u5f55',
30
+                'verbose_name_plural': '\u8bf4\u660e\u4e66\u76ee\u5f55',
31
+            },
32
+        ),
33
+        migrations.CreateModel(
34
+            name='IntroCategoryInfo',
35
+            fields=[
36
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
38
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
39
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
40
+                ('category', models.CharField(blank=True, help_text='\u5206\u7c7b', max_length=255, null=True, unique=True, verbose_name='category')),
41
+                ('position', models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position')),
42
+            ],
43
+            options={
44
+                'verbose_name': '\u8bf4\u660e\u4e66\u5206\u7c7b',
45
+                'verbose_name_plural': '\u8bf4\u660e\u4e66\u5206\u7c7b',
46
+            },
47
+        ),
48
+        migrations.CreateModel(
49
+            name='IntroContentInfo',
50
+            fields=[
51
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
52
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
53
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
54
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
55
+                ('title', models.CharField(blank=True, help_text='\u6807\u9898', max_length=255, null=True, verbose_name='title')),
56
+                ('content', models.CharField(blank=True, help_text='\u5185\u5bb9', max_length=255, null=True, verbose_name='content')),
57
+                ('pdf', models.FileField(blank=True, help_text='PDF \u6587\u4ef6', null=True, upload_to=intro.models.upload_path, verbose_name='pdf')),
58
+                ('position', models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position')),
59
+                ('catalog', models.ForeignKey(blank=True, help_text='\u76ee\u5f55', null=True, on_delete=django.db.models.deletion.CASCADE, to='intro.IntroCatalogInfo', verbose_name='catalog')),
60
+            ],
61
+            options={
62
+                'verbose_name': '\u8bf4\u660e\u4e66\u5185\u5bb9',
63
+                'verbose_name_plural': '\u8bf4\u660e\u4e66\u5185\u5bb9',
64
+            },
65
+        ),
66
+        migrations.CreateModel(
67
+            name='IntroFavoriteInfo',
68
+            fields=[
69
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
70
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
71
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
72
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
73
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=255, null=True, verbose_name='user_id')),
74
+                ('content', models.IntegerField(default=0, help_text='\u5185\u5bb9', verbose_name='content')),
75
+            ],
76
+            options={
77
+                'verbose_name': 'introfavoriteinfo',
78
+                'verbose_name_plural': 'introfavoriteinfo',
79
+            },
80
+        ),
81
+        migrations.CreateModel(
82
+            name='IntroNameInfo',
83
+            fields=[
84
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
85
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
86
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
87
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
88
+                ('name', models.CharField(blank=True, help_text='\u540d\u79f0', max_length=255, null=True, unique=True, verbose_name='name')),
89
+                ('position', models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position')),
90
+                ('category', models.ForeignKey(blank=True, help_text='\u5206\u7c7b', null=True, on_delete=django.db.models.deletion.CASCADE, to='intro.IntroCategoryInfo', verbose_name='category')),
91
+            ],
92
+            options={
93
+                'verbose_name': '\u8bf4\u660e\u4e66\u540d\u79f0',
94
+                'verbose_name_plural': '\u8bf4\u660e\u4e66\u540d\u79f0',
95
+            },
96
+        ),
97
+        migrations.AddField(
98
+            model_name='introcataloginfo',
99
+            name='name',
100
+            field=models.ForeignKey(blank=True, help_text='\u540d\u79f0', null=True, on_delete=django.db.models.deletion.CASCADE, to='intro.IntroNameInfo', verbose_name='name'),
101
+        ),
102
+    ]

+ 0 - 0
intro/migrations/__init__.py


+ 167 - 0
intro/models.py

@@ -0,0 +1,167 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+from django.db import models
6
+from django.utils.translation import ugettext_lazy as _
7
+from TimeConvert import TimeConvert as tc
8
+
9
+from manual.basemodels import CreateUpdateMixin
10
+from utils.url_utils import upload_file_url
11
+
12
+
13
+def upload_path(instance, old_filename):
14
+    return 'file/{ym}/{stamp}{ext}'.format(
15
+        ym=tc.local_string(format='%Y%m'),
16
+        stamp=tc.local_timestamp(ms=True),
17
+        ext=os.path.splitext(old_filename)[1].lower(),
18
+    )
19
+
20
+
21
+class IntroCategoryInfo(CreateUpdateMixin):
22
+    category = models.CharField(_(u'category'), max_length=255, blank=True, null=True, help_text=u'分类', unique=True)
23
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
24
+
25
+    class Meta:
26
+        verbose_name = _(u'说明书分类')
27
+        verbose_name_plural = _(u'说明书分类')
28
+
29
+    def __unicode__(self):
30
+        return unicode(self.category)
31
+
32
+    @property
33
+    def data(self):
34
+        names = IntroNameInfo.objects.filter(category=self.pk, status=True).order_by('position')
35
+        names = [name.data for name in names]
36
+        return {
37
+            'pk': self.pk,
38
+            'category': self.category,
39
+            'names': names,
40
+        }
41
+
42
+    @property
43
+    def data2(self):
44
+        return {
45
+            'pk': self.pk,
46
+            'category': self.category,
47
+        }
48
+
49
+
50
+class IntroNameInfo(CreateUpdateMixin):
51
+    name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'名称', unique=True)
52
+    category = models.ForeignKey(IntroCategoryInfo, verbose_name=_(u'category'), blank=True, null=True, help_text=u'分类')
53
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
54
+
55
+    class Meta:
56
+        verbose_name = _(u'说明书名称')
57
+        verbose_name_plural = _(u'说明书名称')
58
+
59
+    def __unicode__(self):
60
+        return unicode(self.name)
61
+
62
+    @property
63
+    def data(self):
64
+        catalogs = IntroCatalogInfo.objects.filter(name=self.pk, status=True).order_by('position')
65
+        catalogs = [catalog.data for catalog in catalogs]
66
+        return {
67
+            'pk': self.pk,
68
+            'name': self.name,
69
+            'catalogs': catalogs,
70
+        }
71
+
72
+    @property
73
+    def data2(self):
74
+        return {
75
+            'pk': self.pk,
76
+            'name': self.name,
77
+            'category': self.category.data2,
78
+        }
79
+
80
+
81
+class IntroCatalogInfo(CreateUpdateMixin):
82
+    catalog = models.CharField(_(u'catalog'), max_length=255, blank=True, null=True, help_text=u'目录')
83
+    name = models.ForeignKey(IntroNameInfo, verbose_name=_(u'name'), blank=True, null=True, help_text=u'名称')
84
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
85
+
86
+    class Meta:
87
+        verbose_name = _(u'说明书目录')
88
+        verbose_name_plural = _(u'说明书目录')
89
+
90
+    def __unicode__(self):
91
+        return unicode(self.catalog)
92
+
93
+    @property
94
+    def data(self):
95
+        contents = IntroContentInfo.objects.filter(catalog=self.pk, status=True).order_by('position')
96
+        contents = [content.data for content in contents]
97
+        return {
98
+            'pk': self.pk,
99
+            'catalog': self.catalog,
100
+            'contents': contents,
101
+        }
102
+
103
+    @property
104
+    def data2(self):
105
+        return {
106
+            'pk': self.pk,
107
+            'catalog': self.catalog,
108
+            'name': self.name.data2,
109
+        }
110
+
111
+
112
+class IntroContentInfo(CreateUpdateMixin):
113
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'标题')
114
+    content = models.CharField(_(u'content'), max_length=255, blank=True, null=True, help_text=u'内容')
115
+    pdf = models.FileField(_(u'pdf'), upload_to=upload_path, blank=True, null=True, help_text=u'PDF 文件')
116
+    catalog = models.ForeignKey(IntroCatalogInfo, verbose_name=_(u'catalog'), blank=True, null=True, help_text=u'目录')
117
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
118
+
119
+    class Meta:
120
+        verbose_name = _(u'说明书内容')
121
+        verbose_name_plural = _(u'说明书内容')
122
+
123
+    def __unicode__(self):
124
+        return unicode(self.title)
125
+
126
+    @property
127
+    def pdf_url(self):
128
+        return upload_file_url(self.pdf)
129
+
130
+    @property
131
+    def data(self):
132
+        return {
133
+            'pk': self.pk,
134
+            'title': self.title,
135
+            'content': self.content,
136
+            'pdf_url': self.pdf_url,
137
+        }
138
+
139
+    @property
140
+    def data2(self):
141
+        return {
142
+            'pk': self.pk,
143
+            'title': self.title,
144
+            'content': self.content,
145
+            'pdf_url': self.pdf_url,
146
+            'catalog': self.catalog.data2,
147
+        }
148
+
149
+
150
+class IntroFavoriteInfo(CreateUpdateMixin):
151
+    user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
152
+    content = models.IntegerField(_(u'content'), default=0, help_text=u'内容')
153
+
154
+    class Meta:
155
+        verbose_name = _(u'introfavoriteinfo')
156
+        verbose_name_plural = _(u'introfavoriteinfo')
157
+
158
+    def __unicode__(self):
159
+        return unicode(self.pk)
160
+
161
+    @property
162
+    def data(self):
163
+        try:
164
+            icon = IntroContentInfo.objects.get(pk=self.content)
165
+        except IntroContentInfo.DoesNotExist:
166
+            icon = None
167
+        return icon and icon.data2

+ 7 - 0
intro/tests.py

@@ -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.

+ 7 - 0
intro/views.py

@@ -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.

+ 3 - 0
isort.sh

@@ -0,0 +1,3 @@
1
+#!/bin/bash
2
+
3
+isort -rc -sp . .

+ 23 - 0
manage.py

@@ -0,0 +1,23 @@
1
+#!/usr/bin/env python
2
+import os
3
+import sys
4
+
5
+
6
+if __name__ == "__main__":
7
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "manual.settings")
8
+    try:
9
+        from django.core.management import execute_from_command_line
10
+    except ImportError:
11
+        # The above import may fail for some other reason. Ensure that the
12
+        # issue is really that Django is missing to avoid masking other
13
+        # exceptions on Python 2.
14
+        try:
15
+            import django
16
+        except ImportError:
17
+            raise ImportError(
18
+                "Couldn't import Django. Are you sure it's installed and "
19
+                "available on your PYTHONPATH environment variable? Did you "
20
+                "forget to activate a virtual environment?"
21
+            )
22
+        raise
23
+    execute_from_command_line(sys.argv)

+ 0 - 0
manual/__init__.py


+ 26 - 0
manual/basemodels.py

@@ -0,0 +1,26 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+
6
+
7
+class CreateUpdateMixin(models.Model):
8
+    status = models.BooleanField(_(u'status'), default=True, help_text=_(u'状态'), db_index=True)
9
+    created_at = models.DateTimeField(_(u'created_at'), auto_now_add=True, editable=True, help_text=_(u'创建时间'))
10
+    updated_at = models.DateTimeField(_(u'updated_at'), auto_now=True, editable=True, help_text=_(u'更新时间'))
11
+
12
+    class Meta:
13
+        abstract = True
14
+
15
+
16
+class SexChoicesMixin(models.Model):
17
+    MALE = 1
18
+    FEMALE = 0
19
+
20
+    SEX_TYPE = (
21
+        (MALE, u'男'),
22
+        (FEMALE, u'女'),
23
+    )
24
+
25
+    class Meta:
26
+        abstract = True

+ 27 - 0
manual/deploy.bak/manual.ini

@@ -0,0 +1,27 @@
1
+# manual_uwsgi.ini file
2
+[uwsgi]
3
+
4
+# Django-related settings
5
+# the base directory (full path)
6
+chdir           = /home/diors/work/manual
7
+# Django's wsgi file
8
+module          = manual.wsgi
9
+# the virtualenv (full path)
10
+# home            = /path/to/virtualenv
11
+
12
+# process-related settings
13
+# master
14
+master          = true
15
+# maximum number of worker processes
16
+processes       = 10
17
+# the socket (use the full path to be safe
18
+socket          = /home/paiai/work/manual/manual/deploy/manual.sock
19
+# ... with appropriate permissions - may be needed
20
+chmod-socket    = 777
21
+# clear environment on exit
22
+vacuum          = true
23
+
24
+# 11: Resource temporarily unavailable
25
+reload-mercy    = 64
26
+max-requests    = 8192
27
+listen          = 4096

+ 40 - 0
manual/deploy.bak/manual_nginx.conf

@@ -0,0 +1,40 @@
1
+# manual_nginx.conf
2
+
3
+# the upstream component nginx needs to connect to
4
+upstream manual {
5
+    # server unix:///home/paiai/work/manual/manual/deploy/manual.sock; # for a file socket
6
+    server 127.0.0.1:8888; # for a web port socket (we'll use this first)
7
+}
8
+
9
+# configuration of the server
10
+server {
11
+    # the port your site will be served on
12
+    listen      80;
13
+    # the domain name it will serve for
14
+    server_name .a.com; # substitute your machine's IP address or FQDN
15
+    charset     utf-8;
16
+
17
+    # max upload size
18
+    client_max_body_size 75M;   # adjust to taste
19
+
20
+    # JS接口安全域名 & 业务域名 验证
21
+    location /xxx.txt {
22
+        alias /home/paiai/work/manual/docs/we/xxx.txt;
23
+    }
24
+
25
+    # Django media
26
+    location /media  {
27
+        alias /home/paiai/work/manual/media;  # your Django project's media files - amend as required
28
+    }
29
+
30
+    location /static {
31
+        alias /home/paiai/work/manual/collect_static; # your Django project's static files - amend as required
32
+    }
33
+
34
+    # Finally, send all non-media requests to the Django server.
35
+    location / {
36
+        # uwsgi_pass  manual;
37
+        proxy_pass  http://manual;
38
+        include     /home/paiai/work/manual/manual/deploy/uwsgi_params; # the uwsgi_params file you installed
39
+    }
40
+}

+ 10 - 0
manual/deploy.bak/manual_supervisor.ini

@@ -0,0 +1,10 @@
1
+[program:manual]
2
+command=/home/paiai/env/bin/uwsgi --ini /home/paiai/work/manual/manual/deploy/manual.ini
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=QUIT
8
+stdout_logfile=/var/log/supervisor_manual_access.log
9
+stderr_logfile=/var/log/supervisor_manual_error.log
10
+user=diors

+ 15 - 0
manual/deploy.bak/uwsgi_params

@@ -0,0 +1,15 @@
1
+uwsgi_param	QUERY_STRING		$query_string;
2
+uwsgi_param	REQUEST_METHOD		$request_method;
3
+uwsgi_param	CONTENT_TYPE		$content_type;
4
+uwsgi_param	CONTENT_LENGTH		$content_length;
5
+
6
+uwsgi_param	REQUEST_URI		$request_uri;
7
+uwsgi_param	PATH_INFO		$document_uri;
8
+uwsgi_param	DOCUMENT_ROOT		$document_root;
9
+uwsgi_param	SERVER_PROTOCOL		$server_protocol;
10
+uwsgi_param	UWSGI_SCHEME		$scheme;
11
+
12
+uwsgi_param	REMOTE_ADDR		$remote_addr;
13
+uwsgi_param	REMOTE_PORT		$remote_port;
14
+uwsgi_param	SERVER_PORT		$server_port;
15
+uwsgi_param	SERVER_NAME		$server_name;

+ 16 - 0
manual/func_settings.py

@@ -0,0 +1,16 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import redis_extensions as redis
4
+
5
+
6
+def redis_conf(conf):
7
+    return {
8
+        'host': conf.get('HOST', 'localhost'),
9
+        'port': conf.get('PORT', 6379),
10
+        'password': '{0}:{1}'.format(conf.get('USER', ''), conf.get('PASSWORD', '')) if conf.get('USER') else '',
11
+        'db': conf.get('db', 0),
12
+    }
13
+
14
+
15
+def redis_connect(conf):
16
+    return redis.StrictRedisExtensions(connection_pool=redis.ConnectionPool(**redis_conf(conf)))

+ 43 - 0
manual/local_settings.py

@@ -0,0 +1,43 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+
6
+# DEBUG = False
7
+
8
+ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
9
+
10
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
12
+
13
+TEMPLATES = [
14
+    {
15
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
16
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
17
+        # 'APP_DIRS': True,
18
+        'OPTIONS': {
19
+            'context_processors': [
20
+                'django.template.context_processors.debug',
21
+                'django.template.context_processors.request',
22
+                'django.contrib.auth.context_processors.auth',
23
+                'django.contrib.messages.context_processors.messages',
24
+            ],
25
+            'loaders': [
26
+                'django.template.loaders.filesystem.Loader',
27
+                'django.template.loaders.app_directories.Loader',
28
+            ],
29
+        },
30
+    },
31
+]
32
+
33
+# DOMAIN
34
+DOMAIN = 'http://a.com'
35
+
36
+# 邮件设置
37
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
38
+SERVER_EMAIL = 'error.notify@exmail.com'
39
+EMAIL_HOST_USER = 'error.notify@exmail.com'
40
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
41
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
42
+ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
43
+EMAIL_SUBJECT_PREFIX = u'[Templet] '

+ 254 - 0
manual/settings.py

@@ -0,0 +1,254 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""
4
+Django settings for manual project.
5
+
6
+Generated by 'django-admin startproject' using Django 1.11.3.
7
+
8
+For more information on this file, see
9
+https://docs.djangoproject.com/en/1.11/topics/settings/
10
+
11
+For the full list of settings and their values, see
12
+https://docs.djangoproject.com/en/1.11/ref/settings/
13
+"""
14
+
15
+import os
16
+
17
+
18
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
19
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
21
+
22
+
23
+# Quick-start development settings - unsuitable for production
24
+# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
25
+
26
+# SECURITY WARNING: keep the secret key used in production secret!
27
+SECRET_KEY = '0=hpv21&am(7(k5ab!^zjvvl=ntj)^i@7)87t47uzumt_5rq$+'
28
+
29
+# SECURITY WARNING: don't run with debug turned on in production!
30
+DEBUG = True
31
+
32
+ALLOWED_HOSTS = []
33
+
34
+
35
+# Application definition
36
+
37
+INSTALLED_APPS = [
38
+    'django.contrib.admin',
39
+    'django.contrib.auth',
40
+    'django.contrib.contenttypes',
41
+    'django.contrib.sessions',
42
+    'django.contrib.messages',
43
+    'django.contrib.staticfiles',
44
+    'django_uniapi',
45
+    'django_we',
46
+    'account',
47
+    'api',
48
+    'config',
49
+    'intro',
50
+    'message',
51
+    'support',
52
+]
53
+
54
+MIDDLEWARE = [
55
+    'django.middleware.security.SecurityMiddleware',
56
+    'django.contrib.sessions.middleware.SessionMiddleware',
57
+    'django.middleware.common.CommonMiddleware',
58
+    # 'django.middleware.csrf.CsrfViewMiddleware',
59
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
60
+    'django.contrib.messages.middleware.MessageMiddleware',
61
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
62
+    'detect.middleware.UserAgentDetectionMiddleware',
63
+]
64
+
65
+ROOT_URLCONF = 'manual.urls'
66
+
67
+TEMPLATES = [
68
+    {
69
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
70
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
71
+        # 'APP_DIRS': True,
72
+        'OPTIONS': {
73
+            'context_processors': [
74
+                'django.template.context_processors.debug',
75
+                'django.template.context_processors.request',
76
+                'django.contrib.auth.context_processors.auth',
77
+                'django.contrib.messages.context_processors.messages',
78
+            ],
79
+            'loaders': [
80
+                ('django.template.loaders.cached.Loader', [
81
+                    'django.template.loaders.filesystem.Loader',
82
+                    'django.template.loaders.app_directories.Loader',
83
+                ]),
84
+            ],
85
+        },
86
+    },
87
+]
88
+
89
+WSGI_APPLICATION = 'manual.wsgi.application'
90
+
91
+
92
+# Database
93
+# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
94
+
95
+DATABASES = {
96
+    'default': {
97
+        'ENGINE': 'django.db.backends.mysql',
98
+        'NAME': 'manual',
99
+        'USER': 'root',
100
+        'PASSWORD': '',
101
+        'CONN_MAX_AGE': 600,
102
+        'OPTIONS': {
103
+            # Utf8mb4 for Emoji
104
+            #
105
+            # Nickname
106
+            #
107
+            # account.WechatInfo ==> nickname
108
+            #   ALTER TABLE account_wechatinfo MODIFY COLUMN nickname VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
109
+            'charset': 'utf8mb4',
110
+        },
111
+    }
112
+}
113
+
114
+
115
+# Password validation
116
+# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
117
+
118
+AUTH_PASSWORD_VALIDATORS = [
119
+    {
120
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
121
+    },
122
+    {
123
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
124
+    },
125
+    {
126
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
127
+    },
128
+    {
129
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
130
+    },
131
+]
132
+
133
+
134
+# Internationalization
135
+# https://docs.djangoproject.com/en/1.11/topics/i18n/
136
+
137
+LANGUAGE_CODE = 'zh-Hans'
138
+
139
+TIME_ZONE = 'Asia/Shanghai'
140
+
141
+USE_I18N = True
142
+
143
+USE_L10N = True
144
+
145
+USE_TZ = True
146
+
147
+
148
+# Static files (CSS, JavaScript, Images)
149
+# https://docs.djangoproject.com/en/1.11/howto/static-files/
150
+
151
+STATICFILES_DIRS = (
152
+    os.path.join(PROJ_DIR, 'static').replace('\\', '/'),
153
+)
154
+
155
+STATIC_ROOT = os.path.join(BASE_DIR, 'collect_static').replace('\\', '/')
156
+
157
+STATIC_URL = '/static/'
158
+
159
+STATICFILES_FINDERS = (
160
+    'django.contrib.staticfiles.finders.FileSystemFinder',
161
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
162
+    # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
163
+)
164
+
165
+MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
166
+
167
+MEDIA_URL = '/media/'
168
+
169
+# DOMAIN
170
+DOMAIN = 'http://a.com'
171
+
172
+# Redis 设置
173
+REDIS = {
174
+    'default': {
175
+        'HOST': '127.0.0.1',
176
+        'PORT': 6379,
177
+        'USER': '',
178
+        'PASSWORD': '',
179
+        'db': 0,
180
+    }
181
+}
182
+
183
+# 微信设置
184
+WECHAT = {
185
+    'JSAPI': {
186
+        'token': '5201314',
187
+        'appID': '',
188
+        'appsecret': '',
189
+        'mchID': '',
190
+        'apiKey': '',
191
+        'mch_cert': '',
192
+        'mch_key': '',
193
+        'redpack': {
194
+
195
+        }
196
+    },
197
+}
198
+
199
+# 邮件设置
200
+# https://docs.djangoproject.com/en/1.11/howto/error-reporting/#email-reports
201
+# When DEBUG is False, Django will email the users listed in the ADMINS setting
202
+# whenever your code raises an unhandled exception and results in an internal server error (HTTP status code 500).
203
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
204
+# Email address that error messages come from.
205
+SERVER_EMAIL = 'error.notify@exmail.com'
206
+# The email backend to use. For possible shortcuts see django.core.mail.
207
+# The default is to use the SMTP backend.
208
+# Third-party backends can be specified by providing a Python path
209
+# to a module that defines an EmailBackend class.
210
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
211
+# Host for sending email.
212
+EMAIL_HOST = 'smtp.exmail.qq.com'
213
+# Port for sending email.
214
+EMAIL_PORT = 25
215
+# Optional SMTP authentication information for EMAIL_HOST.
216
+EMAIL_HOST_USER = 'error.notify@exmail.com'
217
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
218
+EMAIL_USE_TLS = False
219
+EMAIL_USE_SSL = False
220
+EMAIL_SSL_CERTFILE = None
221
+EMAIL_SSL_KEYFILE = None
222
+EMAIL_TIMEOUT = None
223
+# Default email address to use for various automated correspondence from
224
+# the site managers.
225
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
226
+# People who get code error notifications.
227
+# In the format [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')]
228
+ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
229
+# Not-necessarily-technical managers of the site. They get broken link
230
+# notifications and other various emails.
231
+MANAGERS = ADMINS
232
+# Subject-line prefix for email messages send with django.core.mail.mail_admins
233
+# or ...mail_managers.  Make sure to include the trailing space.
234
+EMAIL_SUBJECT_PREFIX = u'[Templet] '
235
+
236
+# Admin Settings
237
+DISABLE_ACTION = False
238
+
239
+try:
240
+    from local_settings import *
241
+except ImportError:
242
+    pass
243
+
244
+# 依赖 local_settings 中的配置
245
+# 微信授权设置
246
+WECHAT_BASE_REDIRECT_URI = '{0}/we/base_redirect'.format(DOMAIN)
247
+WECHAT_USERINFO_REDIRECT_URI = '{0}/we/userinfo_redirect'.format(DOMAIN)
248
+WECHAT_OAUTH2_REDIRECT_URI = '{0}/we/oauth2?scope={{0}}redirect_url={{1}}'.format(DOMAIN)
249
+
250
+try:
251
+    from func_settings import redis_connect
252
+    REDIS_CACHE = redis_connect(REDIS.get('default', {}))
253
+except ImportError:
254
+    REDIS_CACHE = None

+ 331 - 0
manual/static/templet/js/jswe.js

@@ -0,0 +1,331 @@
1
+!(function(e, t) {
2
+    var config = {
3
+        wxconfig: 'http://api.pai.ai/wx/jsapi_signature',
4
+        callback: 'callback'
5
+    }, wxData = {
6
+        debug: false,
7
+        imgUrl: '',
8
+        link: '',
9
+        desc: '',
10
+        title: '',
11
+        timeLine: ''
12
+    }, wxConfig = {
13
+        hide: false,
14
+        close: false
15
+    }, jsApiList = [
16
+        'checkJsApi',
17
+        'onMenuShareTimeline',
18
+        'onMenuShareAppMessage',
19
+        'onMenuShareQQ',
20
+        'onMenuShareWeibo',
21
+        'hideMenuItems',
22
+        'showMenuItems',
23
+        'hideAllNonBaseMenuItem',
24
+        'showAllNonBaseMenuItem',
25
+        'translateVoice',
26
+        'startRecord',
27
+        'stopRecord',
28
+        'onRecordEnd',
29
+        'playVoice',
30
+        'pauseVoice',
31
+        'stopVoice',
32
+        'uploadVoice',
33
+        'downloadVoice',
34
+        'chooseImage',
35
+        'previewImage',
36
+        'uploadImage',
37
+        'downloadImage',
38
+        'getNetworkType',
39
+        'openLocation',
40
+        'getLocation',
41
+        'hideOptionMenu',
42
+        'showOptionMenu',
43
+        'closeWindow',
44
+        'scanQRCode',
45
+        'chooseWXPay',
46
+        'openEnterpriseRedPacket',
47
+        'openProductSpecificView',
48
+        'addCard',
49
+        'chooseCard',
50
+        'openCard'
51
+    ], wxApiFun
52
+
53
+    function isOpenOnPC() {  // 判断当前网页是否在 PC 浏览器中打开
54
+        var ua = navigator.userAgent
55
+        return /windows nt/i.test(ua) || /macintosh/i.test(ua) || /linux x86_64/i.test(ua)
56
+    }
57
+
58
+    function isOpenInWeixin() {  // 判断当前网页是否在微信内置浏览器中打开
59
+        return /micromessenger/i.test(navigator.userAgent)
60
+    }
61
+
62
+    function getWeixinVersion() {
63
+        var ua = navigator.userAgent,
64
+            mt = ua.match(/micromessenger\/([\d.]+)/i)
65
+        return (mt ? mt[1] : '')
66
+    }
67
+
68
+    // This function checks whether Wechat is the appointed version or not
69
+    // Cmp: http://jsperf.com/regexp-test-vs-indexof-ignore-upper-and-lower
70
+    function isWeixinVersion(version) {
71
+        // return new RegExp('micromessenger/' + version , 'i').test(navigator.userAgent)
72
+        return navigator.userAgent.toLowerCase().indexOf('micromessenger/' + version) != -1
73
+    }
74
+
75
+    function hideOptionMenu() {
76
+        wxConfig.hide = true
77
+        fixedWxData()
78
+    }
79
+
80
+    function showOptionMenu() {
81
+        wxConfig.hide = false
82
+        fixedWxData()
83
+    }
84
+
85
+    function closeWindow() {
86
+        wxConfig.close = true
87
+        fixedWxData()
88
+    }
89
+
90
+    function wxReady(data) {
91
+        data = typeof data === 'object' ? data : JSON.parse(data)
92
+        wx.config({
93
+            debug: wxData.debug,
94
+            appId: data.appId,
95
+            timestamp: data.timestamp,
96
+            nonceStr: data.nonceStr,
97
+            signature: data.signature,
98
+            jsApiList: jsApiList
99
+        })
100
+
101
+        var callbacks = {
102
+            trigger: function (res) {
103
+                // alert('用户点击发送给朋友')
104
+                if (JSWE.wxTrigger) {JSWE.wxTrigger(res)}
105
+            },
106
+            success: function (res) {
107
+                // alert('已分享')
108
+                if (JSWE.wxSuccess) {JSWE.wxSuccess(res)}
109
+            },
110
+            cancel: function (res) {
111
+                // alert('已取消')
112
+                if (JSWE.wxCancel) {JSWE.wxCancel(res)}
113
+            },
114
+            fail: function (res) {
115
+                // alert(JSON.stringify(res))
116
+                if (JSWE.wxFail) {JSWE.wxFail(res)}
117
+            }
118
+        }, shareInfo = function(flag) {
119
+            var _share = {
120
+                title: flag ? wxData.title : (wxData.timeLine || wxData.desc),
121
+                link: wxData.link,
122
+                imgUrl: wxData.imgUrl,
123
+                trigger: callbacks.trigger,
124
+                success: callbacks.success,
125
+                cancel: callbacks.cancel,
126
+                fail: callbacks.fail
127
+            }
128
+            if (flag) _share.desc = wxData.desc
129
+            return _share
130
+        }, wxShareApi = function() {
131
+            // 2. 分享接口
132
+            // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
133
+            wx.onMenuShareAppMessage(shareInfo(1))
134
+            // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
135
+            wx.onMenuShareTimeline(shareInfo(0))
136
+            // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口
137
+            wx.onMenuShareQQ(shareInfo(1))
138
+            // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
139
+            wx.onMenuShareWeibo(shareInfo(1))
140
+        }, wxMenuApi = function () {
141
+            // 8. 界面操作接口
142
+            // 8.1 隐藏右上角菜单
143
+            // 8.2 显示右上角菜单
144
+            if (wxConfig.hide) {wx.hideOptionMenu()} else {wx.showOptionMenu()}
145
+            // 8.7 关闭当前窗口
146
+            if (wxConfig.close) {wx.closeWindow()}
147
+        }, wxApi = function () {
148
+            wxShareApi()
149
+            wxMenuApi()
150
+        }
151
+
152
+        wx.ready(wxApi)
153
+
154
+        return wxApiFun = wxApi
155
+    }
156
+
157
+    if (isOpenInWeixin() || isOpenOnPC()) {
158
+        if ('undefined' !== typeof JSWE_CONF_UPDATE) JSWE_CONF_UPDATE(config)
159
+        $.ajax({
160
+            url: config.wxconfig,
161
+            type: 'get',
162
+            dataType: 'jsonp',
163
+            jsonpCallback: config.callback,
164
+            data: {
165
+                url: window.location.href.split('#')[0]
166
+            },
167
+            success: wxReady
168
+        })
169
+    }
170
+
171
+    function initWxData(data, flag) {
172
+        for(var d in data) {if (d in wxData) wxData[d] = data[d]}
173
+        if (flag) fixedWxData()
174
+    }
175
+
176
+    function changeWxData(key, value, flag) {
177
+        if (key in falDwxDataata) {wxData[key] = value}
178
+        if (flag) fixedWxData()
179
+    }
180
+
181
+    function fixedWxData() {
182
+        if ('undefined' !== typeof wxApiFun) wxApiFun()
183
+    }
184
+
185
+    // 5 图片接口
186
+    // 5.1 拍照、本地选图
187
+    var images = {
188
+        localIds: [],
189
+        serverIds: []
190
+    };
191
+    // function chooseImage(count, directUpload, isShowProgressTips) {
192
+    function chooseImage(choose_params) {
193
+        if ('undefined' === typeof choose_params) choose_params = {}
194
+        wx.chooseImage({
195
+            count: choose_params.count || 9, // 默认9
196
+            sizeType: choose_params.sizeType || ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
197
+            sourceType: choose_params.sourceType || ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
198
+            success: function (res) {
199
+                images.localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
200
+                // 判断是否直接上传
201
+                if (choose_params.directUpload) {setTimeout(uploadImages({localIds: images.localIds, isShowProgressTips: choose_params.isShowProgressTips || 1}), 100)}
202
+                // 拍照、本地选图成功后的回调函数
203
+                if (JSWE.wxChooseImageSuccess) {JSWE.wxChooseImageSuccess(res)}
204
+            }
205
+        });
206
+    }
207
+
208
+    // 5.2 图片预览
209
+    function previewImage(preview_params) {
210
+        wx.previewImage({
211
+            current: preview_params.current, // 当前显示图片的链接,不填则默认为 urls 的第一张
212
+            urls: preview_params.urls // 需要预览的图片链接列表
213
+        });
214
+    }
215
+
216
+    // 5.3 上传图片
217
+    // function uploadImage(localId, isShowProgressTips) {
218
+    function uploadImage(upload_params) {
219
+        // 上传图片为异步处理,重复上传同一图片,返回的serverId也是不同的
220
+        wx.uploadImage({
221
+            localId: upload_params.localId, // 需要上传的图片的本地ID,由chooseImage接口获得
222
+            isShowProgressTips: upload_params.isShowProgressTips || 1, // 默认为1,显示进度提示
223
+            success: function (res) {
224
+                images.serverIds.push(res.serverId); // 返回图片的服务器端ID
225
+                // 上传图片成功后的回调函数
226
+                if (JSWE.wxUploadImageSuccess) {JSWE.wxUploadImageSuccess(res)}
227
+            }
228
+        });
229
+    }
230
+
231
+    // function uploadImages(localIds, isShowProgressTips) {
232
+    function uploadImages(upload_params) {
233
+        var localIds = upload_params.localIds, isShowProgressTips = upload_params.isShowProgressTips || 1
234
+        images.serverIds = [];
235
+        for (var idx in localIds) {uploadImage({localId: localIds[idx], isShowProgressTips: isShowProgressTips})}
236
+    }
237
+
238
+    // 9 微信原生接口
239
+    // 9.1.1 扫描二维码并返回结果
240
+    // 9.1.2 扫描二维码并返回结果
241
+    function scanQRCode(scan_params) {
242
+        if ('undefined' === typeof scan_params) scan_params = {}
243
+        wx.scanQRCode({
244
+            needResult: scan_params.needResult || 0,  // 默认为0,0扫描结果由微信处理,1直接返回扫描结果
245
+            scanType: scan_params.scanType || ['qrCode', 'barCode'],  // 可以指定扫二维码还是一维码,默认二者都有
246
+            success: function (res) {  // 当 needResult 为 1 时,扫码返回的结果
247
+                if (JSWE.wxScanQRCodeSuccess) {JSWE.wxScanQRCodeSuccess(res)}
248
+            }
249
+        });
250
+    }
251
+
252
+    // QRCode & BarCode is different
253
+    function parseScanQRCodeResultStr(resultStr) {
254
+        var strs = resultStr.split(',')
255
+        return strs[strs.length - 1]
256
+    }
257
+
258
+    // 10 微信支付接口
259
+    // 10.1 发起一个支付请求
260
+    function chooseWXPay(wxpay_params) {
261
+        wx.chooseWXPay({
262
+            timestamp: wxpay_params.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
263
+            nonceStr: wxpay_params.nonceStr, // 支付签名随机串,不长于 32 位
264
+            package: wxpay_params.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
265
+            signType: wxpay_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
266
+            paySign: wxpay_params.paySign, // 支付签名
267
+            success: function (res) {
268
+                // 支付成功后的回调函数
269
+                if (JSWE.wxPaySuccess) {JSWE.wxPaySuccess(res)}
270
+            }
271
+        })
272
+    }
273
+
274
+    // xx 微信原生企业红包接口
275
+    // xx.1 发起一个发送原生企业红包请求
276
+    function openEnterpriseRedPacket(wxredpack_params) {
277
+        wx.openEnterpriseRedPacket({
278
+            timeStamp: wxredpack_params.timeStamp, // 红包签名时间戳,注意原生企业红包接口timeStamp字段名需大写其中的S字符,而支付接口timeStamp字段名无需大写其中的S字符。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
279
+            nonceStr: wxredpack_params.nonceStr, // 红包签名随机串,不长于 32 位
280
+            package: encodeURIComponent(wxredpack_params.package), // 发放红包接口返回的prepay_id参数值,提交格式如:prepay_id=***)
281
+            signType: wxredpack_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
282
+            paySign: wxredpack_params.paySign, // 红包签名
283
+            success: function (res) {
284
+                // 发送原生企业红包成功后的回调函数
285
+                if (JSWE.wxEnterpriseRedPacketSuccess) {JSWE.wxEnterpriseRedPacketSuccess(res)}
286
+            }
287
+        })
288
+    }
289
+
290
+    var v = {
291
+        version: '1.0.5',
292
+
293
+        // Basic Vars
294
+        config: config,
295
+        wxData: wxData,
296
+        jsApiList: jsApiList,
297
+
298
+        // Weixin Function
299
+        isOpenInWeixin: isOpenInWeixin,
300
+        getWeixinVersion: getWeixinVersion,
301
+        isWeixinVersion: isWeixinVersion,
302
+
303
+        // Menu Function
304
+        hideOptionMenu: hideOptionMenu,
305
+        showOptionMenu: showOptionMenu,
306
+        closeWindow: closeWindow,
307
+
308
+        // Share Function
309
+        initWxData: initWxData,
310
+        changeWxData: changeWxData,
311
+        fixedWxData: fixedWxData,
312
+
313
+        // Image Function
314
+        images: images,
315
+        chooseImage: chooseImage,
316
+        previewImage: previewImage,
317
+        uploadImage: uploadImage,
318
+        uploadImages: uploadImages,
319
+
320
+        // Scan Function
321
+        scanQRCode: scanQRCode,
322
+        parseScanQRCodeResultStr: parseScanQRCodeResultStr,
323
+
324
+        // Pay Function
325
+        chooseWXPay: chooseWXPay,
326
+
327
+        // EnterpriseRedPacket Function
328
+        openEnterpriseRedPacket: openEnterpriseRedPacket
329
+    }
330
+    e.JSWE = e.V = v
331
+})(window)

+ 32 - 0
manual/urls.py

@@ -0,0 +1,32 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""manual URL Configuration
4
+
5
+The `urlpatterns` list routes URLs to views. For more information please see:
6
+    https://docs.djangoproject.com/en/1.11/topics/http/urls/
7
+Examples:
8
+Function views
9
+    1. Add an import:  from my_app import views
10
+    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
11
+Class-based views
12
+    1. Add an import:  from other_app.views import Home
13
+    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
14
+Including another URLconf
15
+    1. Import the include() function: from django.conf.urls import url, include
16
+    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
17
+"""
18
+from django.conf import settings
19
+from django.conf.urls import include, url
20
+from django.conf.urls.static import static
21
+from django.contrib import admin
22
+
23
+
24
+urlpatterns = [
25
+    url(r'^admin/', admin.site.urls),
26
+    url(r'^api/', include('api.urls', namespace='api')),
27
+    url(r'^uniapi/', include('django_uniapi.urls', namespace='uniapi')),
28
+    url(r'^we/', include('django_we.urls', namespace='wechat')),
29
+]
30
+
31
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
32
+urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 17 - 0
manual/wsgi.py

@@ -0,0 +1,17 @@
1
+"""
2
+WSGI config for manual project.
3
+
4
+It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+For more information on this file, see
7
+https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
8
+"""
9
+
10
+import os
11
+
12
+from django.core.wsgi import get_wsgi_application
13
+
14
+
15
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "manual.settings")
16
+
17
+application = get_wsgi_application()

BIN
media/file/201708/1502869267.33.png


BIN
media/file/201708/1502869283.66.png


BIN
media/file/201708/1502870309.11.png


BIN
media/file/201708/1502870443.78.png


+ 0 - 0
message/__init__.py


+ 13 - 0
message/admin.py

@@ -0,0 +1,13 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from message.models import MessageInfo
6
+
7
+
8
+class MessageInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('msg_image', 'msg_image_airtime', 'msg_image_deadline', 'status', 'created_at', 'updated_at')
10
+    list_filter = ('status', )
11
+
12
+
13
+admin.site.register(MessageInfo, MessageInfoAdmin)

+ 8 - 0
message/apps.py

@@ -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 MessageConfig(AppConfig):
8
+    name = 'message'

+ 33 - 0
message/migrations/0001_initial.py

@@ -0,0 +1,33 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:03
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import message.models
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    initial = True
12
+
13
+    dependencies = [
14
+    ]
15
+
16
+    operations = [
17
+        migrations.CreateModel(
18
+            name='MessageInfo',
19
+            fields=[
20
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
22
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
23
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
24
+                ('msg_image', models.ImageField(blank=True, help_text='\u6d88\u606f\u56fe\u7247', null=True, upload_to=message.models.upload_path, verbose_name='msg_image')),
25
+                ('msg_image_airtime', models.DateTimeField(blank=True, help_text='\u6d88\u606f\u56fe\u7247\u5f00\u59cb\u65e5\u671f', null=True, verbose_name='msg_image_airtime')),
26
+                ('msg_image_deadline', models.DateTimeField(blank=True, help_text='\u6d88\u606f\u56fe\u7247\u622a\u6b62\u65e5\u671f', null=True, verbose_name='msg_image_deadline')),
27
+            ],
28
+            options={
29
+                'verbose_name': 'messageinfo',
30
+                'verbose_name_plural': 'messageinfo',
31
+            },
32
+        ),
33
+    ]

+ 19 - 0
message/migrations/0002_auto_20170816_1604.py

@@ -0,0 +1,19 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 08:04
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('message', '0001_initial'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterModelOptions(
16
+            name='messageinfo',
17
+            options={'verbose_name': '\u56fe\u7247\u6d88\u606f', 'verbose_name_plural': '\u56fe\u7247\u6d88\u606f'},
18
+        ),
19
+    ]

+ 0 - 0
message/migrations/__init__.py


+ 41 - 0
message/models.py

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+from django.db import models
6
+from django.utils.translation import ugettext_lazy as _
7
+from TimeConvert import TimeConvert as tc
8
+
9
+from manual.basemodels import CreateUpdateMixin
10
+from utils.url_utils import upload_file_url
11
+
12
+
13
+def upload_path(instance, old_filename):
14
+    return 'file/{ym}/{stamp}{ext}'.format(
15
+        ym=tc.local_string(format='%Y%m'),
16
+        stamp=tc.local_timestamp(ms=True),
17
+        ext=os.path.splitext(old_filename)[1].lower(),
18
+    )
19
+
20
+
21
+class MessageInfo(CreateUpdateMixin):
22
+    msg_image = models.ImageField(_(u'msg_image'), upload_to=upload_path, blank=True, null=True, help_text=u'消息图片')
23
+    msg_image_airtime = models.DateTimeField(_(u'msg_image_airtime'), blank=True, null=True, help_text=u'消息图片开始日期')
24
+    msg_image_deadline = models.DateTimeField(_(u'msg_image_deadline'), blank=True, null=True, help_text=u'消息图片截止日期')
25
+
26
+    class Meta:
27
+        verbose_name = _(u'图片消息')
28
+        verbose_name_plural = _(u'图片消息')
29
+
30
+    def __unicode__(self):
31
+        return unicode(self.pk)
32
+
33
+    @property
34
+    def msg_image_url(self):
35
+        return upload_file_url(self.msg_image)
36
+
37
+    @property
38
+    def data(self):
39
+        return {
40
+            'msg_image_url': self.msg_image_url,
41
+        }

+ 19 - 0
message/msg_views.py

@@ -0,0 +1,19 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from logit import logit
5
+from TimeConvert import TimeConvert as tc
6
+
7
+from message.models import MessageInfo
8
+from utils.error.response_utils import response
9
+
10
+
11
+@logit
12
+def msg_list_api(request):
13
+    curdt = tc.utc_datetime()
14
+    msgs = MessageInfo.objects.filter(msg_image_airtime__lte=curdt, msg_image_deadline__gt=curdt, status=True)
15
+    msgs = [msg.data for msg in msgs]
16
+
17
+    return response(200, 'Get Message List Success', u'获取消息列表成功', {
18
+        'msgs': msgs,
19
+    })

+ 7 - 0
message/tests.py

@@ -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.

+ 7 - 0
message/views.py

@@ -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.

+ 9 - 0
pep8.sh

@@ -0,0 +1,9 @@
1
+#!/bin/bash
2
+
3
+# Ignoring autogenerated files
4
+#  -- Migration directories
5
+# Ignoring error codes
6
+#  -- E128 continuation line under-indented for visual indent
7
+#  -- E501 line too long
8
+
9
+pep8 --exclude=migrations --ignore=E128,E501 .

+ 15 - 0
requirements.txt

@@ -0,0 +1,15 @@
1
+-e git+https://github.com/andymccurdy/redis-py.git#egg=redis-py
2
+Django==1.11.3
3
+MySQL-python==1.2.5
4
+StatusCode==1.0.0
5
+TimeConvert==1.4.1
6
+django-admin==1.0.4
7
+django-detect==1.0.5
8
+django-json-response==1.1.5
9
+django-logit==1.0.6
10
+django-paginator2==1.0.3
11
+django-shortuuidfield==0.1.3
12
+django-uniapi==1.0.0
13
+django-we==1.0.7
14
+hiredis==0.2.0
15
+redis-extensions==1.1.1

+ 0 - 0
support/__init__.py


+ 25 - 0
support/admin.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.contrib import admin
4
+
5
+from support.models import MachineBackInfo, MachineBodyInfo, MachineSupportPrebookInfo
6
+
7
+
8
+class MachineBodyInfoAdmin(admin.ModelAdmin):
9
+    list_display = ('body', 'status', 'created_at', 'updated_at')
10
+    list_filter = ('status', )
11
+
12
+
13
+class MachineBackInfoAdmin(admin.ModelAdmin):
14
+    list_display = ('back', 'status', 'created_at', 'updated_at')
15
+    list_filter = ('status', )
16
+
17
+
18
+class MachineSupportPrebookInfoAdmin(admin.ModelAdmin):
19
+    list_display = ('user_id', 'name', 'sex', 'phone', 'weekday', 'timeslice', 'body', 'back', 'handle_status', 'status', 'created_at', 'updated_at')
20
+    list_filter = ('weekday', 'timeslice', 'body', 'back', 'handle_status', 'status')
21
+
22
+
23
+admin.site.register(MachineBodyInfo, MachineBodyInfoAdmin)
24
+admin.site.register(MachineBackInfo, MachineBackInfoAdmin)
25
+admin.site.register(MachineSupportPrebookInfo, MachineSupportPrebookInfoAdmin)

+ 8 - 0
support/apps.py

@@ -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 SupportConfig(AppConfig):
8
+    name = 'support'

+ 66 - 0
support/migrations/0001_initial.py

@@ -0,0 +1,66 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:03
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import django.db.models.deletion
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    initial = True
12
+
13
+    dependencies = [
14
+    ]
15
+
16
+    operations = [
17
+        migrations.CreateModel(
18
+            name='MachineBackInfo',
19
+            fields=[
20
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
22
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
23
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
24
+                ('back', models.CharField(blank=True, help_text='\u673a\u80cc', max_length=255, null=True, unique=True, verbose_name='back')),
25
+            ],
26
+            options={
27
+                'verbose_name': '\u673a\u80cc\u914d\u7f6e',
28
+                'verbose_name_plural': '\u673a\u80cc\u914d\u7f6e',
29
+            },
30
+        ),
31
+        migrations.CreateModel(
32
+            name='MachineBodyInfo',
33
+            fields=[
34
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
35
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
36
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
37
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
38
+                ('body', models.CharField(blank=True, help_text='\u673a\u8eab', max_length=255, null=True, unique=True, verbose_name='body')),
39
+            ],
40
+            options={
41
+                'verbose_name': '\u673a\u8eab\u914d\u7f6e',
42
+                'verbose_name_plural': '\u673a\u8eab\u914d\u7f6e',
43
+            },
44
+        ),
45
+        migrations.CreateModel(
46
+            name='MachineSupportPrebookInfo',
47
+            fields=[
48
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
49
+                ('status', models.BooleanField(db_index=True, default=True, help_text='\u72b6\u6001', verbose_name='status')),
50
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at')),
51
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at')),
52
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=255, null=True, verbose_name='user_id')),
53
+                ('name', models.CharField(blank=True, help_text='\u7528\u6237\u59d3\u540d', max_length=255, null=True, verbose_name='name')),
54
+                ('sex', models.IntegerField(choices=[(1, '\u7537'), (0, '\u5973')], default=1, help_text='\u7528\u6237\u6027\u522b', verbose_name='sex')),
55
+                ('phone', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u7535\u8bdd', max_length=255, null=True, verbose_name='phone')),
56
+                ('weekday', models.IntegerField(db_index=True, default=0, help_text='\u5468\u51e0\uff1a\u5468\u65e5\u4e3a0', verbose_name='weekday')),
57
+                ('timeslice', models.IntegerField(choices=[(0, '09:00 - 12:00'), (0, '12:00 - 14:00'), (0, '14:00 - 18:00'), (0, '18:00 - 21:00')], db_index=True, default=0, help_text='\u65f6\u95f4\u6bb5', verbose_name='timeslice')),
58
+                ('back', models.ForeignKey(blank=True, help_text='\u673a\u80cc', null=True, on_delete=django.db.models.deletion.CASCADE, to='support.MachineBackInfo', verbose_name='back')),
59
+                ('body', models.ForeignKey(blank=True, help_text='\u673a\u8eab', null=True, on_delete=django.db.models.deletion.CASCADE, to='support.MachineBodyInfo', verbose_name='body')),
60
+            ],
61
+            options={
62
+                'verbose_name': 'machinesupportprebookinfo',
63
+                'verbose_name_plural': 'machinesupportprebookinfo',
64
+            },
65
+        ),
66
+    ]

+ 25 - 0
support/migrations/0002_auto_20170816_1509.py

@@ -0,0 +1,25 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:09
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('support', '0001_initial'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='machinebackinfo',
17
+            name='position',
18
+            field=models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position'),
19
+        ),
20
+        migrations.AddField(
21
+            model_name='machinebodyinfo',
22
+            name='position',
23
+            field=models.IntegerField(default=1, help_text='\u6392\u5e8f', verbose_name='position'),
24
+        ),
25
+    ]

+ 20 - 0
support/migrations/0003_machinesupportprebookinfo_handle_status.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 07:34
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('support', '0002_auto_20170816_1509'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='machinesupportprebookinfo',
17
+            name='handle_status',
18
+            field=models.IntegerField(choices=[(0, '\u672a\u5904\u7406'), (1, '\u5df2\u8054\u7cfb'), (10, '\u5df2\u5904\u7406')], default=0, help_text='\u5904\u7406\u72b6\u6001', verbose_name='handle_status'),
19
+        ),
20
+    ]

+ 20 - 0
support/migrations/0004_auto_20170816_1604.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.3 on 2017-08-16 08:04
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('support', '0003_machinesupportprebookinfo_handle_status'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AlterField(
16
+            model_name='machinesupportprebookinfo',
17
+            name='timeslice',
18
+            field=models.IntegerField(choices=[(0, '09:00 - 12:00'), (1, '12:00 - 14:00'), (2, '14:00 - 18:00'), (3, '18:00 - 21:00')], db_index=True, default=0, help_text='\u65f6\u95f4\u6bb5', verbose_name='timeslice'),
19
+        ),
20
+    ]

+ 0 - 0
support/migrations/__init__.py


+ 94 - 0
support/models.py

@@ -0,0 +1,94 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import ugettext_lazy as _
5
+
6
+from manual.basemodels import CreateUpdateMixin
7
+
8
+
9
+class MachineBodyInfo(CreateUpdateMixin):
10
+    body = models.CharField(_(u'body'), max_length=255, blank=True, null=True, help_text=u'机身', unique=True)
11
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
12
+
13
+    class Meta:
14
+        verbose_name = _(u'机身配置')
15
+        verbose_name_plural = _(u'机身配置')
16
+
17
+    def __unicode__(self):
18
+        return unicode(self.body)
19
+
20
+    @property
21
+    def data(self):
22
+        return {
23
+            'pk': self.pk,
24
+            'body': self.body,
25
+        }
26
+
27
+
28
+class MachineBackInfo(CreateUpdateMixin):
29
+    back = models.CharField(_(u'back'), max_length=255, blank=True, null=True, help_text=u'机背', unique=True)
30
+    position = models.IntegerField(_(u'position'), default=1, help_text=u'排序')
31
+
32
+    class Meta:
33
+        verbose_name = _(u'机背配置')
34
+        verbose_name_plural = _(u'机背配置')
35
+
36
+    def __unicode__(self):
37
+        return unicode(self.back)
38
+
39
+    @property
40
+    def data(self):
41
+        return {
42
+            'pk': self.pk,
43
+            'back': self.back,
44
+        }
45
+
46
+
47
+class MachineSupportPrebookInfo(CreateUpdateMixin):
48
+    MALE = 1
49
+    FEMALE = 0
50
+
51
+    SEX_TYPE = (
52
+        (MALE, u'男'),
53
+        (FEMALE, u'女'),
54
+    )
55
+
56
+    SLICE0 = 0
57
+    SLICE1 = 1
58
+    SLICE2 = 2
59
+    SLICE3 = 3
60
+
61
+    TIME_SLICE = (
62
+        (SLICE0, u'09:00 - 12:00'),
63
+        (SLICE1, u'12:00 - 14:00'),
64
+        (SLICE2, u'14:00 - 18:00'),
65
+        (SLICE3, u'18:00 - 21:00'),
66
+    )
67
+
68
+    NOT_HANDLE = 0
69
+    HAS_CONTACTED = 1
70
+    HAS_HANDLED = 10
71
+
72
+    HANDLE_STATUS = (
73
+        (NOT_HANDLE, u'未处理'),
74
+        (HAS_CONTACTED, u'已联系'),
75
+        (HAS_HANDLED, u'已处理'),
76
+    )
77
+
78
+    user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
79
+    name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'用户姓名')
80
+    sex = models.IntegerField(_(u'sex'), choices=SEX_TYPE, default=MALE, help_text=u'用户性别')
81
+    phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'用户电话', db_index=True)
82
+    weekday = models.IntegerField(_(u'weekday'), default=0, help_text=u'周几:周日为0', db_index=True)
83
+    timeslice = models.IntegerField(_(u'timeslice'), default=SLICE0, choices=TIME_SLICE, help_text=u'时间段', db_index=True)
84
+    body = models.ForeignKey(MachineBodyInfo, verbose_name=_(u'body'), blank=True, null=True, help_text=u'机身')
85
+    back = models.ForeignKey(MachineBackInfo, verbose_name=_(u'back'), blank=True, null=True, help_text=u'机背')
86
+
87
+    handle_status = models.IntegerField(_(u'handle_status'), choices=HANDLE_STATUS, default=NOT_HANDLE, help_text=u'处理状态')
88
+
89
+    class Meta:
90
+        verbose_name = _(u'machinesupportprebookinfo')
91
+        verbose_name_plural = _(u'machinesupportprebookinfo')
92
+
93
+    def __unicode__(self):
94
+        return unicode(self.pk)

+ 7 - 0
support/tests.py

@@ -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.

+ 57 - 0
support/views.py

@@ -0,0 +1,57 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from logit import logit
5
+
6
+from support.models import MachineBackInfo, MachineBodyInfo, MachineSupportPrebookInfo
7
+from utils.error.errno_utils import MachineStatusCode
8
+from utils.error.response_utils import response
9
+
10
+
11
+@logit
12
+def support_info_api(request):
13
+    bodys = MachineBodyInfo.objects.filter(status=True).order_by('position')
14
+    bodys = [body.data for body in bodys]
15
+
16
+    backs = MachineBackInfo.objects.filter(status=True).order_by('position')
17
+    backs = [back.data for back in backs]
18
+
19
+    return response(200, 'Get Support Info Success', u'获取支持信息成功', {
20
+        'bodys': bodys,
21
+        'backs': backs,
22
+    })
23
+
24
+
25
+@logit
26
+def support_prebook_submit_api(request):
27
+    user_id = request.POST.get('user_id', '')
28
+    name = request.POST.get('name', '')
29
+    sex = int(request.POST.get('sex', 0))
30
+    phone = request.POST.get('phone', '')
31
+    weekday = int(request.POST.get('weekday', 0))
32
+    timeslice = int(request.POST.get('timeslice', 0))
33
+    body = int(request.POST.get('body', 0))
34
+    back = int(request.POST.get('back', 0))
35
+
36
+    try:
37
+        body = MachineBodyInfo.objects.get(pk=body)
38
+    except MachineBodyInfo.DoesNotExist:
39
+        return response(MachineStatusCode.MACHINE_BODY_NOT_FOUND)
40
+
41
+    try:
42
+        back = MachineBackInfo.objects.get(pk=back)
43
+    except MachineBackInfo.DoesNotExist:
44
+        return response(MachineStatusCode.MACHINE_BACK_NOT_FOUND)
45
+
46
+    MachineSupportPrebookInfo.objects.create(
47
+        user_id=user_id,
48
+        name=name,
49
+        sex=sex,
50
+        phone=phone,
51
+        weekday=weekday,
52
+        timeslice=timeslice,
53
+        body=body,
54
+        back=back,
55
+    )
56
+
57
+    return response(200, 'Submit Support Info Success', u'提交支持信息成功')

+ 0 - 0
utils/__init__.py


+ 0 - 0
utils/error/__init__.py


+ 43 - 0
utils/error/errno_utils.py

@@ -0,0 +1,43 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from StatusCode import BaseStatusCode, StatusCodeField
4
+
5
+
6
+class ProfileStatusCode(BaseStatusCode):
7
+    """ 用户相关错误码 4001xx """
8
+    PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description=u'用户不存在')
9
+    # 手机号
10
+    PHONE_ALREADY_EXISTS = StatusCodeField(400105, 'Phone Already Exists', description=u'手机号已经存在')
11
+
12
+
13
+class MachineStatusCode(BaseStatusCode):
14
+    """ 机器相关错误码 4021xx """
15
+    MACHINE_BODY_NOT_FOUND = StatusCodeField(402101, 'Machine Body Not Found', description=u'机身不存在')
16
+    MACHINE_BACK_NOT_FOUND = StatusCodeField(402102, 'Machine Back Not Found', description=u'机背不存在')
17
+
18
+
19
+class OrderStatusCode(BaseStatusCode):
20
+    """ 订单/支付相关错误码 4040xx """
21
+    UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description=u'统一下单失败')
22
+    ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description=u'订单不存在')
23
+    # 订单支付状态
24
+    ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description=u'订单未支付')
25
+    ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description=u'订单支付中')
26
+    ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description=u'微信支付失败')
27
+    # 通知校验状态
28
+    SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description=u'签名校验失败')
29
+    FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description=u'金额校验失败')
30
+
31
+
32
+class PayStatusCode(BaseStatusCode):
33
+    """ 支付相关错误码 4041xx """
34
+
35
+
36
+class WithdrawStatusCode(BaseStatusCode):
37
+    """ 提现相关错误码 4042xx """
38
+    BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description=u'提现金额不足')
39
+
40
+
41
+class TokenStatusCode(BaseStatusCode):
42
+    """ 票据相关错误码 4090xx """
43
+    TOKEN_NOT_FOUND = StatusCodeField(409901, 'Token Not Found', description=u'票据不存在')

+ 18 - 0
utils/error/response_utils.py

@@ -0,0 +1,18 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.http import JsonResponse
4
+from StatusCode import StatusCodeField
5
+
6
+
7
+def response_data(status_code=200, message=None, description=None, data={}, **kwargs):
8
+    return dict({
9
+        'status': status_code,
10
+        'message': message,
11
+        'description': description,
12
+        'data': data,
13
+    }, **kwargs)
14
+
15
+
16
+def response(status_code=200, message=None, description=None, data={}, **kwargs):
17
+    message, description = (message or status_code.message, description or status_code.description) if isinstance(status_code, StatusCodeField) else (message, description)
18
+    return JsonResponse(response_data(status_code, message, description, data, **kwargs), safe=False)

+ 0 - 0
utils/redis/__init__.py


+ 6 - 0
utils/redis/connect.py

@@ -0,0 +1,6 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+
5
+
6
+r = settings.REDIS_CACHE

+ 1 - 0
utils/redis/rkeys.py

@@ -0,0 +1 @@
1
+# -*- coding: utf-8 -*-

+ 7 - 0
utils/url_utils.py

@@ -0,0 +1,7 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+
5
+
6
+def upload_file_url(file_path):
7
+    return file_path and ('{}{}'.format(settings.DOMAIN, file_path.url)) or ''

adminSystem - Gogs: Go Git Service

Brak opisu

README.md 1.8KB

sigmund

Quick and dirty signatures for Objects.

This is like a much faster deepEquals comparison, which returns a string key suitable for caches and the like.

Usage

function doSomething (someObj) {
  var key = sigmund(someObj, maxDepth) // max depth defaults to 10
  var cached = cache.get(key)
  if (cached) return cached

  var result = expensiveCalculation(someObj)
  cache.set(key, result)
  return result
}

The resulting key will be as unique and reproducible as calling JSON.stringify or util.inspect on the object, but is much faster. In order to achieve this speed, some differences are glossed over. For example, the object {0:'foo'} will be treated identically to the array ['foo'].

Also, just as there is no way to summon the soul from the scribblings of a cocaine-addled psychoanalyst, there is no way to revive the object from the signature string that sigmund gives you. In fact, it's barely even readable.

As with util.inspect and JSON.stringify, larger objects will produce larger signature strings.

Because sigmund is a bit less strict than the more thorough alternatives, the strings will be shorter, and also there is a slightly higher chance for collisions. For example, these objects have the same signature:

var obj1 = {a:'b',c:/def/,g:['h','i',{j:'',k:'l'}]}
var obj2 = {a:'b',c:'/def/',g:['h','i','{jkl']}

Like a good Freudian, sigmund is most effective when you already have some understanding of what you're looking for. It can help you help yourself, but you must be willing to do some work as well.

Cycles are handled, and cyclical objects are silently omitted (though the key is included in the signature output.)

The second argument is the maximum depth, which defaults to 10, because that is the maximum object traversal depth covered by most insurance carriers.