"> 3683
+    "622923": "永靖县",
3684
+    "622924": "广河县",
3685
+    "622925": "和政县",
3686
+    "622926": "东乡族自治县",
3687
+    "622927": "积石山保安族东乡族撒拉族自治县"
3688
+  },
3689
+  "623000": {
3690
+    "623001": "合作市",
3691
+    "623021": "临潭县",
3692
+    "623022": "卓尼县",
3693
+    "623023": "舟曲县",
3694
+    "623024": "迭部县",
3695
+    "623025": "玛曲县",
3696
+    "623026": "碌曲县",
3697
+    "623027": "夏河县"
3698
+  },
3699
+  "630000": {
3700
+    "630100": "西宁市",
3701
+    "630200": "海东市",
3702
+    "632200": "海北藏族自治州",
3703
+    "632300": "黄南藏族自治州",
3704
+    "632500": "海南藏族自治州",
3705
+    "632600": "果洛藏族自治州",
3706
+    "632700": "玉树藏族自治州",
3707
+    "632800": "海西蒙古族藏族自治州"
3708
+  },
3709
+  "630100": {
3710
+    "630102": "城东区",
3711
+    "630103": "城中区",
3712
+    "630104": "城西区",
3713
+    "630105": "城北区",
3714
+    "630121": "大通回族土族自治县",
3715
+    "630122": "湟中县",
3716
+    "630123": "湟源县"
3717
+  },
3718
+  "630200": {
3719
+    "630202": "乐都区",
3720
+    "630203": "平安区",
3721
+    "630222": "民和回族土族自治县",
3722
+    "630223": "互助土族自治县",
3723
+    "630224": "化隆回族自治县",
3724
+    "630225": "循化撒拉族自治县"
3725
+  },
3726
+  "632200": {
3727
+    "632221": "门源回族自治县",
3728
+    "632222": "祁连县",
3729
+    "632223": "海晏县",
3730
+    "632224": "刚察县"
3731
+  },
3732
+  "632300": {
3733
+    "632321": "同仁县",
3734
+    "632322": "尖扎县",
3735
+    "632323": "泽库县",
3736
+    "632324": "河南蒙古族自治县"
3737
+  },
3738
+  "632500": {
3739
+    "632521": "共和县",
3740
+    "632522": "同德县",
3741
+    "632523": "贵德县",
3742
+    "632524": "兴海县",
3743
+    "632525": "贵南县"
3744
+  },
3745
+  "632600": {
3746
+    "632621": "玛沁县",
3747
+    "632622": "班玛县",
3748
+    "632623": "甘德县",
3749
+    "632624": "达日县",
3750
+    "632625": "久治县",
3751
+    "632626": "玛多县"
3752
+  },
3753
+  "632700": {
3754
+    "632701": "玉树市",
3755
+    "632722": "杂多县",
3756
+    "632723": "称多县",
3757
+    "632724": "治多县",
3758
+    "632725": "囊谦县",
3759
+    "632726": "曲麻莱县"
3760
+  },
3761
+  "632800": {
3762
+    "632801": "格尔木市",
3763
+    "632802": "德令哈市",
3764
+    "632821": "乌兰县",
3765
+    "632822": "都兰县",
3766
+    "632823": "天峻县"
3767
+  },
3768
+  "640000": {
3769
+    "640100": "银川市",
3770
+    "640200": "石嘴山市",
3771
+    "640300": "吴忠市",
3772
+    "640400": "固原市",
3773
+    "640500": "中卫市"
3774
+  },
3775
+  "640100": {
3776
+    "640104": "兴庆区",
3777
+    "640105": "西夏区",
3778
+    "640106": "金凤区",
3779
+    "640121": "永宁县",
3780
+    "640122": "贺兰县",
3781
+    "640181": "灵武市"
3782
+  },
3783
+  "640200": {
3784
+    "640202": "大武口区",
3785
+    "640205": "惠农区",
3786
+    "640221": "平罗县"
3787
+  },
3788
+  "640300": {
3789
+    "640302": "利通区",
3790
+    "640303": "红寺堡区",
3791
+    "640323": "盐池县",
3792
+    "640324": "同心县",
3793
+    "640381": "青铜峡市"
3794
+  },
3795
+  "640400": {
3796
+    "640402": "原州区",
3797
+    "640422": "西吉县",
3798
+    "640423": "隆德县",
3799
+    "640424": "泾源县",
3800
+    "640425": "彭阳县"
3801
+  },
3802
+  "640500": {
3803
+    "640502": "沙坡头区",
3804
+    "640521": "中宁县",
3805
+    "640522": "海原县"
3806
+  },
3807
+  "650000": {
3808
+    "650100": "乌鲁木齐市",
3809
+    "650200": "克拉玛依市",
3810
+    "650400": "吐鲁番市",
3811
+    "650500": "哈密市",
3812
+    "652300": "昌吉回族自治州",
3813
+    "652700": "博尔塔拉蒙古自治州",
3814
+    "652800": "巴音郭楞蒙古自治州",
3815
+    "652900": "阿克苏地区",
3816
+    "653000": "克孜勒苏柯尔克孜自治州",
3817
+    "653100": "喀什地区",
3818
+    "653200": "和田地区",
3819
+    "654000": "伊犁哈萨克自治州",
3820
+    "654200": "塔城地区",
3821
+    "654300": "阿勒泰地区",
3822
+    "659001": "石河子市",
3823
+    "659002": "阿拉尔市",
3824
+    "659003": "图木舒克市",
3825
+    "659004": "五家渠市",
3826
+    "659006": "铁门关市"
3827
+  },
3828
+  "650100": {
3829
+    "650102": "天山区",
3830
+    "650103": "沙依巴克区",
3831
+    "650104": "新市区",
3832
+    "650105": "水磨沟区",
3833
+    "650106": "头屯河区",
3834
+    "650107": "达坂城区",
3835
+    "650109": "米东区",
3836
+    "650121": "乌鲁木齐县"
3837
+  },
3838
+  "650200": {
3839
+    "650202": "独山子区",
3840
+    "650203": "克拉玛依区",
3841
+    "650204": "白碱滩区",
3842
+    "650205": "乌尔禾区"
3843
+  },
3844
+  "650400": {
3845
+    "650402": "高昌区",
3846
+    "650421": "鄯善县",
3847
+    "650422": "托克逊县"
3848
+  },
3849
+  "650500": {
3850
+    "650502": "伊州区",
3851
+    "650521": "巴里坤哈萨克自治县",
3852
+    "650522": "伊吾县"
3853
+  },
3854
+  "652300": {
3855
+    "652301": "昌吉市",
3856
+    "652302": "阜康市",
3857
+    "652323": "呼图壁县",
3858
+    "652324": "玛纳斯县",
3859
+    "652325": "奇台县",
3860
+    "652327": "吉木萨尔县",
3861
+    "652328": "木垒哈萨克自治县"
3862
+  },
3863
+  "652700": {
3864
+    "652701": "博乐市",
3865
+    "652702": "阿拉山口市",
3866
+    "652722": "精河县",
3867
+    "652723": "温泉县"
3868
+  },
3869
+  "652800": {
3870
+    "652801": "库尔勒市",
3871
+    "652822": "轮台县",
3872
+    "652823": "尉犁县",
3873
+    "652824": "若羌县",
3874
+    "652825": "且末县",
3875
+    "652826": "焉耆回族自治县",
3876
+    "652827": "和静县",
3877
+    "652828": "和硕县",
3878
+    "652829": "博湖县"
3879
+  },
3880
+  "652900": {
3881
+    "652901": "阿克苏市",
3882
+    "652922": "温宿县",
3883
+    "652923": "库车县",
3884
+    "652924": "沙雅县",
3885
+    "652925": "新和县",
3886
+    "652926": "拜城县",
3887
+    "652927": "乌什县",
3888
+    "652928": "阿瓦提县",
3889
+    "652929": "柯坪县"
3890
+  },
3891
+  "653000": {
3892
+    "653001": "阿图什市",
3893
+    "653022": "阿克陶县",
3894
+    "653023": "阿合奇县",
3895
+    "653024": "乌恰县"
3896
+  },
3897
+  "653100": {
3898
+    "653101": "喀什市",
3899
+    "653121": "疏附县",
3900
+    "653122": "疏勒县",
3901
+    "653123": "英吉沙县",
3902
+    "653124": "泽普县",
3903
+    "653125": "莎车县",
3904
+    "653126": "叶城县",
3905
+    "653127": "麦盖提县",
3906
+    "653128": "岳普湖县",
3907
+    "653129": "伽师县",
3908
+    "653130": "巴楚县",
3909
+    "653131": "塔什库尔干塔吉克自治县"
3910
+  },
3911
+  "653200": {
3912
+    "653201": "和田市",
3913
+    "653221": "和田县",
3914
+    "653222": "墨玉县",
3915
+    "653223": "皮山县",
3916
+    "653224": "洛浦县",
3917
+    "653225": "策勒县",
3918
+    "653226": "于田县",
3919
+    "653227": "民丰县"
3920
+  },
3921
+  "654000": {
3922
+    "654002": "伊宁市",
3923
+    "654003": "奎屯市",
3924
+    "654004": "霍尔果斯市",
3925
+    "654021": "伊宁县",
3926
+    "654022": "察布查尔锡伯自治县",
3927
+    "654023": "霍城县",
3928
+    "654024": "巩留县",
3929
+    "654025": "新源县",
3930
+    "654026": "昭苏县",
3931
+    "654027": "特克斯县",
3932
+    "654028": "尼勒克县"
3933
+  },
3934
+  "654200": {
3935
+    "654201": "塔城市",
3936
+    "654202": "乌苏市",
3937
+    "654221": "额敏县",
3938
+    "654223": "沙湾县",
3939
+    "654224": "托里县",
3940
+    "654225": "裕民县",
3941
+    "654226": "和布克赛尔蒙古自治县"
3942
+  },
3943
+  "654300": {
3944
+    "654301": "阿勒泰市",
3945
+    "654321": "布尔津县",
3946
+    "654322": "富蕴县",
3947
+    "654323": "福海县",
3948
+    "654324": "哈巴河县",
3949
+    "654325": "青河县",
3950
+    "654326": "吉木乃县"
3951
+  },
3952
+  "810000": {
3953
+    "810001": "中西區",
3954
+    "810002": "灣仔區",
3955
+    "810003": "東區",
3956
+    "810004": "南區",
3957
+    "810005": "油尖旺區",
3958
+    "810006": "深水埗區",
3959
+    "810007": "九龍城區",
3960
+    "810008": "黃大仙區",
3961
+    "810009": "觀塘區",
3962
+    "810010": "荃灣區",
3963
+    "810011": "屯門區",
3964
+    "810012": "元朗區",
3965
+    "810013": "北區",
3966
+    "810014": "大埔區",
3967
+    "810015": "西貢區",
3968
+    "810016": "沙田區",
3969
+    "810017": "葵青區",
3970
+    "810018": "離島區"
3971
+  },
3972
+  "820000": {
3973
+    "820001": "花地瑪堂區",
3974
+    "820002": "花王堂區",
3975
+    "820003": "望德堂區",
3976
+    "820004": "大堂區",
3977
+    "820005": "風順堂區",
3978
+    "820006": "嘉模堂區",
3979
+    "820007": "路氹填海區",
3980
+    "820008": "聖方濟各堂區"
3981
+  }
3982
+}

