rel=""> 3
+
4
+def DJANGO_FILE_UPLOAD_CALLBACK_FUNC(request, file_path=None, file_url=None):
5
+    """ DJANGO FILE UPLOAD Callback Func """

+ 81 - 0
jdjos/django_we_callback_settings.py

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

+ 16 - 0
jdjos/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
jdjos/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', 'jdjos']
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 = u'[Templet] '

+ 27 - 0
jdjos/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
+]

+ 396 - 0
jdjos/settings.py

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

+ 68 - 0
jdjos/static/jdjos/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
jdjos/static/jdjos/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)

+ 52 - 0
jdjos/urls.py

@@ -0,0 +1,52 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+"""jdjos 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
+]
27
+
28
+urlpatterns += [
29
+    url(r'^api/', include('api.urls', namespace='api')),
30
+    url(r'^uniapi/', include('django_uniapi.urls', namespace='uniapi')),
31
+]
32
+
33
+urlpatterns += [
34
+    # url(r'^s/', include('django_short_url.urls', namespace='django_short_url')),
35
+]
36
+
37
+urlpatterns += [
38
+    url(r'^w/', include('django_we.urls', namespace='shortwechat')),
39
+    url(r'^we/', include('django_we.urls', namespace='wechat')),
40
+]
41
+
42
+urlpatterns += [
43
+    # url(r'^p/', include('page.urls', namespace='shortpage')),
44
+    # url(r'^page/', include('page.urls', namespace='page')),
45
+]
46
+
47
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
48
+urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
49
+
50
+# AdminSite
51
+admin.site.site_title = ''
52
+admin.site.site_header = 'My administration'

+ 17 - 0
jdjos/wsgi.py

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

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

+ 7 - 0
requirements.txt

@@ -0,0 +1,7 @@
1
+StatusCode==1.0.0
2
+furl==2.0.0
3
+jsonfield==2.0.2
4
+mysqlclient==1.3.13
5
+-r requirements_dj.txt
6
+-r requirements_pywe.txt
7
+-r requirements_redis.txt

+ 3 - 0
requirements_deploy.txt

@@ -0,0 +1,3 @@
1
+ipdb==0.11
2
+ipython==5.6.0
3
+uwsgi==2.0.17

+ 2 - 0
requirements_dev.txt

@@ -0,0 +1,2 @@
1
+isort==4.3.9
2
+pycodestyle==2.4.0

+ 15 - 0
requirements_dj.txt

@@ -0,0 +1,15 @@
1
+Django==1.11.16
2
+django-admin==1.3.2
3
+django-detect==1.0.8
4
+django-file==1.0.3
5
+django-json-render==1.0.2
6
+django-json-response==1.1.5
7
+django-logit==1.1.3
8
+django-models-ext==1.1.8
9
+django-redis-connector==1.0.1
10
+django-response==1.1.1
11
+django-rlog==1.0.7
12
+django-short-url==1.1.3
13
+django-six==1.0.4
14
+django-uniapi==1.0.5
15
+django-we==1.4.2

+ 2 - 0
requirements_pywe.txt

@@ -0,0 +1,2 @@
1
+pywe-oauth==1.0.7
2
+pywe-pay==1.0.12

+ 3 - 0
requirements_redis.txt

@@ -0,0 +1,3 @@
1
+hiredis==1.0.0
2
+redis==2.10.6
3
+redis-extensions==1.2.5

+ 4 - 0
sysctl.sh

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

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

+ 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

Kodo/kodo - Gogs: Go Git Service

13 Commissions (03304ad48ab33bab233847d9af9eab52f6469e6c)

Auteur SHA1 Message Date
  Brightcells fabef63211 set line_length=200 for isort 10 ans auparavant
  Brightcells f72ccb1875 isort import 10 ans auparavant
  Brightcells b7a21719c7 change message_list_api to return unread message num 10 ans auparavant
  Brightcells a1bd97d641 change all param to cmp to string true 10 ans auparavant
  Brightcells 165424743a modify message relative apis 10 ans auparavant
  Brightcells c8d93d6339 add api message_delete_api/message_type_delete_api 10 ans auparavant
  Brightcells 454f8cd8ec change some info store in redis & remove some unnecessary params check & add some return for some api 10 ans auparavant
  Brightcells 7d85bed0b0 move errno_utils.py/response_utils.py into error file 10 ans auparavant
  Brightcells 62aff0cb2f move status code into errno_utils.py to unified management 10 ans auparavant
  Brightcells 0786f22bad order_by('-pk') replace of order_by('-created_at') 10 ans auparavant
  Brightcells 464c31b794 support POST method for message relative api 10 ans auparavant
  Brightcells 8f83bd22be add param user_id for msg relative api 10 ans auparavant
  Brightcells 30daca135b add api message_list_api/message_type_list_api/message_read_api 10 ans auparavant