瀏覽代碼

Merge ssh://192.168.136.99:10022/SERVER/AnsjerPush
合并SERVER仓库代码

locky 4 年之前
父節點
當前提交
22a43fddbe
共有 58 個文件被更改,包括 6492 次插入0 次删除
  1. 0 0
      AnsjerPush/__init__.py
  2. 194 0
      AnsjerPush/config.py
  3. 80 0
      AnsjerPush/database_router.py
  4. 3 0
      AnsjerPush/file/alipay/alipay_private_2048.pem
  5. 3 0
      AnsjerPush/file/alipay/alipay_public_2048.pem
  6. 3 0
      AnsjerPush/file/alipay/zosi_alipay_private_2048.pem
  7. 3 0
      AnsjerPush/file/alipay/zosi_alipay_public_2048.pem
  8. 64 0
      AnsjerPush/file/apns_pem/accloud-dev.pem
  9. 67 0
      AnsjerPush/file/apns_pem/accloud.pem
  10. 67 0
      AnsjerPush/file/apns_pem/apns-dis-adcloud.pem
  11. 64 0
      AnsjerPush/file/apns_pem/customizeda-dev.pem
  12. 68 0
      AnsjerPush/file/apns_pem/customizeda.pem
  13. 64 0
      AnsjerPush/file/apns_pem/customizedb-dev.pem
  14. 68 0
      AnsjerPush/file/apns_pem/customizedb.pem
  15. 68 0
      AnsjerPush/file/apns_pem/customizedc.pem
  16. 68 0
      AnsjerPush/file/apns_pem/customizedd.pem
  17. 68 0
      AnsjerPush/file/apns_pem/customizede.pem
  18. 68 0
      AnsjerPush/file/apns_pem/lcc-dev.pem
  19. 68 0
      AnsjerPush/file/apns_pem/lcc.pem
  20. 64 0
      AnsjerPush/file/apns_pem/zccloud-dev.pem
  21. 67 0
      AnsjerPush/file/apns_pem/zccloud.pem
  22. 64 0
      AnsjerPush/file/apns_pem/zosidcloud-dev.pem
  23. 67 0
      AnsjerPush/file/apns_pem/zosidcloud.pem
  24. 二進制
      AnsjerPush/file/font/simhei.ttf
  25. 184 0
      AnsjerPush/file/log type .txt
  26. 69 0
      AnsjerPush/file/后台管理系统说明.txt
  27. 二進制
      AnsjerPush/file/表文档.doc
  28. 161 0
      AnsjerPush/formal_config.py
  29. 201 0
      AnsjerPush/formal_settings.py
  30. 16 0
      AnsjerPush/formal_wsgi.py
  31. 148 0
      AnsjerPush/local_config.py
  32. 220 0
      AnsjerPush/local_settings.py
  33. 16 0
      AnsjerPush/local_wsgi.py
  34. 145 0
      AnsjerPush/settings.py
  35. 59 0
      AnsjerPush/test.py
  36. 146 0
      AnsjerPush/test_config.py
  37. 198 0
      AnsjerPush/test_settings.py
  38. 16 0
      AnsjerPush/test_wsgi.py
  39. 27 0
      AnsjerPush/urls.py
  40. 16 0
      AnsjerPush/wsgi.py
  41. 671 0
      Controller/DetectController.py
  42. 323 0
      Controller/EquipmentInfo.py
  43. 153 0
      Controller/ShadowController.py
  44. 708 0
      Controller/TestDetectController.py
  45. 474 0
      Model/models.py
  46. 68 0
      Object/ETkObject.py
  47. 17 0
      Object/LogUtil.py
  48. 82 0
      Object/RedisObject.py
  49. 134 0
      Object/ResponseObject.py
  50. 126 0
      Object/TokenObject.py
  51. 48 0
      Object/UidTokenObject.py
  52. 216 0
      Service/CommonService.py
  53. 195 0
      Service/ModelService.py
  54. 22 0
      formal_manage.py
  55. 22 0
      local_manage.py
  56. 15 0
      manage.py
  57. 224 0
      requirements.txt
  58. 22 0
      test_manage.py

+ 0 - 0
AnsjerPush/__init__.py


+ 194 - 0
AnsjerPush/config.py

@@ -0,0 +1,194 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: Ansjer
+@software: PyCharm
+@DATE: 2018/7/2 14:06
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: Conf.py
+@Contact: chanjunkai@163.com
+"""
+# 主要静态变量配置文件
+import datetime, os
+
+
+DEBUG_MODE = 'DEV'
+# MODE = 'PRO'
+# 阿里云发邮箱
+ALY_SES_ACCESS_NAME = 'message@dvema.com'
+ALY_SES_ACCESS_PAW = 'SMtp123456'
+ALY_SES_ACCESS_REPLYTO = '***'
+
+# 发送邮件邮箱
+SES_COMPANY_EMAIL = 'user_server@nsst.com'
+AWS_SES_ACCESS_ID = 'AKIAJKPU23EU5QWHFPKQ'
+AWS_SES_ACCESS_SECRET = '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'
+AWS_SES_ACCESS_REGION = 'us-east-1'
+AWS_BUCKET = 'ansjertest'
+# 设定离线时间为5分钟
+OFF_LINE_TIME_DELTA = 5
+
+# token的secret
+OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
+OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
+# access_token超时
+# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
+OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
+# refresh_token超时
+OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
+# 腾讯验证,短信发送
+TX_PHONE_APP_ID = '1400052907'
+TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
+
+# 验证码超时时间
+AuthCode_Expire = 600
+
+# 根路径
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# uid token key
+UID_TOKEN_KEY = 'c+565*j@%^'
+
+# oss param
+OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
+OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
+OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
+
+
+NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
+SERVER_DOMAIN = 'http://www.dvema.com/'
+SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
+DOMAIN_HOST = 'www.dvema.com'
+# SERVER_HOST = 'localhost'
+PAYPAL_CRD = {
+    "mode": "live",  # sandbox or live
+    "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
+    "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
+}
+DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+
+
+JPUSH_CONFIG = {
+    'com.ansjer.accloud_ab': {
+        'Key': 'f0dc047e5e53fd14199de5b0',
+        'Secret': 'aa7f7db33e9f0a7f3871aa1c'},
+    'com.ansjer.adcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_ab': {
+        'Key': 'd9924f56d3cc7c6017965130',
+        'Secret': '869d832d126a232f158b5987'},
+    'com.ansjer.loocamccloud_ab': {
+        'Key': 'd1cc44797b4642b0e05304fe',
+        'Secret': 'c3e8b4ca8c576de61401e56a'},
+    'com.ansjer.loocamdcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_a': {
+        'Key': '57de2a80d68bf270fd6bdf5a',
+        'Secret': '3d354eb6a0b49c2610decf42'},
+    'com.ansjer.accloud_a': {
+        'Key': 'ff95ee685f49c0dc4013347b',
+        'Secret': 'de2c20959f5516fdeeafe78e'},
+    'com.ansjer.adcloud_a': {
+        'Key': '2e47eb1aee9b164460df3668',
+        'Secret': 'b9137d8d684bc248f1809b6d'},
+    'com.ansjer.loocamccloud_a': {
+        'Key': '23c9213215c7ca0ec945629b',
+        'Secret': '81e4b1e859cc8387e2e6c431'},
+    'com.ansjer.loocamdcloud_a': {
+        'Key': '1dbdd60a16e9892d6f68a073',
+        'Secret': '80a97690e7e043109059b403'},
+    'com.ansjer.customizedb_a': {
+        'Key': '9d79630aa49adfa291fe2568',
+        'Secret': '4d8ff52f88136561875a0212'},
+    'com.ansjer.customizedd_a': {
+        'Key': '8fc4f495685bde53341ee25d',
+        'Secret': 'f1da11fa466509fa2670fb66',
+    }
+}
+# type =1
+FCM_CONFIG = {
+    'com.ansjer.zccloud_a': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+    'com.ansjer.loocamccloud_a': 'AAAAb9YP3rk:APA91bFCgd-kbVmpK4EVpfdHH_PJZQCYTkOGnTZdIuBWEz2r7aMRsJYHOH3sB-rwcbaRWgnufTyjX9nGQxb6KxQbWVk4ah_H-M3IqGh6Mb60WQQAuR33V6g_Jes5pGL6ViuIxGHqVMaR',
+    'com.ansjer.loocamdcloud_a': 'AAAAb9YP3rk:APA91bGw2I2KMD4i-5T7nZO_wB8kuAOuqgyqe5rxmY-W5qkpYEx9IL2IfmC_qf6B_xOyjIDDSjckvMo-RauN__SEoxvAkis7042GRkoKpw7cjZ_H8lC-d50PC0GclPzccrOGFusyKbFY',
+    'com.ansjer.customizedb_a': 'AAAAb9YP3rk:APA91bE7kI4vcm-9h_CJNFlOZfc-xwP4Btn6AnjOrwoKV6fgYN7fdarkO76sYxVZiAbDnxsFfOJyP7vQfwyan6mdjuyD5iHdt_XgO22VqniC0vA1V4GJiCS8Tp7LxIX8JVKZl9I_Powt',
+    'com.ansjer.customizeda_a': 'AAAAb9YP3rk:APA91bF0HzizVWDc6dKzobY9fsaKDK4veqkOZehDXshVXs8pEEvNWjR_YWbhP60wsRYCHCal8fWN5cECVOWNMMzDsfU88Ty2AUl8S5FtZsmeDTkoGntQOswBr8Ln7Fm_LAp1VqTf9CpM',
+    'com.ansjer.customizedd_a': 'AAAAb9YP3rk:APA91bHkxOozJWBrlv3eNT0PgwosYENI9aM4Zuzd418cX-iKkpa1zFNC5MkNDKApx1KH4fhmAfaJ6IMRZ0nj5GIxCpstDYCaZWwgC7-etqfSxG5JAq8LOwJx0o_1tUZqwjIic8ztsg0o',
+    'com.ansjer.adcloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.accloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.zccloud_ab': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+}
+SERVER_TYPE = os.environ.get('DJANGO_SETTINGS_MODULE')
+
+APNS_CONFIG = {
+    'com.ansjer.loocamccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/lcc-dev.pem',
+    },
+    'com.ansjer.zosidcloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zosidcloud-dev.pem',
+    },
+    'com.ansjer.customizedb': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedb-dev.pem',
+    },
+    'com.ansjer.customizeda': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizeda-dev.pem',
+    },
+    'com.ansjer.zccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zccloud-dev.pem',
+    },
+    'com.ansjer.accloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/accloud-dev.pem',
+    },
+    'com.ansjer.customizedc':{
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedc.pem',
+    }
+}
+APNS_MODE = 'dev'
+REDIS_ADDRESS = '127.0.0.1'
+
+if SERVER_TYPE == 'AnsjerPush.local_settings':
+    from AnsjerPush.local_config import *
+elif SERVER_TYPE == 'AnsjerPush.test_settings':
+    from AnsjerPush.test_config import *
+elif SERVER_TYPE == 'AnsjerPush.formal_settings':
+    from AnsjerPush.formal_config import *
+
+APNS_CODE = {
+    -1: '只库存不推送',
+    200: '推送成功',
+    400: '请求有问题',
+    403: '证书或Token有问题',
+    405: '请求方式不正确, 只支持POST请求',
+    410: '设备的Token与证书不一致'
+}
+
+JPUSH_CODE = {
+    -1: '只库存不推送',
+    200: '推送成功',
+    400: '错误请求',
+    401: '没有验证信息或者验证失败',
+    403: '请求被拒绝',
+    404: '资源不存在,请求的用户的不存在,请求的格式不被支持',
+    405: '请求方法不合适',
+    410: '请求的资源已下线',
+    429: '请求超出了频率限制',
+    500: '极光内部服务错误',
+    502: '业务服务器下线了或者正在升级。请稍后重试',
+    503: '服务暂时失效',
+    504: '代理超时'
+}
+
+FCM_CODE = {
+    -1: '只库存不推送',
+    200: '推送成功',
+    400: '无效数据',
+    401: '没有验证信息或者验证失败'
+}
+
+APP_TYPE = {
+    1: 'ios',
+    2: 'android'
+}

+ 80 - 0
AnsjerPush/database_router.py

@@ -0,0 +1,80 @@
+# database_router.py
+from django.conf import settings
+
+DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
+
+
+class DatabaseAppsRouter(object):
+    """
+    A router to control all database operations on models for different
+    databases.
+DATABASE_ROUTERS DATABASE_ROUTERS
+    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
+    will fallback to the `default` database.
+
+    Settings example:
+
+    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
+    """
+
+    def db_for_read(self, model, **hints):
+        """"Point all read operations to the specific database."""
+        if model._meta.app_label in DATABASE_MAPPING:
+            return DATABASE_MAPPING[model._meta.app_label]
+        return None
+
+    def db_for_write(self, model, **hints):
+        """Point all write operations to the specific database."""
+        if model._meta.app_label in DATABASE_MAPPING:
+            return DATABASE_MAPPING[model._meta.app_label]
+        return None
+
+    def allow_relation(self, obj1, obj2, **hints):
+        """Allow any relation between apps that use the same database."""
+        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
+        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
+        if db_obj1 and db_obj2:
+            if db_obj1 == db_obj2:
+                return True
+            else:
+                return False
+        return None
+
+    def allow_syncdb(self, db, model):
+        """Make sure that apps only appear in the related database."""
+
+        if db in DATABASE_MAPPING.values():
+            return DATABASE_MAPPING.get(model._meta.app_label) == db
+        elif model._meta.app_label in DATABASE_MAPPING:
+            return False
+        return None
+
+    def allow_migrate(self, db, app_label, model=None, **hints):
+        """
+        Make sure the auth app only appears in the 'auth_db'
+        database.
+        """
+        if db in DATABASE_MAPPING.values():
+            return DATABASE_MAPPING.get(app_label) == db
+        elif app_label in DATABASE_MAPPING:
+            return False
+        return None
+
+    # for Django 1.4 - Django 1.6
+    def allow_syncdb(self, db, model):
+        """Make sure that apps only appear in the related database."""
+
+        if db in DATABASE_MAPPING.values():
+            return DATABASE_MAPPING.get(model._meta.app_label) == db
+        elif model._meta.app_label in DATABASE_MAPPING:
+            return False
+        return None
+
+    # Django 1.7 - Django 1.11
+    def allow_migrate(self, db, app_label, model_name=None, **hints):
+
+        if db in DATABASE_MAPPING.values():
+            return DATABASE_MAPPING.get(app_label) == db
+        elif app_label in DATABASE_MAPPING:
+            return False
+        return None

+ 3 - 0
AnsjerPush/file/alipay/alipay_private_2048.pem

@@ -0,0 +1,3 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCkSf9JD/HprSulQiE6zHkcYX1f5nnDfaM5SCB8iVz/C7ClX+o4fj446kkq74BkB3k40S6RhttQRiRIkRinsR4/ZxgtwWOaUFCPrZebIqm5CBKp6UHCq8aiYFzBomrUWLLvtqfuwMiUh6J+0LLr1lVocQqu2aCCt/D62ac21bAD1WkDQzcDNVoeRAvsnvkyQj2ftN+BdR423/jzWabmNGATBvjk+dzuGMMl8jYDQcJ0nhpOXy3R0ytWMny1OT94VxVX7CwAJt0nzA9aw/f11wfYNqBt33/PuchzHb/Nond/OjIQ2Hg+vVeXnTjoK/FN4Qw5cM0apiHJU2ea7QxhzTZ7AgMBAAECggEBAKPrBOB0HU2mr46Crb8J9qjfiIVf4wuvVmU31toLABOlKUnHql1VcGW/NOO29SZY4pVS+SDQKhAs+9S/mJbwPJrMFS7J7sQv8EF61XI/IpJwQJbxGeDPHTAz7zhwAiD+owJT/7hS945iyjm3p11xi+PaLeuJTgI+3xyaf6FRkf4vnSf1oAicP1Ufg/eHr1BKPpL5BdtSVxtTKwOZpl4IkvvjBTvTVqzl/nHqOJXEew/H2EL48KDMhrZ9IfFCCUgMtowoIys/xcGmcuFbwo1+YIETDCEat4XnGBW/q81C/+l/KtP9scbT17AMBaH0dxLcJHPR1IVn8cVTw+1uctbRRUkCgYEA2wX2cpqGWnfTwb5gPchRr4hTzZx/EpDmNkOiGe1mgwoEH09ucRCAU54IOIWJnlUGO4rs4wwreIA/kO0DQkjM8Q5Xho/Wob/vjLemvgAx1ZaxspIp2fKDd/+Mk1ksXuK9RTASI46uAfQVx4S6vQGAOVnCQd3yHgyeplE9m21X+l0CgYEAwAZ1PqilcjjbhKnIBGbgPNuEGS3MrHanUAEYnb3AAxBjFLioAsgEYCsvE6l2MWz4ZSXLkG7qvNGqndUPY3sxltd9csrYRoTR7wiM1SJ/BVZGS7Bv9DeVlyxVg6WYgjKfDia4+UAIYyJjlB1n4+vmNScnTTzdpHa6CRC0WIAmVrcCgYBkJ+Df3VvM6PjTowv9lKoN++heoM7XHZ24eMsa0h3LlHv4GWmmmGYmVqDpv98DoVvdRJU46mrCitsGmeuglwvou6c4qHP8uXmAvsaoIgyn4GZYA1DSEn0eFaUiCGgq58KnbekNi4R07jo7Z63BGnuZPjt/xU+fgCPYaFQkzCasHQKBgQCoSsCkqq9dXOFe8mG/Id5y17x9SLtxhwf5wQwu6ycGSG1dsrxku6HCGvbGV6WjiaplE2vF6L/HvkW20A0NleGPHsruOUK1AyDuSmMLRJFoPwWavkZQFgr/k7Zai14O9YiaoWtJ30m6gGAfZmovwgZfHTJ/qdFimz9ORJqaWa3AHwKBgH2kjJx5D8c/IuNMQ3yErKj8a41u7852d04W65ZxlhaX3cFGpJVR4REA548U8K9Fy89Z7Xzf6ByI80e9clmy/I71vQfPEpxOGkI/1JnUjHdBDyk6Owqg4zOTUPlF2nUeJfi78G53W/gbqb6kKB2v0cxfk0QElvAPBZGsT9ptZbti
+-----END RSA PRIVATE KEY-----

+ 3 - 0
AnsjerPush/file/alipay/alipay_public_2048.pem

@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6WSh//F8thrOf8Zm5CwIr9+HYy3+K4uRQtZoOolCRkbO47k++4da5/uBJz/vEoyX2/YwyqbdzIRbpwZlSyTKrRJpf5JwUE26dSVIjZ+2FOOUoRqf9MTDHhMEtvRvW2nSD3u4gDSz2vTuQPdiFzpJamuszR9ONdeIIYttoC//BR5TO3aDPHD1Q0D1WVnaBvWWnFUNaaW0BLjqLa3gtxWeUgMSePjusG28EX+aMomqSMReMRC87Hoan0TeQYU35Mgq6ZHGQ8RFBGAPfueHcvvtBLmiuL+UmJS5FyAq6QLCMSd74NnUAJwdQOi4e5vXeQX772DEyA/19RBXA4PESwJ0lwIDAQAB
+-----END PUBLIC KEY-----

+ 3 - 0
AnsjerPush/file/alipay/zosi_alipay_private_2048.pem

@@ -0,0 +1,3 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCAtJ/+dopwzLOA6yq4yDiOIWRs1z1vJicOZF/O1xZOs+UN+B5yJvCsyyInEY9kWFRPoZoYjuZ5x8of23zELozZuebt1cbJOikRhMYQ8HiIZfPX+v9Uqve+j7qDcOAzJJwRz26eak8WA6+au6BVTyOKQa35oY3tbOQWN+Nu7zxqMjxrLmy/+af6WuQhhOY+EYjEMqMpc5iDRJzkSjPuWWzvD9PE5/AJiYsUs0lb9z1s2+O15eKp1nwEOJp3rOwfowin8MjR2BbGZ8SuugK3cU49X84sAsNXo0Anv/htV9Pp6QgAPs0b1E3F3NrHJifl/GAd/hpLby0tsoHKY8d7He+3AgMBAAECggEAEPe/IA0As15JS/W07Cd5TUOZub+mmcCt5XfWKa6xgi61Q+IyIVqA4Ebneb8W6GO8ucLUTeiI+gUc4JZPHI4Gg62+hBxq2JySTXzzn8gUG+dU/mRYxRDFGJpaPHnlFNj+ulpeewQI6R4TSJLz0EtepBuElCR4UZTJfSRhF37yuA03aqp2WVTDmvdLxY8ZIZcbovPA34VIs91Bs+s7UmgQKIT5YkMRUG7En5dgGB0Uce0JeoN35P9U+DrplX0YP51qwf7APQxEeKY/ifaDdGf69tm86wz188/HWOVUeBXKNbcPN/CtreLgb0zR8OLji/FKMIIW3JMrAlQEu3H6t3HIAQKBgQDDufBDF2fn+DmpyggNsKbS/L8M4n9Ak7W/5r3L9vh0RQ+fbqGMazURoqQOt6e+rjnPuoTB42Yk9XEUzZcXF65klq+kY+PVNKOuvVXMJpO+kb71aP6heJ5CQ0KHUsQ2zMxrxl0xdcj8WkBP5mLyz6gdNX8Ypfgq7Rn9YVddYtpPUQKBgQCoVxzyjbEhM5WmhhlUQ+2i/fz0hVmNPC52FN5xhI11VAqT4rS+EBr2LPaWJz0bsVCsaPshePNc/yNwUbWpg4VmdIc9NLHXOyq3mI9uLt4Nzwtm15UrVGuJA2c9CEIBRsrsX89tO1xvOPeHJ0GnX6rV0WByg/uGWyw4Ga3kIG1chwKBgC3t7S6hSHXL7yQjz73+rTcnrmGEqR3rS6QrrnA1lVobdq4QFQLRiZOI2fzGJiv90H0ppMum5CpaGl4qXIFDwl3CapcmBanbf2ma7o5IozT+GJSYPeCAYIrmI6+DwZ2mc3X7B5V6sV5eg2b8hk26YxXvntKW5+SS3R1zgT/d2aLxAoGAMq3XJyaFFuvCt9lTMs0oc5NnXe3vYdemXSI3PZGcTKA6dv8fTsDmfQpYSigR+45MLTuFrzQMgAjHkH7o49mWhZu7Qy/kAlnrqE4jkBZH/3w/gHySifLmVN9Ta0pbJKs9WCAYAFiIoWtTCbtFxq/EbI6WLKOTS4WTMGh6s10Xz20CgYEAmLF68mrO7Noq6w5jy7P2Gb6SvmriUW1jSwcpgq/jC/u83iMg2+++TYmGp01CCxZVPQ+pU8VOuMLoKlm4mn0Im4gxXU6GeGerVb/LO2WkYn7aFMNRV05bgg7nCM5Df1vV6UzYsU4uzM/VC8FFm7/bRTqKv0u3pv+sYGB0YrlZaHs=
+-----END RSA PRIVATE KEY-----

+ 3 - 0
AnsjerPush/file/alipay/zosi_alipay_public_2048.pem

@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1usde7ybZRkDB0cx2a1QAgoVqwV+3HLOmB/f2lmqvWGhT9Kv2T09yu5+HuckQIr7EXIsyd+8gxv483xXkSdzVgm4UKD5QrVSaSs0s0iKOUAc2QOWF/rmzZ8/hDLHteNcYzyOYkr7ws7woCw0aN76oABzpBuch070mvTS7XNvV64YHtBeDo0iAGRaLu7OuHOOUJwQqFGzgaTtZz+TGXNk6ZonfYTcWbTw5Pc4kG9PjweBPpcsJXoCB0Ul9t/V66grhjnJ1TcYqzO0wY1Lr6J3p/0PtKVaG4Ijf6iGfmkOPBMy3lV9NPf/t20nuGLeBPkMMlGTytXVH5DkSB/6INytcQIDAQAB
+-----END PUBLIC KEY-----

+ 64 - 0
AnsjerPush/file/apns_pem/accloud-dev.pem

@@ -0,0 +1,64 @@
+Bag Attributes
+    friendlyName: Apple Development IOS Push Services: com.ansjer.accloud
+    localKeyID: 21 EE 16 12 6C 1D DA 58 D2 49 C0 CC ED FB BC 39 42 F4 CE 08 
+subject=/UID=com.ansjer.accloud/CN=Apple Development IOS Push Services: com.ansjer.accloud/OU=66PK439G56/C=CN
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFiTCCBHGgAwIBAgIIQ5R/pnWDUpgwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNTA3MDAzMDU1WhcNMjEwNTA3MDAzMDU1WjCBiDEiMCAGCgmSJomT8ixk
+AQEMEmNvbS5hbnNqZXIuYWNjbG91ZDFAMD4GA1UEAww3QXBwbGUgRGV2ZWxvcG1l
+bnQgSU9TIFB1c2ggU2VydmljZXM6IGNvbS5hbnNqZXIuYWNjbG91ZDETMBEGA1UE
+CwwKNjZQSzQzOUc1NjELMAkGA1UEBhMCQ04wggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDgFCcND3LLOEj0jxcLQMTrT7CcrNV6M4+UMwbTBKe/U7zkUsW0
+N4GFaMQeRkq9bZgtPpqdq9cqW4ItsNTZpccXyLwBt7hJLMrpOtXFVjfcBflU93Yt
+q1an7jvtUWIKssbNsBjOW4hXeIe+zuvl1mmdr0JKpusFn07uBVB58UMhTyHLNSjy
+F84Gp/EQbi1jLW3uJWnjvoPkp72TBotViN6YWzQyrHSq3LLngHodxVeEj/vJjj3B
+mCfUcKct+DO25pUkyvaZe17fAVlLUKK0jVEyXAhabPc4Ho2Ifh4oIcGVC6CC/0r9
+S0JeD8g18e8zVJTbdH0aB/qReq0EMJD/R4vXAgMBAAGjggHlMIIB4TAJBgNVHRME
+AjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBDwYDVR0gBIIB
+BjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5j
+ZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2Vw
+dGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQg
+Y29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlm
+aWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEFBQcCARYdaHR0cDov
+L3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwTQYD
+VR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20vY2VydGlm
+aWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMB0GA1UdDgQWBBQh7hYSbB3aWNJJ
+wMzt+7w5QvTOCDALBgNVHQ8EBAMCB4AwEAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZI
+hvcNAQEFBQADggEBAD0oroTnPITJgN8lwY0oLlVtjVfgcDjSx8lEbTAplLRlCfmm
+3xAgycRlKT4m03di++uvAX53UXOKxq0d5vgDziD6GL7iS9MwZJxfEnNFoAdiwTYP
+zWWpUR57ZeXRSIPaWaDaLSeUeOI6GeZy7v3FeWS+s398FtpFXpJonmFOwEFfMiGN
+OB9Q1jO8rDRlYklHwEjWjE5FIxK3ijApzXFf6xVDBybWggflghPObvxgIUS4Fb+X
+x62NAo/C0EzoA1ZT1YerYgNTiLVxvDsv8SAPZdwdpQ1xix02iKgxoIdHN9RLq8M8
+9OXTJXoJuGYZ+hVoG4IQ/dYYTmoZMCRxRZWh0TM=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA4BQnDQ9yyzhI9I8XC0DE60+wnKzVejOPlDMG0wSnv1O85FLF
+tDeBhWjEHkZKvW2YLT6anavXKluCLbDU2aXHF8i8Abe4SSzK6TrVxVY33AX5VPd2
+LatWp+477VFiCrLGzbAYzluIV3iHvs7r5dZpna9CSqbrBZ9O7gVQefFDIU8hyzUo
+8hfOBqfxEG4tYy1t7iVp476D5Ke9kwaLVYjemFs0Mqx0qtyy54B6HcVXhI/7yY49
+wZgn1HCnLfgztuaVJMr2mXte3wFZS1CitI1RMlwIWmz3OB6NiH4eKCHBlQuggv9K
+/UtCXg/INfHvM1SU23R9Ggf6kXqtBDCQ/0eL1wIDAQABAoIBAQDEcuaJnLGXWJEd
+VxVs2CvL4OGFekDuEaYe1110QvCN3GwOAEMz7DWxMFg/8oAz8pEnmWsUMENC71cB
+l7YD2kLhpyqsltUJ0itSrjIb/6qZSuN8ijchcsVWFJaVSHQbzQBd87TLtZOinBjm
+P6KwrXmKtMv5hr0lALXCFwam4c1otMNFXi2jl1YQl/sXRyqQRlbUVo7vOveoHAFl
+NIei4sE7Zx1yIiM0ksAPiUIJwDfhuYWiBwZbiatC213t+sE1b4Lo1Dk4xe/IlXXm
+9YiQljxwIs8CdR/wDJ8AEmDf5utb5LhcJrmComHXyxsmNEJJuTEsfVqkAA78TH+w
+wT2HMqYBAoGBAPf/rbZINMZmYslaAO2nvuMU6rrGvaYrOaYpggxYJDKZwFhhdkR6
+CzWlzLl1e5PfQLjgTzcnT6Kl/TGOHWwtPINC1Sm+44IWfVgCEpdp0aW4wWYOu9ZB
+sYFPOiUcgRkcPldSqCoU9A3h4YqBOOoqZydwgeMR4LsTyTwltyY6EYZxAoGBAOdO
+6KxULMVvKRYbgUXms04P/beSKK+oYCXOppYgNRqAUHbm0G9AebYiplfZQP0GZkFK
+ki7epk6EnDvEYyjYytNlYsrfBFOhEbwmwjd2y5SiUEhymPN0+tiyPMS1ea8W2ZmZ
+bMZ4k8fv99WL4GejEzvGax7EPPpbR87XMtko8arHAoGBAPT7mbqLsbabxdJqGqiq
+IKNqMka3tPjFknlWX6WaHIfv4lZsUMxsU053rrk2GRGGZQU8cIRnTP+7QVcGsIIA
+Xdq81p4ad9w2IzQhOARjADrZPBN5Uozg8Fxwy1uFwPHxOJRCjuSjNkPQc2DDLloD
+8UrAlU6ehoXHke7cwwP8MPvBAoGAFuRSVqvF+owzzsJOGvLoD/O5Mm++oxPQ4C0B
+g54QY10LmZjiTABfi00GlW8k5WW6vP96VDUo1GB/bdLT1XkPKbjA/D7yGKy6KprT
+YdXZ9A3UUDc3N/D92nBZX0Zukot6YZ8zirjoN0NijbnYLamP/7x1YYgeWNCr3Kaj
+m6ZXtIUCgYB3HRwiY6rv+4GZfxcHolTeZuV+38uogK65/mntMJrQHDW/CAOER+sj
+sddGGic7UhFeXg8pePqZ9pu091AI40iYSCnap+tnIAby7pPC6GeDhzXdy0Kt3PKL
+K9JU7kNg2TUCje/YJgyhFIFNwIkeAorGjFpoN2ok5W2dTARGcT9+7w==
+-----END RSA PRIVATE KEY-----

+ 67 - 0
AnsjerPush/file/apns_pem/accloud.pem

@@ -0,0 +1,67 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.accloud
+    localKeyID: 21 EE 16 12 6C 1D DA 58 D2 49 C0 CC ED FB BC 39 42 F4 CE 08 
+subject=/UID=com.ansjer.accloud/CN=Apple Push Services: com.ansjer.accloud/OU=66PK439G56/O=side zhang/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGFTCCBP2gAwIBAgIIa8UIpVRjdUswDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNTA3MDIyMzExWhcNMjEwNjA2MDIyMzExWjCBjTEiMCAGCgmSJomT8ixk
+AQEMEmNvbS5hbnNqZXIuYWNjbG91ZDEwMC4GA1UEAwwnQXBwbGUgUHVzaCBTZXJ2
+aWNlczogY29tLmFuc2plci5hY2Nsb3VkMRMwEQYDVQQLDAo2NlBLNDM5RzU2MRMw
+EQYDVQQKDApzaWRlIHpoYW5nMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAOAUJw0Pcss4SPSPFwtAxOtPsJys1Xozj5QzBtMEp79T
+vORSxbQ3gYVoxB5GSr1tmC0+mp2r1ypbgi2w1NmlxxfIvAG3uEksyuk61cVWN9wF
++VT3di2rVqfuO+1RYgqyxs2wGM5biFd4h77O6+XWaZ2vQkqm6wWfTu4FUHnxQyFP
+Ics1KPIXzgan8RBuLWMtbe4laeO+g+SnvZMGi1WI3phbNDKsdKrcsueAeh3FV4SP
++8mOPcGYJ9Rwpy34M7bmlSTK9pl7Xt8BWUtQorSNUTJcCFps9zgejYh+HighwZUL
+oIL/Sv1LQl4PyDXx7zNUlNt0fRoH+pF6rQQwkP9Hi9cCAwEAAaOCAmwwggJoMAwG
+A1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEc
+BgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QFATCB/TCBwwYIKwYBBQUHAgIwgbYM
+gbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1
+bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBh
+bmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA1BggrBgEFBQcC
+ARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkwEwYD
+VR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2NybC5h
+cHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4EFgQUIe4WEmwd2ljSScDM7fu8OUL0
+zggwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3Y2QGAwEEAgUAMBAGCiqGSIb3Y2QG
+AwIEAgUAMH0GCiqGSIb3Y2QGAwYEbzBtDBJjb20uYW5zamVyLmFjY2xvdWQwBQwD
+YXBwDBdjb20uYW5zamVyLmFjY2xvdWQudm9pcDAGDAR2b2lwDB9jb20uYW5zamVy
+LmFjY2xvdWQuY29tcGxpY2F0aW9uMA4MDGNvbXBsaWNhdGlvbjANBgkqhkiG9w0B
+AQsFAAOCAQEAxVbw8gXMQ/bb/X9uwJ+mqM0PTzfyh21Rzkr40IKJQQaf/dY/Rl07
+mk889Rm6ksQBwTRMeyZghAmfQripZJO+XRRWCjgEX9FxjIatpiYaA46A5N0Qwj+P
+E4564uAF3yYq0xwhJx7SSwSBd00Bc4B7WSgDWCkuJ05kbQe+i4lhcTmDPz5jc/qJ
+4lOZHMZ3JhAQJ4+EhT29BNb/YMBte/S5brAIqjVTZVnfTAtPj9S9A26l0Buor51i
+YXphJJZWDLV9nerkLCVW5/kwcGt1tzuecCHKD0fgkxMfmyaibavEOzWDDsJSOL8R
+m43aKy9xPVw+wvGxv0UbSv3YFwsUYMS+yA==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA4BQnDQ9yyzhI9I8XC0DE60+wnKzVejOPlDMG0wSnv1O85FLF
+tDeBhWjEHkZKvW2YLT6anavXKluCLbDU2aXHF8i8Abe4SSzK6TrVxVY33AX5VPd2
+LatWp+477VFiCrLGzbAYzluIV3iHvs7r5dZpna9CSqbrBZ9O7gVQefFDIU8hyzUo
+8hfOBqfxEG4tYy1t7iVp476D5Ke9kwaLVYjemFs0Mqx0qtyy54B6HcVXhI/7yY49
+wZgn1HCnLfgztuaVJMr2mXte3wFZS1CitI1RMlwIWmz3OB6NiH4eKCHBlQuggv9K
+/UtCXg/INfHvM1SU23R9Ggf6kXqtBDCQ/0eL1wIDAQABAoIBAQDEcuaJnLGXWJEd
+VxVs2CvL4OGFekDuEaYe1110QvCN3GwOAEMz7DWxMFg/8oAz8pEnmWsUMENC71cB
+l7YD2kLhpyqsltUJ0itSrjIb/6qZSuN8ijchcsVWFJaVSHQbzQBd87TLtZOinBjm
+P6KwrXmKtMv5hr0lALXCFwam4c1otMNFXi2jl1YQl/sXRyqQRlbUVo7vOveoHAFl
+NIei4sE7Zx1yIiM0ksAPiUIJwDfhuYWiBwZbiatC213t+sE1b4Lo1Dk4xe/IlXXm
+9YiQljxwIs8CdR/wDJ8AEmDf5utb5LhcJrmComHXyxsmNEJJuTEsfVqkAA78TH+w
+wT2HMqYBAoGBAPf/rbZINMZmYslaAO2nvuMU6rrGvaYrOaYpggxYJDKZwFhhdkR6
+CzWlzLl1e5PfQLjgTzcnT6Kl/TGOHWwtPINC1Sm+44IWfVgCEpdp0aW4wWYOu9ZB
+sYFPOiUcgRkcPldSqCoU9A3h4YqBOOoqZydwgeMR4LsTyTwltyY6EYZxAoGBAOdO
+6KxULMVvKRYbgUXms04P/beSKK+oYCXOppYgNRqAUHbm0G9AebYiplfZQP0GZkFK
+ki7epk6EnDvEYyjYytNlYsrfBFOhEbwmwjd2y5SiUEhymPN0+tiyPMS1ea8W2ZmZ
+bMZ4k8fv99WL4GejEzvGax7EPPpbR87XMtko8arHAoGBAPT7mbqLsbabxdJqGqiq
+IKNqMka3tPjFknlWX6WaHIfv4lZsUMxsU053rrk2GRGGZQU8cIRnTP+7QVcGsIIA
+Xdq81p4ad9w2IzQhOARjADrZPBN5Uozg8Fxwy1uFwPHxOJRCjuSjNkPQc2DDLloD
+8UrAlU6ehoXHke7cwwP8MPvBAoGAFuRSVqvF+owzzsJOGvLoD/O5Mm++oxPQ4C0B
+g54QY10LmZjiTABfi00GlW8k5WW6vP96VDUo1GB/bdLT1XkPKbjA/D7yGKy6KprT
+YdXZ9A3UUDc3N/D92nBZX0Zukot6YZ8zirjoN0NijbnYLamP/7x1YYgeWNCr3Kaj
+m6ZXtIUCgYB3HRwiY6rv+4GZfxcHolTeZuV+38uogK65/mntMJrQHDW/CAOER+sj
+sddGGic7UhFeXg8pePqZ9pu091AI40iYSCnap+tnIAby7pPC6GeDhzXdy0Kt3PKL
+K9JU7kNg2TUCje/YJgyhFIFNwIkeAorGjFpoN2ok5W2dTARGcT9+7w==
+-----END RSA PRIVATE KEY-----

+ 67 - 0
AnsjerPush/file/apns_pem/apns-dis-adcloud.pem

@@ -0,0 +1,67 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.adcloud
+    localKeyID: B3 63 E9 F0 E0 7F 6B 14 B6 8D 6C 1B 11 97 89 7B A1 3E 04 28 
+subject=/UID=com.ansjer.adcloud/CN=Apple Push Services: com.ansjer.adcloud/OU=66PK439G56/O=side zhang/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGFTCCBP2gAwIBAgIIU3tl0oHS3eUwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwOTI5MDIxNTQ3WhcNMjExMDI5MDIxNTQ3WjCBjTEiMCAGCgmSJomT8ixk
+AQEMEmNvbS5hbnNqZXIuYWRjbG91ZDEwMC4GA1UEAwwnQXBwbGUgUHVzaCBTZXJ2
+aWNlczogY29tLmFuc2plci5hZGNsb3VkMRMwEQYDVQQLDAo2NlBLNDM5RzU2MRMw
+EQYDVQQKDApzaWRlIHpoYW5nMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAN+iiSUWCyo7EHe09RiU0aNb3psx3a3EqaVLU1tJdeaa
+YYwrcBnYN8THbY5873N5XYlw7JsUJhWOwU7DxiYvyqPzBpQh+Ua2omNk/euHTfdB
+xtJlabqjW+XGtzidHjwOP0K+ET/GIYCUvC69el+XwWeljq9l8p7zmuKzKwqTyMO3
+5loTDToE7TNBg3YxpI9l2VftpoyoaFFG9QFPihzSfZFm9ccN6j1y5kmdJyFRp+yX
+okwrFmcy+6cTn9OviE88DPcvJz1tkcgNJgM6apHIH5/9hsqZx5+myUu3ThOZTRW7
+bSThh14UKT8+/uebkeVRlBSBz4E4DZZMjThEYK9pOw8CAwEAAaOCAmwwggJoMAwG
+A1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEc
+BgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QFATCB/TCBwwYIKwYBBQUHAgIwgbYM
+gbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1
+bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBh
+bmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA1BggrBgEFBQcC
+ARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkwEwYD
+VR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL2NybC5h
+cHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4EFgQUs2Pp8OB/axS2jWwbEZeJe6E+
+BCgwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3Y2QGAwEEAgUAMBAGCiqGSIb3Y2QG
+AwIEAgUAMH0GCiqGSIb3Y2QGAwYEbzBtDBJjb20uYW5zamVyLmFkY2xvdWQwBQwD
+YXBwDBdjb20uYW5zamVyLmFkY2xvdWQudm9pcDAGDAR2b2lwDB9jb20uYW5zamVy
+LmFkY2xvdWQuY29tcGxpY2F0aW9uMA4MDGNvbXBsaWNhdGlvbjANBgkqhkiG9w0B
+AQsFAAOCAQEAeF+KTcsTG4jwy2e4/4jZqIqIm6cWEYKF1XBJzOzRMQdm84qIydhe
+sBlvZhuq4qXpWKqe+Dwmoc1YfBp6gVCowdjze6AXwZ0GoF0zvvr+pvo9MmMwab6c
+1hV48vWQpFoShUB4UilQWDDoofrb4aYj58L7GzfzkbvyL9Lk+tBfEZ1V/FIHXL/v
+mQMwAhIN8Qf5fmkd+E80hb/aW7P4djKetxMjcuM4xEm3mceRQVj676HKnP85fjyc
+NhDL1N+/eoCKaJR6snjaxRTUFT9ASxmHo+qWY7uxA2xdyvqRTt790420KolSAGIv
+AAOLq5U2IiIv0lq4mYCw4ihx4FpGuZfhDw==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA36KJJRYLKjsQd7T1GJTRo1vemzHdrcSppUtTW0l15pphjCtw
+Gdg3xMdtjnzvc3ldiXDsmxQmFY7BTsPGJi/Ko/MGlCH5RraiY2T964dN90HG0mVp
+uqNb5ca3OJ0ePA4/Qr4RP8YhgJS8Lr16X5fBZ6WOr2XynvOa4rMrCpPIw7fmWhMN
+OgTtM0GDdjGkj2XZV+2mjKhoUUb1AU+KHNJ9kWb1xw3qPXLmSZ0nIVGn7JeiTCsW
+ZzL7pxOf06+ITzwM9y8nPW2RyA0mAzpqkcgfn/2GypnHn6bJS7dOE5lNFbttJOGH
+XhQpPz7+55uR5VGUFIHPgTgNlkyNOERgr2k7DwIDAQABAoIBADnaWFRJwZWpNfdh
+zs4PIbR6/bl0Sv67E4/5TpUdCe0gHwAd9KAa+UJkvQjRdTiQOwxlNDBiJAALsCJq
+LwaN8oQ7SIZly5DpilTgOXgGxrqiP4cKSTHJNOraX2e77ahnnyHq/Zdb8CrNT2lg
+BxpXrebcQ29Ill1cc8MVypqv5I4MtyFyMcLVRlTNL3war99GZZljakhymZW37S/g
+FfKcuiA3NOQTMc072aism5HnIFgpJVxqvcCkgL4W9r0AbNEK90EmkFp0euxFUi7W
+PTQsZmqbVVLmB/3SHU490UM5yfayBjuyw/6x+AZKrJbQH5nxJrbdzcuYki55l0Eo
+wjwlrdECgYEA97f5cnAjNDJwJuyx4nTh0OQbTZsWv8X2XRfPZmvxYUf13S+J+hyN
+AmPSWN5nv2pLEija4l0OGH+XzjvIRQZbAHOt5D78Ryn32RtCzDOc4GizopZxlsJu
+QFpxKcnllojCindRYAdvT0weI0Y46IYxBGbsqlgBOBKzuL7rh7hzFwUCgYEA5xxy
+pPNE+GuzXoLDEq0Rtxk9C/evpVaYF4Hlw1BxTSKdkCG8kEypghqq0iY+dnTtrxGV
+sIAhD7B47/G6mcigLRjX8Py9Iafk8cMWZCMimdv4NARkLv53/zpepBNq+Z27qEbP
+Th9hMaUNbSs8Fzxi9bD1BKU1Dj4sDlMdTbOl/gMCgYAuGFLXg5xZYyVRaOv06rD2
+cOvPthM2njyot/yE34MsCfrrLKRtIRDwJ+kFVmVRzhKoD2gjhRNRZbS20JMEnRkj
+7WKnPT0udNXE1XjJjLUdYP5EO+BpIEbpvKqgDwykxVAF+gvvZZLF27Ej+TYvtoC6
+g/h3/iZD6977erNTLvRjjQKBgQDFCCLRcDkP1bpBTBgFVxmwUUsTZeIQgqf9+pep
+EW7XBTjxhWGeYE1vh061BTfhg8STqdlFdac6GgixABRHQZ0VdYW8hay1Ac+VMe5o
+BvBb3gZYSpoi2n3xUZEYLxnXn9s6FsxsXSJe0cmFlgOQB8KSln/9qt91uhmTSNbF
+xPAygwKBgBnz1GL2+I6URZNwYnzzakeI2d5CFQ3iPSZf9HWUmZrimmRpdBtvp9+R
+Oqb2Uts3jjTPoghqumJ6FQhEELtVHZj17FWsYT48uqBFjePzhV1WjCIqL4UQ9P67
++1/ou+LXNb82n1FnPBs7w3yzcL5cdabJMhT6qXynDAXCOYbU9ita
+-----END RSA PRIVATE KEY-----

+ 64 - 0
AnsjerPush/file/apns_pem/customizeda-dev.pem

@@ -0,0 +1,64 @@
+Bag Attributes
+    friendlyName: Apple Development IOS Push Services: com.ansjer.customizeda
+    localKeyID: 70 A0 2E 70 8B E6 88 0C F6 4D 4E 9F D8 21 23 3F 4A E7 87 86 
+subject=/UID=com.ansjer.customizeda/CN=Apple Development IOS Push Services: com.ansjer.customizeda/OU=47V9C97WG7/C=AU
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFkTCCBHmgAwIBAgIIBpjLEWeGlpowDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMTkxMTA4MDk0MjUwWhcNMjAxMTA3MDk0MjUwWjCBkDEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGExRDBCBgNVBAMMO0FwcGxlIERldmVs
+b3BtZW50IElPUyBQdXNoIFNlcnZpY2VzOiBjb20uYW5zamVyLmN1c3RvbWl6ZWRh
+MRMwEQYDVQQLDAo0N1Y5Qzk3V0c3MQswCQYDVQQGEwJBVTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKcXmdal9BeTF5liE+sIwHVleH3ElolMN2A1qsHI
+rfAeXc+DkHvjqk9W+1+BSln2jkfn0/8UBpIt9WG1Jusj9R4fn5mT0nP81BKL/c64
+qrWGFG4THELiT6Sz9JFYh7co2WFCmLFM7tDStiIWGmWbVKgVs8Vq9tE9zscuyeyo
+W/JfV5Ab7L30qkmpv0dq7S5sQUBBFu+Vs9IshUA/SgoVfutPBSCLrGD+/URAa3p6
+qtnEMxaRXKlxJfrMn6Pxlc8Eyhx/0a1Bn/TJBbGxdvc0+Pxw95hNGhRXwOVN43IL
+q5pepm0UQyri8SPm33/o+9oaJqNzC1gS94e5zqafMdvOb+MCAwEAAaOCAeUwggHh
+MAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEP
+BgNVHSAEggEGMIIBAjCB/wYJKoZIhvdjZAUBMIHxMIHDBggrBgEFBQcCAjCBtgyB
+s1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3Vt
+ZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRl
+cm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFu
+ZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMCkGCCsGAQUFBwIB
+Fh1odHRwOi8vd3d3LmFwcGxlLmNvbS9hcHBsZWNhLzATBgNVHSUEDDAKBggrBgEF
+BQcDAjBNBgNVHR8ERjBEMEKgQKA+hjxodHRwOi8vZGV2ZWxvcGVyLmFwcGxlLmNv
+bS9jZXJ0aWZpY2F0aW9uYXV0aG9yaXR5L3d3ZHJjYS5jcmwwHQYDVR0OBBYEFHCg
+LnCL5ogM9k1On9ghIz9K54eGMAsGA1UdDwQEAwIHgDAQBgoqhkiG92NkBgMBBAIF
+ADANBgkqhkiG9w0BAQUFAAOCAQEAURmmauUmkpMpaXbIAvgXMJwXkjKI0GT9aXLM
+bnwstmVMNZtGNMHzb2U2x81TlEoSdH4+F+9n7DkcrUzrY1Gcr4Q9jkBJ48SrLrq4
+yMmcmz/7Gasph8td19JUP7+wnYrRDQfDynntmkQvnfLhttMUvI0XADcdunui8u9T
+CxZxX300PqHCXW4/D7gOVUuW/PSazxvqN6cTHfAavhbBJqwNP4idNpCUL+D5XSrS
+3OYlyBsvwYFGMcwIa6rmKJCPqBJv71ewk3iK1eRfckZk9imHwRVAfsekQ3VlpFZV
+DZcFTFAjMuvPOYG90ge+lTU6XACctgkbT5y5fBz3Jd/snkpshA==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEApxeZ1qX0F5MXmWIT6wjAdWV4fcSWiUw3YDWqwcit8B5dz4OQ
+e+OqT1b7X4FKWfaOR+fT/xQGki31YbUm6yP1Hh+fmZPSc/zUEov9zriqtYYUbhMc
+QuJPpLP0kViHtyjZYUKYsUzu0NK2IhYaZZtUqBWzxWr20T3Oxy7J7Khb8l9XkBvs
+vfSqSam/R2rtLmxBQEEW75Wz0iyFQD9KChV+608FIIusYP79REBrenqq2cQzFpFc
+qXEl+syfo/GVzwTKHH/RrUGf9MkFsbF29zT4/HD3mE0aFFfA5U3jcgurml6mbRRD
+KuLxI+bff+j72homo3MLWBL3h7nOpp8x285v4wIDAQABAoIBAEqx+TqEa27SeZ0+
+w+qwueCBIPEQHb94y9R9iT0hqegKmyM2DDPl3W+kUbErCgLXfllUoGeKGznjw6sP
+ITvCkkQKHP25oD3rix5TaH2t/dox+xYOAvc/Vh0puS30Zo8ResPI0puH6cZ+O59X
+RCuh2aFeOkQJghady78tAmWCCcB593j4e63pds1dcoCO5TnysQwntwdgpBMvo5yH
+QuazSIHCu1YAmFrz5SwDAqq35hqYGlPpDzStRjTYmNg68ubbOeFGSKknEOzYhKGV
+TnCftDEAkePDBoNT5Mx7mJ+LV4JHBvXMyXSOJX07Wm1yZNMAkibjdxzrcRxtfTpU
+7rYKxZkCgYEA2QLVZZ3JEqqdEI+/5f307ExnGuIR5Z81p7xta81Z50dHQ0w+FglX
+62U9evZQ4iW3+/aoPP/kX22xrjf463wP/ou7f9Jhim7AFqdHud4tEqKubljde6dF
+Hh3CVpwtZLII768GQYHl+DlVVPg/NFhVy4EkfOXxfG4+R8SLsy9ugMcCgYEAxRzO
+z2vVCz7J2s0U6NNn+GZkfZOoAOGtIo/YMxEnf04E5iEy7JmfgGQyKboZOhNnndLI
+6f7G7/f9yWcdYbQUPLXyL8OMCBpF18UugL6OK8cBc4xXOnoE48Un+oYFFRhR27b8
+4Kl9Xpkju4I6y12dMwExXMirBKoedd8hSH0WtAUCgYA9KndLdPW/tScgC+RixvnJ
+wJ8vDJlOx1kwNgQBZpf4gDat2UeGnouRZa+2gMa7dzYsZpF04AtWemyiVlRqy0UW
+0fu/Q2UKx7yMpgmpcd3yargPkiybNcJdgrXMA6knwKaUKBjzTdGr7T0H/hCSiHNm
+MLhAG++8yrXzNIrLrorO8QKBgExoz26usWZrj5om180tbmdnYdsgKwc0dIYqVrft
+FW+PEe0Xv4LiRmdh7+QnBPJzZT5T4Jh8sCeD7bhuJYCjaxjhuyFy30ygyqRLBqzX
+sYsc2N4u7/12IYEeA1JlM4Eq7UiL1dlNOpE6MhTU9e+xuWd1Nepuy4TCYY510ZQl
+EXD1AoGAU19qzaVNhV8I3bnzyQ0m80ouvvRQFiLbxC32QQMyrk7fOfi9p+w3ImHn
+2sYj1p5V1yMx9I4nRoLhLMRyX+Nm5uiVCQLAjE9qd3ES9QKAWhvbSAU2t/RSw0pa
+EYxBc50Xw8vE9x6OI6g9yONgxaXwezhXUsb6uASSZ95SFQQIEBc=
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/customizeda.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.customizeda
+    localKeyID: AD 9A F4 60 88 52 65 93 FA E3 FB 33 9C 4E 4A FB 61 E5 8F E5 
+subject=/UID=com.ansjer.customizeda/CN=Apple Push Services: com.ansjer.customizeda/OU=47V9C97WG7/O=UNIDEN AUSTRALIA PTY. LIMITED/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGPTCCBSWgAwIBAgIIUpCZMQd7VrMwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjEwMzIzMDU1OTQxWhcNMjIwNDIyMDU1OTQwWjCBqDEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGExNDAyBgNVBAMMK0FwcGxlIFB1c2gg
+U2VydmljZXM6IGNvbS5hbnNqZXIuY3VzdG9taXplZGExEzARBgNVBAsMCjQ3VjlD
+OTdXRzcxJjAkBgNVBAoMHVVOSURFTiBBVVNUUkFMSUEgUFRZLiBMSU1JVEVEMQsw
+CQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDZvOle
+Q6Zeq6Vv6TaVMQKGZyBzO7Z2cFZfmcfG2uy7/IGbDEJadVfVDn9CxkBTVtazpPPn
+5C0i56ySoecapBBGPuVcu7wWTgv1AoN7sjk6Nom1U6wt6EVvYqt8W7n3aXa7/Y3j
+rRFOtOAyDEJpE8YgC8j3kdDYeLYKTZbjVeVb5RKF6xprhFrgFx+82hulW0aRevkh
+AEufAQ6GqdBtTnnIBpkTg04qVtrulb0uTL/nn9LUZ+XTBCEKgMDoyQ6i208M++kO
+1gWedUQWiNbLuDYYm4XxC36pwwPEXetrM9025moO68U0OWDuqNBsYED7GHXt4iw1
+7l4n3/50ODUA8y0CAwEAAaOCAnkwggJ1MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgw
+FoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEcBgNVHSAEggETMIIBDzCCAQsGCSqG
+SIb3Y2QFATCB/TCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNl
+cnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhl
+IHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBv
+ZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFj
+dGljZSBzdGF0ZW1lbnRzLjA1BggrBgEFBQcCARYpaHR0cDovL3d3dy5hcHBsZS5j
+b20vY2VydGlmaWNhdGVhdXRob3JpdHkwEwYDVR0lBAwwCgYIKwYBBQUHAwIwMAYD
+VR0fBCkwJzAloCOgIYYfaHR0cDovL2NybC5hcHBsZS5jb20vd3dkcmNhLmNybDAd
+BgNVHQ4EFgQUrZr0YIhSZZP64/sznE5K+2Hlj+UwDgYDVR0PAQH/BAQDAgeAMBAG
+CiqGSIb3Y2QGAwEEAgUAMBAGCiqGSIb3Y2QGAwIEAgUAMIGJBgoqhkiG92NkBgMG
+BHsweQwWY29tLmFuc2plci5jdXN0b21pemVkYTAFDANhcHAMG2NvbS5hbnNqZXIu
+Y3VzdG9taXplZGEudm9pcDAGDAR2b2lwDCNjb20uYW5zamVyLmN1c3RvbWl6ZWRh
+LmNvbXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24wDQYJKoZIhvcNAQELBQADggEB
+AE9EVp44vYDUJDR8xcp+69xwTnf5yAKRdtvUJJNNQWzHkQRboy06Ae+vlX8VUQkF
+LToEhAMKuHGZPVbkbtyBOSeXFYQfE2A5DnWbiGW/0j9NX6upF3xVknovhwu+yNps
+rA7w3FFDsT9a/IkTdEE2LfaOsrkoflo2Wvu2BSM/oQf5Q7sgoUjQwp3by5j2VXWQ
+6ik+lWNHSOAdfTN0xtbgnQ2yvxlsxmMPgqaDZVuAdG1Zv7s/vEZJDWhi8C8NkN8U
+6tg4XE50owmpgxQHjFNcH4J0lWg9ZMnLob9Lc+AMnttOe29PexpursZwGFnanOce
+uBEd7/zZZAi40FUwGUAWQSw=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0Nm86V5Dpl6rpW/pNpUxAoZnIHM7tnZwVl+Zx8ba7Lv8gZsM
+Qlp1V9UOf0LGQFNW1rOk8+fkLSLnrJKh5xqkEEY+5Vy7vBZOC/UCg3uyOTo2ibVT
+rC3oRW9iq3xbufdpdrv9jeOtEU604DIMQmkTxiALyPeR0Nh4tgpNluNV5VvlEoXr
+GmuEWuAXH7zaG6VbRpF6+SEAS58BDoap0G1OecgGmRODTipW2u6VvS5Mv+ef0tRn
+5dMEIQqAwOjJDqLbTwz76Q7WBZ51RBaI1su4NhibhfELfqnDA8Rd62sz3Tbmag7r
+xTQ5YO6o0GxgQPsYde3iLDXuXiff/nQ4NQDzLQIDAQABAoIBABRVrvQV7oZLX3EL
+NWzQF+aECZR6Q80fOQ7f3gw6i2RTZWGFJCttvVEg+HlhmgX3V1QuR5hL8saGSIJg
+AI2M5X6EKGmSnuDQVycI4wbES3UQ+vBrjEsOaTghgTzrLc8sjMM3yJdBHL+7pxbR
+KKpqqrc2tY8Ks9Le0NoApM9VMS7Ul64k1FvZeK1mrq51ASoA++gPkohuAvY9thlR
+U5vQ4NyTUlqWtPSLbvmtFm/QZVQxSe3SZJ66AdQbdoYvL+vzcthFXXRqbh/RzLHi
+yKxiZwouN2+UsHfEf/wX5H2MeJwfiPvcSAvxAZG2LmIns47TBjBXZw7BX0ow49Bf
+B5UKm+0CgYEA7J6s0s1iPu7mmB1++g9K5aMQpQxcqYAVqe9pp3BlY/0uhGxVhfjo
+hBxpqpVCHIjG7S/Uj5fQNcMS2t7n/pHAkhGo0oMitcS80d0Pv82kfGzxg8kHSwJH
+bo0R6GPWkiKYqf5k0AodUBDr5RihdmwGdLPV5rQnvOyk4AylgmxD5w8CgYEA4fTP
+fH2/ZAmrGZ9HzMApRvAxGFytalKrtXC9FmOKt5j03M6zVhD/uef1sFhkqQfNch7q
+FSkdyLSnLxButosH91PuBZ7lZ4j6fdLOrSVvJmtDgy9eLPMZCtJA+pZEBq9WvU6w
+Fvzh+UqQiqezk1qmNNBuvnEEcUAT6OfdPC4m4gMCgYEA5sVWjn2q3PAIy8Cwr1cs
+18vyltX4pYN7HXKXG6td52fs70/tFS2OMBK555mMkeHACWHFsIoCkfyALiCKKNij
+9JHukek9eAO5mAA+tPTuAEAMSFhOmZROcH57QaflhD7jn5+xWMkUj5sBRj2Ac8ul
+fDpMWqXHv9UYdD9BSHMowncCgYA1/fWGXdXZ5aWI3dKeTlyOot5YX+zgNxjopiPI
+sch3zojUKmAX9L/mgFLo7HZBHagt2M0+eTCwwNoiGN8bp2Uj6F474ZgXgVe5sSHJ
+peRFXeQn+K2cOAqH123HcxiWMFedrKvPEmY812BqgWAbmdGVVSjhQMg694DKan0m
+4DBSWQKBgQDeE1pzWjARibuw6BDNHTfT0XLi9+95+vGXSUZydE1Q+L4qstODhFxi
+sg/clUBAaAU8LercQv5SGto1i6CP7mA5112+sPlrfltfSAI5pBpTIK54K3nh5gpu
+tF4iK0HmLUQpckQjrKwd7YsfzJX54rugBa7P8ZpffPwx4og/Dm4mLQ==
+-----END RSA PRIVATE KEY-----

+ 64 - 0
AnsjerPush/file/apns_pem/customizedb-dev.pem

@@ -0,0 +1,64 @@
+Bag Attributes
+    friendlyName: Apple Development IOS Push Services: com.ansjer.customizedb
+    localKeyID: 53 D0 A7 6B CF E9 9C 46 ED 44 01 01 01 14 02 52 A6 45 AB C5 
+subject=/UID=com.ansjer.customizedb/CN=Apple Development IOS Push Services: com.ansjer.customizedb/OU=485ZBTNP7J/C=AU
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFkTCCBHmgAwIBAgIIFWKl1P6fRqAwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMTkxMTA4MDgzNzI3WhcNMjAxMTA3MDgzNzI3WjCBkDEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGIxRDBCBgNVBAMMO0FwcGxlIERldmVs
+b3BtZW50IElPUyBQdXNoIFNlcnZpY2VzOiBjb20uYW5zamVyLmN1c3RvbWl6ZWRi
+MRMwEQYDVQQLDAo0ODVaQlROUDdKMQswCQYDVQQGEwJBVTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBANOI0TfZ1x5kwSw7/Sdr+oNxgPeJxZq7Khib9IvQ
+5ZW0hqEXgnm9dyDjpAc2FDjL/FzMUtCviJmxQrISW3dzcwtkKzY//vlQ58ZhlNVG
+m6COjzeJKnP2kdmXipzt9kwPK6tfNxhrNFqUXMH889soFL49W0AykK/hrUKD++0c
+IwXed4lRAxXJORePI27ErhRT8+B5yTjLnZOa+4kwwS2RstGioKHTjpVKDZwYuaM0
+72P4QK+fetFv9dl049sM+1oJ7Z0sT5wvWV0l2TLGTkoma+GR+JCU5GGAmtLyvrco
+NlKLw8JGqSBstZbp1dQFA7BAIuyEt+Jxjkzgu4s+2FROvnUCAwEAAaOCAeUwggHh
+MAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEP
+BgNVHSAEggEGMIIBAjCB/wYJKoZIhvdjZAUBMIHxMIHDBggrBgEFBQcCAjCBtgyB
+s1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3Vt
+ZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRl
+cm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFu
+ZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMCkGCCsGAQUFBwIB
+Fh1odHRwOi8vd3d3LmFwcGxlLmNvbS9hcHBsZWNhLzATBgNVHSUEDDAKBggrBgEF
+BQcDAjBNBgNVHR8ERjBEMEKgQKA+hjxodHRwOi8vZGV2ZWxvcGVyLmFwcGxlLmNv
+bS9jZXJ0aWZpY2F0aW9uYXV0aG9yaXR5L3d3ZHJjYS5jcmwwHQYDVR0OBBYEFFPQ
+p2vP6ZxG7UQBAQEUAlKmRavFMAsGA1UdDwQEAwIHgDAQBgoqhkiG92NkBgMBBAIF
+ADANBgkqhkiG9w0BAQUFAAOCAQEAIvlmy2VPFbbY0BdmA1LjoGfUOtcJd8B6gpav
+n0oVzohIT1t8TBe/GPvgMFmGgPDJkuyJnI7lwvsxUoIJ/eJu+We2Y0iF8BvE3hPx
+VU+epvzOmxFnsxg+2go3pZEGv2eRgmbarYai5N37x0ERES3F4dQDzsnr1gxAO1QH
+fZr39DL0wU9gJUEq1wNsAt6CDyCdsdQPuwuB6X0iVeCiRs5SnjR4x7HDC4DlzC8q
+5Azej615Nel88V6+Cvu7evc7kyT8iAXsUCJSiL8ioxaLfHLZ7HjO567+aWidcG6e
+E5wHJ8wvVe+SCgSrCz1z+LU+7VaYrr+0hw/coxUtlZ/U/LJerg==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA04jRN9nXHmTBLDv9J2v6g3GA94nFmrsqGJv0i9DllbSGoReC
+eb13IOOkBzYUOMv8XMxS0K+ImbFCshJbd3NzC2QrNj/++VDnxmGU1UaboI6PN4kq
+c/aR2ZeKnO32TA8rq183GGs0WpRcwfzz2ygUvj1bQDKQr+GtQoP77RwjBd53iVED
+Fck5F48jbsSuFFPz4HnJOMudk5r7iTDBLZGy0aKgodOOlUoNnBi5ozTvY/hAr596
+0W/12XTj2wz7WgntnSxPnC9ZXSXZMsZOSiZr4ZH4kJTkYYCa0vK+tyg2UovDwkap
+IGy1lunV1AUDsEAi7IS34nGOTOC7iz7YVE6+dQIDAQABAoIBAATziOIjyTM+7H6a
+kNxhXKXC/F/O678pblzRGbJEG3ublCMlJ7qtwqIUcfxxOZvyul95sN6NnUbrrUH5
+yAZNv32exJwqd09io2ozUoaDdpUVgSitnz7DoLpVH7t4nnE9UapbOjDqIXmVS26G
++zZAA+xIB2DDTjuvrpyF9lmgdPloJsA3Q+X3nFfNgbt8Z8xGF2xs8a3R/83k8y+F
+UueDV56v/ivIebPULSOsktEQXtKDgYLNIuMXDztJSflTr0M+Uq9qPIs6fQl55ssk
+o7G55vAzlTBlwSWiOSubecAmiyFeWY3E16uVAYcTKXYEjSpFTqAyNjBTElsgSiuh
+kSODACkCgYEA898WP5Edd9tUgJCr0OQcxdCmXhTZI2fcAqEcjfCyxKHRXAzDwrKN
+Dpx5Es+Ymn2bRGoLCYpeXpxHZTnuRakrO8zM34PbCWWul14oAKzB5vn7TRuNJXOE
+HeOeztF8FEao3YAQlmDR8SqgG0+Z0fkU0/668r0nxz87NQhVmryF8csCgYEA3g4G
+AmULfpRebUyNIn+rtyd2R6IqR3S3HYrfZNRcPPN4V6T5hRN9Fbg+xu3HvY4xl2ww
+XTa5Fq48+K+Hahq+VLyG1FCQWKaJkcKwhUsE811SWVqBUDiFiE+t0+xMdiuuWA2+
+WBnxXJuRZjX7Lz3uvyhkPYvg3GGl+gIe2UhTCL8CgYBO/BAOoodyzFzADxs22sre
+3scjbYkuSHMffuLqreyYdviB4bh9lKrO8qrcBtScJNZnABl3C5Oo/OC6zlp3wWS5
+R4mLUv9cOcykReCa51kd4BUeUjAS0ykVLSXAQmT3hDMpGlNFATK981pLvipJsWyn
+hLcz60O46qfYc0yWgzy2pQKBgDZbgncVmzG6Sj9BTQ9T54onW9n/RZBGbwkJVNRO
+8+FrzFrQJNDBy44Ek0GJNaseYt9peZFn8vktX/mUpnZ/43XEzNCuhBdfdS+8VA/d
+JHIatyzubddgbrfGoYvOPY1z9fLSMnVIrH/6CSbXMg0HgSlETv2N/1aThNjlqf2Q
+qdrrAoGBAO7cE80xgQaZxu3x9ZtM4PeiE2thVjdZEGAyQlRyS6BO0iGMGWjpeUZq
+2ZJvm8AurRv7Wtmjf9EsoIJKbnGI3wBH0Sbs8LZ8ksI4Dtcx8UPeqzeUwfM/VQdH
+qARGkCRJD/nBbZIy+sOfBysnYVr099kEoGFW8XA0IoGveVkGv8p9
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/customizedb.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.customizedb
+    localKeyID: 53 D0 A7 6B CF E9 9C 46 ED 44 01 01 01 14 02 52 A6 45 AB C5 
+subject=/UID=com.ansjer.customizedb/CN=Apple Push Services: com.ansjer.customizedb/OU=485ZBTNP7J/O=Winplus Australasia PTY LTD/C=AU
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGOzCCBSOgAwIBAgIIGg1yKiemRoEwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMTkxMTA4MDgzODA0WhcNMjAxMjA3MDgzODA0WjCBpjEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGIxNDAyBgNVBAMMK0FwcGxlIFB1c2gg
+U2VydmljZXM6IGNvbS5hbnNqZXIuY3VzdG9taXplZGIxEzARBgNVBAsMCjQ4NVpC
+VE5QN0oxJDAiBgNVBAoMG1dpbnBsdXMgQXVzdHJhbGFzaWEgUFRZIExURDELMAkG
+A1UEBhMCQVUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTiNE32dce
+ZMEsO/0na/qDcYD3icWauyoYm/SL0OWVtIahF4J5vXcg46QHNhQ4y/xczFLQr4iZ
+sUKyElt3c3MLZCs2P/75UOfGYZTVRpugjo83iSpz9pHZl4qc7fZMDyurXzcYazRa
+lFzB/PPbKBS+PVtAMpCv4a1Cg/vtHCMF3neJUQMVyTkXjyNuxK4UU/Pgeck4y52T
+mvuJMMEtkbLRoqCh046VSg2cGLmjNO9j+ECvn3rRb/XZdOPbDPtaCe2dLE+cL1ld
+Jdkyxk5KJmvhkfiQlORhgJrS8r63KDZSi8PCRqkgbLWW6dXUBQOwQCLshLficY5M
+4LuLPthUTr51AgMBAAGjggJ5MIICdTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaA
+FIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHAYDVR0gBIIBEzCCAQ8wggELBgkqhkiG
+92NkBQEwgf0wgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0
+aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0
+aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2Yg
+dXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3Rp
+Y2Ugc3RhdGVtZW50cy4wNQYIKwYBBQUHAgEWKWh0dHA6Ly93d3cuYXBwbGUuY29t
+L2NlcnRpZmljYXRlYXV0aG9yaXR5MBMGA1UdJQQMMAoGCCsGAQUFBwMCMDAGA1Ud
+HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuYXBwbGUuY29tL3d3ZHJjYS5jcmwwHQYD
+VR0OBBYEFFPQp2vP6ZxG7UQBAQEUAlKmRavFMA4GA1UdDwEB/wQEAwIHgDAQBgoq
+hkiG92NkBgMBBAIFADAQBgoqhkiG92NkBgMCBAIFADCBiQYKKoZIhvdjZAYDBgR7
+MHkMFmNvbS5hbnNqZXIuY3VzdG9taXplZGIwBQwDYXBwDBtjb20uYW5zamVyLmN1
+c3RvbWl6ZWRiLnZvaXAwBgwEdm9pcAwjY29tLmFuc2plci5jdXN0b21pemVkYi5j
+b21wbGljYXRpb24wDgwMY29tcGxpY2F0aW9uMA0GCSqGSIb3DQEBCwUAA4IBAQA7
+mHftwMQj6bfXVzXMf6qsvM1gc/842e1/4aPiM4A+09dgJFUXz6Zq7Zw7JkcMWJdx
+bJsDcH4FoNR0Z24RM8wKjaYsPUQz6I3CBgSC5Zj5OnbSDuYLLciqDPWdVqtZD0EK
+/oMt7O8H4H640ZvzNOpfa+WPTBlyWWoYJvQJtFmPNajmaMKGj5BIxgmxW+bAGbdN
+IOiIgebFn0vcXiybptS6nthvBf7qqs5y0Z9ZwOh2Z/e3ognHv35Qa5fBfjuDf0Gb
+mUa7fAoI/MVdu2TwvdmZQ6Ob1IPL9z1yPHBeUwt8qGDqaqeoXo7uVg/qQCAHEz+n
+b4AM1a7aHD1YTAnbk+FH
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA04jRN9nXHmTBLDv9J2v6g3GA94nFmrsqGJv0i9DllbSGoReC
+eb13IOOkBzYUOMv8XMxS0K+ImbFCshJbd3NzC2QrNj/++VDnxmGU1UaboI6PN4kq
+c/aR2ZeKnO32TA8rq183GGs0WpRcwfzz2ygUvj1bQDKQr+GtQoP77RwjBd53iVED
+Fck5F48jbsSuFFPz4HnJOMudk5r7iTDBLZGy0aKgodOOlUoNnBi5ozTvY/hAr596
+0W/12XTj2wz7WgntnSxPnC9ZXSXZMsZOSiZr4ZH4kJTkYYCa0vK+tyg2UovDwkap
+IGy1lunV1AUDsEAi7IS34nGOTOC7iz7YVE6+dQIDAQABAoIBAATziOIjyTM+7H6a
+kNxhXKXC/F/O678pblzRGbJEG3ublCMlJ7qtwqIUcfxxOZvyul95sN6NnUbrrUH5
+yAZNv32exJwqd09io2ozUoaDdpUVgSitnz7DoLpVH7t4nnE9UapbOjDqIXmVS26G
++zZAA+xIB2DDTjuvrpyF9lmgdPloJsA3Q+X3nFfNgbt8Z8xGF2xs8a3R/83k8y+F
+UueDV56v/ivIebPULSOsktEQXtKDgYLNIuMXDztJSflTr0M+Uq9qPIs6fQl55ssk
+o7G55vAzlTBlwSWiOSubecAmiyFeWY3E16uVAYcTKXYEjSpFTqAyNjBTElsgSiuh
+kSODACkCgYEA898WP5Edd9tUgJCr0OQcxdCmXhTZI2fcAqEcjfCyxKHRXAzDwrKN
+Dpx5Es+Ymn2bRGoLCYpeXpxHZTnuRakrO8zM34PbCWWul14oAKzB5vn7TRuNJXOE
+HeOeztF8FEao3YAQlmDR8SqgG0+Z0fkU0/668r0nxz87NQhVmryF8csCgYEA3g4G
+AmULfpRebUyNIn+rtyd2R6IqR3S3HYrfZNRcPPN4V6T5hRN9Fbg+xu3HvY4xl2ww
+XTa5Fq48+K+Hahq+VLyG1FCQWKaJkcKwhUsE811SWVqBUDiFiE+t0+xMdiuuWA2+
+WBnxXJuRZjX7Lz3uvyhkPYvg3GGl+gIe2UhTCL8CgYBO/BAOoodyzFzADxs22sre
+3scjbYkuSHMffuLqreyYdviB4bh9lKrO8qrcBtScJNZnABl3C5Oo/OC6zlp3wWS5
+R4mLUv9cOcykReCa51kd4BUeUjAS0ykVLSXAQmT3hDMpGlNFATK981pLvipJsWyn
+hLcz60O46qfYc0yWgzy2pQKBgDZbgncVmzG6Sj9BTQ9T54onW9n/RZBGbwkJVNRO
+8+FrzFrQJNDBy44Ek0GJNaseYt9peZFn8vktX/mUpnZ/43XEzNCuhBdfdS+8VA/d
+JHIatyzubddgbrfGoYvOPY1z9fLSMnVIrH/6CSbXMg0HgSlETv2N/1aThNjlqf2Q
+qdrrAoGBAO7cE80xgQaZxu3x9ZtM4PeiE2thVjdZEGAyQlRyS6BO0iGMGWjpeUZq
+2ZJvm8AurRv7Wtmjf9EsoIJKbnGI3wBH0Sbs8LZ8ksI4Dtcx8UPeqzeUwfM/VQdH
+qARGkCRJD/nBbZIy+sOfBysnYVr099kEoGFW8XA0IoGveVkGv8p9
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/customizedc.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.customizedc
+    localKeyID: 7C 1A EE 33 33 71 66 7C 92 4E 03 CC F9 CA 61 C4 EC 4B C7 61 
+subject=/UID=com.ansjer.customizedc/CN=Apple Push Services: com.ansjer.customizedc/OU=9NRJLKV9UB/O=SMD Technologies Pty LTD/C=ZA
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGODCCBSCgAwIBAgIId0lTH2/IAa4wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjEwMzEwMTA1NTE0WhcNMjIwNDA5MTA1NTEzWjCBozEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGMxNDAyBgNVBAMMK0FwcGxlIFB1c2gg
+U2VydmljZXM6IGNvbS5hbnNqZXIuY3VzdG9taXplZGMxEzARBgNVBAsMCjlOUkpM
+S1Y5VUIxITAfBgNVBAoMGFNNRCBUZWNobm9sb2dpZXMgUHR5IExURDELMAkGA1UE
+BhMCWkEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaX1qQS/CHT5r5
+EVvly9+0XHhTt8I6Rynom3Y7NIoYb1TMTBHlIE9DQ+g4VXBrENkdYjRuicR5ADto
+MfNjRxse3+7E3gg1Nt2oUsDodWDlrjLEUDh+FFF8mcXAAwiiOdB3daUUvxkplcCU
+4/3mNpBmVfCWenCKY8gLHqtXHC71SWd4dSTj0tg8/62F1MUCh3gPpb65hhDAtWgS
+KiQ3rbsL/kXllEGbAkO7fokRu3Nd7telCnO8PHT30BSqgJnOSJZhD+ZGxZLlqIqS
+T6axSWeZC0dllZO/XAPkfQmJgMl7fS5PdSrfoPl2CM+Xa1f/eUrxa86tRSGAgl8G
+O3JZMAZ3AgMBAAGjggJ5MIICdTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFIgn
+Fwmpthhgi+zruvZHWcVSVKO3MIIBHAYDVR0gBIIBEzCCAQ8wggELBgkqhkiG92Nk
+BQEwgf0wgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZp
+Y2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVu
+IGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNl
+LCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ug
+c3RhdGVtZW50cy4wNQYIKwYBBQUHAgEWKWh0dHA6Ly93d3cuYXBwbGUuY29tL2Nl
+cnRpZmljYXRlYXV0aG9yaXR5MBMGA1UdJQQMMAoGCCsGAQUFBwMCMDAGA1UdHwQp
+MCcwJaAjoCGGH2h0dHA6Ly9jcmwuYXBwbGUuY29tL3d3ZHJjYS5jcmwwHQYDVR0O
+BBYEFHwa7jMzcWZ8kk4DzPnKYcTsS8dhMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG
+92NkBgMBBAIFADAQBgoqhkiG92NkBgMCBAIFADCBiQYKKoZIhvdjZAYDBgR7MHkM
+FmNvbS5hbnNqZXIuY3VzdG9taXplZGMwBQwDYXBwDBtjb20uYW5zamVyLmN1c3Rv
+bWl6ZWRjLnZvaXAwBgwEdm9pcAwjY29tLmFuc2plci5jdXN0b21pemVkYy5jb21w
+bGljYXRpb24wDgwMY29tcGxpY2F0aW9uMA0GCSqGSIb3DQEBCwUAA4IBAQAwrr5I
+UlUPnRi7j2lEWx3GHK1iovps25C2rz4f11d3+9cZQfOFW96cjoFp2o9hILzril4P
+0YVkaWF3RBGngT27j1OdsdQCRwLHBVw42J4njrNUINJ3FVlSEVXJtAo3fHm3PajB
+KZo96vNbFbZe3MFC+CfEctM1lyizAWEWp68exyZeC8ppM7og55BSWEjSRJQRuon2
+wVHsWUwCNfuTNBomwzzSfQVaZkQogFNwO0F0AIxYUhHDUFG3/v+HfvmIpz0QTH+g
+nzvheeNzz44zDfetSCEy/7z0d4lgCw4QQ3hCko3jvNuv8vF3yZ4Y4Im2VqqFFWuX
+mXV7h0xmGK9HGJZH
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAml9akEvwh0+a+RFb5cvftFx4U7fCOkcp6Jt2OzSKGG9UzEwR
+5SBPQ0PoOFVwaxDZHWI0bonEeQA7aDHzY0cbHt/uxN4INTbdqFLA6HVg5a4yxFA4
+fhRRfJnFwAMIojnQd3WlFL8ZKZXAlOP95jaQZlXwlnpwimPICx6rVxwu9UlneHUk
+49LYPP+thdTFAod4D6W+uYYQwLVoEiokN627C/5F5ZRBmwJDu36JEbtzXe7XpQpz
+vDx099AUqoCZzkiWYQ/mRsWS5aiKkk+msUlnmQtHZZWTv1wD5H0JiYDJe30uT3Uq
+36D5dgjPl2tX/3lK8WvOrUUhgIJfBjtyWTAGdwIDAQABAoIBAAObzwYogErpdXn1
+CezpQyV4M+fhF8089KHXYIApzkCb+aN8874jbzbnU3pdpO8bybeCIC+lUM5zTvA0
+mrfrZZrnKTG47wAvdCcHKDNG6yjzVYR5S5jG6NC8136Rk5YXO+RO/7jobrImKgZQ
+/hJvnBiIUcNywUa65mY8JU03/3EEaPd0CESSvX+IAbJzIW0vRvYJQ5YthiKzBwFg
+2FA7dLnIwMY9vL3ggYPrAgKfyyIGB0wlsLygrldnb2sJ+KiG3r7WlLTIb9Vs9UTv
+09q+Hk4fdRbg/vw1AyUE4oWcZdFGNI8fobUN30u6rjrhJj8ZS4JVngfvShgKiU65
+T0q032ECgYEAx5SpbAr3xebfG2iNPZeV1KlUIXqRVxYvguztgk0m7Q30rTuOfJtp
+SdHRGE+sLPKa3jQXi4xlx0J5oZt0USl6Y0hlWrq2C2W7DXF2T2WEJ/G9HLWZ5yXO
+zt4V6+4t0r9Tn/fPcSAYQzs5brUY1M/Sd7ygD/JIK25V+FxIOseDncsCgYEAxgML
+RHFrGfpN0TPd8Pkycw4NKl+Ft5+iWgyH4TwcXik8otu7vwwWvQRTjToSUtC7W1Vh
+C+S9c4ruX+4QWESiNNDsyybwrfTEQ/8+zoLrLDq6lJLkowqDShMY/Rsrn5ALK91M
+BSnhjTB0Jc20lIXz7KrqbkMfYSDF6Mru3qsLpIUCgYBkEOJ9XGZsoFv+pwYh+o3S
+CXA4NPcIPgRmVZqj+UDqHFQwQysmyNkafQukb9X4107U2DNx8Vvaczb+0y2Xv/8w
+Jk1DF1hNEJ2fmOLj3hdL28WgzmCy517wk6NUHqB1CR7Jlvs4Ugo4M0qRelVP5j81
+b6qlNCoBNJoAI2zHo98WzwKBgQCqnGzU6DS0CWuLWfldbBn/8Jd/bxa0BKPKmwlp
+FHYM29MXL+Uw06Wz70peYS7wlFEafRJFQS3LpGWH2oPSyjBI+ctNr5upB0EkpYTk
+QwGG9oNVCMxffutoo9PgIc0QivhVLzQVLuTc1mb9O6TgZBqsTQf5QXSEJ8zerBvt
+V8uMUQKBgCI93kALFOS/pc3CnKOtapyIGvyrOx7FX7WbhUiQzgiWlNVxkjklSPba
+H3I2hsjMnfiYXAHZcFl62z6/3v7C0oet2F+dAezMogEvrKhxqXPna+kFV0TLNkxa
+B9eJ1ZaZKdYZzra8nB+YwUbqd6xnCx5+YUETquInluyBv6T41xPp
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/customizedd.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.customizedd
+    localKeyID: 0E C0 56 E3 BF 8A B4 FE 33 B5 87 22 BA 36 F3 DA DD 2A 1C E3 
+subject=/UID=com.ansjer.customizedd/CN=Apple Push Services: com.ansjer.customizedd/OU=QQC4HU6T2E/O=ALKAM SECURITY SP Z O O SPOLKA KOMANDYTOWA/C=PL
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGSjCCBTKgAwIBAgIIEMmTb4XuQ+gwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNzMwMDYxMzI2WhcNMjEwODI5MDYxMzI2WjCBtTEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGQxNDAyBgNVBAMMK0FwcGxlIFB1c2gg
+U2VydmljZXM6IGNvbS5hbnNqZXIuY3VzdG9taXplZGQxEzARBgNVBAsMClFRQzRI
+VTZUMkUxMzAxBgNVBAoMKkFMS0FNIFNFQ1VSSVRZIFNQIFogTyBPIFNQT0xLQSBL
+T01BTkRZVE9XQTELMAkGA1UEBhMCUEwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDD0zDi3fDOkMB2Z/3nj6f/a31ZV8IEM9b85OXpSKHd1LnMsguAVPYC
+L870FGNL2fr4Cyx5gtVkI8l1mxqZSRy4wFnJcryWExd1Ive1z9d5ZMTX+nHehv10
+u5v7zPIKbX3DuSleIInCm6r5nly4NGBALoiXSMYMMxD5QhFd35WnrNZMrSdFQ09W
+WpUmMfW/tLmYNJvIis/y9wbcdtPN3Ixp3yt9khSUYW+zWZO9BvXaG6dwvXDvtj8y
+MfXKNJqQmGYhRc2kYA6CO6uBenVuS0ZWoaf1jnHmvIYXXuimU9ax+UJmL8oZsIZO
+pEtkpo6TrxORpOOZfc2h+rwn98ht4V0DAgMBAAGjggJ5MIICdTAMBgNVHRMBAf8E
+AjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHAYDVR0gBIIB
+EzCCAQ8wggELBgkqhkiG92NkBQEwgf0wgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFu
+Y2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2Nl
+cHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5k
+IGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRp
+ZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wNQYIKwYBBQUHAgEWKWh0dHA6
+Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5MBMGA1UdJQQMMAoG
+CCsGAQUFBwMCMDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuYXBwbGUuY29t
+L3d3ZHJjYS5jcmwwHQYDVR0OBBYEFA7AVuO/irT+M7WHIro289rdKhzjMA4GA1Ud
+DwEB/wQEAwIHgDAQBgoqhkiG92NkBgMBBAIFADAQBgoqhkiG92NkBgMCBAIFADCB
+iQYKKoZIhvdjZAYDBgR7MHkMFmNvbS5hbnNqZXIuY3VzdG9taXplZGQwBQwDYXBw
+DBtjb20uYW5zamVyLmN1c3RvbWl6ZWRkLnZvaXAwBgwEdm9pcAwjY29tLmFuc2pl
+ci5jdXN0b21pemVkZC5jb21wbGljYXRpb24wDgwMY29tcGxpY2F0aW9uMA0GCSqG
+SIb3DQEBCwUAA4IBAQBbdmCg662kU9khIZDnv7pT+rLCK8s0T6m74mAPuonq39dS
+IIEjNaNF/mUFQAzjr5Tqzs/EMonjPcZ8/JrwxaesLsd5fyfo1oYzxopPcY6K9iVk
+jwnSBV3ychPLjGc/HZowYHbfE3Ao8aoxAksQ6w1/mn0+I9VmgA4Nryt7X3/GbyPz
+AgzQZN5Ry5VOw22SJih0ECkjtVs5NOldOZKzsARSm/4Kv1PjFqInIq1FuwWNsUrB
+PX099HmzIQ68ioPdKe8WFt/+srzpCQfc0FefVXr1YtC7sCid8icMqoaVOKtswuNZ
+jnmxi18p8L2xn+nktdydNPjWy/s71haJKvrsUs5F
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAw9Mw4t3wzpDAdmf954+n/2t9WVfCBDPW/OTl6Uih3dS5zLIL
+gFT2Ai/O9BRjS9n6+AsseYLVZCPJdZsamUkcuMBZyXK8lhMXdSL3tc/XeWTE1/px
+3ob9dLub+8zyCm19w7kpXiCJwpuq+Z5cuDRgQC6Il0jGDDMQ+UIRXd+Vp6zWTK0n
+RUNPVlqVJjH1v7S5mDSbyIrP8vcG3HbTzdyMad8rfZIUlGFvs1mTvQb12huncL1w
+77Y/MjH1yjSakJhmIUXNpGAOgjurgXp1bktGVqGn9Y5x5ryGF17oplPWsflCZi/K
+GbCGTqRLZKaOk68TkaTjmX3Nofq8J/fIbeFdAwIDAQABAoIBAQCO2cEPvlesWGwN
+9E6EXYdwDDmksyAVcLukQFZ3l0R9TnE7oaZCN7g+YCHYbAcDjkBGMeB/w7FnG7Qa
+1srQh0UsgST+j3SDBepWyOtq9fN52si9xTTL4C2EuldPmHH4qWbVIH3M7G+SR3yy
+G5QHzC1qpId/1Nakj5wSON/yWugRJ/16IDhfwzsaYwElCKTJBby0SrNpAz/6Nr9x
+17cIsPb2b9aAlxeNA1quwSSOuEGz/4YV9t8WJ+3bdglyjaA/bDQD6APPt9xYS3du
+nze1jhXjmBk4+rf7usISpjGze+sa9y8VT3GcegeKOedRd8Yfd2KmdjjN/4M3gFBG
+HLkt/rYBAoGBAOL0uddiSo7FBM30fAvRBz1RdkUVItmEudih1mKYd529waemI1W1
+JzJW1WTadOEsV30uyDrBO1R/ONp6doiBNPGtNeMBrnCUbgWFtRAGa+Z979FVUgZB
+W7gGlFLtan1nYCJ6XCNQSFzkMmMUO9cMKL98Wyln8Ybzv2IEjn+hbNY/AoGBANzi
+lis3pKVV4tMlUiJiSLMkFzqqM/6rSjy/eM/2ztRqc3bm8+2hDdF75Whf2JcDF9EK
+HQYiGvAqCrrLVcmt/myoAMvWH2OVLCaHeRxMbNmVR0ZBivryjr0ziBOzLxi9kCkb
+eHSESidWN7mlPbJDrsGWsm1nvIFUxGRikhHombA9AoGBANmgZ9ADvZC3lYrUZFqE
+XtfKlIlb4iYIYpk7XrL+lrzYp6jV484cvnBD2a4y2J7PATnVsUS94+F7eQ3vHlh4
+5UbVm/WIuDfV7MY41019itvjaSInGVdMNuH41SPL6zBC/2kwQcfPBQSmATcGwPbN
+/wjW+ydyN6T7eEpKKkZyiUXLAoGAcbM1R6PJ3c2bB8MzRz2+XHBcgLcyMoprmV+a
+Sbqq9s6hBviVYaHAoi+bhCGqNPnDnbgN/qU5ppyNj1+xljI5QCcu2b4rxC/6XQ7p
+ObbhZRMhcDm217xZ22yWREW9HYgel/prA9dA8ITCBI7ZdlS+tSnkSMrYTt6ahW3z
+8MIBsqUCgYEAqv21tNJpDC5ntt9BKEuUALSsBSscD3pXOiR3p4It0zYyp5Xnkwvw
+S8m1oM4Nz1spWwNkrT8C4j4xUCX4aKH0J2Yw2YnztPJlEl1Clwp1GTK5rTGumoJh
+yQXq0MeePDcS7CN/wOEa/O8KwCZ0cJCm8Kuu//VhyFXvtYWPzO9r7yg=
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/customizede.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.customizede
+    localKeyID: 0E C0 56 E3 BF 8A B4 FE 33 B5 87 22 BA 36 F3 DA DD 2A 1C E3 
+subject=/UID=com.ansjer.customizede/CN=Apple Push Services: com.ansjer.customizede/OU=772N5HXAR2/O=ZOSI Technology Co. , Ltd/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGOTCCBSGgAwIBAgIISa0aXjdOKS8wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNzMwMDcyNzUxWhcNMjEwODI5MDcyNzUxWjCBpDEmMCQGCgmSJomT8ixk
+AQEMFmNvbS5hbnNqZXIuY3VzdG9taXplZGUxNDAyBgNVBAMMK0FwcGxlIFB1c2gg
+U2VydmljZXM6IGNvbS5hbnNqZXIuY3VzdG9taXplZGUxEzARBgNVBAsMCjc3Mk41
+SFhBUjIxIjAgBgNVBAoMGVpPU0kgVGVjaG5vbG9neSBDby4gLCBMdGQxCzAJBgNV
+BAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw9Mw4t3wzpDA
+dmf954+n/2t9WVfCBDPW/OTl6Uih3dS5zLILgFT2Ai/O9BRjS9n6+AsseYLVZCPJ
+dZsamUkcuMBZyXK8lhMXdSL3tc/XeWTE1/px3ob9dLub+8zyCm19w7kpXiCJwpuq
++Z5cuDRgQC6Il0jGDDMQ+UIRXd+Vp6zWTK0nRUNPVlqVJjH1v7S5mDSbyIrP8vcG
+3HbTzdyMad8rfZIUlGFvs1mTvQb12huncL1w77Y/MjH1yjSakJhmIUXNpGAOgjur
+gXp1bktGVqGn9Y5x5ryGF17oplPWsflCZi/KGbCGTqRLZKaOk68TkaTjmX3Nofq8
+J/fIbeFdAwIDAQABo4ICeTCCAnUwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSI
+JxcJqbYYYIvs67r2R1nFUlSjtzCCARwGA1UdIASCARMwggEPMIIBCwYJKoZIhvdj
+ZAUBMIH9MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlm
+aWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhl
+biBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVz
+ZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNl
+IHN0YXRlbWVudHMuMDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LmFwcGxlLmNvbS9j
+ZXJ0aWZpY2F0ZWF1dGhvcml0eTATBgNVHSUEDDAKBggrBgEFBQcDAjAwBgNVHR8E
+KTAnMCWgI6Ahhh9odHRwOi8vY3JsLmFwcGxlLmNvbS93d2RyY2EuY3JsMB0GA1Ud
+DgQWBBQOwFbjv4q0/jO1hyK6NvPa3Soc4zAOBgNVHQ8BAf8EBAMCB4AwEAYKKoZI
+hvdjZAYDAQQCBQAwEAYKKoZIhvdjZAYDAgQCBQAwgYkGCiqGSIb3Y2QGAwYEezB5
+DBZjb20uYW5zamVyLmN1c3RvbWl6ZWRlMAUMA2FwcAwbY29tLmFuc2plci5jdXN0
+b21pemVkZS52b2lwMAYMBHZvaXAMI2NvbS5hbnNqZXIuY3VzdG9taXplZGUuY29t
+cGxpY2F0aW9uMA4MDGNvbXBsaWNhdGlvbjANBgkqhkiG9w0BAQsFAAOCAQEAnDLz
+8eGrSkHZc/O6eooFPg44XB7deCDK3Gr8y95nW75sRoqqsHcyusPIkhcsnwL7FNbR
+eMSrxyAFW8KtnrQENXUndw/qQHC0KSgZjcrT23XNrT8/Ey+fzJEIXn/5T2xERPy0
+/6OxBalGfgfsDP0D1muhDmWJteFva2jssuF6iljbrYnRjYrZoqroNbuaOmwBsNlv
+Hl6YER6QW48M7hSKpgmaRacwnD+tNesBz52p2FXz/jjSBmcgapFqVIoO13vFiaGe
+l5YLYF/y1ByjPmE2zQs0PjDmK/iF+cuask5vMgTm77jXCPY2DrAvG2rMOqwzw4IJ
+QX4lrh9xAeTd97yzfg==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAw9Mw4t3wzpDAdmf954+n/2t9WVfCBDPW/OTl6Uih3dS5zLIL
+gFT2Ai/O9BRjS9n6+AsseYLVZCPJdZsamUkcuMBZyXK8lhMXdSL3tc/XeWTE1/px
+3ob9dLub+8zyCm19w7kpXiCJwpuq+Z5cuDRgQC6Il0jGDDMQ+UIRXd+Vp6zWTK0n
+RUNPVlqVJjH1v7S5mDSbyIrP8vcG3HbTzdyMad8rfZIUlGFvs1mTvQb12huncL1w
+77Y/MjH1yjSakJhmIUXNpGAOgjurgXp1bktGVqGn9Y5x5ryGF17oplPWsflCZi/K
+GbCGTqRLZKaOk68TkaTjmX3Nofq8J/fIbeFdAwIDAQABAoIBAQCO2cEPvlesWGwN
+9E6EXYdwDDmksyAVcLukQFZ3l0R9TnE7oaZCN7g+YCHYbAcDjkBGMeB/w7FnG7Qa
+1srQh0UsgST+j3SDBepWyOtq9fN52si9xTTL4C2EuldPmHH4qWbVIH3M7G+SR3yy
+G5QHzC1qpId/1Nakj5wSON/yWugRJ/16IDhfwzsaYwElCKTJBby0SrNpAz/6Nr9x
+17cIsPb2b9aAlxeNA1quwSSOuEGz/4YV9t8WJ+3bdglyjaA/bDQD6APPt9xYS3du
+nze1jhXjmBk4+rf7usISpjGze+sa9y8VT3GcegeKOedRd8Yfd2KmdjjN/4M3gFBG
+HLkt/rYBAoGBAOL0uddiSo7FBM30fAvRBz1RdkUVItmEudih1mKYd529waemI1W1
+JzJW1WTadOEsV30uyDrBO1R/ONp6doiBNPGtNeMBrnCUbgWFtRAGa+Z979FVUgZB
+W7gGlFLtan1nYCJ6XCNQSFzkMmMUO9cMKL98Wyln8Ybzv2IEjn+hbNY/AoGBANzi
+lis3pKVV4tMlUiJiSLMkFzqqM/6rSjy/eM/2ztRqc3bm8+2hDdF75Whf2JcDF9EK
+HQYiGvAqCrrLVcmt/myoAMvWH2OVLCaHeRxMbNmVR0ZBivryjr0ziBOzLxi9kCkb
+eHSESidWN7mlPbJDrsGWsm1nvIFUxGRikhHombA9AoGBANmgZ9ADvZC3lYrUZFqE
+XtfKlIlb4iYIYpk7XrL+lrzYp6jV484cvnBD2a4y2J7PATnVsUS94+F7eQ3vHlh4
+5UbVm/WIuDfV7MY41019itvjaSInGVdMNuH41SPL6zBC/2kwQcfPBQSmATcGwPbN
+/wjW+ydyN6T7eEpKKkZyiUXLAoGAcbM1R6PJ3c2bB8MzRz2+XHBcgLcyMoprmV+a
+Sbqq9s6hBviVYaHAoi+bhCGqNPnDnbgN/qU5ppyNj1+xljI5QCcu2b4rxC/6XQ7p
+ObbhZRMhcDm217xZ22yWREW9HYgel/prA9dA8ITCBI7ZdlS+tSnkSMrYTt6ahW3z
+8MIBsqUCgYEAqv21tNJpDC5ntt9BKEuUALSsBSscD3pXOiR3p4It0zYyp5Xnkwvw
+S8m1oM4Nz1spWwNkrT8C4j4xUCX4aKH0J2Yw2YnztPJlEl1Clwp1GTK5rTGumoJh
+yQXq0MeePDcS7CN/wOEa/O8KwCZ0cJCm8Kuu//VhyFXvtYWPzO9r7yg=
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/lcc-dev.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.loocamccloud
+    localKeyID: 81 BA F8 2E C5 58 9A A4 86 A9 47 F6 EF B7 16 48 1C 7A 1E E9
+subject=/UID=com.ansjer.loocamccloud/CN=Apple Push Services: com.ansjer.loocamccloud/OU=JCHT67XT68/O=Loocam Technology LLC/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGOjCCBSKgAwIBAgIIEh/B+Kd1dVowDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMTkxMDE1MDk0OTQ4WhcNMjAxMTEzMDk0OTQ4WjCBojEnMCUGCgmSJomT8ixk
+AQEMF2NvbS5hbnNqZXIubG9vY2FtY2Nsb3VkMTUwMwYDVQQDDCxBcHBsZSBQdXNo
+IFNlcnZpY2VzOiBjb20uYW5zamVyLmxvb2NhbWNjbG91ZDETMBEGA1UECwwKSkNI
+VDY3WFQ2ODEeMBwGA1UECgwVTG9vY2FtIFRlY2hub2xvZ3kgTExDMQswCQYDVQQG
+EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIfC7vgq2k1bNRW
+4/Ix+W5CYWLzlbZJ3WS+A4X8GEZVkpcuQhADCs/fxMk/KMzl3XvX8YiNQoItKGzK
+Gd0wEtwdjmQvsXwjZG47v7OGE0qj9beQRMYo0gq6SOK7J7r7/8QW9ggmZJXfTqu2
+kQimrpTLeEQdKERbZLxV4i5oOTaAwyzHChNDmvcg2LCjkYaHuOMKtuQHOcMd8RQ4
+DybXkCyPbItIRxuAksMSem2TBHyHoO6Agx3epPgU+9nTbze7eU0OpAiAl7lFqc3i
+80llb8N23q4WUK+1u/5KEVlZjXHvdmOYldVpUK2GUgc4gcBbZoda3DIQ/F8nddTI
+gczwXRMCAwEAAaOCAnwwggJ4MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcX
+Cam2GGCL7Ou69kdZxVJUo7cwggEcBgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QF
+ATCB/TCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4g
+YXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2Us
+IGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBz
+dGF0ZW1lbnRzLjA1BggrBgEFBQcCARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2Vy
+dGlmaWNhdGVhdXRob3JpdHkwEwYDVR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkw
+JzAloCOgIYYfaHR0cDovL2NybC5hcHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4E
+FgQUgbr4LsVYmqSGqUf277cWSBx6HukwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3
+Y2QGAwEEAgUAMBAGCiqGSIb3Y2QGAwIEAgUAMIGMBgoqhkiG92NkBgMGBH4wfAwX
+Y29tLmFuc2plci5sb29jYW1jY2xvdWQwBQwDYXBwDBxjb20uYW5zamVyLmxvb2Nh
+bWNjbG91ZC52b2lwMAYMBHZvaXAMJGNvbS5hbnNqZXIubG9vY2FtY2Nsb3VkLmNv
+bXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24wDQYJKoZIhvcNAQELBQADggEBABr9
+dM8BvrJVSwYRGw39JU7kiGV+dOhcJ59XwOdo2xO05dv1iQJeVDRSpni9yfeHX/pu
+rycGgEm40IQxqTNs851KUqBiLjEwRjkX6MZQMbWnTZY8xiLFKm7lhY6mFUsxfkXH
+/dygY9uZkNCzYfzs8JbWRU8p2uNwF3ZRvZiXITncNWImkdtneSHgF3IQtq/6flc5
+aA89zIH8P7V5/Rvye7JFuM98AXNUHO8XznwgGru6kZCpi4e+83AAXhD9JQmHv/wN
+iHCDWHq1TFS8KgbxfvgGkB2r5vlGkWSo2E3xnxGZu6tiRYgNyNqNsJoMeAtH/352
+xQQMuMMfCP/eKBojC+8=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAwh8Lu+CraTVs1Fbj8jH5bkJhYvOVtkndZL4DhfwYRlWSly5C
+EAMKz9/EyT8ozOXde9fxiI1Cgi0obMoZ3TAS3B2OZC+xfCNkbju/s4YTSqP1t5BE
+xijSCrpI4rsnuvv/xBb2CCZkld9Oq7aRCKaulMt4RB0oRFtkvFXiLmg5NoDDLMcK
+E0Oa9yDYsKORhoe44wq25Ac5wx3xFDgPJteQLI9si0hHG4CSwxJ6bZMEfIeg7oCD
+Hd6k+BT72dNvN7t5TQ6kCICXuUWpzeLzSWVvw3berhZQr7W7/koRWVmNce92Y5iV
+1WlQrYZSBziBwFtmh1rcMhD8Xyd11MiBzPBdEwIDAQABAoIBAAi22BIBDfDVIyON
+GwJ+V9IpTLHieXALdNLW7NWQ8Y4GvWPoAbMyT8PxKaZXHX/FOeIbWY5luPybBluL
+3wlc5RvYf9HjijYmaJXCPOLUO/13ysoIwe2bW4J7xlXXTPtJ4LtuxHCtOIq50ZBH
+y871nJS1ZZq5LFuVNrp1qIeoyhi2LuMdkzjpA5MujOhr9vEgI2P2wBazgL2XZ/i/
+2FTJBWp13wSCj6usohni+Ppm77+/Ax9kj0K2vXTjy/fRDIcmkAHdfzifd3oGHtIs
+zc5m1i5GnKXhUB87GrCSvZ1gqJII2ZgXCroprp1TzrWOtH0mzDtzuqYsT8aUpAuG
+Fbnj0sECgYEA+78DbnEAWfzjQXjUytgE2FVSWINsl0zUyr0mIh85r0QY+eQc3LX6
+RDPQq1qndhwXPGY5Nv+hGncAIP0o6eldT1D6LuAcyTzff025aTMLxAswtj1qSfNL
+vVEA+V8Q45TqKIC+CfzJysx5xk53S3DFesCxGfHk5ozmg8ILQSblUlECgYEAxWbD
+OwEW1Wi9UmQcGfKjM7tN22EpY4qid0BJsib/qqfI++42BAuytFKZyyAnaOaNCEKx
+ktaKGZcTCjtstathMiB+3sjJbqOXV+WntrVb4eu35N+IhhRd/ltdyXqx9PwMeMpC
+i73JZZhw9Wn4M7aAC/M37vw3Ztecx5hiTKixXCMCgYAFb3dcgBYY20vw0vINOyLH
+pPf1L0k97wu9IN5c6D2vmhHL+2/gGBHCPTgpKvjopOyfgjmEBXrt/lnDXJPwkPhR
+yRbNwkr80VKmBgCV0+NSJUe095h+5kDhYolZ2QKdV07C9xyjTJDyzv1mGbWbrGCn
+KNfqDn35uFsVxy/FBVj4MQKBgQCkwCyGzVTqTh1ca1MM0PYm0decFg5+FpGb55ra
+RjQxkRFsOhb47255k0rqPe7GXU0trMQZolbmHrF0Y75KLTvUo0xf0cJURdKlOHXu
+RpTmDF6RdYKzLMJ57HVtF6r4pXgfQ+egS4iP81era4MMXe+miZqQKg5vFdqyttqr
+0ksccwKBgQC0jmQ6/KCE2hvNcNIKJzXDZ+4C5LGn6f6hSe5G2yzyt0KVUuUd1gk9
+/IbwSYbNR5SO8KmHoL3yjhBnv+eVE5VFTgy5Gc+J+ON9pPjvYqpwMjlqBCzhUWZN
+gm9kUGZQPYOvuweViXECMZntcVNxG2bKodaMFHKda3xD2fTSeB6OCg==
+-----END RSA PRIVATE KEY-----

+ 68 - 0
AnsjerPush/file/apns_pem/lcc.pem

@@ -0,0 +1,68 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.loocamccloud
+    localKeyID: 76 F5 5D 9E 6C 0C 35 08 C6 4D 8D 3C A8 E6 2C 28 0E C0 EA E8 
+subject=/UID=com.ansjer.loocamccloud/CN=Apple Push Services: com.ansjer.loocamccloud/OU=JCHT67XT68/O=Loocam Technology LLC/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGOjCCBSKgAwIBAgIIOp4hhKLzQmUwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAxMjA5MDI0MjE2WhcNMjIwMTA4MDI0MjE2WjCBojEnMCUGCgmSJomT8ixk
+AQEMF2NvbS5hbnNqZXIubG9vY2FtY2Nsb3VkMTUwMwYDVQQDDCxBcHBsZSBQdXNo
+IFNlcnZpY2VzOiBjb20uYW5zamVyLmxvb2NhbWNjbG91ZDETMBEGA1UECwwKSkNI
+VDY3WFQ2ODEeMBwGA1UECgwVTG9vY2FtIFRlY2hub2xvZ3kgTExDMQswCQYDVQQG
+EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUn67aPyCs1MaED
+uNITbo5zB+u3x+xlsUgmtg/GRDuYEMhTt6vzLlegX+PMOnz6K28K90iByEYfwo6k
+cgW3GdxxG9pgjGBwe+EUMh/Khx9P51p7AmsjH7PvNKTQlqJVp+0qZC1n6kmp7an+
+ZCxLNEFso9U3/pl6iwRmS1iyl3WPE9B3A3mnLAi1amV6W9bQfa8m7jjtWW4GWSMQ
++yvcf+AE+LcwDe76z9kFzV/RfaQiF0m8+A6lA9djoLHz97ANtSfmS3aUxwChkg1N
+fPZHLREEb4hOpWdvjSUQNlLSZHjqn82zEnKH+ZyKuNI4ouWdL+Wzz0Hp2FVIKXqL
+UQv5BkMCAwEAAaOCAnwwggJ4MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcX
+Cam2GGCL7Ou69kdZxVJUo7cwggEcBgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QF
+ATCB/TCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4g
+YXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2Us
+IGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBz
+dGF0ZW1lbnRzLjA1BggrBgEFBQcCARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2Vy
+dGlmaWNhdGVhdXRob3JpdHkwEwYDVR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkw
+JzAloCOgIYYfaHR0cDovL2NybC5hcHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4E
+FgQUdvVdnmwMNQjGTY08qOYsKA7A6ugwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3
+Y2QGAwEEAgUAMBAGCiqGSIb3Y2QGAwIEAgUAMIGMBgoqhkiG92NkBgMGBH4wfAwX
+Y29tLmFuc2plci5sb29jYW1jY2xvdWQwBQwDYXBwDBxjb20uYW5zamVyLmxvb2Nh
+bWNjbG91ZC52b2lwMAYMBHZvaXAMJGNvbS5hbnNqZXIubG9vY2FtY2Nsb3VkLmNv
+bXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24wDQYJKoZIhvcNAQELBQADggEBACjM
+eZvOQRFP78dl4pT0o2ttgXwMjhFOFFX+GOgNAaen4SzCau6I2xUkfLx6eGg0ovdX
+husk6BKJkM8Zj8QeOEz2qr5mURwfjMhFjEETk0mUSmmHqZAxPNa4RjVp3XMWrRFI
+b6ok5/2GEYijlUTHjDArpUMgjlWFw9BHo6LeaZEzdTNNKWCDXQAAzLwAOdUFo3BG
+3es1sShkhC/CYsHax5+qvf8UmaX1EICvE3JW+3ptYYzPsDY6pE8/1f0T7TYf7by+
+PnNwyJTeqktWwrrB60nwDfQ9+S6eqAI6ZV9Vg5Hte5mLSpTQxJVOIAwKd26jFt4e
+RzhmGL052dQFoj/uQ4M=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAtSfrto/IKzUxoQO40hNujnMH67fH7GWxSCa2D8ZEO5gQyFO3
+q/MuV6Bf48w6fPorbwr3SIHIRh/CjqRyBbcZ3HEb2mCMYHB74RQyH8qHH0/nWnsC
+ayMfs+80pNCWolWn7SpkLWfqSantqf5kLEs0QWyj1Tf+mXqLBGZLWLKXdY8T0HcD
+eacsCLVqZXpb1tB9rybuOO1ZbgZZIxD7K9x/4AT4tzAN7vrP2QXNX9F9pCIXSbz4
+DqUD12OgsfP3sA21J+ZLdpTHAKGSDU189kctEQRviE6lZ2+NJRA2UtJkeOqfzbMS
+cof5nIq40jii5Z0v5bPPQenYVUgpeotRC/kGQwIDAQABAoIBAQCXM1a97Yw45BEk
+PyMH1Eg2hTRbPe2UFdy+R/E1gVp6ExYO3jEzR6mqYaUnUNKQdY3DBPgYgJpUTTXq
+ML8qngTrxUxmHKeN5tS+rEiZ2ESnkd2b24fNPGe9my3pUmAn5i5e+T7b1xvpl/dS
+J44cmjhnUvH7iNzr5Sga5/aDdayM5RF8V5YRKajnPBL4C5+dL71SkbrgvIiQXvBD
+QPdfqTcBfMtPRHmecYgWU4f7NzIWdmymb6QjibDIOdJmTYx/WYIA+/3kSc460lKn
+7jlICR3yoqXhVqrVkl85ApPfpcOzCURyZAnyaBBmWS6UoA2YJLxmwJw2jK7lsgGs
+wFB44pyBAoGBAO2psaTFCDE7ei/oyneHLutjJEetDZKCmTeQTDbAeY/IarmZpI3j
+xx+HbdQCCX3vsgCyKYIUYstK4TZvgn+hnqAO8hJD5VimFuPZP6VbfIQQFe4fCV6q
+UyehLIq0z4EquuNMPx9f/wDbyKYlWy/If5pltdmluIA0wKdu1p91i0qzAoGBAMMi
+GseEV7h4tGUE3XVog3BT1wG9d4lOkkAiH3Rsu0kAPZPOQ/c9rQDMRP61HYHNmHQO
++QGighMGQQgSLrybG4cEe76w6umRVVuxRR4YPmrMGadDglwdvUsyhcPn37ctjW8P
+uZGVmulMJFmiqztAX6oARweRGywyuKILZaHWll4xAoGBAOTat1k28kCWSX7A59ZZ
+snCwM0th9OPUJr4+kWVXyrcOAtdef/qyEBjoTHYO0B0ZJ4tMBHRiNRAVtvC4SC2m
+8Byox6mW/85xgTE/De6pPindY4C9GkSyNwi+Ryn6GqcDApvF3DLWObQV8A0tg7Vr
+mJSQxKAk2Jl8MtgH/1vl0I/JAoGBAKN30KNP9QSu6cCu4K9YzLOvKLFmfdtjHRXp
+ABzScdrn/q3x3/IhkurWNP6Dz6JMD+k6GdQKxZkWZihSwyBxeBAV62yGXWUpwS49
+RT++45soPF1pKq3spAV436KMf1e/K3rc24JgqBPFG7aTlYrflMSf0uE96FuQaigm
+EBAR6oohAoGAaa2dLl110lWg6YpAv52zxtkberrhsQvBYQnBToTXQjXnxuCFkX0F
+kXhQdru3jl+yKKv8AmJlqdNkjdxqdFKp6z5MCPBPmTGciQbGxXVo9r6A5fjLfLZR
+5mjJe4OKRmaIke+sebYjzHn5q4i4EQiZbmOgJ1gykbC4RySSnos7vX4=
+-----END RSA PRIVATE KEY-----

+ 64 - 0
AnsjerPush/file/apns_pem/zccloud-dev.pem

@@ -0,0 +1,64 @@
+Bag Attributes
+    friendlyName: Apple Development IOS Push Services: com.ansjer.zccloud
+    localKeyID: 25 54 61 DA 6A 7C CD 2D 6A 1E 45 64 96 7A 4B 28 89 15 73 D4 
+subject=/UID=com.ansjer.zccloud/CN=Apple Development IOS Push Services: com.ansjer.zccloud/OU=772N5HXAR2/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFiTCCBHGgAwIBAgIIdGAeNPi1q4YwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNDI0MDYwODU4WhcNMjEwNDI0MDYwODU4WjCBiDEiMCAGCgmSJomT8ixk
+AQEMEmNvbS5hbnNqZXIuemNjbG91ZDFAMD4GA1UEAww3QXBwbGUgRGV2ZWxvcG1l
+bnQgSU9TIFB1c2ggU2VydmljZXM6IGNvbS5hbnNqZXIuemNjbG91ZDETMBEGA1UE
+CwwKNzcyTjVIWEFSMjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDQXWXsPbUk9HO3JqA6FbJU7iphv28RK2fEnyryzQPLyp/uGOiV
+uxP7xbOV/3vbEcqSoEvZx4xzMh3xb9LKPpSK1OPr9D+KwbNzh0H7C/n17YdsG6v3
+NWIVsfx6iVK6TUNQYCTvwntao6sij/9ctrLJItBOgwuvne5pHozXLAV49CGHW6Yq
+oAf9hmUCm9WJT/y0aZNlsD1nQUA8vfahBcnDKvEgew53UkAeDTYU9r/TWNWw4r44
+D5momf+vE+rDonUi3gwVrEFRGr+G66CYvGP5F20Xqwlxpw717Oz9gae1fyqkbgEx
+blUq+mv5Xh1yMCmDpeqowVgcyA4Oz1us0OnLAgMBAAGjggHlMIIB4TAJBgNVHRME
+AjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBDwYDVR0gBIIB
+BjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5j
+ZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2Vw
+dGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQg
+Y29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlm
+aWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEFBQcCARYdaHR0cDov
+L3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwTQYD
+VR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20vY2VydGlm
+aWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMB0GA1UdDgQWBBQlVGHaanzNLWoe
+RWSWeksoiRVz1DALBgNVHQ8EBAMCB4AwEAYKKoZIhvdjZAYDAQQCBQAwDQYJKoZI
+hvcNAQEFBQADggEBAMTfpZRXJpz8/1wkWYT2zSxGJcdmtB6oEUmVzjlYod5rDuTl
+yFSJPl/Sy41/anW88UgC231AF3h78q54vdFuQ7rd8ORF3nEDGzib4+7d4QCcguFP
+2Lz27r2goLQhKRIY/isjAUrLFHwcX9hjG9TuhMO+e8a6Vu5gXpXU6/QA1HTqO3ei
+kvfw/MoaAOQuRNofFPNCVO+nOndK61oTHovDcMfU1LG6siJGkVn3VhPAQOd7VbOQ
+eXqd1+LWkBMrJbsdJElKKKl0mQpXHUf4yW2k8Eg2AdZkMOSQKm8IaLXGHzrqg+fk
+sPUfBL2JEaGpAlD56fZYLPRMEdIV4SG8m6BOw4M=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA0F1l7D21JPRztyagOhWyVO4qYb9vEStnxJ8q8s0Dy8qf7hjo
+lbsT+8Wzlf972xHKkqBL2ceMczId8W/Syj6UitTj6/Q/isGzc4dB+wv59e2HbBur
+9zViFbH8eolSuk1DUGAk78J7WqOrIo//XLayySLQToMLr53uaR6M1ywFePQhh1um
+KqAH/YZlApvViU/8tGmTZbA9Z0FAPL32oQXJwyrxIHsOd1JAHg02FPa/01jVsOK+
+OA+ZqJn/rxPqw6J1It4MFaxBURq/huugmLxj+RdtF6sJcacO9ezs/YGntX8qpG4B
+MW5VKvpr+V4dcjApg6XqqMFYHMgODs9brNDpywIDAQABAoIBAF4aMFPXMp40AKaw
+vSA+eEG/JkjrCiA4+C5mrCDn0lURd9UyTqiAyu+kRAz0S12O1WwPFPKQGrBT7A4E
+MLDCPpfMufrwXlouTPtaFMRqjkMxOT3r6WhGJbwinCJDLqeRCVudcFEQBmHTFREQ
+84VNxcTtAiLTME3qB6hV8cmjyfzrg9HgVd/g0/7UmMBlyHeEj8yAbGuTmzndvtYS
+9ADVri+rVo4dVSYo0tlGNmSbz2j+F680ghu8EVqpV1LOOmo54UIWeCqeSY7KskKI
+kLyGZmchUF0AqZnY2/jTJ9DO/opaTFkp6PFwVH4enamWAHFDqKzpj5C8cFnnO55+
+SkdD0LECgYEA9VzVyyyNumOC3I4BHtZ9iGIp7mwrt7lPFDMdtA7MhClI4HCILWcs
+cVahCyphb9u0mpGxZfu2iz6IbiVAsoZL1IiS98xNxn1oarbmqmtHBfu0V0YfprgU
+C26PWO9/5DCwhTD22ZJlKkvy+Yf6NMWgoCkZ7nHbtMsQRgxiQQqoyccCgYEA2WXx
+Fz29tvgy5lw552rD3EIxlxET9EF+Vd3qJCCyVcN0lMzoSBRwHPwJCHNgtSOeV/bn
+KFEfLvslFNCu+AYVpDfm5tEbL+67Xop9XYJZeVe9SKfuy3IGGh+Qo0uDYwQrJwVt
+e+etqL+NuDkjeMs/Toiyc+8/KlkbodWJloL3f90CgYBva+4GasAdsUze3XmyyFKP
+WYyTIfokX3mCwceuWb4UEi9vBItCmwS6n01YpbFDu7DjNGAr4Me/qfEIm9AqggPz
+iuW6s8Np7M87Dr3nC/pfxDLcIxMCgbtQia99Ap3AQUAX6+ldWjaoo36+RDqiNlDA
+CoolFv1ft4s3VC5WPQHzRwKBgCo2DgLaPRzqwk9SNvgFFfSNcb2IVlyF3vbjONQ8
++QOICPnfc3aTaVVnZoWaiXuCZ/tieG2umqRvpTqObnrAC7GwHQhSL7o123G26DKg
+3aie7aTO3qHoCOFHobjjL6apJ4vTFPgK+wAZ+XV8urN4m1kvV58FJDmfxaq9oJJ1
+6YKNAoGAWULDkYaRAklSkM9lGV/OWcBgH/rYZycqwRKaLRdluKoWzYqnpcF5Vj8l
+ENI854Luqo7NpoHfAhyB3TH7CFocTL6BXMO0Naf5q7nmQMqQmcHuPBhyaVCiMXBy
+4aKyiWNMt4Rn54VDbgyYklCZkg3o0kV70yAdWm5blnxnbVpOe+g=
+-----END RSA PRIVATE KEY-----

+ 67 - 0
AnsjerPush/file/apns_pem/zccloud.pem

@@ -0,0 +1,67 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.zccloud
+    localKeyID: 25 54 61 DA 6A 7C CD 2D 6A 1E 45 64 96 7A 4B 28 89 15 73 D4 
+subject=/UID=com.ansjer.zccloud/CN=Apple Push Services: com.ansjer.zccloud/OU=772N5HXAR2/O=ZOSI Technology Co. , Ltd/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGJDCCBQygAwIBAgIIPgEkIypNo/cwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAwNDI0MDYxMzU0WhcNMjEwNTI0MDYxMzU0WjCBnDEiMCAGCgmSJomT8ixk
+AQEMEmNvbS5hbnNqZXIuemNjbG91ZDEwMC4GA1UEAwwnQXBwbGUgUHVzaCBTZXJ2
+aWNlczogY29tLmFuc2plci56Y2Nsb3VkMRMwEQYDVQQLDAo3NzJONUhYQVIyMSIw
+IAYDVQQKDBlaT1NJIFRlY2hub2xvZ3kgQ28uICwgTHRkMQswCQYDVQQGEwJVUzCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANBdZew9tST0c7cmoDoVslTu
+KmG/bxErZ8SfKvLNA8vKn+4Y6JW7E/vFs5X/e9sRypKgS9nHjHMyHfFv0so+lIrU
+4+v0P4rBs3OHQfsL+fXth2wbq/c1YhWx/HqJUrpNQ1BgJO/Ce1qjqyKP/1y2sski
+0E6DC6+d7mkejNcsBXj0IYdbpiqgB/2GZQKb1YlP/LRpk2WwPWdBQDy99qEFycMq
+8SB7DndSQB4NNhT2v9NY1bDivjgPmaiZ/68T6sOidSLeDBWsQVEav4broJi8Y/kX
+bRerCXGnDvXs7P2Bp7V/KqRuATFuVSr6a/leHXIwKYOl6qjBWBzIDg7PW6zQ6csC
+AwEAAaOCAmwwggJoMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL
+7Ou69kdZxVJUo7cwggEcBgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QFATCB/TCB
+wwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5
+IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGlj
+YWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRp
+ZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1l
+bnRzLjA1BggrBgEFBQcCARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNh
+dGVhdXRob3JpdHkwEwYDVR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkwJzAloCOg
+IYYfaHR0cDovL2NybC5hcHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4EFgQUJVRh
+2mp8zS1qHkVklnpLKIkVc9QwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3Y2QGAwEE
+AgUAMBAGCiqGSIb3Y2QGAwIEAgUAMH0GCiqGSIb3Y2QGAwYEbzBtDBJjb20uYW5z
+amVyLnpjY2xvdWQwBQwDYXBwDBdjb20uYW5zamVyLnpjY2xvdWQudm9pcDAGDAR2
+b2lwDB9jb20uYW5zamVyLnpjY2xvdWQuY29tcGxpY2F0aW9uMA4MDGNvbXBsaWNh
+dGlvbjANBgkqhkiG9w0BAQsFAAOCAQEAhbsEWfOwoRPz9wgTlWycYrqKbF/QblbA
+yB7rCsR+d1EM9JbpHl7SuS0GMHUbFr7XR5INiDG+a2IKcLlfLyasKvcycuUs/sW0
+NoCI1726d7F0TEtKN3tUx+rAMi4+dQeg40NfmB1Cg1HkpewT7v5LzuRv6+/sx3bH
+ZSfrKe5ervaYlww79mrTzcR3xzt6aXnHa7zVgi6DaCg6jA4Q/IC3XbTlJK/sk0aG
+taA1utdChItuuy2dLyQjnzqzvMAOLSjD0fDVImFAJ3eMqR5gT3jwNn/GLbws+2F7
+AYN72N8HuPWgfy+PteJWbD07Mf3ODjpRbw9tlltuCJ6nhbRkFI6fCA==
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA0F1l7D21JPRztyagOhWyVO4qYb9vEStnxJ8q8s0Dy8qf7hjo
+lbsT+8Wzlf972xHKkqBL2ceMczId8W/Syj6UitTj6/Q/isGzc4dB+wv59e2HbBur
+9zViFbH8eolSuk1DUGAk78J7WqOrIo//XLayySLQToMLr53uaR6M1ywFePQhh1um
+KqAH/YZlApvViU/8tGmTZbA9Z0FAPL32oQXJwyrxIHsOd1JAHg02FPa/01jVsOK+
+OA+ZqJn/rxPqw6J1It4MFaxBURq/huugmLxj+RdtF6sJcacO9ezs/YGntX8qpG4B
+MW5VKvpr+V4dcjApg6XqqMFYHMgODs9brNDpywIDAQABAoIBAF4aMFPXMp40AKaw
+vSA+eEG/JkjrCiA4+C5mrCDn0lURd9UyTqiAyu+kRAz0S12O1WwPFPKQGrBT7A4E
+MLDCPpfMufrwXlouTPtaFMRqjkMxOT3r6WhGJbwinCJDLqeRCVudcFEQBmHTFREQ
+84VNxcTtAiLTME3qB6hV8cmjyfzrg9HgVd/g0/7UmMBlyHeEj8yAbGuTmzndvtYS
+9ADVri+rVo4dVSYo0tlGNmSbz2j+F680ghu8EVqpV1LOOmo54UIWeCqeSY7KskKI
+kLyGZmchUF0AqZnY2/jTJ9DO/opaTFkp6PFwVH4enamWAHFDqKzpj5C8cFnnO55+
+SkdD0LECgYEA9VzVyyyNumOC3I4BHtZ9iGIp7mwrt7lPFDMdtA7MhClI4HCILWcs
+cVahCyphb9u0mpGxZfu2iz6IbiVAsoZL1IiS98xNxn1oarbmqmtHBfu0V0YfprgU
+C26PWO9/5DCwhTD22ZJlKkvy+Yf6NMWgoCkZ7nHbtMsQRgxiQQqoyccCgYEA2WXx
+Fz29tvgy5lw552rD3EIxlxET9EF+Vd3qJCCyVcN0lMzoSBRwHPwJCHNgtSOeV/bn
+KFEfLvslFNCu+AYVpDfm5tEbL+67Xop9XYJZeVe9SKfuy3IGGh+Qo0uDYwQrJwVt
+e+etqL+NuDkjeMs/Toiyc+8/KlkbodWJloL3f90CgYBva+4GasAdsUze3XmyyFKP
+WYyTIfokX3mCwceuWb4UEi9vBItCmwS6n01YpbFDu7DjNGAr4Me/qfEIm9AqggPz
+iuW6s8Np7M87Dr3nC/pfxDLcIxMCgbtQia99Ap3AQUAX6+ldWjaoo36+RDqiNlDA
+CoolFv1ft4s3VC5WPQHzRwKBgCo2DgLaPRzqwk9SNvgFFfSNcb2IVlyF3vbjONQ8
++QOICPnfc3aTaVVnZoWaiXuCZ/tieG2umqRvpTqObnrAC7GwHQhSL7o123G26DKg
+3aie7aTO3qHoCOFHobjjL6apJ4vTFPgK+wAZ+XV8urN4m1kvV58FJDmfxaq9oJJ1
+6YKNAoGAWULDkYaRAklSkM9lGV/OWcBgH/rYZycqwRKaLRdluKoWzYqnpcF5Vj8l
+ENI854Luqo7NpoHfAhyB3TH7CFocTL6BXMO0Naf5q7nmQMqQmcHuPBhyaVCiMXBy
+4aKyiWNMt4Rn54VDbgyYklCZkg3o0kV70yAdWm5blnxnbVpOe+g=
+-----END RSA PRIVATE KEY-----

+ 64 - 0
AnsjerPush/file/apns_pem/zosidcloud-dev.pem

@@ -0,0 +1,64 @@
+Bag Attributes
+    friendlyName: Apple Development IOS Push Services: com.ansjer.zosidcloud
+    localKeyID: 53 D0 A7 6B CF E9 9C 46 ED 44 01 01 01 14 02 52 A6 45 AB C5 
+subject=/UID=com.ansjer.zosidcloud/CN=Apple Development IOS Push Services: com.ansjer.zosidcloud/OU=66PK439G56/C=CN
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIFjzCCBHegAwIBAgIIAV1gZ5yODtEwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMTkxMTA4MDgyNTAzWhcNMjAxMTA3MDgyNTAzWjCBjjElMCMGCgmSJomT8ixk
+AQEMFWNvbS5hbnNqZXIuem9zaWRjbG91ZDFDMEEGA1UEAww6QXBwbGUgRGV2ZWxv
+cG1lbnQgSU9TIFB1c2ggU2VydmljZXM6IGNvbS5hbnNqZXIuem9zaWRjbG91ZDET
+MBEGA1UECwwKNjZQSzQzOUc1NjELMAkGA1UEBhMCQ04wggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDTiNE32dceZMEsO/0na/qDcYD3icWauyoYm/SL0OWV
+tIahF4J5vXcg46QHNhQ4y/xczFLQr4iZsUKyElt3c3MLZCs2P/75UOfGYZTVRpug
+jo83iSpz9pHZl4qc7fZMDyurXzcYazRalFzB/PPbKBS+PVtAMpCv4a1Cg/vtHCMF
+3neJUQMVyTkXjyNuxK4UU/Pgeck4y52TmvuJMMEtkbLRoqCh046VSg2cGLmjNO9j
++ECvn3rRb/XZdOPbDPtaCe2dLE+cL1ldJdkyxk5KJmvhkfiQlORhgJrS8r63KDZS
+i8PCRqkgbLWW6dXUBQOwQCLshLficY5M4LuLPthUTr51AgMBAAGjggHlMIIB4TAJ
+BgNVHRMEAjAAMB8GA1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBDwYD
+VR0gBIIBBjCCAQIwgf8GCSqGSIb3Y2QFATCB8TCBwwYIKwYBBQUHAgIwgbYMgbNS
+ZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz
+IGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJt
+cyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQg
+Y2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjApBggrBgEFBQcCARYd
+aHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wEwYDVR0lBAwwCgYIKwYBBQUH
+AwIwTQYDVR0fBEYwRDBCoECgPoY8aHR0cDovL2RldmVsb3Blci5hcHBsZS5jb20v
+Y2VydGlmaWNhdGlvbmF1dGhvcml0eS93d2RyY2EuY3JsMB0GA1UdDgQWBBRT0Kdr
+z+mcRu1EAQEBFAJSpkWrxTALBgNVHQ8EBAMCB4AwEAYKKoZIhvdjZAYDAQQCBQAw
+DQYJKoZIhvcNAQEFBQADggEBABYBkephyibIPt7nur9eY0bmqEuPQKNg94ZteOFm
+Fo/8Er9tFYndAqXm7Zr7dhdA9dcL9VYrt+qGAPpOLmfnTPdDXBbB59GXWSrBMio6
+5VU9T3bb/4kVzA54SLbMZAjhgEQDULfYK5KSvDgdJQBJLcQu1OMyNntwrF8Xebmt
+AbpyJcv/HOaaQ2gYC+pQ9ye6eGYEZ9BT0BRp61cXnyiii1sUkOCnaamDWMVKIa2W
+8Ii0k6UIXtS+RIOI9mbN1xKDwIAtjZvldd2AS7802R4Ls1Lklf8tcOlt9VcAN0Xs
+EUf4zCneaB6x/CyjXaqCEmE7mmETNs6UcHNqMv3jJRi1wMI=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA04jRN9nXHmTBLDv9J2v6g3GA94nFmrsqGJv0i9DllbSGoReC
+eb13IOOkBzYUOMv8XMxS0K+ImbFCshJbd3NzC2QrNj/++VDnxmGU1UaboI6PN4kq
+c/aR2ZeKnO32TA8rq183GGs0WpRcwfzz2ygUvj1bQDKQr+GtQoP77RwjBd53iVED
+Fck5F48jbsSuFFPz4HnJOMudk5r7iTDBLZGy0aKgodOOlUoNnBi5ozTvY/hAr596
+0W/12XTj2wz7WgntnSxPnC9ZXSXZMsZOSiZr4ZH4kJTkYYCa0vK+tyg2UovDwkap
+IGy1lunV1AUDsEAi7IS34nGOTOC7iz7YVE6+dQIDAQABAoIBAATziOIjyTM+7H6a
+kNxhXKXC/F/O678pblzRGbJEG3ublCMlJ7qtwqIUcfxxOZvyul95sN6NnUbrrUH5
+yAZNv32exJwqd09io2ozUoaDdpUVgSitnz7DoLpVH7t4nnE9UapbOjDqIXmVS26G
++zZAA+xIB2DDTjuvrpyF9lmgdPloJsA3Q+X3nFfNgbt8Z8xGF2xs8a3R/83k8y+F
+UueDV56v/ivIebPULSOsktEQXtKDgYLNIuMXDztJSflTr0M+Uq9qPIs6fQl55ssk
+o7G55vAzlTBlwSWiOSubecAmiyFeWY3E16uVAYcTKXYEjSpFTqAyNjBTElsgSiuh
+kSODACkCgYEA898WP5Edd9tUgJCr0OQcxdCmXhTZI2fcAqEcjfCyxKHRXAzDwrKN
+Dpx5Es+Ymn2bRGoLCYpeXpxHZTnuRakrO8zM34PbCWWul14oAKzB5vn7TRuNJXOE
+HeOeztF8FEao3YAQlmDR8SqgG0+Z0fkU0/668r0nxz87NQhVmryF8csCgYEA3g4G
+AmULfpRebUyNIn+rtyd2R6IqR3S3HYrfZNRcPPN4V6T5hRN9Fbg+xu3HvY4xl2ww
+XTa5Fq48+K+Hahq+VLyG1FCQWKaJkcKwhUsE811SWVqBUDiFiE+t0+xMdiuuWA2+
+WBnxXJuRZjX7Lz3uvyhkPYvg3GGl+gIe2UhTCL8CgYBO/BAOoodyzFzADxs22sre
+3scjbYkuSHMffuLqreyYdviB4bh9lKrO8qrcBtScJNZnABl3C5Oo/OC6zlp3wWS5
+R4mLUv9cOcykReCa51kd4BUeUjAS0ykVLSXAQmT3hDMpGlNFATK981pLvipJsWyn
+hLcz60O46qfYc0yWgzy2pQKBgDZbgncVmzG6Sj9BTQ9T54onW9n/RZBGbwkJVNRO
+8+FrzFrQJNDBy44Ek0GJNaseYt9peZFn8vktX/mUpnZ/43XEzNCuhBdfdS+8VA/d
+JHIatyzubddgbrfGoYvOPY1z9fLSMnVIrH/6CSbXMg0HgSlETv2N/1aThNjlqf2Q
+qdrrAoGBAO7cE80xgQaZxu3x9ZtM4PeiE2thVjdZEGAyQlRyS6BO0iGMGWjpeUZq
+2ZJvm8AurRv7Wtmjf9EsoIJKbnGI3wBH0Sbs8LZ8ksI4Dtcx8UPeqzeUwfM/VQdH
+qARGkCRJD/nBbZIy+sOfBysnYVr099kEoGFW8XA0IoGveVkGv8p9
+-----END RSA PRIVATE KEY-----

+ 67 - 0
AnsjerPush/file/apns_pem/zosidcloud.pem

@@ -0,0 +1,67 @@
+Bag Attributes
+    friendlyName: Apple Push Services: com.ansjer.zosidcloud
+    localKeyID: 33 18 1A 4E BA FA 07 72 A0 10 4B CC 60 BB 62 FC AC 17 2D 8C 
+subject=/UID=com.ansjer.zosidcloud/CN=Apple Push Services: com.ansjer.zosidcloud/OU=66PK439G56/O=side zhang/C=US
+issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGJTCCBQ2gAwIBAgIIfxSDo6eTJZ0wDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
+ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
+aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMjAxMTA2MDkzNTM5WhcNMjExMjA2MDkzNTM5WjCBkzElMCMGCgmSJomT8ixk
+AQEMFWNvbS5hbnNqZXIuem9zaWRjbG91ZDEzMDEGA1UEAwwqQXBwbGUgUHVzaCBT
+ZXJ2aWNlczogY29tLmFuc2plci56b3NpZGNsb3VkMRMwEQYDVQQLDAo2NlBLNDM5
+RzU2MRMwEQYDVQQKDApzaWRlIHpoYW5nMQswCQYDVQQGEwJVUzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAOjjf6erffZGSjSiWPmHPXSogAmXB2ZbKpJS
+I74sC8md81eTEBq1LbOUWP7DyNEJio3OIMORV2I4p+KgGsqG3U/Kk1AF24UqTBDZ
+xE9GTyNQ57dB0tPMy5U9yW+9gQ8CRkLYW+tMHu5INjyzALohmu44xANQHQrpQyeB
+CyTjgcFsY/Prq+TKl+uYDfhBakktk3jNBYCah805hwckmhVELYnOrXhDLe+up9/A
+6FP8yXOhAva9FMRtcNyguG4yxfRev/07UxwO9go9JSMeCnSx/vbQkXq/WzWw65Me
+2fBFT72KAMJkV1nTo0x0zzZiu7Z+cJUvyH59Oq6oEshRvGT4jX0CAwEAAaOCAnYw
+ggJyMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJU
+o7cwggEcBgNVHSAEggETMIIBDzCCAQsGCSqGSIb3Y2QFATCB/TCBwwYIKwYBBQUH
+AgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0
+eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFu
+ZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBv
+bGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA1Bggr
+BgEFBQcCARYpaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3Jp
+dHkwEwYDVR0lBAwwCgYIKwYBBQUHAwIwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDov
+L2NybC5hcHBsZS5jb20vd3dkcmNhLmNybDAdBgNVHQ4EFgQUMxgaTrr6B3KgEEvM
+YLti/KwXLYwwDgYDVR0PAQH/BAQDAgeAMBAGCiqGSIb3Y2QGAwEEAgUAMBAGCiqG
+SIb3Y2QGAwIEAgUAMIGGBgoqhkiG92NkBgMGBHgwdgwVY29tLmFuc2plci56b3Np
+ZGNsb3VkMAUMA2FwcAwaY29tLmFuc2plci56b3NpZGNsb3VkLnZvaXAwBgwEdm9p
+cAwiY29tLmFuc2plci56b3NpZGNsb3VkLmNvbXBsaWNhdGlvbjAODAxjb21wbGlj
+YXRpb24wDQYJKoZIhvcNAQELBQADggEBAGfYWIUYoC7eHgJCYGzTBipU6pJH5m9h
+kXK7N7QmhX9RTCIkqfKVbhasRHf5JiXDPrHPAwS0saGknFHhUov1KIfE2lZzFY6C
+WakMqn5GgwrU7X0irBJN4pPmH6xIi0aTo2QHfywgLEFnwViV5v1m4+0MN/TZlmwx
+PuIaSFyiQKQE7qO7vrXroPcxgxR2I2b892CEoijhn3ncOaN8QYHB6Ky3JsLPZpTI
+AaXHauyw2yAcoR8cuaoZmWY/y8Jj7DKrUwUc4g0QQ15IX6FCF/GQnOuACZ5pYpEa
+0tUXMoq8hZidu2WkCR0rNe+d6tfJ74UI9V59VSY1OOv5m962dO6M+IA=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA6ON/p6t99kZKNKJY+Yc9dKiACZcHZlsqklIjviwLyZ3zV5MQ
+GrUts5RY/sPI0QmKjc4gw5FXYjin4qAayobdT8qTUAXbhSpMENnET0ZPI1Dnt0HS
+08zLlT3Jb72BDwJGQthb60we7kg2PLMAuiGa7jjEA1AdCulDJ4ELJOOBwWxj8+ur
+5MqX65gN+EFqSS2TeM0FgJqHzTmHBySaFUQtic6teEMt766n38DoU/zJc6EC9r0U
+xG1w3KC4bjLF9F6//TtTHA72Cj0lIx4KdLH+9tCRer9bNbDrkx7Z8EVPvYoAwmRX
+WdOjTHTPNmK7tn5wlS/Ifn06rqgSyFG8ZPiNfQIDAQABAoIBAQCnkUGBsCgC8pJk
+CshASqnSbYcq30uIv2IDRCzJocGdgRPdWEiHHwPSKZRbQx4UImxaGg2DCem5F5Dc
+xFxiiuVkixqxwnUv/L4chcP1Ul6Qx57iQe9jTADT0lBFITJiTT2mzSxeQjgjNTwp
+Mpb8Vj+21ota4uLyveH+45/UHAp505bYZpiKmKdVfswop/dEeTqcZKdnD0wvmF6Z
+h/JY2RpbocvYyffDpsgpc8iibxqlpRo6jKRbnsMZ9t6QnvUtqvggBLL41x1nz3AY
+qUrtRYY3G5jFnIIxqLlioc2CQ4ePp/Zx81E9acW27CG9XxYNeaB60m4YHVuYTvC+
+cp3Vt+AVAoGBAP/mMBZCv/ZvqeRWsGbQYWejioWENUD8s4DnvEea0UFTJ8uZKU2d
+j+SlMOemDjPC7+LX4V54m/HeCxsX9s9mQMZ0GXUFcqq2xAnTGJ/u/BeuDZ8/6Bbn
+iVIPLLPlAKE50P28v3u9elA4yAEVXUtaR8Kx5RmjItbtO/kmC8TXioynAoGBAOj6
+/WIVoJU8Vd4QUMCFLZ06h3QdTUTHf2evol5ikJGfbzCkR6e7YNP1mXQzLMyuUiGV
+Mr5NaIpiw6CK7KGxSkf6XSl+LWFg8WrKjKcArwRXevRDufqbZGRt/Yml1DkPJoG/
+21BtCpgGjd+FPWGn2zPTzbXJXr7HRCuVzRipWiU7AoGBAPv/mLczQIrUZ7+extRY
+vLMsz8qv6Sl4MOTuT/FLumnUE8php9TxJrSJIGHemvnKLCV9Zkh8CUXj3Zfuv3R6
+Q6uqxS8CEGug0+jA4u0MkWBYXL3IVyvHEummW+VDzJz4rKBS8VZPv1WktQCsI1WD
+MEvAl9XpWvPAZ2DxYIGVqLDdAoGAEeRcmzDAAVX93izwlqhjpWR+TIuHiJWtkh/p
+30fAhH5zApBLNHVubTi6TTM06I8mnY+gsv6s3ZvSHD0vCQfdhYOTrgETqRvRznq5
+rIWf6zxwo9ujp7xRTGkqBG0ozCz+rw414IpPOved46GO4jYK3oMQlDpfvVT+Rj6d
+3pv64UsCgYEAjPfepGL/4D7a+t11gfWglUDl6qJfKLaRTkWWp1OrUKn4+kpLQPFg
+D5fA657UQfOfLFynZn+IuxBCzQBDWzERXub1S+9Wpa1WdW3ITdjYZt3gnZi8U0n8
+ZI4dG9obBh8djfj22ZOTPRKkjIiSqXkwEcaR8wlQDFv8tlfZDe78e2g=
+-----END RSA PRIVATE KEY-----

二進制
AnsjerPush/file/font/simhei.ttf


+ 184 - 0
AnsjerPush/file/log type .txt

@@ -0,0 +1,184 @@
+typedef enum _log_type
+{
+	//系统控制(Control)
+	LOG_TYPE_SYSTEM_CTRL		= 0x00010000,
+	LOG_TYPE_BOOT,							//系统开机
+	LOG_TYPE_SHUTDOWN,						//系统关机
+	LOG_TYPE_REBOOT,						//系统重启
+	LOG_TYPE_FORMAT_SUCC,					//格式化磁盘成功
+	LOG_TYPE_FORMAT_FAIL,					//格式化磁盘失败
+	LOG_TYPE_UPGRADE_SUCC,					//升级成功
+	LOG_TYPE_UPGRADE_FAIL,					//升级失败
+	LOG_TYPE_CLEAR_ALARM,					//清除报警
+	LOG_TYPE_OPEN_ALARM,					//开启报警
+	LOG_TYPE_MANUAL_START,					//开启手动录像
+	LOG_TYPE_MANUAL_STOP,					//停止手动录像
+	LOG_TYPE_PTZ_ENTER,						//开始云台控制
+	LOG_TYPE_PTZ_CTRL,						//云台操作
+	LOG_TYPE_PTZ_EXIT,						//结束云台控制
+	LOG_TYPE_AUDIO_CH_CHANGE,				//改变现场音频通道
+	LOG_TYPE_VOLUME_ADJUST,					//调节音量
+	LOG_TYPE_MUTE_ENABLE,					//开启静音
+	LOG_TYPE_MUTE_DISENABLE,				//关闭静音
+	LOG_TYPE_DWELL_ENABLE,					//开启轮循
+	LOG_TYPE_DWELL_DISENABLE,				//关闭轮循
+	LOG_TYPE_LOG_IN,						//登录
+	LOG_TYPE_LOG_OFF,						//登出
+	LOG_TYPE_CHANGE_TIME,					//修改系统时间
+	LOG_TYPE_MANUAL_SNAP_SUCC,               //手动抓图成功
+	LOG_TYPE_MANUAL_SNAP_FAIL,               //手动抓图失败
+
+	//系统配置(Setup)
+	LOG_TYPE_CONFIG			= 0x00020000,
+	LOG_TYPE_CHGE_VIDEO_FORMAT,				//改变视频制式
+	LOG_TYPE_CHGE_VGA_RESOLUTION,			//改变显示器分辨率
+	LOG_TYPE_CHGE_LANGUAGE,					//调整语言
+	LOG_TYPE_CHGE_NET_USER_NUM,				//调整网络用户数目
+	LOG_TYPE_CHGE_TIME_ZONE,				//调整时区
+	LOG_TYPE_NTP_MANUAL,					//手动网络校时
+	LOG_TYPE_NTP_ON,						//开启自动网络校时
+	LOG_TYPE_NTP_OFF,						//关闭自动网络校时
+	LOG_TYPE_CHGE_NTP_SERVER,				//修改网络时间服务器地址
+	LOG_TYPE_CHGE_DST,						//调整夏令时设置
+	LOG_TYPE_PASSWD_ON,						//开启操作密码
+	LOG_TYPE_PASSWD_OFF,					//关闭操作密码
+
+	LOG_TYPE_CHGE_CAM_NAME,					//调整通道名称
+	LOG_TYPE_MODIFY_COLOR,					//调整图像色彩
+	LOG_TYPE_CHGE_HOST_MONITOR,				//调整主监视器画面设置
+	LOG_TYPE_CHGE_SPOT,						//调整辅助输出画面设置
+	LOG_TYPE_CHGE_OSD,						//调整字符叠加设置
+
+	LOG_TYPE_CHGE_LOCAL_ENCODE,				//调整录像流编码参数
+	LOG_TYPE_CHGE_REC_VIDEO_SWITCH,			//调整录像开关设置
+	LOG_TYPE_CHGE_REC_AUDIO_SWITCH,			//调整录制音频开关设置
+	LOG_TYPE_CHGE_REC_REDU_SWITCH,			//调整冗余录像开关设置
+	LOG_TYPE_CHGE_REC_PRE_TIME,				//调整景前录像时间
+	LOG_TYPE_CHGE_REC_POST_TIME,			//调整景后录像时间
+	LOG_TYPE_CHGE_REC_HOLD_TIME,			//调整录像数据过期时间
+
+	LOG_TYPE_CHGE_SCH_SCHEDULE,				//调整定时录像计划
+	LOG_TYPE_CHGE_SCH_MOTION,				//调整移动侦测录像计划
+	LOG_TYPE_CHGE_SCH_ALARM,				//调整报警录像计划
+
+	LOG_TYPE_CHGE_SENSOR_SWITCH,			//调整报警输入开关设置
+	LOG_TYPE_CHGE_SENSOR_TYPE,				//调整报警输入设备类型
+	LOG_TYPE_CHGE_SENSOR_TRIGGER,			//调整报警输入处理方式设置
+	LOG_TYPE_CHGE_SENSOR_SCH,				//调整报警输入侦测计划
+
+	LOG_TYPE_CHGE_MOTION_SWITCH,			//调整移动侦测开关设置
+	LOG_TYPE_CHGE_MOTION_SENS,				//调整移动侦测灵敏度
+	LOG_TYPE_CHGE_MOTION_AREA,				//调整移动侦测区域设置
+	LOG_TYPE_CHGE_MOTION_TRIGGER,			//调整移动侦测处理方式
+	LOG_TYPE_CHGE_MOTION_SCH,				//调整移动侦测计划
+
+	LOG_TYPE_CHGE_VL_TRIGGER,				//调整视频丢失处理方式设置
+
+	LOG_TYPE_CHGE_RELAY_SWITCH,				//调整报警输出开关设置
+	LOG_TYPE_CHGE_RELAY_SCH,				//调整报警输出计划
+
+	LOG_TYPE_BUZZER_ON,						//开启声音报警设备
+	LOG_TYPE_BUZZER_OFF,					//关闭声音报警设备
+	LOG_TYPE_CHGE_BUZZER_SCH,				//调整声音报警计划
+
+	LOG_TYPE_CHGE_HTTP_PORT,				//修改HTTP服务器端口
+	LOG_TYPE_CHGE_SER_PORT,					//修改网络服务器端口
+	LOG_TYPE_CHGE_IP,						//设置网络地址
+	LOG_TYPE_DHCP_SUCC,						//自动获取网络地址成功
+	LOG_TYPE_DHCP_FAIL,						//自动获取网络地址失败
+	LOG_TYPE_CHGE_PPPOE,					//设置PPPoE
+	LOG_TYPE_CHGE_DDNS,						//设置DDNS
+	LOG_TYPE_NET_STREAM_CFG,				//调整网络流编码设置
+
+	LOG_TYPE_CHGE_SERIAL,					//调整云台串口设置
+	LOG_TYPE_PRESET_MODIFY,					//调整预置点
+	LOG_TYPE_CRUISE_MODIFY,					//调整巡航线
+	LOG_TYPE_TRACK_MODIFY,					//调整轨迹
+
+	LOG_TYPE_USER_ADD,						//增加用户
+	LOG_TYPE_USER_MODIFY,					//调整用户权限
+	LOG_TYPE_USER_DELETE,					//删除用户
+	LOG_TYPE_CHANGE_PASSWD,					//修改用户密码
+
+	LOG_TYPE_LOAD_DEFAULT,					//恢复默认配置
+	LOG_TYPE_IMPORT_CONFIG,					//导入配置
+	LOG_TYPE_EXPORT_CONFIG,					//导出配置
+
+	LOG_TYPE_CHGE_IMAGE_MASK,				//图像遮挡
+	LOG_TYPE_RECYCLE_REC_ON,				//开启循环录像
+	LOG_TYPE_RECYCLE_REC_OFF,				//关闭循环录像
+	LOG_TYPE_CHGE_DISK_ALARM,				//调整磁盘报警空间
+
+	LOG_TYPE_CHGE_SEND_EMAIL,				//设置Email 发送人信息
+	LOG_TYPE_CHGE_RECV_EMAIL,				//设置Email 接收人信息
+	LOG_TYPE_CHGE_SNAP_SETTING,             //调整抓图配置
+
+	//yqf add 2017.12.22
+	LOG_TYPE_CHGE_SCH_PIR,					//调整人体红外感应录像计划
+	LOG_TYPE_CHGE_PIR_SWITCH,				//调整人体红外感应开关设置
+	LOG_TYPE_CHGE_PIR_TRIGGER,				//调整人体红外感应处理方式
+	LOG_TYPE_CHGE_PIR_SCH,					//调整人体红外感应计划
+
+	//录像回放(Playback)
+	LOG_TYPE_PLAYBACK		= 0x00040000,
+	LOG_TYPE_PLAYBACK_PLAY,					//播放
+	LOG_TYPE_PLAYBACK_PAUSE,				//暂停
+	LOG_TYPE_PLAYBACK_RESUME,				//恢复播放
+	LOG_TYPE_PLAYBACK_FF,					//快进
+	LOG_TYPE_PLAYBACK_REW,					//快退
+	LOG_TYPE_PLAYBACK_STOP,					//停止
+	LOG_TYPE_PLAYBACK_NEXT_SECTION,			//下一段
+	LOG_TYPE_PLAYBACK_PREV_SECTION,			//上一段
+
+	//数据备份(Backup)
+	LOG_TYPE_BACKUP			= 0x00080000,
+	LOG_TYPE_BACKUP_START,					//开始备份
+	LOG_TYPE_BACKUP_COMPLETE,				//备份完成
+	LOG_TYPE_BACKUP_CANCEL,					//放弃备份
+	LOG_TYPE_BACKUP_FAIL,					//备份失败
+
+	//录像检索(Search)
+	LOG_TYPE_SEARCH			= 0x00100000,
+	LOG_TYPE_SEARCH_TIME,					//按时间检索
+	LOG_TYPE_SEARCH_EVENT,					//按事件检索
+	LOG_TYPE_SEARCH_FILE_MAN,				//文件管理
+	LOG_TYPE_DELETE_FILE,					//删除文件
+	LOG_TYPE_LOCK_FILE,						//锁定文件
+	LOG_TYPE_UNLOCK_FILE,					//解锁文件
+	LOG_TYPE_DELETE_PICTURE,                //删除图片
+	LOG_TYPE_LOCK_PICTURE,                  //锁定图片
+	LOG_TYPE_UNLOCK_PICTURE,                //解锁图片
+
+
+	//查看信息(View information)
+	LOG_TYPE_VIEW_INFO		= 0x00200000,
+	LOG_TYPE_VIEW_SYSTEM,					//查看系统信息
+	LOG_TYPE_VIEW_EVENT,					//查看事件
+	LOG_TYPE_VIEW_LOG,						//查看日志
+	LOG_TYPE_VIEW_NETWORK,					//查看网络状态
+	LOG_TYPE_VIEW_ONLINE_USER,				//查看在线用户
+	LOG_TYPE_VIEW_EXPORT_LOG,				//导出日志
+	LOG_TYPE_VIEW_EXPORT_EVENT,				//导出事件
+
+	//异常信息(Error)
+	LOG_TYPE_ERROR_INFO		= 0x00400000,
+	LOG_TYPE_IP_CONFLICT,					//网络地址冲突
+	LOG_TYPE_NETWORK_ERR,					//网络异常
+	LOG_TYPE_DDNS_ERR,						//DDNS错误
+	LOG_TYPE_DISK_IO_ERR,					//磁盘读写错误
+	LOG_TYPE_UNKNOWN_OFF,					//异常断电
+	LOG_TYPE_UNKNOWN_ERR,					//未知错误
+	LOG_TYPE_ERR_9A9A9A9A,
+	LOG_TYPE_ERR_9A000001,
+	LOG_TYPE_ERR_9A000002,
+	LOG_TYE_DISK_WARNING,                  //磁盘衰减报警
+	LOG_TYE_DISK_DISCONNECT,                  //磁盘掉线报警
+	//LOG_TYE_DISK_NOTFIND,					//开机无硬盘
+
+    LOG_TYPE_EVENT            = 0x00800000,   //事件信息
+    LOG_TYPE_EVENT_MOTION     = 0x00800001,
+    LOG_TYPE_EVENT_SENSOR     = 0x00800002,
+    LOG_TYPE_EVENT_VIDEO_LOSS = 0x00800004,
+    LOG_TYPE_EVENT_COVER      = 0x00800008,
+    LOG_TYPE_EVENT_PIR		  = 0x00800010,   //yqf add 2017.12.26
+}LOG_TYPE;

+ 69 - 0
AnsjerPush/file/后台管理系统说明.txt

@@ -0,0 +1,69 @@
+一.修改记录
+创建时间: 2017/10/25 版本: 1.0
+
+二.项目说明
+	1.架构概述
+		1).现有模块 :  用户模块(包括用户设备共享模块)  设备信息模块 OTA模块 Token验证模块
+		2).整体说明 :                                  OTA模块
+		                                                 ↗ 
+														 
+			用户注册 -> 用户登录  -> Token验证模块 
+
+				 ↓		          ↓                     ↘
+			 用户模块       用户模块             设备信息模块
+		3).各个模块明细说明
+			1. 用户模块: 
+				用户信息处理模块:用户基本操作
+					用户注册:包括获取验证码,然后通过验证注册用户
+					用户登录:返回Token信息给后续验证用户安全性
+					用户信息完善:修改完善用户个人信息
+					用户修改密码:修改用户密码
+					用户重置密码:重置密码(使用邮箱进行重置)
+					显示用户信息:显示本用户当前信息
+					显示所有用户信息:只有管理员与超级管理员可以调用,显示所有的用户信息
+					用户启用、禁用:只有管理员与超级管理员可以调用,启用、禁用设备用户
+				
+				用户设备共享模块:共享用户设备
+					搜索用户:通过用户的信息搜索用户自身另外的账号
+					共享用户设备:主用户把设备共享给搜索到用户(可以共享一台设备,也可以全部等设备)
+					取消用户设备:主用户取消之前共享给其他账号的设备(可以取消共享一台设备,也可以全部等设备)
+					
+			2.Token验证模块:
+				token获取:在登录时调用获取Token
+				token验证:解密access token,验证有效期,如果有效可以继续访问接口,无效返回错误信息
+				token更新:解密refresh token,验证有效性,如果有效并且没有过期就更新access token,然后返回access token,或者返回错误信息
+				token错误信息:通过上面三个接口返回的错误码,解析出来token错误信息,并返回提示用户
+				
+			3.设备信息模块
+				添加设备信息:添加新设备信息
+				查询设备信息:查询本账户名下所有设备信息
+				修改设备信息:修改本账户名下的相应设备信息
+				删除设备信息:删除本账户名下相应设备信息
+				显示所有用户设备信息:只有管理员与超级管理员可以调用,显示所有的用户名下所有设备信息
+			
+			4.OTA模块
+				升级文件上传:上传升级文件(根据文件名称保存到相应的文件夹下)
+				添加版本信息:把上传的升级文件信息更新到数据库表里面
+				获取最新版本信息:通过输入要查询的设备信息,返回查询到该设备最新版本
+				获取最新版本url链接:通过输入要查询的设备信息,返回查询到该设备升级文件的下载链接
+				下载升级文件:通过获取的url下载升级文件
+
+			5.批量处理模块
+				设备信息批量导入:2种内容格式txt格式, 2种调用Excel文件导入方式
+				
+			6.权限模块
+				模型设计:用户、角色、权限,用户 → 多对多 ← 角色, 用户 → 多对多 ← 权限,角色 → 多对多 ← 权限
+			7.日志模块
+			    中间件middle入口进行统计入库,利用nginx自带module进行统计(更精确)
+			8.APP版本信息模块
+			    对应app版本更新,检测版本详细信息
+			9.语言包模块
+			    所有响应信息增加中英适配
+			10.
+
+
+django常用命令
+python3 mangage.py makemigrations
+python3 manage.py migrate --fake
+python3 manage.py sqlmigrate
+python3 manage.py runserver 0.0.0.0:8222

二進制
AnsjerPush/file/表文档.doc


+ 161 - 0
AnsjerPush/formal_config.py

@@ -0,0 +1,161 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: Ansjer
+@software: PyCharm
+@DATE: 2018/7/2 14:06
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: Conf.py
+@Contact: chanjunkai@163.com
+"""
+# 主要静态变量配置文件
+import datetime, os
+
+
+DEBUG_MODE = 'DEV'
+# MODE = 'PRO'
+# 阿里云发邮箱
+ALY_SES_ACCESS_NAME = 'message@dvema.com'
+ALY_SES_ACCESS_PAW = 'SMtp123456'
+ALY_SES_ACCESS_REPLYTO = '***'
+
+# 发送邮件邮箱
+SES_COMPANY_EMAIL = 'user_server@nsst.com'
+AWS_SES_ACCESS_ID = 'AKIAJKPU23EU5QWHFPKQ'
+AWS_SES_ACCESS_SECRET = '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'
+AWS_SES_ACCESS_REGION = 'us-east-1'
+AWS_BUCKET = 'ansjertest'
+# 设定离线时间为5分钟
+OFF_LINE_TIME_DELTA = 5
+
+# token的secret
+OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
+OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
+# access_token超时
+# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
+OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
+# refresh_token超时
+OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
+# 腾讯验证,短信发送
+TX_PHONE_APP_ID = '1400052907'
+TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
+
+# 验证码超时时间
+AuthCode_Expire = 600
+
+# 根路径
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# uid token key
+UID_TOKEN_KEY = 'c+565*j@%^'
+
+# oss param
+OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
+OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
+OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
+
+SERVER_TYPE = 'Ansjer.formal_settings'
+
+NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
+SERVER_DOMAIN = 'http://www.dvema.com/'
+SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
+DOMAIN_HOST = 'www.dvema.com'
+SERVER_HOST = 'localhost'
+
+PAYPAL_CRD = {
+    "mode": "live",  # sandbox or live
+    "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
+    "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
+}
+DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+
+
+JPUSH_CONFIG = {
+    'com.ansjer.accloud_ab': {
+        'Key': 'f0dc047e5e53fd14199de5b0',
+        'Secret': 'aa7f7db33e9f0a7f3871aa1c'},
+    'com.ansjer.adcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_ab': {
+        'Key': 'd9924f56d3cc7c6017965130',
+        'Secret': '869d832d126a232f158b5987'},
+    'com.ansjer.loocamccloud_ab': {
+        'Key': 'd1cc44797b4642b0e05304fe',
+        'Secret': 'c3e8b4ca8c576de61401e56a'},
+    'com.ansjer.loocamdcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_a': {
+        'Key': '57de2a80d68bf270fd6bdf5a',
+        'Secret': '3d354eb6a0b49c2610decf42'},
+    'com.ansjer.accloud_a': {
+        'Key': 'ff95ee685f49c0dc4013347b',
+        'Secret': 'de2c20959f5516fdeeafe78e'},
+    'com.ansjer.adcloud_a': {
+        'Key': '2e47eb1aee9b164460df3668',
+        'Secret': 'b9137d8d684bc248f1809b6d'},
+    'com.ansjer.loocamccloud_a': {
+        'Key': '23c9213215c7ca0ec945629b',
+        'Secret': '81e4b1e859cc8387e2e6c431'},
+    'com.ansjer.loocamdcloud_a': {
+        'Key': '1dbdd60a16e9892d6f68a073',
+        'Secret': '80a97690e7e043109059b403'},
+    'com.ansjer.customizedb_a': {
+        'Key': '9d79630aa49adfa291fe2568',
+        'Secret': '4d8ff52f88136561875a0212'},
+    'com.ansjer.customizedd_a': {
+        'Key': '8fc4f495685bde53341ee25d',
+        'Secret': 'f1da11fa466509fa2670fb66',
+    }
+}
+# type =1
+FCM_CONFIG = {
+    'com.ansjer.zccloud_a': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+    'com.ansjer.loocamccloud_a': 'AAAAb9YP3rk:APA91bFCgd-kbVmpK4EVpfdHH_PJZQCYTkOGnTZdIuBWEz2r7aMRsJYHOH3sB-rwcbaRWgnufTyjX9nGQxb6KxQbWVk4ah_H-M3IqGh6Mb60WQQAuR33V6g_Jes5pGL6ViuIxGHqVMaR',
+    'com.ansjer.loocamdcloud_a': 'AAAAb9YP3rk:APA91bGw2I2KMD4i-5T7nZO_wB8kuAOuqgyqe5rxmY-W5qkpYEx9IL2IfmC_qf6B_xOyjIDDSjckvMo-RauN__SEoxvAkis7042GRkoKpw7cjZ_H8lC-d50PC0GclPzccrOGFusyKbFY',
+    'com.ansjer.customizedb_a': 'AAAAb9YP3rk:APA91bE7kI4vcm-9h_CJNFlOZfc-xwP4Btn6AnjOrwoKV6fgYN7fdarkO76sYxVZiAbDnxsFfOJyP7vQfwyan6mdjuyD5iHdt_XgO22VqniC0vA1V4GJiCS8Tp7LxIX8JVKZl9I_Powt',
+    'com.ansjer.customizeda_a': 'AAAAb9YP3rk:APA91bF0HzizVWDc6dKzobY9fsaKDK4veqkOZehDXshVXs8pEEvNWjR_YWbhP60wsRYCHCal8fWN5cECVOWNMMzDsfU88Ty2AUl8S5FtZsmeDTkoGntQOswBr8Ln7Fm_LAp1VqTf9CpM',
+    'com.ansjer.customizedd_a': 'AAAAb9YP3rk:APA91bHkxOozJWBrlv3eNT0PgwosYENI9aM4Zuzd418cX-iKkpa1zFNC5MkNDKApx1KH4fhmAfaJ6IMRZ0nj5GIxCpstDYCaZWwgC7-etqfSxG5JAq8LOwJx0o_1tUZqwjIic8ztsg0o',
+    'com.ansjer.adcloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.accloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.zccloud_ab': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+}
+
+APNS_CONFIG = {
+    'com.ansjer.loocamccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/lcc.pem',
+    },
+    'com.ansjer.zosidcloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zosidcloud.pem',
+    },
+    'com.ansjer.customizedb': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedb.pem',
+    },
+    'com.ansjer.customizedd': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedd.pem',
+    },
+    'com.ansjer.customizede': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizede.pem',
+    },
+    'com.ansjer.customizeda': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizeda.pem',
+    },
+    'com.ansjer.zccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zccloud.pem',
+    },
+    'com.ansjer.accloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/accloud.pem',
+    },
+    'com.ansjer.adcloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/apns-dis-adcloud.pem',
+    },
+    'com.ansjer.customizedc':{
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedc.pem',
+    }
+}
+APNS_MODE = 'prod'
+REDIS_ADDRESS = '127.0.0.1'
+# REDIS_ADDRESS = '192.168.136.45'

+ 201 - 0
AnsjerPush/formal_settings.py

@@ -0,0 +1,201 @@
+import os
+
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
+DEBUG = False
+ALLOWED_HOSTS = ["*"]
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'corsheaders',
+    'imagekit',
+    'Model'
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'AnsjerPush.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates')]
+        ,
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'AnsjerPush.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
+# formal
+DATABASE_DATA = 'Ansjer81'
+SERVER_HOST = 'database-2.clraczw4p0yj.us-west-1.rds.amazonaws.com'
+DATABASES_USER = 'azrds'
+DATABASES_PASS = 'azrds.x.x'
+
+DATABASE_DATA2 = 'Ansjer81'
+SERVER_HOST2 = 'ansjerpush.clraczw4p0yj.us-west-1.rds.amazonaws.com'
+DATABASES_USER2 = 'azrds'
+DATABASES_PASS2 = 'azrds.x.x'
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA,
+        'USER': DATABASES_USER,
+        'PASSWORD': DATABASES_PASS,
+        'HOST': SERVER_HOST,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    },
+    'mysql02': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA2,
+        'USER': DATABASES_USER2,
+        'PASSWORD': DATABASES_PASS2,
+        'HOST': SERVER_HOST2,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    }
+}
+DATABASE_ROUTERS = ['AnsjerPush.database_router.DatabaseAppsRouter']
+DATABASE_APPS_MAPPING = {
+    'db1': 'default',
+    'db2': 'mysql02',
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.1/howto/static-files/
+
+STATIC_URL = '/static/'
+# 跨域增加忽略
+# 跨域增加忽略
+CORS_ALLOW_CREDENTIALS = True
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ORIGIN_WHITELIST = ()
+
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+
+CORS_ALLOW_HEADERS = (
+    'accept',
+    'accept-encoding',
+    'authorization',
+    'content-type',
+    'dnt',
+    'origin',
+    'user-agent',
+    'x-csrftoken',
+    'x-requested-with',
+)
+
+
+# 日志模块
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'formatters': {
+        'error_format': {
+            # 'format': '{"asctime":"%(asctime)s","thread":"%(threadName)s:%(thread)d","errorline":"%(lineno)d","errorlevel":"%(levelname)s","errorcontent":"%(message)s"}'
+            'format': '%(asctime)s %(threadName)s %(thread)d %(lineno)d %(levelname)s %(message)s'
+        },
+    },
+    'filters': {
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'class': 'django.utils.log.AdminEmailHandler',
+            'include_html': True,
+        },
+        'default': {
+            'level': 'ERROR',
+            'class': 'logging.handlers.RotatingFileHandler',
+            'filename': BASE_DIR + '/static/log/error.log',
+            'maxBytes': 1024 * 1024 * 5,  # 5 MB
+            'backupCount': 5,
+            'formatter': 'error_format',
+        },
+        'console': {
+            'level': 'ERROR',
+            'class': 'logging.StreamHandler',
+            'formatter': 'error_format'
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['default', 'console'],
+            # 'handlers': ['mail_admins','default','console'],
+            'level': 'ERROR',
+            'propagate': False
+        },
+    }
+}

+ 16 - 0
AnsjerPush/formal_wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for AnsjerPush project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.formal_settings')
+
+application = get_wsgi_application()

+ 148 - 0
AnsjerPush/local_config.py

@@ -0,0 +1,148 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: Ansjer
+@software: PyCharm
+@DATE: 2018/7/2 14:06
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: Conf.py
+@Contact: chanjunkai@163.com
+"""
+# 主要静态变量配置文件
+import datetime, os
+
+
+DEBUG_MODE = 'DEV'
+# MODE = 'PRO'
+# 阿里云发邮箱
+ALY_SES_ACCESS_NAME = 'message@dvema.com'
+ALY_SES_ACCESS_PAW = 'SMtp123456'
+ALY_SES_ACCESS_REPLYTO = '***'
+
+# 发送邮件邮箱
+SES_COMPANY_EMAIL = 'user_server@nsst.com'
+AWS_SES_ACCESS_ID = 'AKIAJKPU23EU5QWHFPKQ'
+AWS_SES_ACCESS_SECRET = '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'
+AWS_SES_ACCESS_REGION = 'us-east-1'
+AWS_BUCKET = 'ansjertest'
+# 设定离线时间为5分钟
+OFF_LINE_TIME_DELTA = 5
+
+# token的secret
+OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
+OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
+# access_token超时
+# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
+OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
+# refresh_token超时
+OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
+# 腾讯验证,短信发送
+TX_PHONE_APP_ID = '1400052907'
+TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
+
+# 验证码超时时间
+AuthCode_Expire = 600
+
+# 根路径
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# uid token key
+UID_TOKEN_KEY = 'c+565*j@%^'
+
+# oss param
+OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
+OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
+OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
+
+SERVER_TYPE = 'Ansjer.local_settings'
+
+NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
+SERVER_DOMAIN = 'http://www.dvema.com/'
+SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
+DOMAIN_HOST = 'www.dvema.com'
+SERVER_HOST = '192.168.136.45'
+# SERVER_HOST = 'localhost'
+PAYPAL_CRD = {
+    "mode": "live",  # sandbox or live
+    "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
+    "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
+}
+DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+
+
+JPUSH_CONFIG = {
+    'com.ansjer.accloud_ab': {
+        'Key': 'f0dc047e5e53fd14199de5b0',
+        'Secret': 'aa7f7db33e9f0a7f3871aa1c'},
+    'com.ansjer.adcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_ab': {
+        'Key': 'd9924f56d3cc7c6017965130',
+        'Secret': '869d832d126a232f158b5987'},
+    'com.ansjer.loocamccloud_ab': {
+        'Key': 'd1cc44797b4642b0e05304fe',
+        'Secret': 'c3e8b4ca8c576de61401e56a'},
+    'com.ansjer.loocamdcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_a': {
+        'Key': '57de2a80d68bf270fd6bdf5a',
+        'Secret': '3d354eb6a0b49c2610decf42'},
+    'com.ansjer.accloud_a': {
+        'Key': 'ff95ee685f49c0dc4013347b',
+        'Secret': 'de2c20959f5516fdeeafe78e'},
+    'com.ansjer.adcloud_a': {
+        'Key': '2e47eb1aee9b164460df3668',
+        'Secret': 'b9137d8d684bc248f1809b6d'},
+    'com.ansjer.loocamccloud_a': {
+        'Key': '23c9213215c7ca0ec945629b',
+        'Secret': '81e4b1e859cc8387e2e6c431'},
+    'com.ansjer.loocamdcloud_a': {
+        'Key': '1dbdd60a16e9892d6f68a073',
+        'Secret': '80a97690e7e043109059b403'},
+    'com.ansjer.customizedb_a': {
+        'Key': '9d79630aa49adfa291fe2568',
+        'Secret': '4d8ff52f88136561875a0212'},
+    'com.ansjer.customizedd_a': {
+        'Key': '8fc4f495685bde53341ee25d',
+        'Secret': 'f1da11fa466509fa2670fb66',
+    }
+}
+# type =1
+FCM_CONFIG = {
+    'com.ansjer.zccloud_a': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+    'com.ansjer.loocamccloud_a': 'AAAAb9YP3rk:APA91bFCgd-kbVmpK4EVpfdHH_PJZQCYTkOGnTZdIuBWEz2r7aMRsJYHOH3sB-rwcbaRWgnufTyjX9nGQxb6KxQbWVk4ah_H-M3IqGh6Mb60WQQAuR33V6g_Jes5pGL6ViuIxGHqVMaR',
+    'com.ansjer.loocamdcloud_a': 'AAAAb9YP3rk:APA91bGw2I2KMD4i-5T7nZO_wB8kuAOuqgyqe5rxmY-W5qkpYEx9IL2IfmC_qf6B_xOyjIDDSjckvMo-RauN__SEoxvAkis7042GRkoKpw7cjZ_H8lC-d50PC0GclPzccrOGFusyKbFY',
+    'com.ansjer.customizedb_a': 'AAAAb9YP3rk:APA91bE7kI4vcm-9h_CJNFlOZfc-xwP4Btn6AnjOrwoKV6fgYN7fdarkO76sYxVZiAbDnxsFfOJyP7vQfwyan6mdjuyD5iHdt_XgO22VqniC0vA1V4GJiCS8Tp7LxIX8JVKZl9I_Powt',
+    'com.ansjer.customizeda_a': 'AAAAb9YP3rk:APA91bF0HzizVWDc6dKzobY9fsaKDK4veqkOZehDXshVXs8pEEvNWjR_YWbhP60wsRYCHCal8fWN5cECVOWNMMzDsfU88Ty2AUl8S5FtZsmeDTkoGntQOswBr8Ln7Fm_LAp1VqTf9CpM',
+    'com.ansjer.customizedd_a': 'AAAAb9YP3rk:APA91bHkxOozJWBrlv3eNT0PgwosYENI9aM4Zuzd418cX-iKkpa1zFNC5MkNDKApx1KH4fhmAfaJ6IMRZ0nj5GIxCpstDYCaZWwgC7-etqfSxG5JAq8LOwJx0o_1tUZqwjIic8ztsg0o',
+    'com.ansjer.adcloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.accloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.zccloud_ab': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+}
+
+APNS_CONFIG = {
+    'com.ansjer.loocamccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/lcc-dev.pem',
+    },
+    'com.ansjer.zosidcloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zosidcloud-dev.pem',
+    },
+    'com.ansjer.customizedb': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedb-dev.pem',
+    },
+    'com.ansjer.customizeda': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizeda-dev.pem',
+    },
+    'com.ansjer.zccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zccloud-dev.pem',
+    },
+    'com.ansjer.accloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/accloud-dev.pem',
+    }
+}
+APNS_MODE = 'dev'
+REDIS_ADDRESS = '127.0.0.1'

+ 220 - 0
AnsjerPush/local_settings.py

@@ -0,0 +1,220 @@
+import os
+
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
+DEBUG = True
+ALLOWED_HOSTS = ["*"]
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'corsheaders',
+    'imagekit',
+    'Model'
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'AnsjerPush.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates')]
+        ,
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'AnsjerPush.wsgi.application'
+
+
+
+# Database
+# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
+# formal
+# DATABASE_DATA = 'Ansjer81'
+# SERVER_HOST = 'localhost'
+# DATABASES_USER = 'ansjer'
+# DATABASES_PASS = '1234'
+# test
+# DATABASE_DATA = 'AnsjerTest'
+# SERVER_HOST = '13.56.215.252'
+# DATABASES_USER = 'ansjer'
+# DATABASES_PASS = '1234'
+DATABASE_DATA = 'ansjerlocal'
+SERVER_HOST = '127.0.0.1'
+DATABASES_USER = 'root'
+DATABASES_PASS = '123456'
+
+DATABASE_DATA2 = 'asjl'
+SERVER_HOST2 = '127.0.0.1'
+DATABASES_USER2 = 'root'
+DATABASES_PASS2 = '123456'
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA,
+        'USER': DATABASES_USER,
+        'PASSWORD': DATABASES_PASS,
+        'HOST': SERVER_HOST,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    },
+    'mysql02': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA2,
+        'USER': DATABASES_USER2,
+        'PASSWORD': DATABASES_PASS2,
+        'HOST': SERVER_HOST2,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    }
+}
+DATABASE_ROUTERS = ['AnsjerPush.database_router.DatabaseAppsRouter']
+DATABASE_APPS_MAPPING = {
+    'db1': 'default',
+    'db2': 'mysql02',
+}
+
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.1/howto/static-files/
+
+STATIC_URL = '/static/'
+# 跨域增加忽略
+# 跨域增加忽略
+CORS_ALLOW_CREDENTIALS = True
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ORIGIN_WHITELIST = ()
+
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+
+CORS_ALLOW_HEADERS = (
+    'accept',
+    'accept-encoding',
+    'authorization',
+    'content-type',
+    'dnt',
+    'origin',
+    'user-agent',
+    'x-csrftoken',
+    'x-requested-with',
+)
+
+
+# 日志模块
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'formatters': {
+        'error_format': {
+            # 'format': '{"asctime":"%(asctime)s","thread":"%(threadName)s:%(thread)d","errorline":"%(lineno)d","errorlevel":"%(levelname)s","errorcontent":"%(message)s"}'
+            'format': '%(asctime)s %(threadName)s %(thread)d %(lineno)d %(levelname)s %(message)s'
+        },
+    },
+    'filters': {
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'class': 'django.utils.log.AdminEmailHandler',
+            'include_html': True,
+        },
+        'default': {
+            'level': 'ERROR',
+            'class': 'logging.handlers.RotatingFileHandler',
+            'filename': BASE_DIR + '/static/log/error.log',
+            'maxBytes': 1024 * 1024 * 5,  # 5 MB
+            'backupCount': 5,
+            'formatter': 'error_format',
+        },
+        'console': {
+            # 'level': 'ERROR',
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'error_format'
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['default', 'console'],
+            # 'handlers': ['mail_admins','default','console'],
+            # 'level': 'ERROR',
+            'level': 'DEBUG',
+            'propagate': True
+        },
+        # 'django.db.backends': {
+        #     'handlers': ['console'],
+        #     'propagate': True,
+        #     'level': 'DEBUG',
+        # },
+    }
+}
+

+ 16 - 0
AnsjerPush/local_wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for AnsjerPush project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.local_settings')
+
+application = get_wsgi_application()

+ 145 - 0
AnsjerPush/settings.py

@@ -0,0 +1,145 @@
+import os
+
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
+DEBUG = True
+ALLOWED_HOSTS = ["*"]
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'corsheaders',
+    'imagekit',
+    'Model'
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'AnsjerPush.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates')]
+        ,
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'AnsjerPush.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
+# formal
+# DATABASE_DATA = 'Ansjer81'
+# SERVER_HOST = 'localhost'
+# DATABASES_USER = 'ansjer'
+# DATABASES_PASS = '1234'
+# test
+# DATABASE_DATA = 'AnsjerTest'
+# SERVER_HOST = '13.56.215.252'
+# DATABASES_USER = 'ansjer'
+# DATABASES_PASS = '1234'
+DATABASE_DATA = 'AnsjerLocal'
+SERVER_HOST = '192.168.136.45'
+DATABASES_USER = 'ansjer'
+DATABASES_PASS = 'ansjer.x.x'
+
+DATABASES = {'default': {
+    'ENGINE': 'django.db.backends.mysql',
+    'NAME': DATABASE_DATA,
+    'USER': DATABASES_USER,
+    'PASSWORD': DATABASES_PASS,
+    'HOST': SERVER_HOST,
+    'PORT': '3306',
+    'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+    'AUTOCOMMIT': True
+}}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.1/howto/static-files/
+
+STATIC_URL = '/static/'
+# 跨域增加忽略
+# 跨域增加忽略
+CORS_ALLOW_CREDENTIALS = True
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ORIGIN_WHITELIST = ()
+
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+
+CORS_ALLOW_HEADERS = (
+    'accept',
+    'accept-encoding',
+    'authorization',
+    'content-type',
+    'dnt',
+    'origin',
+    'user-agent',
+    'x-csrftoken',
+    'x-requested-with',
+)

+ 59 - 0
AnsjerPush/test.py

@@ -0,0 +1,59 @@
+import datetime
+import time
+from datetime import timezone, timedelta, datetime
+
+import boto3
+
+from AnsjerPush.config import AWS_SES_ACCESS_ID, AWS_SES_ACCESS_SECRET
+
+# client = boto3.client('cloudwatch', region_name='us-east-1', aws_access_key_id=AWS_SES_ACCESS_ID, aws_secret_access_key=AWS_SES_ACCESS_SECRET)
+# response = client.get_dashboard(DashboardName='string')
+# print(response)
+
+# localtime = time.localtime(1601482210)
+# current_month = localtime.tm_mon
+# print(current_month)
+#
+# time_struct = [localtime.tm_year, localtime.tm_mon, 1, 0, 0, 0, 0, 0, 0]
+# start_time = int(time.mktime(tuple(time_struct)))
+# print(start_time)
+#
+# time_struct[1] = current_month + 1
+# end_time = int(time.mktime(tuple(time_struct)))
+# print(end_time)
+
+# current_time = int(1601565010 - 8 * 3600)
+# print(current_time)
+# count = int(current_time / (24 * 3600))
+# current_time = count * 24 * 3600
+# print(current_time)
+
+# localtime = time.localtime(1601565010)
+# datetime.datetime()
+# current_day = localtime.tm_mday
+#
+# time_struct = [localtime.tm_year, localtime.tm_mon, current_day, 0, 0, 0, 0, 0, 0]
+# current_time = int(time.mktime(tuple(time_struct)))
+# print(current_time)
+
+
+# gmtime = time.gmtime(1601568621 + 28800)
+# current_day = gmtime.tm_mday
+# print(current_day)
+# print(gmtime.tm_hour)
+# time_struct = [gmtime.tm_year, gmtime.tm_mon, current_day, 0, 0, 0, 0, 0, 0]
+# current_time = int(time.mktime(tuple(time_struct)))
+# print(current_time - 28800)
+
+
+gmtime = time.gmtime(int(time.time()) + 28800)
+current_month = gmtime.tm_mon - 1
+print(current_month)
+
+time_struct = [gmtime.tm_year, current_month, 1, 0, 0, 0, 0, 0, gmtime.tm_isdst]
+start_time = int(time.mktime(tuple(time_struct))) - 28800
+print(start_time)
+
+time_struct[1] = current_month + 1
+end_time = int(time.mktime(tuple(time_struct))) - 28800
+print(end_time)

+ 146 - 0
AnsjerPush/test_config.py

@@ -0,0 +1,146 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: Ansjer
+@software: PyCharm
+@DATE: 2018/7/2 14:06
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: Conf.py
+@Contact: chanjunkai@163.com
+"""
+# 主要静态变量配置文件
+import datetime, os
+
+
+# MODE = 'PRO'
+# 阿里云发邮箱
+ALY_SES_ACCESS_NAME = 'message@dvema.com'
+ALY_SES_ACCESS_PAW = 'SMtp123456'
+ALY_SES_ACCESS_REPLYTO = '***'
+
+# 发送邮件邮箱
+SES_COMPANY_EMAIL = 'user_server@nsst.com'
+AWS_SES_ACCESS_ID = 'AKIAJKPU23EU5QWHFPKQ'
+AWS_SES_ACCESS_SECRET = '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'
+AWS_SES_ACCESS_REGION = 'us-east-1'
+AWS_BUCKET = 'ansjertest'
+# 设定离线时间为5分钟
+OFF_LINE_TIME_DELTA = 5
+
+# token的secret
+OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
+OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
+# access_token超时
+# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
+OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
+# refresh_token超时
+OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
+# 腾讯验证,短信发送
+TX_PHONE_APP_ID = '1400052907'
+TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
+
+# 验证码超时时间
+AuthCode_Expire = 600
+
+# 根路径
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# uid token key
+UID_TOKEN_KEY = 'c+565*j@%^'
+
+# oss param
+OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
+OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
+OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
+
+SERVER_TYPE = 'Ansjer.test_settings'
+
+NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
+SERVER_DOMAIN = 'http://www.dvema.com/'
+SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
+DOMAIN_HOST = 'www.dvema.com'
+SERVER_HOST = 'localhost'
+PAYPAL_CRD = {
+    "mode": "live",  # sandbox or live
+    "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
+    "client_secret": "ENT-J08N3Fw0B0uAokg4RukljAwO9hFHPf8whE6-Dwd8oBWJO8AWMgpdTKpfB1pOy89t4bsFEzMWDowm"
+}
+DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
+
+
+JPUSH_CONFIG = {
+    'com.ansjer.accloud_ab': {
+        'Key': 'f0dc047e5e53fd14199de5b0',
+        'Secret': 'aa7f7db33e9f0a7f3871aa1c'},
+    'com.ansjer.adcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_ab': {
+        'Key': 'd9924f56d3cc7c6017965130',
+        'Secret': '869d832d126a232f158b5987'},
+    'com.ansjer.loocamccloud_ab': {
+        'Key': 'd1cc44797b4642b0e05304fe',
+        'Secret': 'c3e8b4ca8c576de61401e56a'},
+    'com.ansjer.loocamdcloud_ab': {
+        'Key': '76d97b535185114985608234',
+        'Secret': 'c9a92b301043cc9c52778692'},
+    'com.ansjer.zccloud_a': {
+        'Key': '57de2a80d68bf270fd6bdf5a',
+        'Secret': '3d354eb6a0b49c2610decf42'},
+    'com.ansjer.accloud_a': {
+        'Key': 'ff95ee685f49c0dc4013347b',
+        'Secret': 'de2c20959f5516fdeeafe78e'},
+    'com.ansjer.adcloud_a': {
+        'Key': '2e47eb1aee9b164460df3668',
+        'Secret': 'b9137d8d684bc248f1809b6d'},
+    'com.ansjer.loocamccloud_a': {
+        'Key': '23c9213215c7ca0ec945629b',
+        'Secret': '81e4b1e859cc8387e2e6c431'},
+    'com.ansjer.loocamdcloud_a': {
+        'Key': '1dbdd60a16e9892d6f68a073',
+        'Secret': '80a97690e7e043109059b403'},
+    'com.ansjer.customizedb_a': {
+        'Key': '9d79630aa49adfa291fe2568',
+        'Secret': '4d8ff52f88136561875a0212'},
+    'com.ansjer.customizedd_a': {
+        'Key': '8fc4f495685bde53341ee25d',
+        'Secret': 'f1da11fa466509fa2670fb66',
+    }
+}
+# type =1
+FCM_CONFIG = {
+    'com.ansjer.zccloud_a': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+    'com.ansjer.loocamccloud_a': 'AAAAb9YP3rk:APA91bFCgd-kbVmpK4EVpfdHH_PJZQCYTkOGnTZdIuBWEz2r7aMRsJYHOH3sB-rwcbaRWgnufTyjX9nGQxb6KxQbWVk4ah_H-M3IqGh6Mb60WQQAuR33V6g_Jes5pGL6ViuIxGHqVMaR',
+    'com.ansjer.loocamdcloud_a': 'AAAAb9YP3rk:APA91bGw2I2KMD4i-5T7nZO_wB8kuAOuqgyqe5rxmY-W5qkpYEx9IL2IfmC_qf6B_xOyjIDDSjckvMo-RauN__SEoxvAkis7042GRkoKpw7cjZ_H8lC-d50PC0GclPzccrOGFusyKbFY',
+    'com.ansjer.customizedb_a': 'AAAAb9YP3rk:APA91bE7kI4vcm-9h_CJNFlOZfc-xwP4Btn6AnjOrwoKV6fgYN7fdarkO76sYxVZiAbDnxsFfOJyP7vQfwyan6mdjuyD5iHdt_XgO22VqniC0vA1V4GJiCS8Tp7LxIX8JVKZl9I_Powt',
+    'com.ansjer.customizeda_a': 'AAAAb9YP3rk:APA91bF0HzizVWDc6dKzobY9fsaKDK4veqkOZehDXshVXs8pEEvNWjR_YWbhP60wsRYCHCal8fWN5cECVOWNMMzDsfU88Ty2AUl8S5FtZsmeDTkoGntQOswBr8Ln7Fm_LAp1VqTf9CpM',
+    'com.ansjer.customizedd_a': 'AAAAb9YP3rk:APA91bHkxOozJWBrlv3eNT0PgwosYENI9aM4Zuzd418cX-iKkpa1zFNC5MkNDKApx1KH4fhmAfaJ6IMRZ0nj5GIxCpstDYCaZWwgC7-etqfSxG5JAq8LOwJx0o_1tUZqwjIic8ztsg0o',
+    'com.ansjer.adcloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.accloud_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
+    'com.ansjer.zccloud_ab': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
+}
+
+APNS_CONFIG = {
+    'com.ansjer.loocamccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/lcc-dev.pem',
+    },
+    'com.ansjer.zosidcloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zosidcloud-dev.pem',
+    },
+    'com.ansjer.customizedb': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizedb-dev.pem',
+    },
+    'com.ansjer.customizeda': {
+        'pem_path': 'AnsjerPush/file/apns_pem/customizeda-dev.pem',
+    },
+    'com.ansjer.zccloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/zccloud-dev.pem',
+    },
+    'com.ansjer.accloud': {
+        'pem_path': 'AnsjerPush/file/apns_pem/accloud-dev.pem',
+    }
+}
+APNS_MODE = 'dev'
+REDIS_ADDRESS = '127.0.0.1'

+ 198 - 0
AnsjerPush/test_settings.py

@@ -0,0 +1,198 @@
+import os
+
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
+DEBUG = True
+ALLOWED_HOSTS = ["*"]
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'corsheaders',
+    'imagekit',
+    'Model'
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'corsheaders.middleware.CorsMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'AnsjerPush.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates')]
+        ,
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'AnsjerPush.wsgi.application'
+
+
+
+# 服务器类型
+DATABASE_DATA = 'AnsjerTest'
+SERVER_HOST = 'database-2.clraczw4p0yj.us-west-1.rds.amazonaws.com'
+DATABASES_USER = 'azrds'
+DATABASES_PASS = 'azrds.x.x'
+
+DATABASE_DATA2 = 'AnsjerTest'
+SERVER_HOST2 = 'ansjerpush.clraczw4p0yj.us-west-1.rds.amazonaws.com'
+DATABASES_USER2 = 'azrds'
+DATABASES_PASS2 = 'azrds.x.x'
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA,
+        'USER': DATABASES_USER,
+        'PASSWORD': DATABASES_PASS,
+        'HOST': SERVER_HOST,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    },
+    'mysql02': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': DATABASE_DATA2,
+        'USER': DATABASES_USER2,
+        'PASSWORD': DATABASES_PASS2,
+        'HOST': SERVER_HOST2,
+        'PORT': '3306',
+        'OPTIONS': {'charset': 'utf8mb4', 'use_unicode': True, 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},
+        'AUTOCOMMIT': True
+    }
+}
+DATABASE_ROUTERS = ['AnsjerPush.database_router.DatabaseAppsRouter']
+DATABASE_APPS_MAPPING = {
+    'db1': 'default',
+    'db2': 'mysql02',
+}
+
+# Password validation
+# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.1/howto/static-files/
+
+STATIC_URL = '/static/'
+# 跨域增加忽略
+# 跨域增加忽略
+CORS_ALLOW_CREDENTIALS = True
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_ORIGIN_WHITELIST = ()
+
+CORS_ALLOW_METHODS = (
+    'DELETE',
+    'GET',
+    'OPTIONS',
+    'PATCH',
+    'POST',
+    'PUT',
+    'VIEW',
+)
+
+CORS_ALLOW_HEADERS = (
+    'accept',
+    'accept-encoding',
+    'authorization',
+    'content-type',
+    'dnt',
+    'origin',
+    'user-agent',
+    'x-csrftoken',
+    'x-requested-with',
+)
+
+# 日志模块
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'formatters': {
+        'error_format': {
+            # 'format': '{"asctime":"%(asctime)s","thread":"%(threadName)s:%(thread)d","errorline":"%(lineno)d","errorlevel":"%(levelname)s","errorcontent":"%(message)s"}'
+            'format': '%(asctime)s %(threadName)s %(thread)d %(lineno)d %(levelname)s %(message)s'
+        },
+    },
+    'filters': {
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'class': 'django.utils.log.AdminEmailHandler',
+            'include_html': True,
+        },
+        'default': {
+            'level': 'ERROR',
+            'class': 'logging.handlers.RotatingFileHandler',
+            'filename': BASE_DIR + '/static/log/error.log',
+            'maxBytes': 1024 * 1024 * 5,  # 5 MB
+            'backupCount': 5,
+            'formatter': 'error_format',
+        },
+        'console': {
+            'level': 'ERROR',
+            'class': 'logging.StreamHandler',
+            'formatter': 'error_format'
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['default', 'console'],
+            # 'handlers': ['mail_admins','default','console'],
+            'level': 'ERROR',
+            'propagate': True
+        },
+    }
+}

+ 16 - 0
AnsjerPush/test_wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for AnsjerPush project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_settings')
+
+application = get_wsgi_application()

+ 27 - 0
AnsjerPush/urls.py

@@ -0,0 +1,27 @@
+"""AnsjerPush URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.1/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+from Controller import DetectController, ShadowController, TestDetectController, EquipmentInfo
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('notify/push', DetectController.NotificationView.as_view()),
+    path('deviceShadow/update', ShadowController.update_device_shadow),
+    path('deviceShadow/generateUTK', ShadowController.generate_utk),
+    path('test/notify/push', TestDetectController.NotificationView.as_view()),
+    path('eq/del', EquipmentInfo.deleteExpireEquipmentInfo),
+]

+ 16 - 0
AnsjerPush/wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for AnsjerPush project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.settings')
+
+application = get_wsgi_application()

+ 671 - 0
Controller/DetectController.py

@@ -0,0 +1,671 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerFormal
+@software: PyCharm
+@DATE: 2019/1/14 15:57
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: DetectController.py
+@Contact: chanjunkai@163.com
+"""
+import os
+import time
+
+import apns2
+import jpush as jpush
+import oss2
+from django.http import JsonResponse
+from django.views.generic.base import View
+from pyfcm import FCMNotification
+from AnsjerPush.config import SERVER_TYPE
+from AnsjerPush.config import OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET, DETECT_PUSH_DOMAIN, JPUSH_CONFIG, FCM_CONFIG, \
+    APNS_CONFIG, BASE_DIR, APNS_MODE
+from Model.models import Equipment_Info, UidPushModel, SysMsgModel
+from Object.ETkObject import ETkObject
+from Object.LogUtil import LogUtil
+from Object.RedisObject import RedisObject
+from Object.UidTokenObject import UidTokenObject
+from Service.CommonService import CommonService
+
+'''
+http://push.dvema.com/notify/push?etk=Y2lTRXhMTjBWS01sWlpURTVJU0ZWTlJ6RXhNVUU9T3o=&n_time=1526845794&channel=1&event_type=704&is_st=0
+http://push.dvema.com/deviceShadow/generateUTK?username=debug_user&password=debug_password&uid=VVDHCVBYDKFMJRWA111A
+'''
+
+
+# 移动侦测接口
+class NotificationView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request.GET)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request.POST)
+
+    def validation(self, request_dict):
+
+        uidToken = request_dict.get('uidToken', None)
+        etk = request_dict.get('etk', None)
+        channel = request_dict.get('channel', '1')
+        n_time = request_dict.get('n_time', None)
+        event_type = request_dict.get('event_type', None)
+        is_st = request_dict.get('is_st', None)
+        # print("aaa")
+        # return JsonResponse(0,safe=False)
+        if not all([channel, n_time]):
+            return JsonResponse(status=200, data={
+                'code': 444,
+                'msg': 'param is wrong'})
+        if etk:
+            eto = ETkObject(etk)
+            uid = eto.uid
+            if len(uid) != 20:
+                return JsonResponse(status=200, data={'code': 404, 'msg': 'data is not exist'})
+        else:
+            utko = UidTokenObject(uidToken)
+            uid = utko.UID
+
+        pkey = '{uid}_{channel}_{event_type}_ptl'.format(uid=uid, event_type=event_type, channel=channel)
+        # ykey = 'MUJ887NLR8K8GBM9111A_redis_qs'.format(uid=uid)
+        ykey = '{uid}_redis_qs'.format(uid=uid)
+        # dkey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=event_type, channel=channel)
+        is_sys_msg = self.is_sys_msg(int(event_type))
+        if is_sys_msg is True:
+            dkey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=event_type, channel=channel)
+        else:
+            dkey = '{uid}_{channel}_flag'.format(uid=uid, channel=channel)
+
+        # 判断redisObj.get_data(key=pkey):不为空
+        redisObj = RedisObject(db=6)
+        have_ykey = redisObj.get_data(key=ykey)  # uid_set 数据库缓存
+        have_pkey = redisObj.get_data(key=pkey)  # 一分钟限制key
+        have_dkey = redisObj.get_data(key=dkey)  # 推送类型限制
+
+        # 一分钟外,推送开启状态
+        detect_med_type = 0  # 0推送旧机制 1存库不推送,2推送存库
+        # 暂时注销
+        if have_pkey:
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'msg': 'Push once every 10 seconds'}
+            else:
+                res_data = {'code': 0, 'msg': 'Push it once a minute'}
+
+            return JsonResponse(status=200, data=res_data)
+
+        # 数据库读取数据
+        if have_ykey:
+            redis_list = eval(redisObj.get_data(key=ykey))
+            print(have_ykey)
+        else:
+            # 从数据库查询出来
+            uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid,uid_set__detect_status=1). \
+                values('token_val', 'app_type', 'appBundleId','m_code',
+                       'push_type', 'userID_id', 'userID__NickName',
+                       'lang', 'm_code', 'tz', 'uid_set__nickname', 'uid_set__detect_interval', 'uid_set__detect_group',
+                       'uid_set__channel')
+            print(uid_push_qs)
+            # 新建一个list接收数据
+            redis_list = []
+            # 把数据库数据追加进redis_list
+            for qs in uid_push_qs:
+                redis_list.append(qs)
+            # 修改redis数据,并设置过期时间为10分钟
+            redisObj.set_data(key=ykey, val=str(redis_list), expire=600)
+            if not redis_list:
+                res_data = {'code': 404, 'msg': 'error !'}
+                return JsonResponse(status=200, data=res_data)
+
+            # 此时应该更新一下redis里面的dkey的有效时间
+            # detect_interval = redis_list[0]['uid_set__detect_interval']
+            # tmp_channel = redis_list[0]['uid_set__channel']
+            # self.do_update_detect_interval(uid, tmp_channel, redisObj, detect_interval)
+
+        if not redis_list:
+            print("没有redi_list")
+            res_data = {'code': 0, 'msg': 'no redi_list success!'}
+            return JsonResponse(status=200, data=res_data)
+
+        # is_sys_msg = self.is_sys_msg(int(event_type))
+        nickname = redis_list[0]['uid_set__nickname']
+        detect_interval = redis_list[0]['uid_set__detect_interval']
+        detect_group = redis_list[0]['uid_set__detect_group']
+        now_time = int(time.time())
+        if not nickname:
+            nickname = uid
+
+        if detect_group is not None:
+            if have_dkey:
+                detect_med_type = 1  # 1为存库不推送
+            else:
+                detect_med_type = 2  # 为2的话,既推送,又存库
+                # detect_group=0允许全部推送的时候
+                if detect_group == '0'or detect_group == '':
+                    redisObj.set_data(key=dkey, val=1, expire=detect_interval)
+                else:
+                    detect_group_list = detect_group.split(',')
+                    if event_type in detect_group_list:
+                        if detect_interval < 60:
+                            detect_interval = 60
+                        redisObj.set_data(key=dkey, val=1, expire=detect_interval)
+                # 改为1秒
+                # 如果不是正式
+                if SERVER_TYPE!="Ansjer.formal_settings":
+                    redisObj.set_data(key=pkey, val=1, expire=10)
+                else:
+                    redisObj.set_data(key=pkey, val=1, expire=60)
+
+            # 打印have_ykey
+        # return JsonResponse(status=200, data={'pkey': 0, 'have_ykey': have_ykey, 'have_pkey': have_pkey, 'have_ykey': have_dkey})
+
+        # 旧模式并且没有pkey,重新创建一个
+        if not detect_group and not have_pkey:
+            # 设置推送时间为60秒一次
+            # 如果不是正式
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                redisObj.set_data(key=pkey, val=1, expire=10)
+            else:
+                redisObj.set_data(key=pkey, val=1, expire=60)
+        auth = oss2.Auth(OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET)
+        bucket = oss2.Bucket(auth, 'oss-cn-shenzhen.aliyuncs.com', 'apg')
+        kwag_args = {
+            'uid': uid,
+            'channel': channel,
+            'event_type': event_type,
+            'n_time': n_time,
+            # 'appBundleId': appBundleId,
+            # 'token_val': token_val,
+            # 'msg_title': msg_title,
+            # 'msg_text': msg_text
+        }
+        eq_list = []
+        sys_msg_list = []
+        userID_ids = []
+        do_apns_code = ''
+        do_fcm_code = ''
+        do_jpush_code = ''
+        for up in redis_list:
+            push_type = up['push_type']
+            appBundleId = up['appBundleId']
+            token_val = up['token_val']
+            lang = up['lang']
+            tz = up['tz']
+            if tz is None or tz == '':
+                tz = 0
+            # 发送标题
+            msg_title = self.get_msg_title(appBundleId=appBundleId, nickname=nickname)
+            # 发送内容
+            msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                         event_type=event_type)
+            kwag_args['appBundleId'] = appBundleId
+            kwag_args['token_val'] = token_val
+            kwag_args['msg_title'] = msg_title
+            kwag_args['msg_text'] = msg_text
+            push_server_status = 0
+            #推送
+            if detect_med_type == 2 or detect_med_type == 0:
+                if push_type == 0:  # ios apns
+                    print('do_apns')
+                    # self.do_apns(**kwag_args)
+                    do_apns_code = self.do_apns(**kwag_args)
+                    if isinstance(do_apns_code, int):
+                        push_server_status = do_apns_code
+                    else:
+                        push_server_status = 400
+                elif push_type == 1:  # android gcm
+                    print('do_fcm')
+                    do_fcm_code = self.do_fcm(**kwag_args)
+                    push_server_status = 200
+                elif push_type == 2:  # android jpush
+                    print('do_jpush')
+                    do_jpush_code = self.do_jpush(**kwag_args)
+                    push_server_status = do_jpush_code
+                    # return JsonResponse(status=200, data={'code': 0, '状态:': self.do_jpush(**kwag_args)})
+            if detect_med_type == 1:
+                do_apns_code = '只存库不推送'
+                do_fcm_code = '只存库不推送'
+                do_jpush_code = '只存库不推送'
+            # 以下是存库
+            userID_id = up["userID_id"]
+            int_is_st = int(is_st)
+            if userID_id not in userID_ids:
+                eq_list.append(Equipment_Info(
+                    userID_id=userID_id,
+                    eventTime=n_time,
+                    eventType=event_type,
+                    devUid=uid,
+                    devNickName=nickname,
+                    Channel=channel,
+                    alarm='Motion \tChannel:{channel}'.format(channel=channel),
+                    is_st=int_is_st,
+                    receiveTime=n_time,
+                    addTime=now_time
+                ))
+                if is_sys_msg:
+                    sys_msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                                     event_type=event_type, is_sys=1)
+                    sys_msg_list.append(SysMsgModel(
+                        userID_id=userID_id,
+                        msg=sys_msg_text,
+                        addTime=now_time,
+                        updTime=now_time,
+                        uid=uid,
+                        eventType=event_type))
+                userID_ids.append(userID_id)
+        if is_sys_msg:
+            SysMsgModel.objects.bulk_create(sys_msg_list)
+        Equipment_Info.objects.bulk_create(eq_list)
+        if is_st == '0' or is_st == '2':
+            print("is_st=0or2")
+            for up in redis_list:
+                if up['push_type'] == 0:  # ios apns
+                    up['do_apns_code'] = do_apns_code
+                elif up['push_type'] == 1:  # android gcm
+                    up['do_fcm_code'] = do_fcm_code
+                elif up['push_type'] == 2:  # android jpush
+                    up['do_jpush_code'] = do_jpush_code
+                up['test_or_www'] = SERVER_TYPE
+                del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+            return JsonResponse(status=200, data={'code': 0, 'msg': 'success 0 or 2' ,'re_list':redis_list})
+
+        elif is_st == '1':
+            print("is_st=1")
+            # Endpoint以杭州为例,其它Region请按实际情况填写。
+            obj = '{uid}/{channel}/{filename}.jpeg'.format(uid=uid, channel=channel, filename=n_time)
+            # 设置此签名URL在60秒内有效。
+            url = bucket.sign_url('PUT', obj, 7200)
+            for up in redis_list:
+                up['do_apns_code'] = do_apns_code
+                up['do_fcm_code'] = do_fcm_code
+                up['do_jpush_code'] = do_jpush_code
+                up['test_or_www'] = SERVER_TYPE
+                del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+                # 不是正式服务器
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'img_push': url, 'msg': 'success', 're_list': redis_list}
+            else:
+                # 是正式服务器的时候
+                res_data = {'code': 0, 'img_push': url, 'msg': 'success'}
+            return JsonResponse(status=200, data=res_data)
+
+        elif is_st == '3':
+            print("is_st=3")
+            # 人形检测带动图
+            # Endpoint以杭州为例,其它Region请按实际情况填写。
+            img_url_list = []
+            for i in range(int(is_st)):
+                obj = '{uid}/{channel}/{filename}_{st}.jpeg'. \
+                    format(uid=uid, channel=channel, filename=n_time, st=i)
+                # 设置此签名URL在60秒内有效。
+                url = bucket.sign_url('PUT', obj, 7200)
+                img_url_list.append(url)
+
+            for up in redis_list:
+                up['do_apns_code'] = do_apns_code
+                up['do_fcm_code'] = do_fcm_code
+                up['do_jpush_code'] = do_jpush_code
+                up['test_or_www'] = SERVER_TYPE
+                del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+
+            # 不是正式服务器
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3', 're_list': redis_list}
+            else:
+                # 是正式服务器的时候
+                res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3'}
+            return JsonResponse(status=200, data=res_data)
+
+
+    def get_msg_title(self, appBundleId, nickname):
+        package_title_config = {
+            'com.ansjer.customizedd_a': 'DVS',
+            'com.ansjer.zccloud_a': 'ZosiSmart',
+            'com.ansjer.zccloud_ab': '周视',
+            'com.ansjer.adcloud_a': 'ADCloud',
+            'com.ansjer.adcloud_ab': 'ADCloud',
+            'com.ansjer.accloud_a': 'ACCloud',
+            'com.ansjer.loocamccloud_a': 'Loocam',
+            'com.ansjer.loocamdcloud_a': 'Anlapus',
+            'com.ansjer.customizedb_a': 'COCOONHD',
+            'com.ansjer.customizeda_a': 'Guardian365',
+            'com.ansjer.customizedc_a': 'PatrolSecure',
+        }
+        if appBundleId in package_title_config.keys():
+            return package_title_config[appBundleId] + '(' + nickname + ')'
+        else:
+            return nickname
+
+    def is_sys_msg(self, event_type):
+        event_type_list = [702, 703, 704]
+        if event_type in event_type_list:
+            return True
+        return False
+
+    def get_msg_text(self, channel, n_time, lang, tz, event_type, is_sys=0):
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz,lang=lang)
+        etype = int(event_type)
+        if lang == 'cn':
+            if etype == 704:
+                msg_type = '电量过低'
+            elif etype == 702:
+                msg_type = '摄像头休眠'
+            elif etype == 703:
+                msg_type = '摄像头唤醒'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} 通道:{channel}'.format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+                # send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+        else:
+            if etype == 704:
+                msg_type = 'Low battery'
+            elif etype == 702:
+                msg_type = 'Camera sleep'
+            elif etype == 703:
+                msg_type = 'Camera wake'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} channel:{channel}'. \
+                    format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} channel:{channel} date:{date}'. \
+                    format(msg_type=msg_type, channel=channel, date=n_date)
+        return send_text
+
+    def do_jpush(self, uid, channel, appBundleId, token_val, event_type, n_time,
+                 msg_title, msg_text):
+        app_key = JPUSH_CONFIG[appBundleId]['Key']
+        master_secret = JPUSH_CONFIG[appBundleId]['Secret']
+        # 此处换成各自的app_key和master_secre
+        _jpush = jpush.JPush(app_key, master_secret)
+        push = _jpush.create_push()
+        # if you set the logging level to "DEBUG",it will show the debug logging.
+        # _jpush.set_logging("DEBUG")
+        # push.audience = jpush.all_
+        push.audience = jpush.registration_id(token_val)
+        push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                     "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+        android = jpush.android(alert=msg_text, priority=1, style=1, alert_type=7,
+                                big_text=msg_text, title=msg_title,
+                                extras=push_data)
+        push.notification = jpush.notification(android=android)
+        push.platform = jpush.all_
+        res = push.send()
+        print(res)
+        return res.status_code
+        # try:
+        #     res = push.send()
+        #     print(res)
+        # except Exception as e:
+        #     print("jpush fail")
+        #     print("Exception")
+        #     print(repr(e))
+        #     return
+        # else:
+        #     print("jpush success")
+        #     return
+
+    def do_fcm(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
+        try:
+            serverKey = FCM_CONFIG[appBundleId]
+        except Exception as e:
+            return 'serverKey abnormal'
+        push_service = FCMNotification(api_key=serverKey)
+        data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+        result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
+                                                   message_body=msg_text, data_message=data,
+                                                   extra_kwargs={
+                                                       'default_vibrate_timings': True,
+                                                       'default_sound': True,
+                                                       'default_light_settings': True
+                                                   })
+        print('fcm push ing')
+        print(result)
+        return result
+
+    def do_apns(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title,
+                msg_text):
+        try:
+            cli = apns2.APNSClient(mode=APNS_MODE,
+                                   client_cert=os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
+
+            push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                         "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+            alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
+            payload = apns2.Payload(alert=alert, custom=push_data)
+
+            # return uid, channel, appBundleId, str(token_val), event_type, n_time, msg_title,msg_text
+            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
+            res = cli.push(n=n, device_token=token_val, topic=appBundleId)
+            print(res.status_code)
+
+            #     200, 推送成功。
+            #   400, 请求有问题。
+            #   403, 证书或Token有问题。
+            #   405, 请求方式不正确, 只支持POST请求
+            #   410, 设备的Token与证书不一致
+            if res.status_code == 200:
+                return res.status_code
+            else:
+                print('apns push fail')
+                print(res.reason)
+                return res.status_code
+        except (ValueError, ArithmeticError):
+            return 'The program has a numeric format exception, one of the arithmetic exceptions'
+        except Exception as e:
+            print(repr(e))
+            return repr(e)
+
+    def do_update_detect_interval(self, uid, channel, redisObject, detect_interval):
+        if channel == 0:
+            channel = 17
+        else:
+            channel += 1
+        for i in range(1, channel):
+            tmpDKey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=51, channel=i)
+            if tmpDKey is not False:
+                llt = redisObject.get_ttl(tmpDKey)
+                if llt > detect_interval:
+                    redisObject.set_data(key=tmpDKey, val=1, expire=detect_interval)
+
+            tmpDKey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=54, channel=i)
+            if tmpDKey is not False:
+                llt = redisObject.get_ttl(tmpDKey)
+                if llt > detect_interval:
+                    redisObject.set_data(key=tmpDKey, val=1, expire=detect_interval)
+
+
+# http://test.dvema.com/detect/add?uidToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJQMldOR0pSRDJFSEE1RVU5MTExQSJ9.xOCI5lerk8JOs5OcAzunrKCfCrtuPIZ3AnkMmnd-bPY&n_time=1526845794&channel=1&event_type=51&is_st=0
+# 移动侦测接口
+class PushNotificationView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        # operation = kwargs.get('operation')
+        return self.validation(request.GET)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        # operation = kwargs.get('operation')
+        return self.validation(request.POST)
+
+    def validation(self, request_dict):
+        etk = request_dict.get('etk', None)
+        channel = request_dict.get('channel', '1')
+        n_time = request_dict.get('n_time', None)
+        event_type = request_dict.get('event_type', None)
+        is_st = request_dict.get('is_st', None)
+        eto = ETkObject(etk)
+        uid = eto.uid
+        if len(uid) == 20:
+            redisObj = RedisObject(db=6)
+            # pkey = '{uid}_{channel}_ptl'.format(uid=uid, channel=channel)
+            pkey = '{uid}_ptl'.format(uid=uid)
+            ykey = '{uid}_redis_qs'.format(uid=uid)
+            if redisObj.get_data(key=pkey):
+                res_data = {'code': 0, 'msg': 'success,!33333333333'}
+                return JsonResponse(status=200, data=res_data)
+            else:
+                redisObj.set_data(key=pkey, val=1, expire=60)
+            ##############
+            redis_data = redisObj.get_data(key=ykey)
+            if redis_data:
+                redis_list = eval(redis_data)
+            else:
+                # 设置推送时间为60秒一次
+                redisObj.set_data(key=pkey, val=1, expire=60)
+                print("从数据库查到数据")
+                # 从数据库查询出来
+                uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid, uid_set__detect_status=1). \
+                    values('token_val', 'app_type', 'appBundleId',
+                           'push_type', 'userID_id', 'lang','m_code',
+                           'tz', 'uid_set__nickname')
+                # 新建一个list接收数据
+                redis_list = []
+                # 把数据库数据追加进redis_list
+                for qs in uid_push_qs:
+                    redis_list.append(qs)
+                # 修改redis数据,并设置过期时间为10分钟
+            if redis_list:
+                redisObj.set_data(key=ykey, val=str(redis_list), expire=600)
+                auth = oss2.Auth(OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET)
+                bucket = oss2.Bucket(auth, 'oss-cn-shenzhen.aliyuncs.com', 'apg')
+                self.do_bulk_create_info(redis_list, n_time, channel, event_type, is_st, uid)
+                if is_st == '0' or is_st == '2':
+                    return JsonResponse(status=200, data={'code': 0, 'msg': 'success44444444444444444'})
+                elif is_st == '1':
+                    # Endpoint以杭州为例,其它Region请按实际情况填写。
+                    obj = '{uid}/{channel}/{filename}.jpeg'.format(uid=uid, channel=channel, filename=n_time)
+                    # 设置此签名URL在60秒内有效。
+                    url = bucket.sign_url('PUT', obj, 7200)
+                    res_data = {'code': 0, 'img_push': url, 'msg': 'success'}
+                    return JsonResponse(status=200, data=res_data)
+                elif is_st == '3':
+                    # 人形检测带动图
+                    img_url_list = []
+                    for i in range(int(is_st)):
+                        obj = '{uid}/{channel}/{filename}_{st}.jpeg'. \
+                            format(uid=uid, channel=channel, filename=n_time, st=i)
+                        # 设置此签名URL在60秒内有效。
+                        url = bucket.sign_url('PUT', obj, 7200)
+                        img_url_list.append(url)
+                    res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success'}
+                    return JsonResponse(status=200, data=res_data)
+            else:
+                return JsonResponse(status=200, data={'code': 404, 'msg': 'data is not exist'})
+        else:
+            return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong etk'})
+
+    def do_bulk_create_info(self, uaqs, n_time, channel, event_type, is_st, uid):
+        now_time = int(time.time())
+        # 设备昵称
+        userID_ids = []
+        sys_msg_list = []
+        is_sys_msg = self.is_sys_msg(int(event_type))
+        is_st = int(is_st)
+        eq_list = []
+        nickname = uaqs[0]['uid_set__nickname']
+        if not nickname:
+            nickname = uid
+        for ua in uaqs:
+            lang = ua['lang']
+            tz = ua['tz']
+            userID_id = ua["userID_id"]
+            if userID_id not in userID_ids:
+                eq_list.append(Equipment_Info(
+                    userID_id=userID_id,
+                    eventTime=n_time,
+                    eventType=event_type,
+                    devUid=uid,
+                    devNickName=nickname,
+                    Channel=channel,
+                    alarm='Motion \tChannel:{channel}'.format(channel=channel),
+                    is_st=is_st,
+                    receiveTime=n_time,
+                    addTime=now_time
+                ))
+                if is_sys_msg:
+                    sys_msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                                     event_type=event_type, is_sys=1)
+                    sys_msg_list.append(SysMsgModel(
+                        userID_id=userID_id,
+                        msg=sys_msg_text,
+                        addTime=now_time,
+                        updTime=now_time,
+                        uid=uid,
+                        eventType=event_type))
+        if eq_list:
+            print('eq_list')
+            Equipment_Info.objects.bulk_create(eq_list)
+        if is_sys_msg:
+            print('sys_msg')
+            SysMsgModel.objects.bulk_create(sys_msg_list)
+        return True
+
+    def is_sys_msg(self, event_type):
+        event_type_list = [702, 703, 704]
+        if event_type in event_type_list:
+            return True
+        return False
+
+    def get_msg_text(self, channel, n_time, lang, tz, event_type, is_sys=0):
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz)
+        etype = int(event_type)
+        if lang == 'cn':
+            if etype == 704:
+                msg_type = '电量过低'
+            elif etype == 702:
+                msg_type = '摄像头休眠'
+            elif etype == 703:
+                msg_type = '摄像头唤醒'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} 通道:{channel}'.format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+        else:
+            if etype == 704:
+                msg_type = 'Low battery'
+            elif etype == 702:
+                msg_type = 'Camera sleep'
+            elif etype == 703:
+                msg_type = 'Camera wake'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} channel:{channel}'. \
+                    format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} channel:{channel} date:{date}'. \
+                    format(msg_type=msg_type, channel=channel, date=n_date)
+        return send_text

+ 323 - 0
Controller/EquipmentInfo.py

@@ -0,0 +1,323 @@
+import json
+import time
+import traceback
+import requests
+
+import oss2
+from django.utils.decorators import method_decorator
+from django.views.decorators.csrf import csrf_exempt
+from django.views.generic import TemplateView
+from django.views.generic.base import View
+
+from AnsjerPush.config import OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET
+from AnsjerPush.config import SERVER_DOMAIN
+from Model.models import Device_Info, VodHlsModel, Equipment_Info
+from Model.models import Device_User
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from Object.UidTokenObject import UidTokenObject
+from Service.CommonService import CommonService
+from Service.ModelService import ModelService
+
+
+'''
+http://192.168.136.45:8077/equipment/info?token=test&operation=add&devUid=2N1K3LE78TYJ38CE111A&Channel=0&eventType=1&eventTime=1234567890&operation=add&alarm=21342134&receiveTime=1234567891234567
+http://test.dvema.com/equipment/info?token=test&operation=query&page=1&line=10
+http://192.168.136.45:8077/equipment/info?token=test&operation=delete&devUid=UKPAH63V23U4ZHEB111A&id=5&id=6&id=7
+http://192.168.136.40:8077/equipment/info?token=test&operation=update&devUid=UKPAH63V23U4ZHEB111A&id=3&id=4
+http://192.168.136.45:8077/equipment/info?token=test&operation=queryByAdmin&page=1&line=10&userID_id=151547867345163613800138001
+http://192.168.136.45:8077/equipment/info?token=test&operation=deleteByAdmin&id=5&id=6&id=7
+
+'''
+
+
+class EquipmentInfo(View):
+    @method_decorator(csrf_exempt)
+    def dispatch(self, *args, **kwargs):
+        return super(EquipmentInfo, self).dispatch(*args, **kwargs)
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request_dict=request.GET)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request_dict=request.POST)
+
+    def validation(self, request_dict, *args, **kwargs):
+        token = request_dict.get('token', None)
+        response = ResponseObject()
+        tko = TokenObject(token)
+        if tko.code != 0:
+            return response.json(tko.code)
+        userID = tko.userID
+        operation = request_dict.get('operation', None)
+        if not userID:
+            return response.json(309)
+        if operation == 'query':
+            return self.query_info(request_dict, userID, response)
+        elif operation == 'add':
+            return self.add_info(request_dict, userID, response)
+        elif operation == 'update':
+            return self.update_info(request_dict, userID, response)
+        elif operation == 'delete':
+            return self.delete_info(request_dict, userID, response)
+        elif operation == 'findByTime':
+            return self.findByTime_info(request_dict, userID, response)
+        if operation == 'queryByAdmin':
+            return self.query_all_info(request_dict, userID, response)
+        if operation == 'deleteByAdmin':
+            return self.delete_by_admin(request_dict, userID, response)
+        if operation == 'delete_ByApi':
+            return self.delete_api(request_dict, userID, response)
+        else:
+            return response.json(444, {"parm": "operation"})
+
+    def add_info(self, request_dict, userID, response):
+        devUid = request_dict.get('devUid', None)
+        Channel = request_dict.get('Channel', None)
+        eventType = request_dict.get('eventType', None)
+        eventTime = request_dict.get('eventTime', None)
+        receiveTime = request_dict.get('receiveTime', None)
+        alarm = request_dict.get('alarm', None)
+        if not devUid or not Channel or not eventType or not eventTime or not alarm or not receiveTime:
+            return response.json(444, 'devUid, Channel, eventType, eventTime, alarm, receiveTime')
+        own_device = ModelService.check_own_device(userID, UID=devUid)
+        if own_device is not True:
+            return response.json(14)
+        device_info = Device_Info.objects.filter(UID=devUid, userID_id=userID)
+        if not device_info.exists():
+            return response.json(14)
+        try:
+            nowTime = int(time.time())
+            equipment_info = Equipment_Info(
+                userID=Device_User.objects.get(userID=userID),
+                devUid=devUid,
+                Channel=Channel,
+                eventType=eventType,
+                receiveTime=receiveTime,
+                eventTime=eventTime,
+                devNickName=device_info[0].NickName,
+                addTime=nowTime,
+                alarm=alarm)
+            equipment_info.save()
+        except Exception:
+            errorInfo = traceback.format_exc()
+            print(errorInfo)
+            return response.json(500, errorInfo)
+        else:
+            if equipment_info.id:
+                return response.json(0, {'infoID': equipment_info.id,
+                                         'devUid': devUid,
+                                         'Channel': Channel,
+                                         'alarm': alarm,
+                                         'eventType': eventType,
+                                         'eventTime': eventTime,
+                                         'receiveTime': receiveTime,
+                                         'devNickName': equipment_info.devNickName})
+            else:
+                return response.json(500)
+
+    def query_info(self, request_dict, userID, response):
+        page = int(request_dict.get('page', None))
+        line = int(request_dict.get('line', None))
+        if not page or not line:
+            return response.json(444, 'page,line')
+        qs = Equipment_Info.objects.filter(userID_id=userID)
+        uid = request_dict.get('uid', None)
+        if uid:
+            qs = qs.filter(devUid=uid)
+            dvqs = Device_Info.objects.filter(UID=uid).values('Type', 'NickName')
+            uid_type_dict = {uid: {'type': dvqs[0]['Type'], 'NickName': dvqs[0]['NickName']}}
+        else:
+            dvqs = Device_Info.objects.filter(userID_id=userID).values('UID', 'Type', 'NickName')
+            uid_type_dict = {}
+            for dv in dvqs:
+                uid_type_dict[dv['UID']] = {'type': dv['Type'], 'NickName': dv['NickName']}
+        if not qs.exists():
+            return response.json(0, {'datas': [], 'count': 0})
+        count = qs.count()
+        res = qs[(page - 1) * line:page * line]
+        send_json = CommonService.qs_to_dict(res)
+        res = []
+        for p in send_json['datas']:
+            p['fields']['viewAccont'] = ''
+            p['fields']['viewPwd'] = ''
+            devUid = p['fields']['devUid']
+            if devUid in uid_type_dict.keys():
+                p['fields']['uid_type'] = uid_type_dict[devUid]['type']
+                p['fields']['devNickName'] = uid_type_dict[devUid]['NickName']
+            else:
+                p['uid_type'] = ''
+            res.append(p)
+        return response.json(0, {'datas': res, 'count': count})
+
+
+    def update_info(self, request_dict, userID, response):
+        is_update_all = request_dict.get('is_update_all', 0)
+        eventType = request_dict.get('eventType', None)
+        if int(is_update_all) == 1:
+            try:
+                eq = Equipment_Info.objects.filter(userID_id=userID)
+                if eventType:
+                    eq = eq.filter(eventType=int(eventType))
+                is_update = eq.update(status=1)
+                return response.json(0, {'update_count': is_update})
+            except Exception as e:
+                print(repr(e))
+            return response.json(0, {'update_success': 'all'})
+        else:
+            id_list = request_dict.getlist('id[]', None)
+            if id_list is None or len(id_list) < 1:
+                id_list = request_dict.getlist('id', None)
+            param_flag = CommonService.get_param_flag(data=[id_list])
+            if param_flag is True:
+                count = 0
+                for id in id_list:
+                    try:
+                        eq = Equipment_Info.objects.filter(id=int(id))
+                        if eq.exists():
+                            own_dev = ModelService.check_own_device(userID, eq[0].devUid)
+                            if own_dev is True:
+                                count += 1
+                                eq.update(status=1)
+                    except Exception as e:
+                        print(repr(e))
+                return response.json(0, {'update_success': count})
+            else:
+                return response.json(444)
+
+    def delete_info(self, request_dict, userID, response):
+        id_list = request_dict.getlist('id[]', None)
+        if id_list is None or len(id_list) < 1:
+            id_list = request_dict.getlist('id', None)
+        param_flag = CommonService.get_param_flag(data=[id_list])
+        if param_flag is True:
+            try:
+                for id in id_list:
+                    eq = Equipment_Info.objects.filter(id=id)
+                    if eq.exists():
+                        own_dev = ModelService.check_own_device(userID, eq[0].devUid)
+                        if own_dev is True:
+                            eq.delete()
+            except Exception as e:
+                errorInfo = traceback.format_exc()
+                print(errorInfo)
+                return response.json(424, repr(e))
+            else:
+                return response.json(0)
+        else:
+            return response.json(444)
+
+    def findByTime_info(self, request_dict, userID, response):
+        startTime = request_dict.get('startTime')
+        endTime = request_dict.get('endTime')
+        page = int(request_dict.get('page', None))
+        line = int(request_dict.get('line', None))
+        if not startTime or not endTime or not page or not line:
+            return response.json(444, 'startTime, endTime, page, line')
+        uid_list = Device_Info.objects.filter(userID_id=userID).values_list('UID', flat=True)
+        if not len(uid_list):
+            return response.json(0, {'datas': [], 'count': 0})
+        qs = Equipment_Info.objects.filter(userID_id=userID,
+                                           eventTime__range=(startTime, endTime)).order_by('-id')
+        if qs.exists():
+            count = qs.count()
+            res = qs[(page - 1) * line:page * line]
+            send_json = CommonService.qs_to_dict(res)
+            send_json['count'] = count
+            return response.json(0, send_json)
+
+    # 管理员查询指定用户的推送信息接口
+    def query_all_info(self, request_dict, userID, response):
+        page = int(request_dict.get('page', None))
+        line = int(request_dict.get('line', None))
+        username = request_dict.get('username', None)
+        uid = request_dict.get('uid', None)
+        devNickName = request_dict.get('devNickName', None)
+        Channel = request_dict.get('Channel', None)
+        if not page or not line:
+            return response.json(444, 'page,line')
+        check_perm = ModelService.check_perm(userID=userID, permID=30)
+        if not check_perm:
+            return response.json(404)
+
+        if not username and not uid and not devNickName and not Channel:
+            qs = Equipment_Info.objects.all().order_by('-id')
+        elif username:
+            user = Device_User.objects.filter(username=username)
+            users = CommonService.qs_to_dict(user)
+            # 从字典类型中找到对应的pk值,即是userID
+            user_id = users.get('datas')[0].get('pk')
+            if not user.exists():
+                return response.json(104)
+
+            uid_list = Device_Info.objects.filter(userID_id=user_id).values_list('UID', flat=True)
+            if not len(uid_list):
+                return response.json(0, {'datas': [], 'count': 0})
+            qs = Equipment_Info.objects.filter(userID_id=user_id).order_by('-id')
+        elif uid:
+            qs = Equipment_Info.objects.filter(devUid=uid).order_by('-id')
+        elif devNickName:
+            qs = Equipment_Info.objects.filter(devNickName=devNickName).order_by('-id')
+        elif Channel:
+            qs = Equipment_Info.objects.filter(Channel=Channel).order_by('-id')
+        if not qs.exists():
+            return response.json(0, {'datas': [], 'count': 0})
+        count = qs.count()
+        res = qs[(page - 1) * line:page * line]
+        send_json = CommonService.qs_to_dict(res)
+        send_json['count'] = count
+        return response.json(0, send_json)
+
+    # 管理员的删除推送信息的接口
+    def delete_by_admin(self, request_dict, userID, response):
+        id_list = request_dict.getlist('id', None)
+        if not len(id_list):
+            return response.json(444, 'id is None or not list')
+        check_perm = ModelService.check_perm(userID=userID, permID=10)
+        if check_perm is True:
+            try:
+                is_delete = Equipment_Info.objects.filter(id__in=id_list).delete()
+            except Exception as e:
+                errorInfo = traceback.format_exc()
+                print(errorInfo)
+                return response.json(424, {'details': repr(e)})
+            else:
+                return response.json(0, {'delete_count': is_delete[0]})
+        else:
+            return response.json(404)
+
+
+    def delete_api(self, request_dict, userID, response):
+        equipment_info = request_dict.get('equipment_info', None)
+        content = json.loads(equipment_info)
+        search_kwargs = CommonService.get_kwargs(data=content)
+        # qs = Equipment_Info.objects.filter(**search_kwargs)
+        ei_qs = Equipment_Info.objects.filter(**search_kwargs)
+        ei_qs.delete()
+        ei_count = ei_qs.count()
+        while (ei_count > 1000):
+            ei_qs[0:1000].delete()
+'''
+use information_schema;
+select concat(round(sum(data_length/1024/1024),2),'MB') as data from tables where table_schema='Ansjer81';
+'''
+# 删除访问日志
+def deleteExpireEquipmentInfo(request):
+    response = ResponseObject()
+    import time
+    nowTime = int(time.time())
+    equipment_info_count = Equipment_Info.objects.filter(addTime__lte=str(nowTime - 3600 * 24 * 7)).count() # 删除7天前的数据
+    if equipment_info_count > 100000:   # 数据量大于10w才删
+        cycles_count = 10
+        try:
+            for i in range(cycles_count):
+                ei = Equipment_Info.objects.filter(addTime__lte=str(nowTime - 3600 * 24 * 7))[0:10000]
+                id_list = list(ei.values_list("id", flat=True))
+                Equipment_Info.objects.filter(id__in=id_list).delete()
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, repr(e))
+
+

+ 153 - 0
Controller/ShadowController.py

@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerPush
+@software: PyCharm
+@DATE: 2020/2/14 13:54
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: ShadowController.py
+@Contact: chanjunkai@163.com
+"""
+# 测试环境
+# test.shadow.dvema.com
+# 生产环境
+# shadow.dvema.com
+# 设备影子更新
+from Object.LogUtil import LogUtil
+from Object.ResponseObject import ResponseObject
+from Object.ETkObject import ETkObject
+import time
+from Model.models import Device_Info, UidSetModel, UID_Preview, VoicePromptModel
+from Service.CommonService import CommonService
+from django.http import JsonResponse
+from Object.UidTokenObject import UidTokenObject
+
+def generate_utk(request):
+    request.encoding = 'utf-8'
+    response = ResponseObject()
+    if request.method == 'GET':
+        request_dict = request.GET
+    elif request.method == 'POST':
+        request_dict = request.POST
+    else:
+        return response.json(444,'wrong method')
+    username = request_dict.get('username',None)
+    password = request_dict.get('password',None)
+    uid = request_dict.get('uid',None)
+    if username and password:
+        if username == 'debug_user' and password == 'debug_password':
+            # utko = UidTokenObject()
+            # # right
+            # utko.generate(data={'uid': uid})
+            etkObj = ETkObject(etk='')
+            etk = etkObj.encrypt(uid)
+            return response.json(0, {'etk': etk})
+        else:
+            return response.json(404)
+    else:
+        return response.json(444,'username password')
+
+
+# 设备影子更新
+def update_device_shadow(request):
+    request.encoding = 'utf-8'
+    response = ResponseObject()
+    if request.method == 'POST':
+        request_dict = request.POST
+    elif request.method == 'GET':
+        request_dict = request.GET
+    else:
+        return response.json(444)
+    etk = request_dict.get('etk', None)
+    eto = ETkObject(etk)
+    uid = eto.uid
+    if uid:
+        # 重置按钮
+        is_reset = request_dict.get('is_reset', None)
+        # 传1则重置设备信息
+        if is_reset == '1':
+            UidSetModel.objects.filter(uid=uid).delete()
+            # 重置设备,判断设备为已删除
+            Device_Info.objects.filter(UID=uid).update(isExist=2)
+            # 删除预览图
+            uid_pre_qs = UID_Preview.objects.filter(uid=uid)
+            if uid_pre_qs.exists():
+                uid_pre_qs.delete()
+
+            # 删除语音提示
+            voice_qs = VoicePromptModel.objects.filter(uid=uid)
+            if voice_qs.exists():
+                voice_qs.delete()
+
+        # return JsonResponse(status=200, data={'code': 0, 'msg': 'success', 'data': {}})
+        ucode = request_dict.get('ucode', None)
+        version = request_dict.get('version', None)
+        p2p_region = request_dict.get('p2p_region', None)
+        tz = request_dict.get('tz', None)
+        video_code = request_dict.get('video_code', None)
+        ip = CommonService.get_ip_address(request)
+        channel = request_dict.get('channel', None)
+        cloud_vod = request_dict.get('cloud_vod', None)
+        push_status = request_dict.get('push_status', None)
+        pwd = request_dict.get('pwd', None)
+        resetTime = request_dict.get('resetTime', None)
+        is_alexa = request_dict.get('is_alexa', None)
+        is_human = request_dict.get('is_human', None)
+        is_custom_voice = request_dict.get('is_custom', None)
+        double_wifi = request_dict.get('double_wifi', None)
+
+        us_qs = UidSetModel.objects.filter(uid=uid)
+        # 更新
+        nowTime = int(time.time())
+
+        print('-------')
+        print(resetTime)
+        print('-------')
+        qs_dict = {
+            'updTime': nowTime,
+            'ip': ip
+        }
+        if channel:
+            qs_dict['channel'] = channel
+        if p2p_region:
+            qs_dict['p2p_region'] = p2p_region
+        if ucode:
+            qs_dict['ucode'] = ucode
+        if version:
+            qs_dict['version'] = version
+        if tz:
+            qs_dict['tz'] = tz
+        if video_code:
+            qs_dict['video_code'] = video_code
+        if cloud_vod:
+            qs_dict['cloud_vod'] = cloud_vod
+        if push_status:
+            qs_dict['detect_status'] = push_status
+        if pwd:
+            qs_dict['pwd'] = pwd
+        if is_human:
+            qs_dict['is_human'] = is_human
+        if is_custom_voice:
+            qs_dict['is_custom_voice'] = is_custom_voice
+        if double_wifi:
+            qs_dict['double_wifi'] = double_wifi
+        if us_qs.exists():
+            if is_alexa and us_qs[0].is_alexa == 0:
+                qs_dict['is_alexa'] = is_alexa
+            us_qs.update(**qs_dict)
+            # 如果推送状态开启,返回推送url
+            return JsonResponse(status=200, data={'code': 0, 'msg': 'success', 'data': {}})
+        # 新增
+        else:
+            if is_alexa:
+                qs_dict['is_alexa'] = is_alexa
+            qs_dict['uid'] = uid
+            qs_dict['addTime'] = nowTime
+            UidSetModel.objects.create(**qs_dict)
+            # 如果推送状态开启,返回推送url
+            return JsonResponse(status=200, data={'code': 0, 'msg': 'success', 'data': {}})
+    else:
+        return JsonResponse(status=200, data={'code': 403, 'msg': 'error etk'})

+ 708 - 0
Controller/TestDetectController.py

@@ -0,0 +1,708 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerFormal
+@software: PyCharm
+@DATE: 2019/1/14 15:57
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: DetectController.py
+@Contact: chanjunkai@163.com
+"""
+import os
+import time
+
+import apns2
+import jpush as jpush
+import oss2
+from django.http import JsonResponse, HttpResponse
+from django.views.generic.base import View
+from pyfcm import FCMNotification
+from AnsjerPush.config import SERVER_TYPE, JPUSH_CODE, APNS_CODE, APP_TYPE
+from AnsjerPush.config import OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET, DETECT_PUSH_DOMAIN, JPUSH_CONFIG, FCM_CONFIG, \
+    APNS_CONFIG, BASE_DIR, APNS_MODE
+from Model.models import Equipment_Info, UidPushModel, SysMsgModel
+from Object.ETkObject import ETkObject
+from Object.RedisObject import RedisObject
+from Object.UidTokenObject import UidTokenObject
+from Service.CommonService import CommonService
+import json
+
+'''
+http://push.dvema.com/notify/push?etk=Y2lTRXhMTjBWS01sWlpURTVJU0ZWTlJ6RXhNVUU9T3o=&n_time=1526845794&channel=1&event_type=704&is_st=0
+http://push.dvema.com/deviceShadow/generateUTK?username=debug_user&password=debug_password&uid=VVDHCVBYDKFMJRWA111A
+'''
+
+
+# 移动侦测接口
+class NotificationView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request.GET, 0)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        return self.validation(request.POST, 1)
+
+    def validation(self, request_dict, request_type):
+        uidToken = request_dict.get('uidToken', None)
+        etk = request_dict.get('etk', None)
+        channel = request_dict.get('channel', '1')
+        n_time = request_dict.get('n_time', None)
+        event_type = request_dict.get('event_type', None)
+        is_st = request_dict.get('is_st', None)
+        # print("aaa")
+        # return JsonResponse(0,safe=False)
+        if not all([channel, n_time]):
+            return JsonResponse(status=200, data={
+                'code': 444,
+                'msg': 'param is wrong'})
+        if etk:
+            eto = ETkObject(etk)
+            uid = eto.uid
+            if len(uid) != 20:
+                return JsonResponse(status=200, data={'code': 404, 'msg': 'data is not exist'})
+        else:
+            utko = UidTokenObject(uidToken)
+            uid = utko.UID
+        pkey = '{uid}_{channel}_{event_type}_ptl'.format(uid=uid, event_type=event_type, channel=channel)
+        # ykey = 'MUJ887NLR8K8GBM9111A_redis_qs'.format(uid=uid)
+        ykey = '{uid}_redis_qs'.format(uid=uid)
+        dkey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=event_type, channel=channel)
+        # 判断redisObj.get_data(key=pkey):不为空
+        redisObj = RedisObject(db=6)
+        have_ykey = redisObj.get_data(key=ykey)  # uid_set 数据库缓存
+        have_pkey = redisObj.get_data(key=pkey)  # 一分钟限制key
+        have_dkey = redisObj.get_data(key=dkey)  # 推送类型限制
+
+        # 一分钟外,推送开启状态
+        detect_med_type = 0  # 0推送旧机制 1存库不推送,2推送存库
+        # 暂时注销
+        if have_pkey:
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'msg': 'Push once every 10 seconds'}
+            else:
+                res_data = {'code': 0, 'msg': 'Push it once a minute'}
+            return JsonResponse(status=200, data=res_data)
+
+        # 数据库读取数据
+        if have_ykey:
+            redis_list = eval(redisObj.get_data(key=ykey))
+        else:
+            # 从数据库查询出来
+            uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid, uid_set__detect_status=1). \
+                values('token_val', 'app_type', 'appBundleId', 'm_code',
+                       'push_type', 'userID_id', 'userID__NickName', 'userID__username',
+                       'lang', 'm_code', 'tz', 'uid_set__nickname', 'uid_set__detect_interval', 'uid_set__detect_group',
+                       'uid_set__channel')
+            # 新建一个list接收数据
+            redis_list = []
+            # 把数据库数据追加进redis_list
+            for qs in uid_push_qs:
+                redis_list.append(qs)
+            # 修改redis数据,并设置过期时间为10分钟
+            redisObj.set_data(key=ykey, val=str(redis_list), expire=600)
+            if not redis_list:
+                res_data = {'code': 404, 'msg': 'error !'}
+                return JsonResponse(status=200, data=res_data)
+
+            # 此时应该更新一下redis里面的dkey的有效时间
+            detect_interval = redis_list[0]['uid_set__detect_interval']
+            channel = redis_list[0]['uid_set__channel']
+            self.do_update_detect_interval(uid, channel, redisObj, detect_interval)
+
+
+        if not redis_list:
+            print("没有redi_list")
+            res_data = {'code': 0, 'msg': 'no redi_list success!'}
+            return JsonResponse(status=200, data=res_data)
+
+        is_sys_msg = self.is_sys_msg(int(event_type))
+        nickname = redis_list[0]['uid_set__nickname']
+        detect_interval = redis_list[0]['uid_set__detect_interval']
+        detect_group = redis_list[0]['uid_set__detect_group']
+        now_time = int(time.time())
+        if not nickname:
+            nickname = uid
+        print('detect_group')
+        print(detect_group)
+        print(detect_interval)
+        if detect_group:
+            if have_dkey:
+                detect_med_type = 1  # 1为存库不推送
+            else:
+                detect_med_type = 2  # 为2的话,既推送,又存库
+                # detect_group=0允许全部推送的时候
+                if detect_group == '0':
+                    redisObj.set_data(key=dkey, val=1, expire=detect_interval)
+                else:
+                    detect_group_list = detect_group.split(',')
+                    if event_type in detect_group_list:
+                        if detect_interval < 60:
+                            detect_interval = 60
+                        redisObj.set_data(key=dkey, val=1, expire=detect_interval)
+                # 改为1秒
+                # 如果不是正式
+                if SERVER_TYPE!="Ansjer.formal_settings":
+                    redisObj.set_data(key=pkey, val=1, expire=10)
+                else:
+                    redisObj.set_data(key=pkey, val=1, expire=60)
+
+            # 打印have_ykey
+        # return JsonResponse(status=200, data={'pkey': 0, 'have_ykey': have_ykey, 'have_pkey': have_pkey, 'have_ykey': have_dkey})
+
+        # 旧模式并且没有pkey,重新创建一个
+        if not detect_group and not have_pkey:
+            # 设置推送时间为60秒一次
+            # 如果不是正式
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                redisObj.set_data(key=pkey, val=1, expire=10)
+            else:
+                redisObj.set_data(key=pkey, val=1, expire=60)
+        auth = oss2.Auth(OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET)
+        bucket = oss2.Bucket(auth, 'oss-cn-shenzhen.aliyuncs.com', 'apg')
+        kwag_args = {
+            'uid': uid,
+            'channel': channel,
+            'event_type': event_type,
+            'n_time': n_time,
+            # 'appBundleId': appBundleId,
+            # 'token_val': token_val,
+            # 'msg_title': msg_title,
+            # 'msg_text': msg_text
+        }
+        eq_list = []
+        sys_msg_list = []
+        userID_ids = []
+        do_apns_code = ''
+        do_fcm_code = ''
+        do_jpush_code = ''
+        for up in redis_list:
+            push_type = up['push_type']
+            appBundleId = up['appBundleId']
+            token_val = up['token_val']
+            lang = up['lang']
+            tz = up['tz']
+            # 发送标题
+            msg_title = self.get_msg_title(appBundleId=appBundleId, nickname=nickname)
+            # 发送内容
+            msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                         event_type=event_type)
+            kwag_args['appBundleId'] = appBundleId
+            kwag_args['token_val'] = token_val
+            kwag_args['msg_title'] = msg_title
+            kwag_args['msg_text'] = msg_text
+            #推送
+            if detect_med_type == 2 or detect_med_type == 0:
+                if push_type == 0:  # ios apns
+                    print('do_apns')
+                    # self.do_apns(**kwag_args)
+                    do_apns_code = self.do_apns(**kwag_args)
+                    up['push_code'] = do_apns_code
+                elif push_type == 1:  # android gcm
+                    print('do_fcm')
+                    do_fcm_code = self.do_fcm(**kwag_args)
+                    up['push_code'] = do_fcm_code
+                elif push_type == 2:  # android jpush
+                    print('do_jpush')
+                    do_jpush_code = self.do_jpush(**kwag_args)
+                    up['push_code'] = do_jpush_code
+                    # return JsonResponse(status=200, data={'code': 0, '状态:': self.do_jpush(**kwag_args)})
+
+            if detect_med_type == 1:
+                do_apns_code = '只存库不推送'
+                do_fcm_code = '只存库不推送'
+                do_jpush_code = '只存库不推送'
+                up['push_code'] = -1
+
+            # 以下是存库
+            userID_id = up["userID_id"]
+            int_is_st = int(is_st)
+            if userID_id not in userID_ids:
+                eq_list.append(Equipment_Info(
+                    userID_id=userID_id,
+                    eventTime=n_time,
+                    eventType=event_type,
+                    devUid=uid,
+                    devNickName=nickname,
+                    Channel=channel,
+                    alarm='Motion \tChannel:{channel}'.format(channel=channel),
+                    is_st=int_is_st,
+                    receiveTime=n_time,
+                    addTime=now_time
+                ))
+                if is_sys_msg:
+                    sys_msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                                     event_type=event_type, is_sys=1)
+                    sys_msg_list.append(SysMsgModel(
+                        userID_id=userID_id,
+                        msg=sys_msg_text,
+                        addTime=now_time,
+                        updTime=now_time,
+                        uid=uid,
+                        eventType=event_type))
+                userID_ids.append(userID_id)
+        if is_sys_msg:
+            SysMsgModel.objects.bulk_create(sys_msg_list)
+        Equipment_Info.objects.bulk_create(eq_list)
+
+        if is_st == '0' or is_st == '2':
+            print("is_st=0or2")
+            for up in redis_list:
+                # if up['push_type'] == 0:  # ios apns
+                #     up['do_apns_code'] = do_apns_code
+                # elif up['push_type'] == 1:  # android gcm
+                #     up['do_fcm_code'] = do_fcm_code
+                # elif up['push_type'] == 2:  # android jpush
+
+                try:
+
+                    code = up['push_code']
+                    print("push_code is ")
+                    print(code)
+                    if up['push_type'] == 0:
+                        up['push_res'] = '{code} {desc}'.format(code=code, desc=APNS_CODE[code])
+                        up['push_type'] = '苹果推送'
+                    elif up['push_type'] == 1:
+                        up['push_res'] = code
+                        up['push_type'] = '谷歌推送'
+                    elif up['push_type'] == 2:
+                        up['push_res'] = '{code} {desc}'.format(code=code, desc=JPUSH_CODE[code])
+                        up['push_type'] = '极光推送'
+
+
+                except KeyError as ke:
+                    print(ke)
+                else:
+                    del up['push_code']
+
+                #up['test_or_www'] = SERVER_TYPE
+                if SERVER_TYPE == 'Ansjer.formal_settings':
+                    up['server_type'] = '正式服务器'
+                else:
+                    up['server_type'] = '测试服务器'
+
+                up['app_type'] = APP_TYPE[up['app_type']]
+
+                # del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+
+                if request_type == 0:
+                    data = ""
+                    for up in redis_list:
+                        data += ("<p>" + repr(up) + "</p>")
+                    return HttpResponse(repr(data))
+                else:
+                    return JsonResponse(status=200, data={'code': 0, 'msg': 'success 0 or 2', 're_list': redis_list})
+
+        elif is_st == '1':
+            print("is_st=1")
+            # Endpoint以杭州为例,其它Region请按实际情况填写。
+            obj = '{uid}/{channel}/{filename}.jpeg'.format(uid=uid, channel=channel, filename=n_time)
+            # 设置此签名URL在60秒内有效。
+            url = bucket.sign_url('PUT', obj, 7200)
+            for up in redis_list:
+                up['do_apns_code'] = do_apns_code
+                up['do_fcm_code'] = do_fcm_code
+                up['do_jpush_code'] = do_jpush_code
+                up['test_or_www'] = SERVER_TYPE
+                del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+                # 不是正式服务器
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'img_push': url, 'msg': 'success', 're_list': redis_list}
+            else:
+                # 是正式服务器的时候
+                res_data = {'code': 0, 'img_push': url, 'msg': 'success'}
+
+            return JsonResponse(status=200, data=res_data)
+
+        elif is_st == '3':
+            print("is_st=3")
+            # 人形检测带动图
+            # Endpoint以杭州为例,其它Region请按实际情况填写。
+            img_url_list = []
+            for i in range(int(is_st)):
+                obj = '{uid}/{channel}/{filename}_{st}.jpeg'. \
+                    format(uid=uid, channel=channel, filename=n_time, st=i)
+                # 设置此签名URL在60秒内有效。
+                url = bucket.sign_url('PUT', obj, 7200)
+                img_url_list.append(url)
+
+            for up in redis_list:
+                up['do_apns_code'] = do_apns_code
+                up['do_fcm_code'] = do_fcm_code
+                up['do_jpush_code'] = do_jpush_code
+                up['test_or_www'] = SERVER_TYPE
+                del up['push_type']
+                del up['userID_id']
+                del up['userID__NickName']
+                del up['lang']
+                del up['tz']
+                del up['uid_set__nickname']
+                del up['uid_set__detect_interval']
+                del up['uid_set__detect_group']
+
+            # 不是正式服务器
+            if SERVER_TYPE != "Ansjer.formal_settings":
+                res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3', 're_list': redis_list}
+            else:
+                # 是正式服务器的时候
+                res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3'}
+            return JsonResponse(status=200, data=res_data)
+
+
+    def get_msg_title(self, appBundleId, nickname):
+        package_title_config = {
+            'com.ansjer.customizedd_a': 'DVS',
+            'com.ansjer.zccloud_a': 'ZosiSmart',
+            'com.ansjer.zccloud_ab': '周视',
+            'com.ansjer.adcloud_a': 'ADCloud',
+            'com.ansjer.adcloud_ab': 'ADCloud',
+            'com.ansjer.accloud_a': 'ACCloud',
+            'com.ansjer.loocamccloud_a': 'Loocam',
+            'com.ansjer.loocamdcloud_a': 'Anlapus',
+            'com.ansjer.customizedb_a': 'COCOONHD',
+            'com.ansjer.customizeda_a': 'Guardian365',
+            'com.ansjer.customizedc_a': 'PatrolSecure',
+        }
+        if appBundleId in package_title_config.keys():
+            return package_title_config[appBundleId] + '(' + nickname + ')'
+        else:
+            return nickname
+
+    def is_sys_msg(self, event_type):
+        event_type_list = [702, 703, 704]
+        if event_type in event_type_list:
+            return True
+        return False
+
+    def get_msg_text(self, channel, n_time, lang, tz, event_type, is_sys=0):
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz,lang=lang)
+        etype = int(event_type)
+        if lang == 'cn':
+            if etype == 704:
+                msg_type = '电量过低'
+            elif etype == 702:
+                msg_type = '摄像头休眠'
+            elif etype == 703:
+                msg_type = '摄像头唤醒'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} 通道:{channel}'.format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+                # send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+        else:
+            if etype == 704:
+                msg_type = 'Low battery'
+            elif etype == 702:
+                msg_type = 'Camera sleep'
+            elif etype == 703:
+                msg_type = 'Camera wake'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} channel:{channel}'. \
+                    format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} channel:{channel} date:{date}'. \
+                    format(msg_type=msg_type, channel=channel, date=n_date)
+        return send_text
+
+    def do_jpush(self, uid, channel, appBundleId, token_val, event_type, n_time,
+                 msg_title, msg_text):
+        app_key = JPUSH_CONFIG[appBundleId]['Key']
+        master_secret = JPUSH_CONFIG[appBundleId]['Secret']
+        # 此处换成各自的app_key和master_secre
+        _jpush = jpush.JPush(app_key, master_secret)
+        push = _jpush.create_push()
+        # if you set the logging level to "DEBUG",it will show the debug logging.
+        # _jpush.set_logging("DEBUG")
+        # push.audience = jpush.all_
+        push.audience = jpush.registration_id(token_val)
+        push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                     "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+        android = jpush.android(alert=msg_text, priority=1, style=1, alert_type=7,
+                                big_text=msg_text, title=msg_title,
+                                extras=push_data)
+        push.notification = jpush.notification(android=android)
+        push.platform = jpush.all_
+        res = push.send()
+        print(res)
+        return res.status_code
+        # try:
+        #     res = push.send()
+        #     print(res)
+        # except Exception as e:
+        #     print("jpush fail")
+        #     print("Exception")
+        #     print(repr(e))
+        #     return
+        # else:
+        #     print("jpush success")
+        #     return
+
+    def do_fcm(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
+        try:
+            serverKey = FCM_CONFIG[appBundleId]
+        except Exception as e:
+            return 'serverKey abnormal'
+        push_service = FCMNotification(api_key=serverKey)
+        data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+        result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
+                                                   message_body=msg_text, data_message=data,
+                                                   extra_kwargs={
+                                                       'default_vibrate_timings': True,
+                                                       'default_sound': True,
+                                                       'default_light_settings': True
+                                                   })
+        print('fcm push ing')
+        print(result)
+        return result
+
+    def do_apns(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title,
+                msg_text):
+        try:
+            cli = apns2.APNSClient(mode=APNS_MODE,
+                                   client_cert=os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
+
+            push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
+                         "received_at": n_time, "sound": "sound.aif", "uid": uid, "zpush": "1", "channel": channel}
+            alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
+            payload = apns2.Payload(alert=alert, custom=push_data)
+
+            # return uid, channel, appBundleId, str(token_val), event_type, n_time, msg_title,msg_text
+            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
+            res = cli.push(n=n, device_token=token_val, topic=appBundleId)
+            print(res.status_code)
+
+            #     200, 推送成功。
+            #   400, 请求有问题。
+            #   403, 证书或Token有问题。
+            #   405, 请求方式不正确, 只支持POST请求
+            #   410, 设备的Token与证书不一致
+            if res.status_code == 200:
+                return res.status_code
+            else:
+                print('apns push fail')
+                print(res.reason)
+                return res.status_code
+        except (ValueError, ArithmeticError):
+            return 'The program has a numeric format exception, one of the arithmetic exceptions'
+        except Exception as e:
+            print(repr(e))
+            return repr(e)
+
+
+    def getJPushReport(self, msg_id, appBundleId):
+        app_key = JPUSH_CONFIG[appBundleId]['Key']
+        master_secret = JPUSH_CONFIG[appBundleId]['Secret']
+        _jpush = jpush.JPush(app_key, master_secret)
+        report = _jpush.create_report()
+        res = report.get_received_detail(msg_id)
+        print("getJPushReport")
+        print(res)
+
+    def do_update_detect_interval(self, uid, channel, redisObject, detect_interval):
+        if channel == 0:
+            channel = 17
+        else:
+            channel += 1
+        for i in range(1, channel):
+            tmpDKey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=51, channel=i)
+            if tmpDKey is not False:
+                llt = redisObject.get_ttl(tmpDKey)
+                if llt > detect_interval:
+                    redisObject.set_data(key=tmpDKey, val=1, expire=detect_interval)
+
+            tmpDKey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, event_type=54, channel=i)
+            if tmpDKey is not False:
+                llt = redisObject.get_ttl(tmpDKey)
+                if llt > detect_interval:
+                    redisObject.set_data(key=tmpDKey, val=1, expire=detect_interval)
+
+
+
+# http://test.dvema.com/detect/add?uidToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJQMldOR0pSRDJFSEE1RVU5MTExQSJ9.xOCI5lerk8JOs5OcAzunrKCfCrtuPIZ3AnkMmnd-bPY&n_time=1526845794&channel=1&event_type=51&is_st=0
+# 移动侦测接口
+class PushNotificationView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        # operation = kwargs.get('operation')
+        return self.validation(request.GET)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        # operation = kwargs.get('operation')
+        return self.validation(request.POST)
+
+    def validation(self, request_dict):
+        etk = request_dict.get('etk', None)
+        channel = request_dict.get('channel', '1')
+        n_time = request_dict.get('n_time', None)
+        event_type = request_dict.get('event_type', None)
+        is_st = request_dict.get('is_st', None)
+        eto = ETkObject(etk)
+        uid = eto.uid
+        if len(uid) == 20:
+            redisObj = RedisObject(db=6)
+            # pkey = '{uid}_{channel}_ptl'.format(uid=uid, channel=channel)
+            pkey = '{uid}_ptl'.format(uid=uid)
+            ykey = '{uid}_redis_qs'.format(uid=uid)
+            if redisObj.get_data(key=pkey):
+                res_data = {'code': 0, 'msg': 'success,!33333333333'}
+                return JsonResponse(status=200, data=res_data)
+            else:
+                redisObj.set_data(key=pkey, val=1, expire=60)
+            ##############
+            redis_data = redisObj.get_data(key=ykey)
+            if redis_data:
+                redis_list = eval(redis_data)
+            else:
+                # 设置推送时间为60秒一次
+                redisObj.set_data(key=pkey, val=1, expire=60)
+                print("从数据库查到数据")
+                # 从数据库查询出来
+                uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid, uid_set__detect_status=1). \
+                    values('token_val', 'app_type', 'appBundleId',
+                           'push_type', 'userID_id', 'lang','m_code',
+                           'tz', 'uid_set__nickname')
+                # 新建一个list接收数据
+                redis_list = []
+                # 把数据库数据追加进redis_list
+                for qs in uid_push_qs:
+                    redis_list.append(qs)
+                # 修改redis数据,并设置过期时间为10分钟
+            if redis_list:
+                redisObj.set_data(key=ykey, val=str(redis_list), expire=600)
+                auth = oss2.Auth(OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET)
+                bucket = oss2.Bucket(auth, 'oss-cn-shenzhen.aliyuncs.com', 'apg')
+                self.do_bulk_create_info(redis_list, n_time, channel, event_type, is_st, uid)
+                if is_st == '0' or is_st == '2':
+                    return JsonResponse(status=200, data={'code': 0, 'msg': 'success44444444444444444'})
+                elif is_st == '1':
+                    # Endpoint以杭州为例,其它Region请按实际情况填写。
+                    obj = '{uid}/{channel}/{filename}.jpeg'.format(uid=uid, channel=channel, filename=n_time)
+                    # 设置此签名URL在60秒内有效。
+                    url = bucket.sign_url('PUT', obj, 7200)
+                    res_data = {'code': 0, 'img_push': url, 'msg': 'success'}
+                    return JsonResponse(status=200, data=res_data)
+                elif is_st == '3':
+                    # 人形检测带动图
+                    img_url_list = []
+                    for i in range(int(is_st)):
+                        obj = '{uid}/{channel}/{filename}_{st}.jpeg'. \
+                            format(uid=uid, channel=channel, filename=n_time, st=i)
+                        # 设置此签名URL在60秒内有效。
+                        url = bucket.sign_url('PUT', obj, 7200)
+                        img_url_list.append(url)
+                    res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success'}
+                    return JsonResponse(status=200, data=res_data)
+            else:
+                return JsonResponse(status=200, data={'code': 404, 'msg': 'data is not exist'})
+        else:
+            return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong etk'})
+
+    def do_bulk_create_info(self, uaqs, n_time, channel, event_type, is_st, uid):
+        now_time = int(time.time())
+        # 设备昵称
+        userID_ids = []
+        sys_msg_list = []
+        is_sys_msg = self.is_sys_msg(int(event_type))
+        is_st = int(is_st)
+        eq_list = []
+        nickname = uaqs[0]['uid_set__nickname']
+        if not nickname:
+            nickname = uid
+        for ua in uaqs:
+            lang = ua['lang']
+            tz = ua['tz']
+            userID_id = ua["userID_id"]
+            if userID_id not in userID_ids:
+                eq_list.append(Equipment_Info(
+                    userID_id=userID_id,
+                    eventTime=n_time,
+                    eventType=event_type,
+                    devUid=uid,
+                    devNickName=nickname,
+                    Channel=channel,
+                    alarm='Motion \tChannel:{channel}'.format(channel=channel),
+                    is_st=is_st,
+                    receiveTime=n_time,
+                    addTime=now_time
+                ))
+                if is_sys_msg:
+                    sys_msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
+                                                     event_type=event_type, is_sys=1)
+                    sys_msg_list.append(SysMsgModel(
+                        userID_id=userID_id,
+                        msg=sys_msg_text,
+                        addTime=now_time,
+                        updTime=now_time,
+                        uid=uid,
+                        eventType=event_type))
+        if eq_list:
+            print('eq_list')
+            Equipment_Info.objects.bulk_create(eq_list)
+        if is_sys_msg:
+            print('sys_msg')
+            SysMsgModel.objects.bulk_create(sys_msg_list)
+        return True
+
+    def is_sys_msg(self, event_type):
+        event_type_list = [702, 703, 704]
+        if event_type in event_type_list:
+            return True
+        return False
+
+    def get_msg_text(self, channel, n_time, lang, tz, event_type, is_sys=0):
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz)
+        etype = int(event_type)
+        if lang == 'cn':
+            if etype == 704:
+                msg_type = '电量过低'
+            elif etype == 702:
+                msg_type = '摄像头休眠'
+            elif etype == 703:
+                msg_type = '摄像头唤醒'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} 通道:{channel}'.format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} 通道:{channel} 日期:{date}'.format(msg_type=msg_type, channel=channel, date=n_date)
+        else:
+            if etype == 704:
+                msg_type = 'Low battery'
+            elif etype == 702:
+                msg_type = 'Camera sleep'
+            elif etype == 703:
+                msg_type = 'Camera wake'
+            else:
+                msg_type = ''
+            if is_sys:
+                send_text = '{msg_type} channel:{channel}'. \
+                    format(msg_type=msg_type, channel=channel)
+            else:
+                send_text = '{msg_type} channel:{channel} date:{date}'. \
+                    format(msg_type=msg_type, channel=channel, date=n_date)
+        return send_text

+ 474 - 0
Model/models.py

@@ -0,0 +1,474 @@
+from itertools import chain
+
+from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
+from django.db import models
+# from django.utils import six
+# from django.utils.encoding import python_2_unicode_compatible
+from six import python_2_unicode_compatible
+from imagekit.models import ProcessedImageField
+from imagekit.processors import ResizeToFill
+import six
+from AnsjerPush.config import SERVER_DOMAIN
+
+
+class PermissionsManager(models.Manager):
+    use_in_migrations = True
+
+    def get_by_natural_key(self, permName):
+        return self.get(
+            permName=permName
+        )
+
+
+class RoleManager(models.Manager):
+    """
+    The manager for the auth's Role model.
+    """
+    use_in_migrations = True
+
+    def get_by_natural_key(self, roleName):
+        return self.get(roleName=roleName)
+
+
+class UserManager(BaseUserManager):
+
+    def create_user(self, username, password, userID, is_active,
+                    user_isValid, **extra_fields):
+        user = self.model(
+            username=username,
+            userID=userID,
+            is_active=is_active,
+            user_isValid=user_isValid,
+            **extra_fields
+        )
+        user.set_password(password)
+        user.save(using=self._db)
+
+        role = Role.objects.get(rid=1)
+        user.role.add(role)
+        return user
+
+    def create_superuser(self, username, password, userID, is_active,
+                         user_isValid, **extra_fields):
+        # extra_fields.setdefault('is_superuser', is_superuser)
+        is_superuser = extra_fields.get('is_superuser', None)
+        if is_superuser != 100 and is_superuser != 1:
+            raise ValueError('Superuser must have is_superuser=1 or 100.')
+
+        return self.create_user(username, password, userID, is_active,
+                                user_isValid, **extra_fields)
+
+
+@python_2_unicode_compatible
+class Permissions(models.Model):
+    permName = models.CharField(blank=True, null=True, max_length=32, unique=True, verbose_name=u'权限名称')
+    description = models.CharField(blank=True, null=True, max_length=128, verbose_name=u'描述信息', default='')
+    objects = PermissionsManager()
+
+    def __str__(self):
+        return "%s" % (
+            six.text_type(self.description))
+
+    class Meta:
+        ordering = ['permName']
+        db_table = 'permissions'
+        verbose_name = u'role permission'
+        verbose_name_plural = verbose_name
+        unique_together = (('permName'),)
+        app_label = "db1"
+    def natural_key(self):
+        return (self.permName)
+
+
+class Role(models.Model):
+    rid = models.SmallIntegerField(primary_key=True, unique=True, verbose_name=u'用户角色组ID')
+    roleName = models.CharField(max_length=32, unique=True,
+                                default='User', verbose_name=u'角色名称')
+    permission = models.ManyToManyField(to='Permissions', blank=True, verbose_name=u'权限', db_table='role_permissions')
+    Description = models.TextField(blank=True, default='', verbose_name=u'描述信息')
+
+    objects = RoleManager()
+
+    class Meta:
+        ordering = ['roleName']
+        db_table = 'role'
+        verbose_name = u'用户角色'
+        verbose_name_plural = verbose_name
+        app_label = "db1"
+
+    def __str__(self):
+        return self.roleName
+
+    def natural_key(self):
+        return (self.roleName,)
+
+    def get_all_permission(self):
+        perms = self.permission.all()
+
+        permslist = []
+        if self.rid == 100:
+            perms = Permissions.objects.all()
+            for perm in perms:
+                permslist.append(perm.permName)
+            return permslist
+        else:
+            permlist_tmp = []
+            for perm in perms:
+                permlist_tmp.append(perm.permName)
+            permSet = set(permlist_tmp)
+            permslist = list(permSet)
+            permslist.sort()
+            return permslist
+
+
+class Device_User(AbstractBaseUser):
+    userID = models.CharField(blank=True, max_length=32, primary_key=True,
+                              verbose_name=u'用户ID', unique=True)
+    role = models.ManyToManyField(to='Role', blank=True, verbose_name=u'用户角色', db_table='user_role')
+
+    username = models.CharField(max_length=64, verbose_name=u'用户名', default='', blank=True)
+    password = models.CharField(max_length=128, verbose_name=u'密码')
+    userEmail = models.EmailField(max_length=64, verbose_name=u'邮箱', default='', blank=True)
+    # 实际的路径就是 MEDIA_ROOT/Image/filename,所以可用upload_to来指定文件存放的前缀路径
+    userIconPath = ProcessedImageField(blank=True, upload_to='User/Images/', default='static/User/default.png',
+                                       verbose_name=u'头像',
+                                       # 图片将处理成85x85的尺寸
+                                       processors=[ResizeToFill(85, 85)], )
+    userIconUrl = models.URLField(blank=True, max_length=128, default=SERVER_DOMAIN
+                                                                      + 'account/getAvatar/User/defaultUser.png')
+    NickName = models.CharField(blank=True, max_length=64, default='', verbose_name=u'用户昵称')
+    is_superuser = models.IntegerField(blank=True, default=0, verbose_name=u'用户类型')
+    is_active = models.BooleanField(blank=True, default=False, verbose_name=u'用户活动状态')
+    data_joined = models.DateTimeField(blank=True, verbose_name=u'加入时间', auto_now_add=True)
+    last_login = models.DateTimeField(blank=True, verbose_name='u最后登录时间', auto_now=True)
+    user_isValid = models.BooleanField(blank=True, default=False, verbose_name=u'用户有效性', help_text=u'是否是认证通过的用户')
+    online = models.BooleanField(blank=True, default=False, verbose_name=u'用户登录状态')
+    machine_code = models.CharField(blank=True, max_length=128, default='', verbose_name=u'机械码')
+
+    language = models.CharField(blank=True, max_length=16, default='en', verbose_name=u'语言地区')
+    # 手机注册增加字段
+    phone = models.CharField(max_length=16, verbose_name=u'手机号', default='', blank=True)
+
+    objects = UserManager()
+
+    USERNAME_FIELD = 'userID'  # 必须有一个唯一标识
+    REQUIRED_FIELDS = ['is_superuser']  # 创建superuser时的必须字段
+
+    class Meta:
+        ordering = ('-data_joined',)
+        verbose_name = u'用户信息'
+        db_table = 'device_user'
+        get_latest_by = 'last_login'
+        app_label = "db1"
+
+    def __str__(self):
+        return self.username
+
+    def get_role_id(self):
+        rids = []
+        roles = self.role.all()
+        for role in roles:
+            rids.append(role.rid)
+        return rids
+
+    def get_all_permission(self):
+        roles = self.role.all()
+        perms = self.permission.all()
+
+        permslist = []
+        for role in roles:
+            if role.rid == 100:
+                perms = Permissions.objects.all()
+                for perm in perms:
+                    permslist.append(perm.permName)
+                return permslist
+
+        for perm in perms:
+            permslist.append(perm.permName)
+
+        permSet = set(permslist)
+        for role in roles:
+            permlist_tmp = []
+            for perm in role.permission.all():
+                permlist_tmp.append(perm.permName)
+
+            permSet_tmp = set(permlist_tmp)
+            permSet = permSet.union(permSet_tmp)
+            permslist = list(permSet)
+            permslist.sort()
+            return permslist
+
+    @property
+    def is_staff(self):
+        return self.is_superuser
+
+
+class Device_Info(models.Model):
+    id = models.CharField(blank=True, max_length=32, primary_key=True)
+    userID = models.ForeignKey(Device_User, blank=True, to_field='userID', on_delete=models.CASCADE)
+    NickName = models.CharField(blank=True, max_length=32, verbose_name=u'设备名称')
+    UID = models.CharField(blank=True, max_length=32, verbose_name=u'设备UID', default='')
+    SID = models.CharField(blank=True, max_length=32, verbose_name=u'服务器ID', default='')
+    View_Account = models.CharField(blank=True, max_length=32, verbose_name=u'设备用户名称', default='')
+    View_Password = models.CharField(blank=True, max_length=32, verbose_name=u'设备密码', default='')
+    AudioFormat = models.IntegerField(blank=True, default=1, verbose_name=u'初始化影像')
+    EventNotification = models.IntegerField(blank=True, verbose_name=u'报警类型', default=0)
+    ChannelIndex = models.IntegerField(blank=True, verbose_name=u'通道号', default=0)
+    Online = models.BooleanField(blank=True, default=False, verbose_name=u'设备是否在线')
+    mMonitorIndex = models.IntegerField(blank=True, default=-1)
+    Type = models.IntegerField(blank=True, verbose_name='设备类型')
+    DebugMode = models.IntegerField(blank=True, verbose_name=u'调试模式', default=0)
+    NotificationMode = models.IntegerField(blank=True, verbose_name=u'是否报警', default=0)
+    qvgaLevel = models.BooleanField(blank=True, verbose_name=u'SD与HD画面', default=0,
+                                    help_text=u'0代表:SD、1代表:HD')
+    isShare = models.BooleanField(blank=True, verbose_name=u'共享设备',
+                                  help_text=u'是否为共享获取的设备', default=False)
+    primaryUserID = models.CharField(blank=True, verbose_name='主用户id', max_length=32, default='')
+    primaryMaster = models.CharField(max_length=64, verbose_name=u'主用户名', default='')
+    data_joined = models.DateTimeField(blank=True, verbose_name=u'设备添加时间', auto_now_add=True)
+    update_time = models.DateTimeField(blank=True, verbose_name=u'更新时间', auto_now=True, null=True)
+
+    ip = models.CharField(blank=True, max_length=20, default='', verbose_name=u'设备区域ip')
+    area = models.CharField(blank=True, max_length=100, default='', verbose_name=u'设备区域area')
+    # ios要求新增字段
+    version = models.CharField(blank=True, max_length=20, default='', verbose_name=u'版本号')
+    iSNotification = models.BooleanField(blank=True, verbose_name=u'报警通知 0:关闭,1:开启)', default=False)
+    isVod = models.SmallIntegerField(blank=True, default=0, verbose_name='是否支持云存')  # 是否支持云存设备
+    isExist = models.SmallIntegerField(blank=True, default=1, verbose_name='是否被删除')  # 是否被删除了(需主用户交互) 1存在,0不存在,2设备被重置
+    ###
+    REQUIRED_FIELDS = []
+
+    def __str__(self):
+        return self.NickName
+
+    def model_to_dict(self, fields=None, exclude=None):
+        opts = self._meta
+        data = {}
+        for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
+            if not getattr(f, 'editable', False):
+                continue
+            if fields and f.name not in fields:
+                continue
+            if exclude and f.name in exclude:
+                continue
+            data[f.name] = f.value_from_object(self)
+        return data
+
+    class Meta:
+        db_table = 'device_info'
+        ordering = ('-data_joined',)
+        verbose_name = u'用户设备信息表'
+        verbose_name_plural = verbose_name
+        app_label = "db1"
+
+
+class Equipment_Info(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    devUid = models.CharField(default='', blank=True, max_length=32, verbose_name=u'设备ID')
+    devNickName = models.CharField(blank=True, max_length=32, default='', verbose_name=u'设备昵称')
+    Channel = models.IntegerField(default=1, blank=True, verbose_name=u'设备通道')
+    eventType = models.IntegerField(default=0, blank=True, verbose_name=u'事件类型')
+    status = models.BooleanField(blank=True, default=False, verbose_name=u'事件状态')
+    alarm = models.CharField(blank=True, max_length=256, verbose_name=u'报警信息')
+    eventTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'设备报警时间')
+    receiveTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'接收到报警时间')
+    userID_id =  models.CharField(default='', blank=True, max_length=32, verbose_name=u'用户ID')
+    is_st = models.SmallIntegerField(default=0, verbose_name='是否截图')  # 0 否,1 是图,2,视频
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'equipment_info'
+        verbose_name = u'设备信息推送表'
+        verbose_name_plural = verbose_name
+        ordering = ('-id',)
+        app_label = "db2"
+
+
+# 系统发送信息新到用户
+class SysMsgModel(models.Model):
+    #更新
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    userID_id =  models.CharField(default='', blank=True, max_length=32, verbose_name=u'用户ID')
+    msg = models.TextField(blank=True, default='', verbose_name=u'消息发送内容')
+    status = models.SmallIntegerField(verbose_name='是否已读', default=0)  # 0:否,1:是
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    updTime = models.IntegerField(verbose_name='更新时间', default=0)
+    eventType = models.IntegerField(verbose_name='消息类型', default=0) # 默认系统消息类型,0系统消息,1 ipcamera消息
+    uid = models.CharField(default='', max_length=20, db_index=True, verbose_name='设备UID')
+
+    class Meta:
+        db_table = 'sys_msg'
+        verbose_name = '系统消息'
+        verbose_name_plural = verbose_name
+        ordering = ('-id',)
+        app_label = "db2"
+
+# 设备推送重构
+# 设备配置表,新
+class UidSetModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    uid = models.CharField(max_length=20, db_index=True, unique=True, verbose_name='设备UID')
+    channel = models.SmallIntegerField(default=0, verbose_name='通道数量')  #
+    detect_status = models.SmallIntegerField(default=0, verbose_name='推送开关')  # 状态[0:关闭,1:开启,2:用户解绑]
+    detect_interval = models.IntegerField(verbose_name='推送间隔', default=60)  # 秒
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    updTime = models.IntegerField(verbose_name='更新时间', default=0)
+    ucode = models.CharField(max_length=32, verbose_name='设备产品码', default='')
+    version = models.CharField(max_length=32, verbose_name='设备版本', default='')
+    p2p_region = models.CharField(max_length=16, verbose_name='设备p2p区域', default='ALL')  # ALL CN EU US
+    cloud_vod = models.SmallIntegerField(default=2, verbose_name='云存开关')  # 0,关闭,1开启,2,不支持
+    tz = models.CharField(default='', max_length=16, verbose_name='设备时区')  # +8
+    video_code = models.SmallIntegerField(default=0, verbose_name='编码类型')  # 0:264,1:265
+    nickname = models.CharField(default='', max_length=64, verbose_name='设备昵称')
+    ip = models.CharField(max_length=20, default='', verbose_name=u'设备ip')
+    # 设备重置后第一次启动时间
+    is_alexa = models.IntegerField(default=0, verbose_name='是否支持alexa')  # 0为不支持,1为支持,2为开启alexa发现
+    detect_group = models.CharField(default='', max_length=32, verbose_name=u'检测类型')
+    pwd = models.CharField(max_length=32, default='', verbose_name=u'设备密码')  # 暂时是预留字段
+    resetTime = models.IntegerField(default=0, verbose_name='设备重置时间')
+    region_alexa = models.CharField(max_length=8, verbose_name='设备alexa区域', default='ALL')  # ALL CN EU US
+    deviceModel = models.CharField(blank=True, max_length=64, default='', verbose_name=u'设备型号')
+    TimeZone = models.CharField(blank=True, max_length=50, default='', verbose_name=u'时区')
+    TimeStatus = models.SmallIntegerField(default=0, verbose_name='同步手机时间开关。0:关闭,1:开启')
+    SpaceUsable = models.CharField(blank=True, max_length=20, default='', verbose_name=u'可用空间')
+    SpaceSum = models.CharField(blank=True, max_length=20, default='', verbose_name=u'总空间')
+    MirrorType = models.IntegerField(blank=True, default=0, verbose_name=u'镜像类型 0:关闭镜像,1:上下镜像,2:左右镜像,3:上下左右镜像')
+    RecordType = models.IntegerField(blank=True, default=0, verbose_name=u'录像模式(0:关闭,1:全是录像,3:报警录像)')
+    OutdoorModel = models.IntegerField(blank=True, default=0, verbose_name=u'室外模式 0:关闭,1:开启')
+    WIFIName = models.CharField(blank=True, max_length=50, default='', verbose_name=u'无线名称')
+    isDetector = models.SmallIntegerField(default=0, verbose_name=u'侦测开关0:关闭,1:开启')
+    DetectorRank = models.IntegerField(default=0, verbose_name=u'侦测灵敏度 0:低,1:中,2:高,3:最高')
+    is_human = models.IntegerField(default=0, verbose_name='是否支持人形追踪。0:不支持,1:支持')
+    is_custom_voice = models.IntegerField(default=0, verbose_name='是否支持自定义语音。0:不支持,1:支持')
+    double_wifi = models.IntegerField(default=0, verbose_name='是否支持双频wifi。0:不支持,1:支持')
+
+    class Meta:
+        db_table = 'uid_set'
+        verbose_name = u'设备配置表'
+        verbose_name_plural = verbose_name
+        ordering = ('id',)
+        app_label = "db1"
+
+
+# 设备关联用户推送
+class UidPushModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增id')
+    userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)
+    uid_set = models.ForeignKey(UidSetModel, to_field='id', on_delete=models.CASCADE)
+    appBundleId = models.CharField(blank=True, max_length=32, verbose_name=u'appID')
+    app_type = models.IntegerField(default=0, verbose_name=u'app类型 1:ios,2:安卓')
+    push_type = models.IntegerField(default=0, verbose_name=u'推送类型')  # 0,apns 1,安卓gcm 2,极光
+    token_val = models.CharField(default='', max_length=160, verbose_name=u'设备验证令牌')
+    m_code = models.CharField(default='', max_length=64, verbose_name='手机唯一标识')
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    updTime = models.IntegerField(verbose_name='更新时间', default=0)
+    lang = models.CharField(max_length=8, verbose_name='推送语言类型', default='en')  # en英文 cn中文
+    tz = models.CharField(max_length=8, verbose_name='utc时区', default='0')
+
+    class Meta:
+        db_table = 'uid_push'
+        verbose_name = '设备绑定'
+        verbose_name_plural = verbose_name
+        ordering = ('-id',)
+        app_label = "db1"
+
+
+class UID_Preview(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    uid = models.CharField(default='', max_length=20, db_index=True, verbose_name='设备UID')
+    channel = models.SmallIntegerField(verbose_name=u'通道号', default=0)
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    updTime = models.IntegerField(verbose_name='更新时间', default=0)
+
+    class Meta:
+        db_table = 'uid_preview'
+        verbose_name = '设备预览图'
+        verbose_name_plural = verbose_name
+        ordering = ('id',)
+        app_label = "db1"
+
+class Access_Log(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    user = models.CharField(max_length=100, default='', db_index=True, blank=True, verbose_name=u'用户')
+    operation = models.CharField(max_length=100, db_index=True, default='', blank=True, verbose_name=u'操作')
+    ip = models.CharField(max_length=100, default='', db_index=True, blank=True, verbose_name=u'访问ip地址')
+    url = models.CharField(max_length=150, default='', blank=True, verbose_name=u'访问路径')
+    status = models.IntegerField(default=0, blank=True, verbose_name=u'状态')
+    time = models.DateTimeField(null=True, blank=True, db_index=True, verbose_name=u'访问时间')
+    content = models.TextField(blank=True, default='', verbose_name=u'参数内容')
+
+    class Meta:
+        db_table = 'access_log'
+        verbose_name = u'访问日志表'
+        verbose_name_plural = verbose_name
+        app_label = "db1"
+
+# 存储通表
+class VodBucketModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    bucket = models.CharField(max_length=30, verbose_name='设备UID')
+    storeDay = models.IntegerField(default=0, verbose_name='存储生命周期(天)')
+    content = models.TextField(verbose_name='描述', default='')
+    endpoint = models.CharField(max_length=125, default='', verbose_name='存储节点')
+    area = models.CharField(max_length=16, default='', verbose_name='区域')
+    region = models.CharField(max_length=16, default='', verbose_name='regionID')
+
+    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    updTime = models.IntegerField(verbose_name='更新时间', default=0)
+
+    mold = models.SmallIntegerField(default=0, verbose_name='存储区域类型')  # 0:国内阿里 1:国外aws
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'vod_bucket'
+        verbose_name = u'存储通信息'
+        verbose_name_plural = verbose_name
+        ordering = ('-id',)
+        app_label = "db1"
+
+
+class VodHlsModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='回放列表主键')
+    uid = models.CharField(max_length=20, verbose_name='设备UID')
+    channel = models.SmallIntegerField(default=0, verbose_name='通道')
+    time = models.IntegerField(verbose_name='播放列表名字时间戳', default=0, db_index=True)
+    endTime = models.IntegerField(verbose_name='删除时间', default=0)
+    sec = models.IntegerField(verbose_name='秒数', default=0)
+    bucket = models.ForeignKey(VodBucketModel, blank=True, to_field='id', on_delete=models.CASCADE, default=1,
+                               verbose_name='存储空间')
+    fg = models.SmallIntegerField(default=0,verbose_name='时间片段数') # 0为阿里云存储方案 >0为亚马逊方案
+
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'vod_hls'
+        verbose_name = u'云存回放信息表'
+        verbose_name_plural = verbose_name
+        ordering = ('-id',)
+        app_label = "db1"
+
+
+class VoicePromptModel(models.Model):
+    id = models.AutoField(primary_key=True)
+    title = models.CharField(max_length=128, default='', verbose_name='语音标题')
+    type = models.SmallIntegerField(default=0, verbose_name='语音类型。0:进入语音,1:离开语音')
+    filename = models.CharField(max_length=120, default='', verbose_name='文件名')
+    language = models.CharField(max_length=16, default='', verbose_name='语言类型')
+    classification = models.SmallIntegerField(default=1, verbose_name='语音分类。0:系统,1:自定义')
+    add_time = models.IntegerField(default=0, verbose_name='添加时间')
+    uid = models.CharField(max_length=20, default='0', verbose_name='关联设备UID')
+    channel = models.IntegerField(default=0, verbose_name='通道号')
+    status = models.SmallIntegerField(default=1, verbose_name='是否启用。0:不启用,1:启用')
+
+    class Meta:
+        db_table = 'voice_prompt'
+        verbose_name = '语音提示表'
+        verbose_name_plural = verbose_name
+

+ 68 - 0
Object/ETkObject.py

@@ -0,0 +1,68 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerFormal
+@software: PyCharm
+@DATE: 2019/6/1 17:25
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: ETkObject.py
+@Contact: chanjunkai@163.com
+"""
+
+import base64
+import urllib.parse
+from random import Random
+
+
+class ETkObject(object):
+    def __init__(self, etk):
+        self.uid = ''
+        self.parseUid(etk)
+
+    def parseUid(self, etk):
+        try:
+            c = base64.b64decode(etk)
+            c = c[2:-2]
+            c = urllib.parse.unquote(c.decode('utf-8'))
+            c = base64.b64decode(c)
+            uid = c.decode('utf-8')
+            if len(uid) == 20:
+                self.uid = uid
+                print(uid)
+        except Exception as e:
+            print(repr(e))
+
+    def encrypt(self,data):
+        s = data.encode()
+        s = base64.b64encode(s)
+        startStr = self.randomParam()
+        endStr = self.randomParam()
+        s = '{startStr}{s}{endStr}'.format(startStr=startStr,s=s.decode('utf-8'),endStr=endStr)
+        s = base64.b64encode(s.encode())
+        s=s.decode('utf-8')
+        return s
+
+    def randomParam(self):
+        characterSet = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsT' \
+                       'tUuVvWwXxYyZz0123456789'
+        length = len(characterSet) - 1
+
+        random = Random()
+        ss = ''
+        for index in range(2):
+            ss += characterSet[random.randint(0, length)]
+        return ss
+
+
+# etkObj = ETkObject(etk='')
+# PP = etkObj.encrypt('HLK7EJ2VYLNHHUMG111A')
+# print('encode_data:')
+# print(PP)
+# print('decode_data:')
+# eobj = ETkObject(etk='T1dha3hDUkZOVk9UVTBOMDVVVWxOTlJqRXhNVUU9U2w=')
+# uid = eobj.uid
+# print(uid)
+

+ 17 - 0
Object/LogUtil.py

@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+from AnsjerPush.config import BASE_DIR
+
+
+class LogUtil:
+
+    @staticmethod
+    def write_log(file_path=None, log=''):
+        if not file_path:
+            file_path = '/'.join((BASE_DIR, 'static/test.log'))
+
+        file = open(file_path, 'a+')
+        file.write(log)
+        file.write('\n')
+        file.flush()
+        file.close()

+ 82 - 0
Object/RedisObject.py

@@ -0,0 +1,82 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerOA
+@software: PyCharm
+@DATE: 2018/8/8 17:00
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: RedisObject.py
+@Contact: chanjunkai@163.com
+"""
+import redis
+
+from AnsjerPush.config import REDIS_ADDRESS
+
+# SERVER_HOST = '192.168.136.45'
+
+'''
+db=3  -> 统计在线人数用
+'''
+
+
+class RedisObject:
+
+    def __init__(self, db=0):
+        self.POOL = redis.ConnectionPool(host=REDIS_ADDRESS, port=6379, db=db)
+        self.CONN = redis.Redis(connection_pool=self.POOL)
+
+    def set_data(self, key, val, expire=0):
+        try:
+            self.CONN.set(key, val)
+            if expire > 0:
+                self.CONN.expire(key, expire)
+        except Exception as e:
+            return False
+        else:
+            return True
+
+    def get_data(self, key):
+        try:
+            val = self.CONN.get(key)
+        except Exception as e:
+            print(repr(e))
+            return False
+        else:
+            if val:
+                return val.decode('utf-8')
+            else:
+                return False
+
+    def del_data(self, key):
+        try:
+            val = self.CONN.delete(key)
+        except Exception as e:
+            print(repr(e))
+            return False
+        else:
+            return True
+
+    def get_size(self):
+        return self.CONN.dbsize()
+
+    # 向列表插入数据
+    def rpush(self, name, val):
+        self.CONN.rpush(name, val)
+
+    # 获取列表长度
+    def llen(self, name):
+        return self.CONN.llen(name=name)
+
+    # 获取列表所有数据
+    def lrange(self, name, start, end):
+        return self.CONN.lrange(name, start, end)
+
+    def get_ttl(self, key):
+        ttl = self.CONN.ttl(key)
+        if ttl:
+            return ttl
+        else:
+            return 0

+ 134 - 0
Object/ResponseObject.py

@@ -0,0 +1,134 @@
+from django.shortcuts import HttpResponse
+import simplejson as json
+
+
+class ResponseObject(object):
+    def __init__(self, lang='en'):
+        self.lang = lang
+
+    def data(self, code, res={}):
+        data_en = {
+            0: 'Success',
+            5: 'Please try again one minute later!',
+            10: res,
+            12: 'You are not the primary user of the device!',
+            14: 'Device is not belong to you',
+            15: 'Device has been bound',
+            44: 'System error! Can not send email',
+            48: 'System object error!',
+            89: 'Already send the code, please check it or get it again after 10m',
+            90: 'please check code or get it again after 1m',
+            99: 'Mail doesn\'t exist!',
+            100: 'Phone format error!',
+            101: 'Phone already existed!',
+            102: 'Phone doesn\'t exist!',
+            103: 'Mail already existed!',
+            104: 'Account doesn\'t exist!',
+            105: 'Email format error!',
+            107: 'The username not conform to the rules!',
+            109: 'The password not conform to the rules!',
+            110: 'user doesn\'t activated',
+            111: 'Error password',
+            119: 'The qr code has expired',
+            120: 'The code has expired',
+            121: 'The verification code is wrong!',
+            173: 'Data does not exists!',
+            174: 'Data already exists!',
+            176: 'Delete error',
+            177: 'Update error',
+            178: 'ADD error',
+            179: 'Nickname repeated',
+            306: 'The link has expired!',
+            309: 'Please ReLogin! errmsg token',
+            404: 'You don not have permission to access this!',
+            414: 'Please confirm the request url!',
+            424: 'Database Error !',
+            444: 'Wrong parameters!',
+            474: 'System Maintaining!',
+            475: 'App Version too low, please upgrade!',
+            500: 'Query Database Error:',
+            700: 'Upload file error',
+            701: 'The file does not exist!',
+            711: 'Do not downgrade',
+            712: 'Area needs to be consistent',
+            713: 'Storage rules cannot be changed during the validity period',
+            900: 'There is no information about this version!',
+            901: 'Getting URL failure!',
+            902: 'No update!',
+            903: 'Error filename',
+            906: 'Cause of file operation error',
+            907: 'The download file does not exist!',
+        }
+        data_cn = {
+            0: '成功',
+            5: '请一分钟后再尝试',
+            10: res,
+            12: '非设备主用户',
+            14: '设备不属于您',
+            15: '设备已被绑定',
+            44: '系统错误!无法发送电子邮件',
+            48: '系统对象错误',
+            89: '已发验证码,请检测或10分钟后重新获取。',
+            90: '请检测或1分钟后重新获取。',
+            99: '邮箱不存在!',
+            100: '手机格式错误!',
+            101: '手机已存在!',
+            102: '手机不存在!',
+            103: '邮箱已存在!',
+            104: '账户不存在!',
+            105: '邮箱格式错误!',
+            107: '用户名格式不符合!',
+            109: '密码格式不符合!',
+            110: '用户未激活!',
+            111: '密码不正确!',
+            119: '二维码过期',
+            120: '验证码过期',
+            121: '验证码错了!',
+            173: '数据不存在!',
+            174: '数据已存在!',
+            176: '删除错误',
+            177: '更新错误',
+            178: '增加错误',
+            179: '名称不能重复',
+            306: '链接已超过有效期!',
+            309: '请重新登录!',
+            404: '您没有访问的权限!',
+            414: '请确认请求url!',
+            424: '数据库错误!',
+            444: '参数错误!',
+            474: '系统维护中!',
+            475: '版本过低,请升级程序!',
+            500: '查询数据库错误!',
+            700: '上传文件错误',
+            701: '文件不存在',
+            711: '不可降级',
+            712: '区域不一致',
+            713: '有效期内不可更改存储规则',
+            900: '版本信息不存在',
+            901: '获取链接失败',
+            902: '无更新!',
+            903: '文件名不符合!',
+            906: '文件操作错误',
+            907: '文件不存在!',
+        }
+        if self.lang == 'cn':
+            msg = data_cn
+        elif self.lang == 'zh-Hans':
+            msg = data_cn
+        elif self.lang == 'zh-Hant':
+            msg = data_cn
+        else:
+            msg = data_en
+        try:
+            message = msg[code]
+        except Exception as e:
+            message = '系统错误,code不存在'
+        return {'result_code': code, 'reason': message, 'result': res, 'error_code': code}
+
+    def formal(self, code, res={}):
+        formal_data = self.data(code, res)
+        return json.dumps(formal_data, ensure_ascii=False)
+
+    def json(self, code, res={}):
+        result = self.formal(code, res)
+        return HttpResponse(result)

+ 126 - 0
Object/TokenObject.py

@@ -0,0 +1,126 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerOA
+@software: PyCharm
+@DATE: 2018/8/13 15:36
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: TokenObject.py
+@Contact: chanjunkai@163.com
+"""
+from AnsjerPush.config import OAUTH_ACCESS_TOKEN_SECRET, OAUTH_REFRESH_TOKEN_SECRET, OAUTH_ACCESS_TOKEN_TIME, \
+    OAUTH_REFRESH_TOKEN_TIME
+import jwt, time
+from Object.RedisObject import RedisObject
+
+
+class TokenObject:
+
+    def __init__(self, token=None):
+        if token == 'local':
+            token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySUQiOiIxNTg0MzUxODk2MjgyMTM4MDAxMzgwMDAiLCJsYW5nIjoiZW4iLCJ1c2VyIjoiMTM2ODAzMTc1OTYiLCJtX2NvZGUiOiIxMjM0MTMyNDMyMTQiLCJleHAiOjE1ODcyNzcwNjB9.c0LV_XyxwbzUlYqMJqx7vw9f19Jv-0kGnUHuu_go-mo'
+        if token == 'test':
+            token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiMTM4MDAxMzgwMDEiLCJleHAiOjE1Njk5OTg4OTYsInVzZXJJRCI6IjE1MTU2NDI2MjMzNzkzOTUxMzgwMDEzODAwMSIsImxhbmciOiJlbiIsIm1fY29kZSI6IjEyMzQxMzI0MzIxNCJ9.VAQtT9AbCCfXcrNj9DL5cvVasMDoI7AP8ptgU1GoMu8'
+        self.token = token
+        self.lang = None
+        self.userID = None
+        self.user = ''
+        self.code = 0
+        # 令牌校验
+        self.valid()
+
+    def valid(self):
+        if self.token is None:
+            self.code = 309
+            return
+        try:
+            res = jwt.decode(self.token, OAUTH_ACCESS_TOKEN_SECRET, algorithms='HS256')
+            # print(res)
+            self.userID = res.get('userID', None)
+            self.lang = res.get('lang', None)
+            self.user = res.get('user', '')
+            # 刷新登录时间
+            if self.userID:
+                print(self.user)
+                redisObj = RedisObject(db=3)
+                redisObj.set_data(key=self.userID, val=self.user, expire=300)
+
+        except jwt.ExpiredSignatureError as e:
+            print('过期')
+            print(repr(e))
+            self.code = 309
+            return
+        except Exception as e:
+            self.code = 309
+            return
+        else:
+            if not self.userID:
+                self.code = 309
+                return
+            else:
+                if self.userID:
+                    self.code = 0
+                    return res
+                else:
+                    self.code = 309
+                    return
+    # token加密
+    def generate(self, data={}):
+        try:
+            access_expire = int(OAUTH_ACCESS_TOKEN_TIME.total_seconds())
+            refresh_expire = int(OAUTH_REFRESH_TOKEN_TIME.total_seconds())
+            now_stamp = int(time.time())
+            access_data = data
+            refresh_data = data
+            access_data['exp'] = access_expire + now_stamp
+            refresh_data['exp'] = refresh_expire + now_stamp
+            access_token = jwt.encode(access_data,
+                                      OAUTH_ACCESS_TOKEN_SECRET,
+                                      algorithm='HS256')
+            refresh_token = jwt.encode(
+                refresh_data,
+                OAUTH_REFRESH_TOKEN_SECRET,
+                algorithm='HS256')
+            res = {
+                'access_token': access_token.decode('utf-8'),
+                'access_expire': access_expire,
+                'refresh_expire': refresh_expire,
+                'refresh_token': refresh_token.decode('utf-8'),
+            }
+        except Exception as e:
+            self.code = 309
+            print(repr(e))
+        else:
+            self.code = 0
+            return res
+
+    def refresh(self):
+        if not self.token:
+            self.code = 309
+            return
+        try:
+            res = jwt.decode(self.token, OAUTH_REFRESH_TOKEN_SECRET, algorithms='HS256')
+        except jwt.ExpiredSignatureError as e:
+            print('过期')
+            print(repr(e))
+            self.code = 309
+        except Exception as e:
+            self.code = 309
+            print(repr(e))
+        else:
+            self.code = 0
+            userID = res.get('userID', '')
+            user = res.get('user', '')
+            lang = self.lang
+            refreshRes = self.generate(data={'userID': userID, 'lang': lang, 'user': user})
+            return refreshRes
+
+# import jwt
+#
+#
+# token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOiIxNTMzODg0NDE4NTE5MTM4MDAxMzgwMDAiLCJleHAiOjE1NTU1NTEwNjUsInVzZXIiOiIxMTFAcXEuY29tIiwibGFuZyI6ImVuIn0.waPlfIBucSA7rFfnsxOKIVJ_cL6xiP33cAiz1IDoteY'
+# res = jwt.decode(token, 'a+jbgnw%@1%zy^=@dn62%', algorithms='HS256')
+# print(res)

+ 48 - 0
Object/UidTokenObject.py

@@ -0,0 +1,48 @@
+#!/usr/bin/env python3  
+# -*- coding: utf-8 -*-  
+"""
+@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
+@AUTHOR: ASJRD018
+@NAME: AnsjerFormal
+@software: PyCharm
+@DATE: 2018/12/5 11:52
+@Version: python3.6
+@MODIFY DECORD:ansjer dev
+@file: UidTokenObject.py
+@Contact: chanjunkai@163.com
+"""
+from AnsjerPush.config import UID_TOKEN_KEY
+import jwt
+# UID_TOKEN_KEY = 'c+565*j@%^'
+
+
+class UidTokenObject:
+
+    def __init__(self, token=None):
+        self.token = token
+        self.UID = ''
+        self.channel = ''
+        self.flag = self.valid()
+
+    def valid(self):
+        token = self.token
+        if self.token is None:
+            return False
+        res = jwt.decode(token, UID_TOKEN_KEY, algorithms='HS256')
+        print(res)
+        UID = res.get('uid', None)
+        channel = res.get('channel', None)
+        if UID is None:
+            return False
+        self.UID = UID
+        self.channel = channel
+
+    def generate(self, data={}):
+        token = jwt.encode(data, UID_TOKEN_KEY, algorithm='HS256').decode('utf-8')
+        self.token=token
+        return token
+#
+# uto = UidTokenObject()
+# res = uto.generate(data={'uid':'VVDHCVBYDKFMJRWA111A','channel':'1'})
+# print('---')
+# print(res)

+ 216 - 0
Service/CommonService.py

@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+import datetime
+import time
+from pathlib import Path
+from random import Random
+import ipdb
+import simplejson as json
+from django.core import serializers
+from django.utils import timezone
+from pyipip import IPIPDatabase
+
+from AnsjerPush.config import BASE_DIR
+
+
+# 复用性且公用较高封装代码在这
+class CommonService:
+
+    # 添加模糊搜索
+    @staticmethod
+    def get_kwargs(data={}):
+        kwargs = {}
+        for (k, v) in data.items():
+            if v is not None and v != u'':
+                kwargs[k + '__icontains'] = v
+        return kwargs
+
+    # 定义静态方法
+    # 格式化query_set转dict
+    @staticmethod
+    def qs_to_dict(query_set):
+        sqlJSON = serializers.serialize('json', query_set)
+        sqlList = json.loads(sqlJSON)
+        sqlDict = dict(zip(["datas"], [sqlList]))
+        return sqlDict
+
+    # 获取文件大小
+    @staticmethod
+    def get_file_size(file_path='', suffix_type='', decimal_point=0):
+
+        # for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
+        # path = Path() / 'D:/TestServer/123444.mp4'
+        path = Path() / file_path
+        size = path.stat().st_size
+        mb_size = 0.0
+        if suffix_type == 'MB':
+            mb_size = size / 1024.0 / 1024.0
+        if decimal_point != 0:
+            mb_size = round(mb_size, decimal_point)
+        return mb_size
+
+    @staticmethod
+    def get_param_flag(data=[]):
+        # print(data)
+        flag = True
+        for v in data:
+            if v is None:
+                flag = False
+                break
+        return flag
+
+    @staticmethod
+    def get_ip_address(request):
+        """
+        获取ip地址
+        :param request:
+        :return:
+        """
+        try:
+            real_ip = request.META['HTTP_X_FORWARDED_FOR']
+            clientIP = real_ip.split(",")[0]
+        except:
+            try:
+                clientIP = request.META['REMOTE_ADDR']
+            except Exception as e:
+                clientIP = ''
+        return clientIP
+
+    # @获取一天每个小时的datetime.datetime
+    @staticmethod
+    def getTimeDict(times):
+        time_dict = {}
+        t = 0
+        for x in range(24):
+            if x < 10:
+                x = '0' + str(x)
+            else:
+                x = str(x)
+            a = times.strftime("%Y-%m-%d") + " " + x + ":00:00"
+            time_dict[t] = timezone.datetime.strptime(a, '%Y-%m-%d %H:%M:%S')
+            t += 1
+        return time_dict
+
+    # 根据ip获取地址
+    @staticmethod
+    def getAddr(ip):
+        base_dir = BASE_DIR
+        # ip数据库
+        db = IPIPDatabase(base_dir + '/DB/17monipdb.dat')
+        addr = db.lookup(ip)
+        ts = addr.split('\t')[0]
+        return ts
+
+    # 通过ip检索ipip指定信息 lang为CN或EN
+    @staticmethod
+    def getIpIpInfo(ip, lang, update=False):
+        ipbd_dir = BASE_DIR + "/DB/mydata4vipday2.ipdb"
+        db = ipdb.City(ipbd_dir)
+        if update:
+            from var_dump import var_dump
+            var_dump('is_update')
+            rr = db.reload(ipbd_dir)
+            var_dump(rr)
+        info = db.find_map(ip, lang)
+        return info
+
+    @staticmethod
+    def getUserID(userPhone='13800138000', getUser=True, setOTAID=False, μs=True):
+        if μs == True:
+            if getUser == True:
+                timeID = str(round(time.time() * 1000000))
+                userID = timeID + userPhone
+
+                return userID
+            else:
+                if setOTAID == False:
+                    timeID = str(round(time.time() * 1000000))
+                    ID = userPhone + timeID
+
+                    return ID
+                else:
+                    timeID = str(round(time.time() * 1000000))
+                    eID = '13800' + timeID + '138000'
+                    return eID
+        else:
+            if getUser == True:
+                timeID = str(round(time.time() * 1000))
+                userID = timeID + userPhone
+
+                return userID
+            else:
+                if setOTAID == False:
+                    timeID = str(round(time.time() * 1000))
+                    ID = userPhone + timeID
+
+                    return ID
+                else:
+                    timeID = str(round(time.time() * 1000))
+                    eID = '13800' + timeID + '138000'
+                    return eID
+
+    # 生成随机数
+    @staticmethod
+    def RandomStr(randomlength=8, number=True):
+        str = ''
+        if number == False:
+            characterSet = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsT' \
+                           'tUuVvWwXxYyZz0123456789'
+        else:
+            characterSet = '0123456789'
+
+        length = len(characterSet) - 1
+
+        random = Random()
+        for index in range(randomlength):
+            str += characterSet[random.randint(0, length)]
+        return str
+
+    # 生成订单好
+    @staticmethod
+    def createOrderID():
+        random_id = CommonService.RandomStr(6, True)
+        order_id = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + str(random_id)
+        print('orderID:')
+        print(order_id)
+        return order_id
+
+    # qs转换list datetime处理
+    @staticmethod
+    def qs_to_list(qs):
+        res = []
+        # print(qs)
+        for ps in qs:
+            if 'add_time' in ps:
+                ps['add_time'] = ps['add_time'].strftime("%Y-%m-%d %H:%M:%S")
+            if 'update_time' in ps:
+                ps['update_time'] = ps['update_time'].strftime("%Y-%m-%d %H:%M:%S")
+            if 'end_time' in ps:
+                ps['end_time'] = ps['end_time'].strftime("%Y-%m-%d %H:%M:%S")
+            if 'data_joined' in ps:
+                if ps['data_joined']:
+                    ps['data_joined'] = ps['data_joined'].strftime("%Y-%m-%d %H:%M:%S")
+                else:
+                    ps['data_joined'] = ''
+            res.append(ps)
+        return res
+
+    # 获取当前时间
+    @staticmethod
+    def get_now_time_str(n_time, tz, lang):
+        print(n_time)
+        print(tz)
+        print(lang)
+        n_time = int(n_time) + 3600 * float(tz)
+        if lang == 'cn':
+            return time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(int(n_time)))
+        else:
+            return time.strftime('%m-%d-%Y %H:%M:%S', time.gmtime(int(n_time)))
+
+    @staticmethod
+    def app_log_log(uid='None', tz='0'):
+        file_path = '/'.join((BASE_DIR, 'static/app_log.log'))
+        file = open(file_path, 'a+')
+        file.write("uid:" + uid + "; " + "; tz:" + tz)
+        file.write('\n')
+        file.flush()
+        file.close()

+ 195 - 0
Service/ModelService.py

@@ -0,0 +1,195 @@
+import logging
+import time
+
+import requests
+
+from AnsjerPush.config import BASE_DIR
+from Model.models import *
+from Service.CommonService import CommonService
+import json
+from django.db.models import Q
+
+
+# 针对模型封装的复用性代码
+class ModelService:
+    # 获取当前用户角色名
+    @staticmethod
+    def getRole(rid):
+        return Role.objects.get(rid=rid).roleName
+
+    # 获取用户所有权限
+    @staticmethod
+    def own_permission(userID):
+        permission = Device_User.objects.get(userID=userID).role.values_list('permission', flat=True)
+        if permission:
+            return list(permission)
+        return []
+
+    # 获取用户角色相关
+    @staticmethod
+    def own_role(userID):
+        try:
+            role_qs = Device_User.objects.get(userID=userID).role.values('rid', 'roleName')
+            if role_qs.exists():
+                return {'rid': role_qs[0]['rid'], 'roleName': role_qs[0]['roleName']}
+        except Exception as e:
+            pass
+        return {'rid': '', 'roleName': ''}
+
+    # 检测权限有无
+    @staticmethod
+    def check_perm(userID, permID):
+        try:
+            perm_list = Device_User.objects.get(userID=userID).role.values_list('permission', flat=True)
+            if perm_list:
+                if permID in perm_list:
+                    return True
+        except Exception as e:
+            return False
+        return False
+
+    # 根据设备主键ID判断是否拥有该设备
+    @staticmethod
+    def check_user_own_device(userID, deviceID):
+        try:
+            dvqs = Device_Info.objects.filter(userID_id=userID).values_list('id', flat=True)
+            if dvqs:
+                if deviceID in dvqs:
+                    return True
+        except Exception as e:
+            return False
+        return False
+
+    # 根据设设备唯一名称UID判断是否拥有该设备
+    @staticmethod
+    def check_own_device(userID, UID):
+        dvqs = Device_Info.objects.filter(userID_id=userID, UID=UID)
+        if dvqs.exists():
+            return True
+        return False
+
+    # 根据userID获取用户名
+    @staticmethod
+    def get_user_name(userID):
+        try:
+            if userID:
+                device_user = Device_User.objects.get(userID=userID)
+                return device_user.username
+            else:
+                return ''
+        except Exception as e:
+            return ''
+
+    @staticmethod
+    def get_user_mark(userID):
+        if userID:
+            qs = Device_User.objects.filter(userID=userID).values('username', 'userEmail', 'phone')
+            if qs[0]['username']:
+                return qs[0]['username']
+            elif qs[0]['userEmail']:
+                return qs[0]['userEmail']
+            elif qs[0]['phone']:
+                return qs[0]['phone']
+            else:
+                return ''
+        else:
+            return ''
+
+    # 根据username获取userID
+    @staticmethod
+    def get_userID_byname(username):
+        try:
+            device_user = Device_User.objects.get(Q(username=username) | Q(userEmail=username) | Q(phone=username))
+        except Exception as e:
+            return None
+        else:
+            return device_user.userID
+
+    # 访问日志添加
+    @staticmethod
+    def addAccessLog(data):
+        try:
+            access_log = Access_Log.objects.create(**data)
+        except Exception as e:
+            return False
+        else:
+            return True
+
+    # 访问日志批量添加
+    @staticmethod
+    def add_batch_log(data_list):
+        try:
+            if data_list:
+                querysetlist = []
+                for i in data_list:
+                    data = json.loads(i.decode('utf-8'))
+                    querysetlist.append(Access_Log(**data))
+                Access_Log.objects.bulk_create(querysetlist)
+            else:
+                return
+        except Exception as e:
+            print('ggga')
+            print(repr(e))
+            return False
+        else:
+            return True
+
+
+    # 通过用户名获取userIDLIST
+    @staticmethod
+    def get_user_list_by_username(username):
+        userID_list = Device_User.objects.filter(Q(username=username) | Q(userEmail=username) | Q(phone=username)). \
+            values_list('userID', flat=True)
+        return userID_list
+
+    @staticmethod
+    def del_eq_info(userID, uid):
+        notify_alexa_delete(userID, uid)
+        ei_qs = Equipment_Info.objects.filter(userID_id=userID, devUid=uid)
+        ei_qs.delete()
+        # ei_count = ei_qs.count()
+        # while (ei_count > 1000):
+        #     ei_qs[0:1000].delete()
+
+        # 根据设备主键ID判断是否拥有该设备
+
+    # 获取绑定用户设备列表
+    @staticmethod
+    def get_uid_list(userID):
+        uid_list = Device_Info.objects.filter(userID_id=userID).values_list('UID', flat=True)
+        return list(uid_list)
+
+    @staticmethod
+    def notify_alexa_add(uid, userID, nickname):
+        url = 'https://www.zositech.xyz/deviceStatus/addOrUpdate'
+        data = {
+            'UID': uid,
+            'userID': userID,
+            'uid_nick': nickname
+        }
+        try:
+            requests.post(url, data=data, timeout=5)
+        except Exception as e:
+            print(repr(e))
+
+    @staticmethod
+    def add_log(ip, userID, operation):
+        file_path = '/'.join((BASE_DIR, 'static/delete_device.log'))
+        file = open(file_path, 'a+')
+        file.write(ip + "; username:" + userID + "; time:" + time.strftime(
+                "%Y-%m-%d %H:%M:%S", time.localtime()) + "; " + operation)
+        file.write('\n')
+        file.flush()
+        file.close()
+
+
+def notify_alexa_delete(userID, UID):
+    url = 'https://www.zositech.xyz/deviceStatus/delete'
+    data = {
+        'userID': userID,
+        'UID': UID
+    }
+    try:
+        requests.post(url=url, data=data, timeout=5)
+    except Exception as e:
+        print(repr(e))

+ 22 - 0
formal_manage.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == '__main__':
+    arg_m = sys.argv[1]
+    if arg_m == 'makemigrations':
+        print('do not makemigrations')
+        exit()
+    if arg_m == 'migrate':
+        print('do not migrate')
+        exit()
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.formal_settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)

+ 22 - 0
local_manage.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == '__main__':
+    arg_m = sys.argv[1]
+    if arg_m == 'makemigrations':
+        print('do not makemigrations')
+        exit()
+    if arg_m == 'migrate':
+        print('do not migrate')
+        exit()
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.local_settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)

+ 15 - 0
manage.py

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == '__main__':
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)

+ 224 - 0
requirements.txt

@@ -0,0 +1,224 @@
+alabaster==0.7.12
+anaconda-client==1.7.2
+anaconda-navigator==1.9.7
+anaconda-project==0.8.3
+asgiref==3.2.3
+asn1crypto==1.0.1
+astroid==2.3.1
+astropy==3.2.1
+atomicwrites==1.3.0
+attrs==19.2.0
+Babel==2.7.0
+backcall==0.1.0
+backports.functools-lru-cache==1.5
+backports.os==0.1.1
+backports.shutil-get-terminal-size==1.0.0
+backports.tempfile==1.0
+backports.weakref==1.0.post1
+beautifulsoup4==4.8.0
+bitarray==1.0.1
+bkcharts==0.2
+bleach==3.1.0
+bokeh==1.3.4
+boto==2.49.0
+Bottleneck==1.2.1
+certifi==2019.9.11
+cffi==1.12.3
+chardet==3.0.4
+Click==7.0
+cloudpickle==1.2.2
+clyent==1.2.2
+colorama==0.4.1
+comtypes==1.1.7
+conda==4.7.12
+conda-build==3.18.9
+conda-package-handling==1.6.0
+conda-verify==3.4.2
+contextlib2==0.6.0
+cryptography==2.7
+cycler==0.10.0
+Cython==0.29.13
+cytoolz==0.10.0
+dask==2.5.2
+decorator==4.4.0
+defusedxml==0.6.0
+distributed==2.5.2
+Django==3.0.3
+django-appconf==1.0.3
+django-cors-headers==3.2.1
+django-imagekit==4.0.2
+django-tinymce==2.8.0
+docutils==0.15.2
+entrypoints==0.3
+et-xmlfile==1.0.1
+fastcache==1.1.0
+filelock==3.0.12
+Flask==1.1.1
+fsspec==0.5.2
+future==0.17.1
+gevent==1.4.0
+glob2==0.7
+greenlet==0.4.15
+gunicorn==20.0.4
+h5py==2.9.0
+HeapDict==1.0.1
+html5lib==1.0.1
+idna==2.8
+imageio==2.6.0
+imagesize==1.1.0
+importlib-metadata==0.23
+ipykernel==5.1.2
+ipython==7.8.0
+ipython-genutils==0.2.0
+ipywidgets==7.5.1
+isort==4.3.21
+itsdangerous==1.1.0
+jdcal==1.4.1
+jedi==0.15.1
+Jinja2==2.10.3
+joblib==0.13.2
+json5==0.8.5
+jsonschema==3.0.2
+jupyter==1.0.0
+jupyter-client==5.3.3
+jupyter-console==6.0.0
+jupyter-core==4.5.0
+jupyterlab==1.1.4
+jupyterlab-server==1.0.6
+keyring==18.0.0
+kiwisolver==1.1.0
+lazy-object-proxy==1.4.2
+libarchive-c==2.8
+llvmlite==0.29.0
+locket==0.2.0
+lxml==4.4.1
+MarkupSafe==1.1.1
+matplotlib==3.1.1
+mccabe==0.6.1
+menuinst==1.4.16
+mistune==0.8.4
+mkl-fft==1.0.14
+mkl-random==1.1.0
+mkl-service==2.3.0
+mock==3.0.5
+more-itertools==7.2.0
+mpmath==1.1.0
+msgpack==0.6.1
+multipledispatch==0.6.0
+mysqlclient==1.4.6
+navigator-updater==0.2.1
+nbconvert==5.6.0
+nbformat==4.4.0
+networkx==2.3
+nltk==3.4.5
+nose==1.3.7
+notebook==6.0.1
+numba==0.45.1
+numexpr==2.7.0
+numpy==1.16.5
+numpydoc==0.9.1
+olefile==0.46
+openpyxl==3.0.0
+packaging==19.2
+pandas==0.25.1
+pandocfilters==1.4.2
+parso==0.5.1
+partd==1.0.0
+path.py==12.0.1
+pathlib2==2.3.5
+patsy==0.5.1
+pep8==1.7.1
+pickleshare==0.7.5
+pilkit==2.0
+Pillow==6.2.0
+pkginfo==1.5.0.1
+pluggy==0.13.0
+ply==3.11
+prometheus-client==0.7.1
+prompt-toolkit==2.0.10
+psutil==5.6.3
+py==1.8.0
+pycodestyle==2.5.0
+pycosat==0.6.3
+pycparser==2.19
+pycrypto==2.6.1
+pycurl==7.43.0.3
+pyflakes==2.1.1
+Pygments==2.4.2
+pylint==2.4.2
+PyMySQL==0.9.3
+pyodbc==4.0.27
+pyOpenSSL==19.0.0
+pyparsing==2.4.2
+pyreadline==2.1
+pyrsistent==0.15.4
+PySocks==1.7.1
+pytest==5.2.1
+pytest-arraydiff==0.3
+pytest-astropy==0.5.0
+pytest-doctestplus==0.4.0
+pytest-openfiles==0.4.0
+pytest-remotedata==0.3.2
+python-dateutil==2.8.0
+pytz==2019.3
+PyWavelets==1.0.3
+pywin32==223
+pywinpty==0.5.5
+PyYAML==5.1.2
+pyzmq==18.1.0
+QtAwesome==0.6.0
+qtconsole==4.5.5
+QtPy==1.9.0
+requests==2.22.0
+rope==0.14.0
+ruamel-yaml==0.15.46
+scikit-image==0.15.0
+scikit-learn==0.21.3
+scipy==1.3.1
+seaborn==0.9.0
+Send2Trash==1.5.0
+simplegeneric==0.8.1
+singledispatch==3.4.0.3
+six==1.12.0
+snowballstemmer==2.0.0
+sortedcollections==1.1.2
+sortedcontainers==2.1.0
+soupsieve==1.9.3
+Sphinx==2.2.0
+sphinxcontrib-applehelp==1.0.1
+sphinxcontrib-devhelp==1.0.1
+sphinxcontrib-htmlhelp==1.0.2
+sphinxcontrib-jsmath==1.0.1
+sphinxcontrib-qthelp==1.0.2
+sphinxcontrib-serializinghtml==1.1.3
+sphinxcontrib-websupport==1.1.2
+spyder==3.3.6
+spyder-kernels==0.5.2
+SQLAlchemy==1.3.9
+sqlparse==0.3.0
+statsmodels==0.10.1
+sympy==1.4
+tables==3.5.2
+tblib==1.4.0
+terminado==0.8.2
+testpath==0.4.2
+toolz==0.10.0
+tornado==6.0.3
+tqdm==4.36.1
+traitlets==4.3.3
+unicodecsv==0.14.1
+urllib3==1.24.2
+wcwidth==0.1.7
+webencodings==0.5.1
+Werkzeug==1.0.0
+widgetsnbextension==3.5.1
+win-inet-pton==1.1.0
+win-unicode-console==0.5
+wincertstore==0.2
+wrapt==1.11.2
+xlrd==1.2.0
+XlsxWriter==1.2.1
+xlwings==0.15.10
+xlwt==1.3.0
+zict==1.0.0
+zipp==0.6.0

+ 22 - 0
test_manage.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == '__main__':
+    arg_m = sys.argv[1]
+    if arg_m == 'makemigrations':
+        print('do not makemigrations')
+        exit()
+    if arg_m == 'migrate':
+        print('do not migrate')
+        exit()
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)