+ 7 - 0
data/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
data/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
docs/COMMANDS.md


+ 2 - 0
docs/CRONTAB.md

@@ -0,0 +1,2 @@
1
+# 定时任务
2
+# m h  dom mon dow   command

+ 3 - 0
isort.sh

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

+ 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", "tiwen.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)

+ 10 - 0
pep8.sh

@@ -0,0 +1,10 @@
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
+#  -- E402 module level import not at top of file
8
+#  -- E501 line too long
9
+
10
+pycodestyle --exclude=build,migrations,.tox --ignore=E128,E402,E501 .

+ 8 - 0
requirements.txt

@@ -0,0 +1,8 @@
1
+StatusCode==1.0.0
2
+furl==2.1.3
3
+jsonfield==3.1.0
4
+mysqlclient==2.1.1
5
+rlog==0.3
6
+-r requirements_dj.txt
7
+-r requirements_pywe.txt
8
+-r requirements_redis.txt

+ 3 - 0
requirements_deploy.txt

@@ -0,0 +1,3 @@
1
+ipdb==0.13.9
2
+ipython==7.34.0
3
+uwsgi==2.0.20

+ 2 - 0
requirements_dev.txt

@@ -0,0 +1,2 @@
1
+isort==5.10.1
2
+pycodestyle==2.8.0

+ 15 - 0
requirements_dj.txt

@@ -0,0 +1,15 @@
1
+Django==3.2.14
2
+django-admin==2.0.1
3
+django-detect==1.0.20
4
+django-file==1.0.4
5
+django-json-render==1.0.3
6
+django-json-response==1.1.5
7
+django-logit==1.1.3
8
+django-models-ext==1.1.10
9
+django-redis-connector==1.0.4
10
+django-response==1.1.1
11
+django-rlog==1.0.7
12
+django-short-url==1.1.6
13
+django-six==1.0.4
14
+django-uniapi==1.0.10
15
+django-we==1.5.6

+ 2 - 0
requirements_pywe.txt

@@ -0,0 +1,2 @@
1
+pywe-oauth==1.1.1
2
+pywe-pay==1.0.14

+ 3 - 0
requirements_redis.txt

@@ -0,0 +1,3 @@
1
+hiredis==2.0.0
2
+redis==4.3.4
3
+redis-extensions==4.0.1

+ 4 - 0
sysctl.sh

@@ -0,0 +1,4 @@
1
+#!/bin/bash
2
+
3
+sudo sysctl -w net.core.somaxconn=65535
4
+sudo sysctl -w net.core.netdev_max_backlog=65535

+ 0 - 0
tiwen/__init__.py


+ 30 - 0
tiwen/basemodels.py

@@ -0,0 +1,30 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.db import models
4
+from django.utils.translation import gettext_lazy as _
5
+
6
+
7
+class BaseModelMixin(models.Model):
8
+    status = models.BooleanField(_('status'), default=True, help_text=_('Status'))
9
+    created_at = models.DateTimeField(_('created_at'), auto_now_add=True, editable=True, help_text=_('Create Time'))
10
+    updated_at = models.DateTimeField(_('updated_at'), auto_now=True, editable=True, help_text=_('Update Time'))
11
+
12
+    class Meta:
13
+        abstract = True
14
+
15
+
16
+class SexModelMixin(models.Model):
17
+    UNKNOWN = 0
18
+    MALE = 1
19
+    FEMALE = 2
20
+
21
+    SEX_TUPLE = (
22
+        (UNKNOWN, '未知'),
23
+        (MALE, '男'),
24
+        (FEMALE, '女'),
25
+    )
26
+
27
+    sex = models.IntegerField(_('sex'), choices=SEX_TUPLE, default=UNKNOWN, help_text=_('Sex'))
28
+
29
+    class Meta:
30
+        abstract = True

+ 93 - 0
tiwen/decorators.py

@@ -0,0 +1,93 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from functools import wraps
4
+
5
+from django.conf import settings
6
+from django.shortcuts import redirect
7
+from furl import furl
8
+from pywe_oauth import get_oauth_redirect_url
9
+from pywe_sign import check_signature
10
+
11
+from utils.error.errno_utils import SignatureStatusCode
12
+from utils.error.response_utils import response
13
+from utils.redis.connect import r
14
+
15
+
16
+def check_user_cookie(func=None, key=settings.COOKIE_USER_CHECK_KEY):
17
+    def decorator(func):
18
+        @wraps(func)
19
+        def returned_wrapper(request, *args, **kwargs):
20
+            user_id = request.get_signed_cookie(key, default='', salt=settings.COOKIE_SALT)
21
+            if not user_id:
22
+                return redirect(get_oauth_redirect_url(settings.WECHAT_OAUTH2_REDIRECT_URI, 'snsapi_userinfo', request.get_full_path()))
23
+            return func(request, *args, **kwargs)
24
+        return returned_wrapper
25
+
26
+    if not func:
27
+        def foo(func):
28
+            return decorator(func)
29
+        return foo
30
+
31
+    return decorator(func)
32
+
33
+
34
+def check_token(func=None, entry=None):
35
+    def decorator(func):
36
+        @wraps(func)
37
+        def returned_wrapper(request, *args, **kwargs):
38
+            if not settings.DEBUG and request.wechat:
39
+                vtoken = request.GET.get('vtoken', '') or request.POST.get('vtoken', '')
40
+                token_check_key = request.GET.get(settings.TOKEN_CHECK_KEY, '') or request.POST.get(settings.TOKEN_CHECK_KEY, '')
41
+                if not r.token_exists(token_check_key, vtoken):
42
+                    # 3rd OAuth
43
+                    # return redirect(settings.WECHAT_OAUTH2_REDIRECT_URL)
44
+                    # Current OAuth
45
+                    redirect_url = furl(entry or settings.WECHAT_OAUTH2_REDIRECT_ENTRY).add({}).url
46
+                    return redirect(get_oauth_redirect_url(settings.WECHAT_OAUTH2_REDIRECT_URI, 'snsapi_userinfo', redirect_url))
47
+            return func(request, *args, **kwargs)
48
+        return returned_wrapper
49
+
50
+    if not func:
51
+        def foo(func):
52
+            return decorator(func)
53
+        return foo
54
+
55
+    return decorator(func)
56
+
57
+
58
+def check_sign(func=None, method='POST'):
59
+    def decorator(func):
60
+        @wraps(func)
61
+        def returned_wrapper(request, *args, **kwargs):
62
+            if not settings.DEBUG and not check_signature(getattr(request, method).dict(), settings.PARAMS_SIGN_KEY):
63
+                return response(SignatureStatusCode.SIGNATURE_ERROR)
64
+            return func(request, *args, **kwargs)
65
+        return returned_wrapper
66
+
67
+    if not func:
68
+        def foo(func):
69
+            return decorator(func)
70
+        return foo
71
+
72
+    return decorator(func)
73
+
74
+
75
+def check_cookie(func=None, entry=None):
76
+    def decorator(func):
77
+        @wraps(func)
78
+        def returned_wrapper(request, *args, **kwargs):
79
+            if not settings.DEBUG and not request.COOKIES.get('user_id'):
80
+                # 3rd OAuth
81
+                # return redirect(settings.WECHAT_OAUTH2_REDIRECT_URL)
82
+                # Current OAuth
83
+                redirect_url = furl(entry or settings.WECHAT_OAUTH2_REDIRECT_ENTRY).add({}).url
84
+                return redirect(get_oauth_redirect_url(settings.WECHAT_OAUTH2_REDIRECT_URI, 'snsapi_userinfo', redirect_url))
85
+            return func(request, *args, **kwargs)
86
+        return returned_wrapper
87
+
88
+    if not func:
89
+        def foo(func):
90
+            return decorator(func)
91
+        return foo
92
+
93
+    return decorator(func)

+ 12 - 0
tiwen/deploy.bak/supervisor_commands/pollqueue.ini

@@ -0,0 +1,12 @@
1
+[program:pollqueue]
2
+command=/home/diors/env/bin/python /home/diors/work/tiwen/manage.py poll_queue
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_pollqueue_access.log
11
+stderr_logfile=/var/log/supervisor_pollqueue_error.log
12
+user=diors

+ 12 - 0
tiwen/deploy.bak/supervisor_commands/rlistlog.ini

@@ -0,0 +1,12 @@
1
+[program:rlistlog]
2
+command=/home/diors/env/bin/python /home/diors/work/tiwen/manage.py rlistlog --key=django:logit:tiwen --filename=/tmp/tiwen.logit.log
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_rlistlog_access.log
11
+stderr_logfile=/var/log/supervisor_rlistlog_error.log
12
+user=diors

+ 35 - 0
tiwen/deploy.bak/tiwen.ini

