First Commit

Brightcells 8 年之前
當前提交
c8aefbedb4
共有 89 個文件被更改,包括 2288 次插入0 次删除
  1. 29 0
      .editorconfig
  2. 19 0
      .gitignore
  3. 8 0
      .isort.cfg
  4. 0 0
      account/__init__.py
  5. 15 0
      account/admin.py
  6. 8 0
      account/apps.py
  7. 43 0
      account/migrations/0001_initial.py
  8. 0 0
      account/migrations/__init__.py
  9. 62 0
      account/models.py
  10. 29 0
      account/oauth_views.py
  11. 7 0
      account/tests.py
  12. 7 0
      account/views.py
  13. 0 0
      api/__init__.py
  14. 7 0
      api/admin.py
  15. 8 0
      api/apps.py
  16. 0 0
      api/migrations/__init__.py
  17. 7 0
      api/models.py
  18. 7 0
      api/tests.py
  19. 36 0
      api/urls.py
  20. 4 0
      api/views.py
  21. 9 0
      check.sh
  22. 0 0
      config/__init__.py
  23. 19 0
      config/admin.py
  24. 8 0
      config/apps.py
  25. 27 0
      config/hot_views.py
  26. 46 0
      config/migrations/0001_initial.py
  27. 0 0
      config/migrations/__init__.py
  28. 58 0
      config/models.py
  29. 7 0
      config/tests.py
  30. 7 0
      config/views.py
  31. 0 0
      intro/__init__.py
  32. 41 0
      intro/admin.py
  33. 8 0
      intro/apps.py
  34. 45 0
      intro/fav_views.py
  35. 29 0
      intro/intro_views.py
  36. 102 0
      intro/migrations/0001_initial.py
  37. 0 0
      intro/migrations/__init__.py
  38. 167 0
      intro/models.py
  39. 7 0
      intro/tests.py
  40. 7 0
      intro/views.py
  41. 3 0
      isort.sh
  42. 23 0
      manage.py
  43. 0 0
      manual/__init__.py
  44. 26 0
      manual/basemodels.py
  45. 27 0
      manual/deploy.bak/manual.ini
  46. 40 0
      manual/deploy.bak/manual_nginx.conf
  47. 10 0
      manual/deploy.bak/manual_supervisor.ini
  48. 15 0
      manual/deploy.bak/uwsgi_params
  49. 16 0
      manual/func_settings.py
  50. 43 0
      manual/local_settings.py
  51. 254 0
      manual/settings.py
  52. 331 0
      manual/static/templet/js/jswe.js
  53. 32 0
      manual/urls.py
  54. 17 0
      manual/wsgi.py
  55. 二進制
      media/file/201708/1502869267.33.png
  56. 二進制
      media/file/201708/1502869283.66.png
  57. 二進制
      media/file/201708/1502870309.11.png
  58. 二進制
      media/file/201708/1502870443.78.png
  59. 0 0
      message/__init__.py
  60. 13 0
      message/admin.py
  61. 8 0
      message/apps.py
  62. 33 0
      message/migrations/0001_initial.py
  63. 19 0
      message/migrations/0002_auto_20170816_1604.py
  64. 0 0
      message/migrations/__init__.py
  65. 41 0
      message/models.py
  66. 19 0
      message/msg_views.py
  67. 7 0
      message/tests.py
  68. 7 0
      message/views.py
  69. 9 0
      pep8.sh
  70. 15 0
      requirements.txt
  71. 0 0
      support/__init__.py
  72. 25 0
      support/admin.py
  73. 8 0
      support/apps.py
  74. 66 0
      support/migrations/0001_initial.py
  75. 25 0
      support/migrations/0002_auto_20170816_1509.py
  76. 20 0
      support/migrations/0003_machinesupportprebookinfo_handle_status.py
  77. 20 0
      support/migrations/0004_auto_20170816_1604.py
  78. 0 0
      support/migrations/__init__.py
  79. 94 0
      support/models.py
  80. 7 0
      support/tests.py
  81. 57 0
      support/views.py
  82. 0 0
      utils/__init__.py
  83. 0 0
      utils/error/__init__.py
  84. 43 0
      utils/error/errno_utils.py
  85. 18 0
      utils/error/response_utils.py
  86. 0 0
      utils/redis/__init__.py
  87. 6 0
      utils/redis/connect.py
  88. 1 0
      utils/redis/rkeys.py
  89. 7 0
      utils/url_utils.py

+ 29 - 0
.editorconfig

@@ -0,0 +1,29 @@
1
+# EditorConfig is awesome: http://EditorConfig.org
2
+
3
+# top-most EditorConfig file
4
+root = true
5
+
6
+# Unix-style newlines with a newline ending every file
7
+[*]
8
+end_of_line = lf
9
+insert_final_newline = false
10
+
11
+# 4 space indentation
12
+[*.py]
13
+indent_style = space
14
+indent_size = 4
15
+
16
+# Tab indentation (no size specified)
17
+[*.js]
18
+indent_style = space
19
+indent_size = 4
20
+
21
+# Tab indentation (no size specified)
22
+[*.html]
23
+indent_style = space
24
+indent_size = 4
25
+
26
+# Matches the exact files either package.json or .travis.yml
27
+[{package.json,.travis.yml}]
28
+indent_style = space
29
+indent_size = 2

+ 19 - 0
.gitignore

@@ -0,0 +1,19 @@
1
+*.db
2
+*.pyc
3
+*.swp
4
+*.idea
5
+*.sqlite3
6
+*.DS_Store
7
+*.python-version
8
+
9
+build
10
+
11
+# Compiled python modules.
12
+*.pyc
13
+
14
+# Setuptools distribution folder.
15
+/dist/
16
+
17
+# Python egg metadata, regenerated from source files by setuptools.
18
+/*.egg-info
19
+/*.egg

+ 8 - 0
.isort.cfg

@@ -0,0 +1,8 @@
1
+# See the menu of settings available here:
2
+#   https://github.com/timothycrosley/isort/wiki/isort-Settings
3
+
4
+[settings]
5
+indent='    '
6
+line_length=120
7
+lines_after_imports=2
8
+skip=migrations

+ 0 - 0
account/__init__.py


+ 15 - 0
account/admin.py

@@ -0,0 +1,15 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from djadmin import ReadonlyModelAdmin
4
+from django.contrib import admin
5
+
6
+from account.models import UserInfo
7
+
8
+
9
+class UserInfoAdmin(ReadonlyModelAdmin, admin.ModelAdmin):
10
+    list_display = ('user_id', 'unionid', 'openid', 'name', 'sex', 'nickname', 'phone', 'country', 'province', 'city', 'location', 'status', 'created_at', 'updated_at')
11
+    list_filter = ('sex', 'status')
12
+    actions = None
13
+
14
+
15
+admin.site.register(UserInfo, UserInfoAdmin)

+ 8 - 0
account/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 AccountConfig(AppConfig):
8
+    name = 'account'

+ 43 - 0
account/migrations/0001_initial.py

@@ -0,0 +1,43 @@
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 shortuuidfield.fields
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    initial = True
12
+
13
+    dependencies = [
14
+    ]
15
+
16
+    operations = [
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()

二進制
media/file/201708/1502869267.33.png


二進制
media/file/201708/1502869283.66.png


二進制
media/file/201708/1502870309.11.png


二進制
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 ''