@@ -0,0 +1,35 @@
1
+# Refer: https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
2
+# tiwen_uwsgi.ini file
3
+[uwsgi]
4
+
5
+# Django-related settings
6
+# the base directory (full path)
7
+chdir           = /home/diors/work/tiwen
8
+# Django's wsgi file
9
+module          = tiwen.wsgi
10
+# the virtualenv (full path)
11
+# home            = /path/to/virtualenv
12
+
13
+# process-related settings
14
+# master
15
+master          = true
16
+# maximum number of worker processes
17
+processes       = 10
18
+# the socket (use the full path to be safe
19
+socket          = /home/diors/work/tiwen/tiwen/deploy/tiwen.sock
20
+# ... with appropriate permissions - may be needed
21
+chmod-socket    = 777
22
+# clear environment on exit
23
+vacuum          = true
24
+
25
+# connect() to unix:///home/xxx/xxx.sock failed (11: Resource temporarily unavailable) while connecting to upstream
26
+# Exec sysctl.sh first
27
+# #!/bin/bash
28
+# sudo sysctl -w net.core.somaxconn=65535
29
+# sudo sysctl -w net.core.netdev_max_backlog=65535
30
+reload-mercy    = 64
31
+max-requests    = 8192
32
+listen          = 8192
33
+
34
+# recv() failed (104: Connection reset by peer) while reading response header from upstream
35
+buffer-size     = 65535

+ 35 - 0
tiwen/deploy.bak/tiwen2.ini

@@ -0,0 +1,35 @@
1
+# Refer: https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
2
+# tiwen_uwsgi.ini file
3
+[uwsgi]
4
+
5
+# Django-related settings
6
+# the base directory (full path)
7
+chdir           = /home/diors/work/tiwen
8
+# Django's wsgi file
9
+module          = tiwen.wsgi
10
+# the virtualenv (full path)
11
+# home            = /path/to/virtualenv
12
+
13
+# process-related settings
14
+# master
15
+master          = true
16
+# maximum number of worker processes
17
+processes       = 10
18
+# the socket (use the full path to be safe
19
+socket          = /home/diors/work/tiwen/tiwen/deploy/tiwen2.sock
20
+# ... with appropriate permissions - may be needed
21
+chmod-socket    = 777
22
+# clear environment on exit
23
+vacuum          = true
24
+
25
+# connect() to unix:///home/xxx/xxx.sock failed (11: Resource temporarily unavailable) while connecting to upstream
26
+# Exec sysctl.sh first
27
+# #!/bin/bash
28
+# sudo sysctl -w net.core.somaxconn=65535
29
+# sudo sysctl -w net.core.netdev_max_backlog=65535
30
+reload-mercy    = 64
31
+max-requests    = 8192
32
+listen          = 8192
33
+
34
+# recv() failed (104: Connection reset by peer) while reading response header from upstream
35
+buffer-size     = 65535

+ 96 - 0
tiwen/deploy.bak/tiwen_nginx.conf

@@ -0,0 +1,96 @@
1
+# tiwen_nginx.conf
2
+
3
+# the upstream component nginx needs to connect to
4
+upstream tiwen {
5
+    # Single Server
6
+    # server unix:///home/diors/work/tiwen/tiwen/deploy/tiwen.sock; # for a file socket
7
+    # Multi Server
8
+    # server unix:///home/diors/work/tiwen/tiwen/deploy/tiwen.sock max_fails=0 weight=1; # for a file socket
9
+    # server unix:///home/diors/work/tiwen/tiwen/deploy/tiwen2.sock max_fails=0 weight=1; # for a file socket
10
+    server 127.0.0.1:8888; # for a web port socket (we'll use this first)
11
+}
12
+
13
+# configuration of the server
14
+server {
15
+    # the port your site will be served on
16
+    listen      80;
17
+    # the domain name it will serve for
18
+    server_name .a.com; # substitute your machine's IP address or FQDN
19
+    charset     utf-8;
20
+
21
+    # access_log off;  # 设置是否存储访问日志
22
+    # access_log /var/log/nginx/tiwen_access.log;
23
+    # error_log /var/log/nginx/tiwen_error.log;
24
+
25
+    # max upload size
26
+    client_max_body_size 75M;   # adjust to taste
27
+
28
+    # JS接口安全域名 & 业务域名 验证
29
+    location /xxx.txt {
30
+        alias /home/diors/work/tiwen/docs/we/xxx.txt;
31
+    }
32
+
33
+    # Django media
34
+    location /media  {
35
+        alias /home/diors/work/tiwen/media;  # your Django project's media files - amend as required
36
+    }
37
+
38
+    location /static {
39
+        alias /home/diors/work/tiwen/collect_static; # your Django project's static files - amend as required
40
+    }
41
+
42
+    # Finally, send all non-media requests to the Django server.
43
+    location / {
44
+        # uwsgi_pass  tiwen;
45
+        proxy_pass  http://tiwen;
46
+        include     /home/diors/work/tiwen/tiwen/deploy/uwsgi_params; # the uwsgi_params file you installed
47
+    }
48
+}
49
+
50
+# configuration of the server
51
+server {
52
+    # the port your site will be served on
53
+    listen      443;
54
+    # the domain name it will serve for
55
+    server_name .a.com; # substitute your machine's IP address or FQDN
56
+    charset     utf-8;
57
+
58
+    # access_log /var/log/nginx/tiwen_ssl_access.log;
59
+    # error_log /var/log/nginx/tiwen_ssl_error.log;
60
+
61
+    ssl on;
62
+    ssl_certificate   cert/214180103970874.pem;
63
+    ssl_certificate_key  cert/214180103970874.key;
64
+    ssl_session_timeout 5m;
65
+    # worker process * exited on signal 11
66
+    # 同一台服务器部署多个 SSL 转发,Nginx 默认是开启 Session 缓存的,导致冲突,验证不通过,连接直接退出
67
+    # 解决方案:禁用 Ningx SSL Session 的缓存
68
+    ssl_session_cache none;
69
+    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
70
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
71
+    ssl_prefer_server_ciphers on;
72
+
73
+    # max upload size
74
+    client_max_body_size 75M;   # adjust to taste
75
+
76
+    # JS接口安全域名 & 业务域名 验证
77
+    location /xxx.txt {
78
+        alias /home/diors/work/tiwen/docs/we/xxx.txt;
79
+    }
80
+
81
+    # Django media
82
+    location /media  {
83
+        alias /home/diors/work/tiwen/media;  # your Django project's media files - amend as required
84
+    }
85
+
86
+    location /static {
87
+        alias /home/diors/work/tiwen/collect_static; # your Django project's static files - amend as required
88
+    }
89
+
90
+    # Finally, send all non-media requests to the Django server.
91
+    location / {
92
+        # uwsgi_pass  tiwen;
93
+        proxy_pass  http://tiwen;
94
+        include     /home/diors/work/tiwen/tiwen/deploy/uwsgi_params; # the uwsgi_params file you installed
95
+    }
96
+}

+ 12 - 0
tiwen/deploy.bak/tiwen_supervisor.ini

@@ -0,0 +1,12 @@
1
+[program:tiwen]
2
+command=/home/diors/env/bin/uwsgi --ini /home/diors/work/tiwen/tiwen/deploy/tiwen.ini
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_tiwen_access.log
11
+stderr_logfile=/var/log/supervisor_tiwen_error.log
12
+user=diors

+ 12 - 0
tiwen/deploy.bak/tiwen_supervisor2.ini

@@ -0,0 +1,12 @@
1
+[program:tiwen2]
2
+command=/home/diors/env/bin/uwsgi --ini /home/diors/work/tiwen/tiwen/deploy/tiwen2.ini
3
+autostart=true
4
+autorestart=true
5
+startretries=3
6
+exitcodes=0,1,2
7
+stopsignal=KILL
8
+stopasgroup=true
9
+killasgroup=true
10
+stdout_logfile=/var/log/supervisor_tiwen2_access.log
11
+stderr_logfile=/var/log/supervisor_tiwen2_error.log
12
+user=diors

+ 15 - 0
tiwen/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;

+ 5 - 0
tiwen/django_file_callback_settings.py

@@ -0,0 +1,5 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def DJANGO_FILE_UPLOAD_CALLBACK_FUNC(request, file_path=None, file_url=None):
5
+    """ DJANGO FILE UPLOAD Callback Func """

+ 84 - 0
tiwen/django_we_callback_settings.py

@@ -0,0 +1,84 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def DJANGO_WE_CFG_FUNC(request, state=None):
5
+    """ WeChat CFG Callback Func """
6
+
7
+
8
+def DJANGO_WE_QUOTE_STATE_FUNC(request, state):
9
+    """ WeChat Quote Callback Func """
10
+    from utils.redis.connect import r
11
+    return r.quote(state, short_uuid=True)
12
+
13
+
14
+def DJANGO_WE_UNQUOTE_STATE_FUNC(request, state):
15
+    """ WeChat UnQuote Callback Func """
16
+    from utils.redis.connect import r
17
+
18
+    # If Not Buf, When Wechat Multi Request, Unquote Will Get None
19
+    # Then Once Should ReOAuth, Will Raise Error
20
+    return r.unquote(state, buf=True) or state
21
+
22
+
23
+def DJANGO_WE_BASE_FUNC(code, state, access_info=None):
24
+    """ WeChat Base Redirect Callback Func """
25
+
26
+
27
+def DJANGO_WE_BASE_COOKIE_FUNC(code, state, access_info=None):
28
+    """ WeChat Base Set Cookie Redirect Callback Func """
29
+
30
+
31
+def DJANGO_WE_USERINFO_FUNC(code, state, access_info=None, userinfo=None):
32
+    """ WeChat Userinfo Redirect Callback Func """
33
+    from django.conf import settings
34
+
35
+    from utils.redis.connect import r
36
+    from utils.user.userinfo_save import userinfo_save
37
+
38
+    # Save profile or something else
39
+    user = userinfo_save(userinfo)
40
+
41
+    token_check_key = getattr(user, settings.TOKEN_CHECK_KEY)
42
+
43
+    return {
44
+        settings.TOKEN_CHECK_KEY: token_check_key,
45
+        'vtoken': r.token(token_check_key, ex=True, time=r.REDIS_EXPIRED_ONE_DAY, buf=False, short_uuid=True),
46
+    }
47
+
48
+
49
+def DJANGO_WE_USERINFO_COOKIE_FUNC(code, state, access_info=None, userinfo=None):
50
+    """ WeChat Userinfo Set Cookie Redirect Callback Func """
51
+    from django.conf import settings
52
+
53
+    from utils.user.userinfo_save import userinfo_save
54
+
55
+    # Save profile or something else
56
+    user = userinfo_save(userinfo)
57
+
58
+    token_check_key = getattr(user, settings.TOKEN_CHECK_KEY)
59
+
60
+    return {}, settings.TOKEN_CHECK_KEY, token_check_key
61
+
62
+
63
+def DJANGO_WE_SHARE_FUNC(request, state=None):
64
+    """ WeChat Share Callback Func """
65
+    # from django.conf import settings
66
+    # return settings.WECHAT_OAUTH2_REDIRECT_URL
67
+
68
+
69
+def DJANGO_WE_MESSAGE_CALLBACK_FUNC(request, xmldict, decrypted):
70
+    """ WeChat Message Callback Func """
71
+
72
+
73
+def DJANGO_WE_COMPONENT_AUTH_FUNC(request, xmldict, decrypted):
74
+    """ WeChat COMPONENT Auth Func """
75
+
76
+
77
+def DJANGO_WE_COMPONENT_CALLBACK_FUNC(request, appid, xmldict, decrypted):
78
+    """ WeChat COMPONENT Callback Func """
79
+
80
+
81
+def DJANGO_WE_REDIS_OBJ_FUNC(request):
82
+    """ WeChat Redis Object Callback Func """
83
+    from utils.redis.connect import r
84
+    return r

+ 16 - 0
tiwen/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)))

+ 17 - 0
tiwen/local_settings_bak.py

@@ -0,0 +1,17 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+# DEBUG = False
4
+
5
+ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'tiwen']
6
+
7
+# DOMAIN
8
+DOMAIN = 'http://a.com'
9
+
10
+# 邮件设置
11
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
12
+SERVER_EMAIL = 'error.notify@exmail.com'
13
+EMAIL_HOST_USER = 'error.notify@exmail.com'
14
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
15
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
16
+ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
17
+EMAIL_SUBJECT_PREFIX = '[Templet] '

+ 27 - 0
tiwen/local_settings_dev_bak.py

@@ -0,0 +1,27 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+
6
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
7
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
8
+
9
+TEMPLATES = [
10
+    {
11
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
12
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
13
+        # 'APP_DIRS': True,
14
+        'OPTIONS': {
15
+            'context_processors': [
16
+                'django.template.context_processors.debug',
17
+                'django.template.context_processors.request',
18
+                'django.contrib.auth.context_processors.auth',
19
+                'django.contrib.messages.context_processors.messages',
20
+            ],
21
+            'loaders': [
22
+                'django.template.loaders.filesystem.Loader',
23
+                'django.template.loaders.app_directories.Loader',
24
+            ],
25
+        },
26
+    },
27
+]

+ 401 - 0
tiwen/settings.py

@@ -0,0 +1,401 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""
4
+Django settings for tiwen 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
+# try:
18
+#     from func_settings import redis_connect
19
+#     REDIS_CACHE = redis_connect(REDIS.get('default', {}))
20
+# except ImportError:
21
+#     REDIS_CACHE = None
22
+from django_redis_connector import connector
23
+
24
+
25
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
26
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
27
+PROJ_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
28
+
29
+
30
+# Quick-start development settings - unsuitable for production
31
+# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
32
+
33
+# SECURITY WARNING: keep the secret key used in production secret!
34
+SECRET_KEY = '0=hpv21&am(7(k5ab!^zjvvl=ntj)^i@7)87t47uzumt_5rq$+'
35
+
36
+# SECURITY WARNING: don't run with debug turned on in production!
37
+DEBUG = True
38
+
39
+ALLOWED_HOSTS = []
40
+
41
+
42
+# Application definition
43
+
44
+INSTALLED_APPS = [
45
+    'django.contrib.admin',
46
+    'django.contrib.auth',
47
+    'django.contrib.contenttypes',
48
+    'django.contrib.sessions',
49
+    'django.contrib.messages',
50
+    'django.contrib.staticfiles',
51
+    # 'django_file_upload',
52
+    # 'django_short_url',
53
+    'django_uniapi',
54
+    'django_admin',
55
+    # 'django_rlog',
56
+    'django_we',
57
+    'commands',
58
+    'api',
59
+]
60
+
61
+MIDDLEWARE = [
62
+    'django.middleware.security.SecurityMiddleware',
63
+    'django.contrib.sessions.middleware.SessionMiddleware',
64
+    'django.middleware.common.CommonMiddleware',
65
+    # 'django.middleware.csrf.CsrfViewMiddleware',
66
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
67
+    'django.contrib.messages.middleware.MessageMiddleware',
68
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
69
+    'detect.middleware.UserAgentDetectionMiddleware',
70
+]
71
+
72
+ROOT_URLCONF = 'tiwen.urls'
73
+
74
+TEMPLATES = [
75
+    {
76
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
77
+        'DIRS': [os.path.join(BASE_DIR, 'templates')],
78
+        # 'APP_DIRS': True,
79
+        'OPTIONS': {
80
+            'context_processors': [
81
+                'django.template.context_processors.debug',
82
+                'django.template.context_processors.request',
83
+                'django.contrib.auth.context_processors.auth',
84
+                'django.contrib.messages.context_processors.messages',
85
+            ],
86
+            'loaders': [
87
+                ('django.template.loaders.cached.Loader', [
88
+                    'django.template.loaders.filesystem.Loader',
89
+                    'django.template.loaders.app_directories.Loader',
90
+                ]),
91
+            ],
92
+        },
93
+    },
94
+]
95
+
96
+WSGI_APPLICATION = 'tiwen.wsgi.application'
97
+
98
+
99
+# Database
100
+# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
101
+
102
+DATABASES = {
103
+    # Create Database
104
+    # CREATE DATABASE tiwen DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
105
+    'default': {
106
+        'ENGINE': 'django.db.backends.mysql',
107
+        'NAME': 'tiwen',
108
+        'USER': 'root',
109
+        'PASSWORD': '',
110
+        'HOST': '127.0.0.1',
111
+        'PORT': 3306,
112
+        'CONN_MAX_AGE': 600,
113
+        'OPTIONS': {
114
+            'charset': 'utf8mb4',
115
+        },
116
+    }
117
+}
118
+
119
+
120
+# Password validation
121
+# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
122
+
123
+AUTH_PASSWORD_VALIDATORS = [
124
+    {
125
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
126
+    },
127
+    {
128
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
129
+    },
130
+    {
131
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
132
+    },
133
+    {
134
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
135
+    },
136
+]
137
+
138
+
139
+# Internationalization
140
+# https://docs.djangoproject.com/en/1.11/topics/i18n/
141
+
142
+LANGUAGE_CODE = 'zh-Hans'
143
+
144
+TIME_ZONE = 'Asia/Shanghai'
145
+
146
+USE_I18N = True
147
+
148
+USE_L10N = True
149
+
150
+USE_TZ = True
151
+
152
+
153
+# Static files (CSS, JavaScript, Images)
154
+# https://docs.djangoproject.com/en/1.11/howto/static-files/
155
+
156
+STATICFILES_DIRS = (
157
+    os.path.join(PROJ_DIR, 'static').replace('\\', '/'),
158
+)
159
+
160
+STATIC_ROOT = os.path.join(BASE_DIR, 'collect_static').replace('\\', '/')
161
+
162
+STATIC_URL = '/static/'
163
+
164
+STATICFILES_FINDERS = (
165
+    'django.contrib.staticfiles.finders.FileSystemFinder',
166
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
167
+    # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
168
+)
169
+
170
+MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
171
+
172
+MEDIA_URL = '/media/'
173
+
174
+# File 设置
175
+FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880  # InMemoryUploadedFile 文件最大值,设置为 5 MB
176
+FILE_UPLOAD_PERMISSIONS = 0o644  # TemporaryUploadedFile 文件权限设置
177
+
178
+# DOMAIN
179
+DOMAIN = 'http://a.com'
180
+
181
+# Redis 设置
182
+REDIS = {
183
+    'default': {
184
+        'HOST': '127.0.0.1',
185
+        'PORT': 6379,
186
+        'USER': '',
187
+        'PASSWORD': '',
188
+        'db': 0,
189
+        'decode_responses': True,
190
+    }
191
+}
192
+
193
+# 微信设置
194
+WECHAT = {
195
+    'JSAPI': {
196
+        'trade_type': 'JSAPI',  # JSAPI-网页支付、NATIVE-原生支付、APP-APP支付、MICROPAY-刷卡支付
197
+        'token': '5201314',
198
+        'appID': '',
199
+        'appsecret': '',
200
+        'encodingaeskey': '',
201
+        'mchID': '',
202
+        'apiKey': '',
203
+        'mch_cert': '',
204
+        'mch_key': '',
205
+        'redpack': {
206
+
207
+        }
208
+    },
209
+}
210
+
211
+WECHAT_DEFAULT_CFG = 'JSAPI'
212
+
213
+# 微信唯一标识
214
+# Choices: 'unionid' or 'openid'
215
+#
216
+# models.py
217
+#   'unique_identifier': self.unionid if settings.WECHAT_UNIQUE_IDENTIFICATION == 'unionid' else self.openid,
218
+# views.py
219
+#   unique_identifier = request.POST.get(settings.WECHAT_UNIQUE_IDENTIFICATION, '')
220
+#   profile = Profile.objects.get(**{settings.WECHAT_UNIQUE_IDENTIFICATION: unique_identifier})
221
+#
222
+# If not bind to OpenPlat, change `WECHAT_UNIQUE_IDENTIFICATION` as `openid`
223
+WECHAT_UNIQUE_IDENTIFICATION = 'unionid'
224
+
225
+# Token 错误重授权设置
226
+TOKEN_CHECK_KEY = ''
227
+# TOKEN_CHECK_KEY = 'user_id'
228
+WECHAT_OAUTH2_REDIRECT_ENTRY = ''
229
+WECHAT_OAUTH2_REDIRECT_URL = ''
230
+
231
+# Cookie 设置
232
+DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE = 31536000  # 单位:秒,1年:365 * 24 * 60 * 60 = 31536000
233
+DJANGO_WE_COOKIE_SALT = COOKIE_SALT = 'djwe'  # Salt for ``set_signed_cookie``
234
+# Cookie 校验设置
235
+COOKIE_USER_CHECK_KEY = ''
236
+# COOKIE_USER_CHECK_KEY = 'user_id'
237
+
238
+# 邮件设置
239
+# https://docs.djangoproject.com/en/1.11/howto/error-reporting/#email-reports
240
+# When DEBUG is False, Django will email the users listed in the ADMINS setting
241
+# whenever your code raises an unhandled exception and results in an internal server error (HTTP status code 500).
242
+# 只有当 DEBUG = False 的时候,才会邮件发送报错信息
243
+# Email address that error messages come from.
244
+SERVER_EMAIL = 'error.notify@exmail.com'
245
+# The email backend to use. For possible shortcuts see django.core.mail.
246
+# The default is to use the SMTP backend.
247
+# Third-party backends can be specified by providing a Python path
248
+# to a module that defines an EmailBackend class.
249
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
250
+# Host for sending email.
251
+EMAIL_HOST = 'smtp.exmail.qq.com'
252
+# Port for sending email.
253
+EMAIL_PORT = 25
254
+# Optional SMTP authentication information for EMAIL_HOST.
255
+EMAIL_HOST_USER = 'error.notify@exmail.com'
256
+EMAIL_HOST_PASSWORD = '<^_^>pwd<^_^>'
257
+EMAIL_USE_TLS = False
258
+EMAIL_USE_SSL = False
259
+EMAIL_SSL_CERTFILE = None
260
+EMAIL_SSL_KEYFILE = None
261
+# Default: None
262
+# Specifies a timeout in seconds for blocking operations like the connection attempt.
263
+# socket.error: [Errno 110] Connection timed out
264
+# 如果不设置 EMAIL_TIMEOUT,一旦 sock.connect 连接有问题 error: (110, 'Connection timed out')
265
+# 就会导致进程一直卡住,最终导致服务崩溃
266
+# EMAIL_TIMEOUT = None
267
+EMAIL_TIMEOUT = 3
268
+# Default email address to use for various automated correspondence from
269
+# the site managers.
270
+DEFAULT_FROM_EMAIL = 'error.notify <error.notify@exmail.com>'
271
+# People who get code error notifications.
272
+# In the format [('Full Name', 'email@example.com'), ('Full Name', 'anotheremail@example.com')]
273
+# Default: [], not send code error notifications.
274
+# https://github.com/django/django/blob/5fcfe5361e5b8c9738b1ee4c1e9a6f293a7dda40/django/core/mail/__init__.py#L90
275
+# ADMINS = [('Zhang San', 'san.zhang@exmail.com'), ('Li Si', 'si.li@exmail.com')]
276
+ADMINS = []
277
+# Not-necessarily-technical managers of the site. They get broken link
278
+# notifications and other various emails.
279
+MANAGERS = ADMINS
280
+# Subject-line prefix for email messages send with django.core.mail.mail_admins
281
+# or ...mail_managers.  Make sure to include the trailing space.
282
+EMAIL_SUBJECT_PREFIX = '[Templet] '
283
+
284
+# Django-Admin Settings
285
+DJANGO_ADMIN_DISABLE_DELETE_SELECTED = False
286
+
287
+# Django-FILE-UPLOAD Settings
288
+DJANGO_FILE_UPLOAD_USE_YM = True
289
+DJANGO_FILE_UPLOAD_USE_DT = True
290
+
291
+# Django-Logit Settings
292
+DJANGO_LOGIT_ENABLED = True
293
+DJANGO_LOGIT_BODY_FLAG = False
294
+DJANGO_LOGIT_RES_FLAG = False
295
+
296
+# Django-Onerror Settings
297
+DJANGO_ONERROR_ACCEPT_REPORT = True
298
+DJANGO_ONERROR_ADMIN_SITE_REGISTER = True
299
+
300
+# Django-Short-URL Settings
301
+# Redirect url when short url not exists
302
+DJANGO_SHORT_URL_REDIRECT_URL = ''
303
+
304
+# Django-We Settings
305
+DJANGO_WE_QUOTE_OR_NOT = True
306
+DJANGO_WE_MODEL_DISPLAY_OR_NOT = True
307
+# Enable Cookie or not
308
+# DJANGO_WE_BASE_REDIRECT_SET_COOKIE = False
309
+# DJANGO_WE_USERINFO_REDIRECT_SET_COOKIE = True
310
+# Cookie Config
311
+DJANGO_WE_COOKIE_MAX_AGE = COOKIE_MAX_AGE
312
+DJANGO_WE_COOKIE_SALT = COOKIE_SALT
313
+
314
+# 开发调试相关配置
315
+if DEBUG:
316
+    try:
317
+        from .local_settings_dev import *
318
+    except ImportError:
319
+        pass
320
+
321
+try:
322
+    from .local_settings import *
323
+except ImportError:
324
+    pass
325
+
326
+try:
327
+    from .django_file_callback_settings import *
328
+except ImportError:
329
+    pass
330
+
331
+try:
332
+    from .django_we_callback_settings import *
333
+except ImportError:
334
+    pass
335
+
336
+# 依赖 local_settings 中的配置
337
+# 微信授权设置
338
+# WECHAT_OAUTH2_REDIRECT_URI = '{0}/we/oauth2?scope={{0}}&redirect_url={{1}}'.format(DOMAIN)
339
+# Shorten URL
340
+# ``o`` is short for oauth2
341
+# ``r`` is short for redirect_url
342
+WECHAT_OAUTH2_REDIRECT_URI = '{0}/we/o?scope={{0}}&r={{1}}'.format(DOMAIN)
343
+WECHAT_OAUTH2_USERINFO_REDIRECT_URI = '{0}/we/o?r={{0}}'.format(DOMAIN)  # Scope default snsapi_userinfo
344
+WECHAT_BASE_REDIRECT_URI = '{0}/we/base_redirect'.format(DOMAIN)
345
+WECHAT_USERINFO_REDIRECT_URI = '{0}/we/userinfo_redirect'.format(DOMAIN)
346
+WECHAT_DIRECT_BASE_REDIRECT_URI = '{0}/we/direct_base_redirect'.format(DOMAIN)
347
+WECHAT_DIRECT_USERINFO_REDIRECT_URI = '{0}/we/direct_userinfo_redirect'.format(DOMAIN)
348
+
349
+# Redis 连接
350
+WECHAT_REDIS_OBJ = REDIS_CACHE = connector(REDIS.get('default', {}))
351
+
352
+# LOGGER 设置
353
+# python manage.py rlistlog --key=django:logit:tiwen --filename=/tmp/tiwen.logit.log
354
+LOGGING = {
355
+    'version': 1,
356
+    'disable_existing_loggers': False,
357
+    'formatters': {
358
+        'verbose': {
359
+            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
360
+        },
361
+        'simple': {
362
+            'format': '%(levelname)s %(message)s'
363
+        },
364
+    },
365
+    'handlers': {
366
+        'logit': {
367
+            'level': 'DEBUG',
368
+            'class': 'rlog.RedisListHandler',
369
+            'redis_client': REDIS_CACHE,
370
+            'key': 'django:logit:tiwen',
371
+            'formatter': 'verbose',
372
+        },
373
+        'console': {
374
+            'level': 'DEBUG',
375
+            'class': 'logging.StreamHandler',
376
+            'formatter': 'verbose'
377
+        },
378
+    },
379
+    'loggers': {
380
+        'logit': {
381
+            'handlers': ['logit'],
382
+            'level': 'DEBUG',
383
+            'propagate': True,
384
+        },
385
+        'console': {
386
+            'handlers': ['console'],
387
+            'level': 'DEBUG',
388
+            'propagate': True,
389
+        },
390
+    },
391
+}
392
+
393
+# MAX_BIGINT
394
+# Why Not ``sys.maxint``
395
+# n bit platform sys.maxint = 2 ** (n - 1) - 1
396
+# 64 bit 9223372036854775807, 32 bit 2147483647
397
+from django.db.models import BigIntegerField  # isort:skip
398
+MAX_BIGINT = BigIntegerField.MAX_BIGINT
399
+
400
+# DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
401
+DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

+ 68 - 0
tiwen/static/tiwen/css/common.css

@@ -0,0 +1,68 @@
1
+.text-center {
2
+    text-align: center;
3
+}
4
+
5
+/*
6
+  文本溢出省略,元素需要设置定宽度
7
+  */
8
+/* 单行 */
9
+.text_ellipsis {
10
+    overflow: hidden;
11
+    white-space: nowrap;
12
+    text-overflow: ellipsis;
13
+}
14
+/* 多行 */
15
+.a {
16
+    overflow: hidden;
17
+    text-overflow: ellipsis;
18
+    display: -webkit-box;
19
+    -webkit-line-clamp: 2;
20
+    -webkit-box-orient: vertical;
21
+}
22
+
23
+/*
24
+  浮动 & 清除浮动
25
+ */
26
+.fl {
27
+    float: left;
28
+}
29
+
30
+.fr {
31
+    float: right;
32
+}
33
+
34
+.clearfix {
35
+    zoom: 1;
36
+}
37
+
38
+.clearfix:after {
39
+    content: "\200B";
40
+    display: block;
41
+    height: 0;
42
+    clear: both;
43
+}
44
+
45
+.clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
46
+.clearfix { *zoom:1; }
47
+
48
+/*
49
+  解决安卓微信点击图片预览问题
50
+  这个会让 img 标签的点击事件失效,如果想要点击图片就要给上面再写一层
51
+  需要长按识别二维码的时候,则再单独给该图片添加
52
+  ```css
53
+  pointer-events: auto;
54
+  ```
55
+  来恢复对应的点击事件
56
+  */
57
+img {
58
+    pointer-events: none;
59
+}
60
+
61
+.clickable {
62
+    pointer-events: auto;
63
+}
64
+
65
+/* Input 自动填充后,移除 Chrome 默认黄色 */
66
+input:-webkit-autofill {
67
+    box-shadow: 0 0 0 1000px white inset !important;
68
+}

+ 574 - 0
tiwen/static/tiwen/js/jswe.js

@@ -0,0 +1,574 @@
1
+!(function(e, t) {
2
+    var config = {
3
+        wxconfig: 'http://api.tt4it.com/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
+        baseFlag: false,
15
+        baseHide: false,
16
+        close: false,
17
+        hideMenuItems: [],
18
+        showMenuItems: []
19
+    }, jsApiList = [
20
+        'checkJsApi',
21
+        'onMenuShareTimeline',
22
+        'onMenuShareAppMessage',
23
+        'onMenuShareQQ',
24
+        'onMenuShareWeibo',
25
+        'onMenuShareQZone',
26
+        'hideMenuItems',
27
+        'showMenuItems',
28
+        'hideAllNonBaseMenuItem',
29
+        'showAllNonBaseMenuItem',
30
+        'translateVoice',
31
+        'startRecord',
32
+        'stopRecord',
33
+        'onRecordEnd',
34
+        'playVoice',
35
+        'pauseVoice',
36
+        'stopVoice',
37
+        'uploadVoice',
38
+        'downloadVoice',
39
+        'chooseImage',
40
+        'previewImage',
41
+        'uploadImage',
42
+        'downloadImage',
43
+        'getLocalImgData',
44
+        'getNetworkType',
45
+        'openLocation',
46
+        'getLocation',
47
+        'hideOptionMenu',
48
+        'showOptionMenu',
49
+        'closeWindow',
50
+        'scanQRCode',
51
+        'chooseWXPay',
52
+        'openEnterpriseRedPacket',
53
+        'openProductSpecificView',
54
+        'addCard',
55
+        'chooseCard',
56
+        'openCard'
57
+    ], wxApiFun
58
+
59
+    function isEmpty(obj) {
60
+        if (obj == null) return true
61
+        if (obj.length > 0) return false
62
+        if (obj.length === 0) return true
63
+        for (var key in obj) {
64
+            if (Object.prototype.hasOwnProperty.call(obj, key)) return false
65
+        }
66
+        return true
67
+    }
68
+
69
+    function isNotEmpty(obj) {
70
+        return !isEmpty(obj)
71
+    }
72
+
73
+    function isOpenOnPC() {  // 判断当前网页是否在 PC 浏览器中打开
74
+        var ua = navigator.userAgent
75
+        return /windows nt/i.test(ua) || /macintosh/i.test(ua) || /linux x86_64/i.test(ua)
76
+    }
77
+
78
+    function isOpenInWeixin() {  // 判断当前网页是否在微信内置浏览器中打开
79
+        return /micromessenger/i.test(navigator.userAgent)
80
+    }
81
+
82
+    function getWeixinVersion() {
83
+        var ua = navigator.userAgent,
84
+            mt = ua.match(/micromessenger\/([\d.]+)/i)
85
+        return (mt ? mt[1] : '')
86
+    }
87
+
88
+    // This function checks whether Wechat is the appointed version or not
89
+    // Cmp: http://jsperf.com/regexp-test-vs-indexof-ignore-upper-and-lower
90
+    function isWeixinVersion(version) {
91
+        // return new RegExp('micromessenger/' + version , 'i').test(navigator.userAgent)
92
+        return navigator.userAgent.toLowerCase().indexOf('micromessenger/' + version) != -1
93
+    }
94
+
95
+    function hideOptionMenu() {
96
+        wxConfig.hide = true
97
+        fixedWxData()
98
+    }
99
+
100
+    function showOptionMenu() {
101
+        wxConfig.hide = false
102
+        fixedWxData()
103
+    }
104
+
105
+    function hideMenuItems(items) {
106
+        wxConfig.hideMenuItems = items
107
+        fixedWxData()
108
+    }
109
+
110
+    function showMenuItems(items) {
111
+        wxConfig.showMenuItems = items
112
+        fixedWxData()
113
+    }
114
+
115
+    function hideAllNonBaseMenuItem() {
116
+        wxConfig.baseFlag = true
117
+        wxConfig.baseHide = true
118
+        fixedWxData()
119
+    }
120
+
121
+    function showAllNonBaseMenuItem() {
122
+        wxConfig.baseFlag = true
123
+        wxConfig.baseHide = false
124
+        fixedWxData()
125
+    }
126
+
127
+    function closeWindow() {
128
+        wxConfig.close = true
129
+        fixedWxData()
130
+    }
131
+
132
+    function wxReady(data) {
133
+        data = typeof data === 'object' ? data : JSON.parse(data)
134
+        wx.config({
135
+            debug: wxData.debug,
136
+            appId: data.appId,
137
+            timestamp: data.timestamp,
138
+            nonceStr: data.nonceStr,
139
+            signature: data.signature,
140
+            jsApiList: jsApiList
141
+        })
142
+
143
+        var callbacks = {
144
+            trigger: function (res) {
145
+                // alert('用户点击发送给朋友')
146
+                if (JSWE.wxTrigger) {JSWE.wxTrigger(res)}
147
+            },
148
+            success: function (res) {
149
+                // alert('已分享')
150
+                if (JSWE.wxSuccess) {JSWE.wxSuccess(res)}
151
+            },
152
+            cancel: function (res) {
153
+                // alert('已取消')
154
+                if (JSWE.wxCancel) {JSWE.wxCancel(res)}
155
+            },
156
+            fail: function (res) {
157
+                // alert(JSON.stringify(res))
158
+                if (JSWE.wxFail) {JSWE.wxFail(res)}
159
+            }
160
+        }, shareInfo = function(flag) {
161
+            var _share = {
162
+                title: flag ? wxData.title : (wxData.timeLine || wxData.desc),
163
+                link: wxData.link,
164
+                imgUrl: wxData.imgUrl,
165
+                trigger: callbacks.trigger,
166
+                success: callbacks.success,
167
+                cancel: callbacks.cancel,
168
+                fail: callbacks.fail
169
+            }
170
+            if (flag) _share.desc = wxData.desc
171
+            return _share
172
+        }, wxShareApi = function() {
173
+            // 2. 分享接口
174
+            // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
175
+            wx.onMenuShareAppMessage(shareInfo(1))
176
+            // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
177
+            wx.onMenuShareTimeline(shareInfo(0))
178
+            // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口
179
+            wx.onMenuShareQQ(shareInfo(1))
180
+            // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
181
+            wx.onMenuShareWeibo(shareInfo(1))
182
+            // 2.5 监听“分享到QQ空间”按钮点击、自定义分享内容及分享结果接口
183
+            wx.onMenuShareQZone(shareInfo(1))
184
+        }, wxMenuApi = function () {
185
+            // 8. 界面操作接口
186
+            // 8.1 隐藏右上角菜单
187
+            // 8.2 显示右上角菜单
188
+            if (wxConfig.hide) {wx.hideOptionMenu()} else {wx.showOptionMenu()}
189
+            // 8.3 批量隐藏菜单项
190
+            if (isNotEmpty(wxConfig.hideMenuItems)) {
191
+                wx.hideMenuItems({
192
+                    menuList: wxConfig.hideMenuItems,
193
+                    success: function (res) {
194
+                        if (JSWE.wxHideMenuItemsSuccess) {JSWE.wxHideMenuItemsSuccess(res)}
195
+                    },
196
+                    fail: function (res) {
197
+                        if (JSWE.wxHideMenuItemsFail) {JSWE.wxHideMenuItemsFail(res)}
198
+                    }
199
+                })
200
+            }
201
+            // 8.4 批量显示菜单项
202
+            if (isNotEmpty(wxConfig.showMenuItems)) {
203
+                wx.showMenuItems({
204
+                    menuList: wxConfig.showMenuItems,
205
+                    success: function (res) {
206
+                        if (JSWE.wxShowMenuItemsSuccess) {JSWE.wxShowMenuItemsSuccess(res)}
207
+                    },
208
+                    fail: function (res) {
209
+                        if (JSWE.wxShowMenuItemsFail) {JSWE.wxShowMenuItemsFail(res)}
210
+                    }
211
+                })
212
+            }
213
+            // 8.5 隐藏所有非基本菜单项
214
+            // 8.6 显示所有被隐藏的非基本菜单项
215
+            if (wxConfig.baseFlag) {
216
+                if (wxConfig.baseHide) {wx.hideAllNonBaseMenuItem()} else {wx.showAllNonBaseMenuItem()}
217
+            }
218
+            // 8.7 关闭当前窗口
219
+            if (wxConfig.close) {wx.closeWindow()}
220
+        }, wxVoiceApi = function() {
221
+            // 4.3 监听录音自动停止
222
+            wx.onVoiceRecordEnd({
223
+                complete: function (res) {
224
+                    voice.localId = res.localId
225
+                    if (JSWE.wxVoiceRecordEnd) {JSWE.wxVoiceRecordEnd(res)}
226
+                }
227
+            })
228
+            // 4.7 监听录音播放停止
229
+            wx.onVoicePlayEnd({
230
+                complete: function (res) {
231
+                    if (JSWE.wxVoicePlayEnd) {JSWE.wxVoicePlayEnd(res)}
232
+                }
233
+            })
234
+        }, wxApi = function () {
235
+            wxShareApi()
236
+            wxMenuApi()
237
+            wxVoiceApi()
238
+        }
239
+
240
+        wx.ready(wxApi)
241
+
242
+        return wxApiFun = wxApi
243
+    }
244
+
245
+    if (isOpenInWeixin() || isOpenOnPC()) {
246
+        if ('undefined' !== typeof JSWE_CONF_UPDATE) JSWE_CONF_UPDATE(config)
247
+        $.ajax({
248
+            url: config.wxconfig,
249
+            type: 'get',
250
+            dataType: 'jsonp',
251
+            jsonpCallback: config.callback,
252
+            data: {
253
+                url: window.location.href.split('#')[0]
254
+            },
255
+            success: wxReady
256
+        })
257
+    }
258
+
259
+    function initWxData(data, flag) {
260
+        for(var d in data) {if (d in wxData) wxData[d] = data[d]}
261
+        if (flag) fixedWxData()
262
+    }
263
+
264
+    function changeWxData(key, value, flag) {
265
+        if (key in falDwxDataata) {wxData[key] = value}
266
+        if (flag) fixedWxData()
267
+    }
268
+
269
+    function fixedWxData() {
270
+        if ('undefined' !== typeof wxApiFun) wxApiFun()
271
+    }
272
+
273
+    // 3 智能接口
274
+    var voice = {
275
+        localId: '',
276
+        serverId: ''
277
+    }
278
+    // 3.1 识别音频并返回识别结果
279
+    function translateVoice() {
280
+        if (voice.localId == '') {
281
+            if (JSWE.wxTranslateVoiceEmpty) {JSWE.wxTranslateVoiceEmpty()}
282
+            return
283
+        }
284
+        wx.translateVoice({
285
+            localId: voice.localId,
286
+            complete: function (res) {
287
+                if (JSWE.wxTranslateVoiceComplete) {JSWE.wxTranslateVoiceComplete(res)}
288
+            }
289
+        })
290
+    }
291
+
292
+    // 4 音频接口
293
+    // 4.1 开始录音
294
+    function startRecord() {
295
+        wx.startRecord({
296
+            cancel: function () {
297
+                if (JSWE.wxStartRecordCancel) {JSWE.wxStartRecordCancel(res)}
298
+            }
299
+        })
300
+    }
301
+
302
+    // 4.2 停止录音
303
+    function stopRecord() {
304
+        wx.stopRecord({
305
+          success: function (res) {
306
+              voice.localId = res.localId
307
+              if (JSWE.wxStopRecordSuccess) {JSWE.wxStopRecordSuccess(res)}
308
+          },
309
+          fail: function (res) {
310
+              if (JSWE.wxStopRecordFail) {JSWE.wxStopRecordFail(res)}
311
+          }
312
+        })
313
+    }
314
+
315
+    // 4.4 播放音频
316
+    function playVoice() {
317
+        if (voice.localId == '') {
318
+            if (JSWE.wxPlayVoiceEmpty) {JSWE.wxPlayVoiceEmpty()}
319
+            return
320
+        }
321
+        wx.playVoice({
322
+            localId: voice.localId
323
+        })
324
+    }
325
+
326
+    // 4.5 暂停播放音频
327
+    function pauseVoice() {
328
+        if (voice.localId == '') {
329
+            if (JSWE.wxPauseVoiceEmpty) {JSWE.wxPauseVoiceEmpty()}
330
+            return
331
+        }
332
+        wx.pauseVoice({
333
+            localId: voice.localId
334
+        })
335
+    }
336
+
337
+    // 4.6 停止播放音频
338
+    function stopVoice() {
339
+        if (voice.localId == '') {
340
+            if (JSWE.wxStopVoiceEmpty) {JSWE.wxStopVoiceEmpty()}
341
+            return
342
+        }
343
+        wx.stopVoice({
344
+            localId: voice.localId
345
+        })
346
+    }
347
+
348
+    // 4.8 上传语音
349
+    function uploadVoice() {
350
+        var localId = voice.localId
351
+        if (localId == '') {
352
+            if (JSWE.wxUploadVoiceEmpty) {JSWE.wxUploadVoiceEmpty()}
353
+            return
354
+        }
355
+        wx.uploadVoice({
356
+            localId: localId,
357
+            success: function (res) {
358
+                voice.serverId = res.serverId
359
+                if (JSWE.wxUploadVoiceSuccess) {JSWE.wxUploadVoiceSuccess(res, localId)}
360
+            }
361
+        })
362
+    }
363
+
364
+    // 4.9 下载语音
365
+    function downloadVoice() {
366
+        var serverId = voice.serverId
367
+        if (serverId == '') {
368
+            if (JSWE.wxDownloadVoiceEmpty) {JSWE.wxDownloadVoiceEmpty()}
369
+            return
370
+        }
371
+        wx.downloadVoice({
372
+            serverId: serverId,
373
+            success: function (res) {
374
+                voice.localId = res.localId
375
+                if (JSWE.wxDownloadVoiceSuccess) {JSWE.wxDownloadVoiceSuccess(res, serverId)}
376
+            }
377
+        })
378
+    }
379
+
380
+    // 5 图片接口
381
+    var images = {
382
+        localIds: [],
383
+        serverIds: []
384
+    }
385
+    // 5.1 拍照、本地选图
386
+    function chooseImage(choose_params) {
387
+        if ('undefined' === typeof choose_params) choose_params = {}
388
+        wx.chooseImage({
389
+            count: choose_params.count || 9, // 默认9
390
+            sizeType: choose_params.sizeType || ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
391
+            sourceType: choose_params.sourceType || ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
392
+            success: function (res) {
393
+                images.localIds = res.localIds // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
394
+                // 判断是否直接上传
395
+                if (choose_params.directUpload) {setTimeout(uploadImages({localIds: images.localIds, isShowProgressTips: choose_params.isShowProgressTips || 1}), 100)}
396
+                // 拍照、本地选图成功后的回调函数
397
+                if (JSWE.wxChooseImageSuccess) {JSWE.wxChooseImageSuccess(res, choose_params.extras || {})}
398
+            }
399
+        })
400
+    }
401
+
402
+    // 5.2 图片预览
403
+    function previewImage(preview_params) {
404
+        wx.previewImage({
405
+            current: preview_params.current, // 当前显示图片的链接,不填则默认为 urls 的第一张
406
+            urls: preview_params.urls // 需要预览的图片链接列表
407
+        })
408
+    }
409
+
410
+    // 5.3 上传图片
411
+    function uploadImage(upload_params) {
412
+        // 上传图片为异步处理,重复上传同一图片,返回的serverId也是不同的
413
+        var localId = upload_params.localId
414
+        wx.uploadImage({
415
+            localId: localId, // 需要上传的图片的本地ID,由chooseImage接口获得
416
+            isShowProgressTips: upload_params.isShowProgressTips || 1, // 默认为1,显示进度提示
417
+            success: function (res) {
418
+                images.serverIds.push(res.serverId) // 返回图片的服务器端ID
419
+                // 上传图片成功后的回调函数
420
+                if (JSWE.wxUploadImageSuccess) {JSWE.wxUploadImageSuccess(res, localId)}
421
+            }
422
+        })
423
+    }
424
+
425
+    function uploadImages(upload_params) {
426
+        var localIds = upload_params.localIds, isShowProgressTips = upload_params.isShowProgressTips || 1
427
+        images.serverIds = []
428
+        for (var idx in localIds) {uploadImage({localId: localIds[idx], isShowProgressTips: isShowProgressTips})}
429
+    }
430
+
431
+    // 5.4 下载图片
432
+    function downloadImage(download_params) {
433
+        var serverId = download_params.serverId
434
+        wx.downloadImage({
435
+            serverId: serverId, // 需要下载的图片的服务器端ID,由uploadImage接口获得
436
+            isShowProgressTips: download_params.isShowProgressTips || 1, // 默认为1,显示进度提示
437
+            success: function (res) {
438
+                images.localId.push(res.localId)
439
+                if (JSWE.wxDownloadImageSuccess) {JSWE.wxDownloadImageSuccess(res, serverId)}
440
+            }
441
+        })
442
+    }
443
+
444
+    function downloadImages(download_params) {
445
+        var serverIds = download_params.serverIds, isShowProgressTips = download_params.isShowProgressTips || 1
446
+        images.localIds = []
447
+        for (var idx in serverIds) {downloadImage({serverId: serverIds[idx], isShowProgressTips: isShowProgressTips})}
448
+    }
449
+
450
+    function getLocalImgData(localId) {
451
+        wx.getLocalImgData({
452
+            localId: localId, // 图片的localID
453
+            success: function (res) {
454
+                // var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
455
+                if (JSWE.wxGetLocalImgDataSuccess) {JSWE.wxGetLocalImgDataSuccess(res)}
456
+            }
457
+        })
458
+    }
459
+
460
+    // 9 微信原生接口
461
+    // 9.1.1 扫描二维码并返回结果
462
+    // 9.1.2 扫描二维码并返回结果
463
+    function scanQRCode(scan_params) {
464
+        if ('undefined' === typeof scan_params) scan_params = {}
465
+        wx.scanQRCode({
466
+            needResult: scan_params.needResult || 0,  // 默认为0,0扫描结果由微信处理,1直接返回扫描结果
467
+            scanType: scan_params.scanType || ['qrCode', 'barCode'],  // 可以指定扫二维码还是一维码,默认二者都有
468
+            success: function (res) {  // 当 needResult 为 1 时,扫码返回的结果
469
+                if (JSWE.wxScanQRCodeSuccess) {JSWE.wxScanQRCodeSuccess(res)}
470
+            }
471
+        })
472
+    }
473
+
474
+    // QRCode & BarCode is different
475
+    function parseScanQRCodeResultStr(resultStr) {
476
+        var strs = resultStr.split(',')
477
+        return strs[strs.length - 1]
478
+    }
479
+
480
+    // 10 微信支付接口
481
+    // 10.1 发起一个支付请求
482
+    function chooseWXPay(wxpay_params) {
483
+        wx.chooseWXPay({
484
+            timestamp: wxpay_params.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
485
+            nonceStr: wxpay_params.nonceStr, // 支付签名随机串,不长于 32 位
486
+            package: wxpay_params.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
487
+            signType: wxpay_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
488
+            paySign: wxpay_params.paySign, // 支付签名
489
+            success: function (res) {
490
+                // 支付成功后的回调函数
491
+                if (JSWE.wxPaySuccess) {JSWE.wxPaySuccess(res)}
492
+            }
493
+        })
494
+    }
495
+
496
+    // xx 微信原生企业红包接口
497
+    // xx.1 发起一个发送原生企业红包请求
498
+    function openEnterpriseRedPacket(wxredpack_params) {
499
+        wx.openEnterpriseRedPacket({
500
+            timeStamp: wxredpack_params.timeStamp, // 红包签名时间戳,注意原生企业红包接口timeStamp字段名需大写其中的S字符,而支付接口timeStamp字段名无需大写其中的S字符。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
501
+            nonceStr: wxredpack_params.nonceStr, // 红包签名随机串,不长于 32 位
502
+            package: encodeURIComponent(wxredpack_params.package), // 发放红包接口返回的prepay_id参数值,提交格式如:prepay_id=***)
503
+            signType: wxredpack_params.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
504
+            paySign: wxredpack_params.paySign, // 红包签名
505
+            success: function (res) {
506
+                // 发送原生企业红包成功后的回调函数
507
+                if (JSWE.wxEnterpriseRedPacketSuccess) {JSWE.wxEnterpriseRedPacketSuccess(res)}
508
+            }
509
+        })
510
+    }
511
+
512
+    var v = {
513
+        version: '1.0.5',
514
+
515
+        // Basic Vars
516
+        config: config,
517
+        wxData: wxData,
518
+        jsApiList: jsApiList,
519
+
520
+        isEmpty: isEmpty,
521
+        isNotEmpty: isNotEmpty,
522
+
523
+        // Weixin Function
524
+        isOpenInWeixin: isOpenInWeixin,
525
+        getWeixinVersion: getWeixinVersion,
526
+        isWeixinVersion: isWeixinVersion,
527
+
528
+        // Menu Function
529
+        hideOptionMenu: hideOptionMenu,
530
+        showOptionMenu: showOptionMenu,
531
+        hideMenuItems: hideMenuItems,
532
+        showMenuItems: showMenuItems,
533
+        hideAllNonBaseMenuItem: hideAllNonBaseMenuItem,
534
+        showAllNonBaseMenuItem: showAllNonBaseMenuItem,
535
+        closeWindow: closeWindow,
536
+
537
+        // Share Function
538
+        initWxData: initWxData,
539
+        changeWxData: changeWxData,
540
+        fixedWxData: fixedWxData,
541
+
542
+        // Voice Function
543
+        voice: voice,
544
+        translateVoice: translateVoice,
545
+        startRecord: startRecord,
546
+        stopRecord: stopRecord,
547
+        playVoice: playVoice,
548
+        pauseVoice: pauseVoice,
549
+        stopVoice: stopVoice,
550
+        uploadVoice: uploadVoice,
551
+        downloadVoice: downloadVoice,
552
+
553
+        // Image Function
554
+        images: images,
555
+        chooseImage: chooseImage,
556
+        previewImage: previewImage,
557
+        uploadImage: uploadImage,
558
+        uploadImages: uploadImages,
559
+        downloadImage: downloadImage,
560
+        downloadImages: downloadImages,
561
+        getLocalImgData: getLocalImgData,
562
+
563
+        // Scan Function
564
+        scanQRCode: scanQRCode,
565
+        parseScanQRCodeResultStr: parseScanQRCodeResultStr,
566
+
567
+        // Pay Function
568
+        chooseWXPay: chooseWXPay,
569
+
570
+        // EnterpriseRedPacket Function
571
+        openEnterpriseRedPacket: openEnterpriseRedPacket
572
+    }
573
+    e.JSWE = e.V = v
574
+})(window)

+ 56 - 0
tiwen/urls.py

@@ -0,0 +1,56 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""tiwen 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.static import static
20
+from django.contrib import admin
21
+from django.urls import include, re_path
22
+
23
+
24
+urlpatterns = [
25
+    re_path(r'^admin/', admin.site.urls),
26
+]
27
+
28
+urlpatterns += [
29
+    re_path(r'^api/', include(('api.urls', 'api'), namespace='api')),
30
+    re_path(r'^uniapi/', include(('django_uniapi.urls', 'uniapi'), namespace='uniapi')),
31
+]
32
+
33
+urlpatterns += [
34
+    # url(r'^s/', include(('django_short_url.urls', 'django_short_url'), namespace='django_short_url')),
35
+]
36
+
37
+urlpatterns += [
38
+    re_path(r'^w/', include(('django_we.urls', 'shortwechat'), namespace='shortwechat')),
39
+    re_path(r'^we/', include(('django_we.urls', 'wechat'), namespace='wechat')),
40
+]
41
+
42
+urlpatterns += [
43
+    # re_path(r'^p/', include(('page.urls', 'shortpage'), namespace='shortpage')),
44
+    # re_path(r'^page/', include(('page.urls', 'page'), namespace='page')),
45
+]
46
+
47
+if settings.DEBUG:
48
+    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
49
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
50
+
51
+# AdminSite
52
+admin.site.site_header = 'Django administration'
53
+admin.site.site_title = 'Django site admin'
54
+# Make site_url/index_title None to hidden
55
+admin.site.site_url = '/'
56
+admin.site.index_title = 'Site administration'

+ 17 - 0
tiwen/wsgi.py

@@ -0,0 +1,17 @@
1
+"""
2
+WSGI config for tiwen 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", "tiwen.settings")
16
+
17
+application = get_wsgi_application()

+ 0 - 0
utils/__init__.py


+ 0 - 0
utils/error/__init__.py


+ 77 - 0
utils/error/errno_utils.py

@@ -0,0 +1,77 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from StatusCode import BaseStatusCode, StatusCodeField
4
+
5
+
6
+class ParamStatusCode(BaseStatusCode):
7
+    """ 4000xx 参数相关错误码 """
8
+    PARAM_NOT_FOUND = StatusCodeField(400000, 'Param Not Found', description='参数不存在')
9
+
10
+
11
+class ProfileStatusCode(BaseStatusCode):
12
+    """ 4001xx 用户相关错误码 """
13
+    PROFILE_NOT_FOUND = StatusCodeField(400101, 'Profile Not Found', description='用户不存在')
14
+
15
+
16
+class PhoneStatusCode(BaseStatusCode):
17
+    """ 4002xx 手机相关错误码 """
18
+    INVALID_PHONE = StatusCodeField(400200, 'Invalid Phone', description='非法手机号')
19
+    PHONE_NOT_FOUND = StatusCodeField(400201, 'Phone Not Found', description='手机号不存在')
20
+    PHONE_ALREADY_EXISTS = StatusCodeField(400202, 'Phone Already Exists', description='手机号已存在')
21
+
22
+
23
+class OrderStatusCode(BaseStatusCode):
24
+    """ 4040xx 订单/支付相关错误码 """
25
+    UNIFIED_ORDER_FAIL = StatusCodeField(404000, 'Unified Order Fail', description='统一下单失败')
26
+    ORDER_NOT_FOUND = StatusCodeField(404001, 'Order Not Found', description='订单不存在')
27
+    # 订单支付状态
28
+    ORDER_NOT_PAY = StatusCodeField(404011, 'Order Not Pay', description='订单未支付')
29
+    ORDER_PAYING = StatusCodeField(404012, 'Order Paying', description='订单支付中')
30
+    ORDER_PAY_FAIL = StatusCodeField(404013, 'Order Pay Fail', description='微信支付失败')
31
+    # 通知校验状态
32
+    SIGN_CHECK_FAIL = StatusCodeField(404090, 'Sign Check Fail', description='签名校验失败')
33
+    FEE_CHECK_FAIL = StatusCodeField(404091, 'FEE Check Fail', description='金额校验失败')
34
+
35
+
36
+class PayStatusCode(BaseStatusCode):
37
+    """ 4041xx 支付相关错误码 """
38
+
39
+
40
+class WithdrawStatusCode(BaseStatusCode):
41
+    """ 4042xx 提现相关错误码 """
42
+    BALANCE_INSUFFICIENT = StatusCodeField(404200, 'Balance Insufficient', description='提现金额不足')
43
+
44
+
45
+class TokenStatusCode(BaseStatusCode):
46
+    """ 4090xx 票据相关错误码 """
47
+    TOKEN_NOT_FOUND = StatusCodeField(409001, 'Token Not Found', description='票据不存在')
48
+
49
+
50
+class SignatureStatusCode(BaseStatusCode):
51
+    """ 4091xx 签名校验错误 """
52
+    SIGNATURE_ERROR = StatusCodeField(409101, 'Signature Error', description='签名错误')
53
+
54
+
55
+class GVCodeStatusCode(BaseStatusCode):
56
+    """ 4092xx 图形验证码相关错误码 """
57
+    GRAPHIC_VCODE_ERROR = StatusCodeField(409201, 'Graphic VCode Error', description='图形验证码错误')
58
+
59
+
60
+class SVCodeStatusCode(BaseStatusCode):
61
+    """ 4093xx 短信验证码相关错误码 """
62
+    SMS_QUOTA_LIMIT = StatusCodeField(409300, 'SMS Quota Limit', description='短信次数超限')
63
+    SMS_VCODE_ERROR = StatusCodeField(409301, 'SMS VCode Error', description='验证码错误,请稍后重试')
64
+    SMS_VCODE_HAS_SEND = StatusCodeField(409302, 'SMS VCode Has Send', description='验证码已发送,请勿重复获取')
65
+
66
+
67
+class InsufficientStatusCode(BaseStatusCode):
68
+    """ 4095xx 不足相关错误码 """
69
+    BALANCE_INSUFFICIENT = StatusCodeField(409501, 'Balance Insufficient', description='余额不足')
70
+    INTEGRAL_INSUFFICIENT = StatusCodeField(409502, 'Integral Insufficient', description='积分不足')
71
+
72
+
73
+class PermissionStatusCode(BaseStatusCode):
74
+    """ 4099xx 权限相关错误码 """
75
+    PERMISSION_DENIED = StatusCodeField(409900, 'Permission Denied', description='权限不足')
76
+    UPLOAD_PERMISSION_DENIED = StatusCodeField(409910, 'Upload Permission Denied', description='上传权限不足')
77
+    UPDATE_PERMISSION_DENIED = StatusCodeField(409930, 'Update Permission Denied', description='更新权限不足')

+ 24 - 0
utils/error/response_utils.py

@@ -0,0 +1,24 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from json_response import JsonpResponse, 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={}, msg_args=[], msg_kwargs={}, desc_args=[], desc_kwargs={}, request=None, callback=None, **kwargs):
17
+    # Final Message and Description
18
+    message, description = (message or status_code.message, description or status_code.description) if isinstance(status_code, StatusCodeField) else (message, description)
19
+    # Final Response Data
20
+    resp_data = response_data(status_code, (message or '').format(*msg_args, **msg_kwargs), (description or '').format(*desc_args, **desc_kwargs), data, **kwargs)
21
+    # Assign Callback
22
+    callback = callback or (request and request.GET.get('callback'))
23
+    # Call JsonResponse or JsonpResponse
24
+    return JsonpResponse(callback, resp_data, safe=False) if callback else JsonResponse(resp_data, 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 -*-

+ 0 - 0
utils/user/__init__.py


+ 18 - 0
utils/user/userinfo_save.py

@@ -0,0 +1,18 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+
4
+def userinfo_save(userinfo):
5
+    """ Save profile or something else """
6
+    # from account.models import UserInfo
7
+    # from django.conf import settings
8
+    #
9
+    # unique_identifier = userinfo.get(settings.WECHAT_UNIQUE_IDENTIFICATION, '')
10
+    #
11
+    # user, created = UserInfo.objects.select_for_update().get_or_create(**{settings.WECHAT_UNIQUE_IDENTIFICATION: unique_identifier})
12
+    # user.unionid = userinfo.get('unionid', '')
13
+    # user.openid = userinfo.get('openid', '')
14
+    # user.nickname = userinfo.get('nickname', '')
15
+    # user.avatar = userinfo.get('headimgurl', '')
16
+    # user.save()
17
+    #
18
+    # return user

pai2 - Gogs: Go Git Service

拍爱

errorcode 332B

    1、用户信息 —— 400 4000 —— 摄影师不存在 4001 —— 摄影师密码错误 4010 —— 用户名已注册 4011 —— 用户名不存在 4012 —— 用户密码错误 2、照片上传 —— 401 4010 —— 参数错误 4011 —— 摄影师不存在 4012 —— 照片已存在