فهرست منبع

合并测试服代码(除AI)

locky 2 سال پیش
والد
کامیت
a917d0a4a5
65فایلهای تغییر یافته به همراه4503 افزوده شده و 1843 حذف شده
  1. 8 0
      AnsjerPush/cn_config/__init__.py
  2. 49 22
      AnsjerPush/cn_config/cn_formal_config.py
  3. 2 2
      AnsjerPush/cn_config/cn_formal_settings.py
  4. 1 1
      AnsjerPush/cn_config/cn_formal_wsgi.py
  5. 25 134
      AnsjerPush/config.py
  6. 8 0
      AnsjerPush/dev_config/__init__.py
  7. 27 1
      AnsjerPush/dev_config/local_config.py
  8. 1 1
      AnsjerPush/dev_config/local_settings.py
  9. 1 1
      AnsjerPush/dev_config/local_wsgi.py
  10. 8 0
      AnsjerPush/eur_config/__init__.py
  11. 49 8
      AnsjerPush/eur_config/eur_formal_config.py
  12. 1 1
      AnsjerPush/eur_config/eur_formal_settings.py
  13. 1 1
      AnsjerPush/eur_config/eur_formal_wsgi.py
  14. 0 3
      AnsjerPush/file/alipay/alipay_private_2048.pem
  15. 0 3
      AnsjerPush/file/alipay/alipay_public_2048.pem
  16. 0 3
      AnsjerPush/file/alipay/zosi_alipay_private_2048.pem
  17. 0 3
      AnsjerPush/file/alipay/zosi_alipay_public_2048.pem
  18. BIN
      AnsjerPush/file/font/simhei.ttf
  19. 0 184
      AnsjerPush/file/log type .txt
  20. 0 69
      AnsjerPush/file/后台管理系统说明.txt
  21. BIN
      AnsjerPush/file/表文档.doc
  22. 8 0
      AnsjerPush/test_config/__init__.py
  23. 0 0
      AnsjerPush/test_config/test.py
  24. 48 32
      AnsjerPush/test_config/test_config.py
  25. 2 1
      AnsjerPush/test_config/test_settings.py
  26. 1 1
      AnsjerPush/test_config/test_wsgi.py
  27. 6 6
      AnsjerPush/urls.py
  28. 8 0
      AnsjerPush/us_config/__init__.py
  29. 49 22
      AnsjerPush/us_config/formal_config.py
  30. 1 1
      AnsjerPush/us_config/formal_settings.py
  31. 1 1
      AnsjerPush/us_config/formal_wsgi.py
  32. 1 83
      Controller/AiController.py
  33. 22 6
      Controller/ComboCron/ComboCronPushController.py
  34. 178 367
      Controller/DetectController.py
  35. 90 289
      Controller/DetectControllerV2.py
  36. 69 177
      Controller/PowerWarningController.py
  37. 21 3
      Controller/ShadowController.py
  38. 76 21
      Controller/gatewayController.py
  39. 2 12
      Model/models.py
  40. 12 42
      Object/ETkObject.py
  41. 20 12
      Object/ResponseObject.py
  42. 66 2
      Service/CommonService.py
  43. 542 0
      Service/DevicePushService.py
  44. 0 107
      Service/GatewayService.py
  45. 97 0
      Service/HuaweiPushService/HuaweiPushService.py
  46. 71 0
      Service/HuaweiPushService/push_admin/__init__.py
  47. 190 0
      Service/HuaweiPushService/push_admin/_app.py
  48. 55 0
      Service/HuaweiPushService/push_admin/_http.py
  49. 613 0
      Service/HuaweiPushService/push_admin/_message_serializer.py
  50. 915 0
      Service/HuaweiPushService/push_admin/_messages.py
  51. 224 0
      Service/HuaweiPushService/push_admin/messaging.py
  52. 433 0
      Service/PushService.py
  53. 91 0
      Service/VivoPushService/push_admin/APIConstants.py
  54. 13 0
      Service/VivoPushService/push_admin/APIError.py
  55. 103 0
      Service/VivoPushService/push_admin/APIMessage.py
  56. 73 0
      Service/VivoPushService/push_admin/APISender.py
  57. 131 0
      Service/VivoPushService/push_admin/APISenderBase.py
  58. 17 0
      Service/VivoPushService/push_admin/APISignUtil.py
  59. 0 0
      Service/VivoPushService/push_admin/__init__.py
  60. 1 1
      cn_formal_manage.py
  61. 1 1
      eur_formal_manage.py
  62. 1 1
      formal_manage.py
  63. 1 1
      local_manage.py
  64. 68 216
      requirements.txt
  65. 1 1
      test_manage.py

+ 8 - 0
AnsjerPush/cn_config/__init__.py

@@ -0,0 +1,8 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : __init__.py.py
+@Time    : 2022/11/29 10:41
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""

+ 49 - 22
AnsjerPush/cn_formal_config.py → AnsjerPush/cn_config/cn_formal_config.py

@@ -1,22 +1,30 @@
-#!/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
+独立于config.py的配置文件
 """
 """
-# 主要静态变量配置文件
-import datetime, os
+import os
+import datetime
 
 
+# 配置信息
+CONFIG_INFO = 'cn'
+
+"""
+AWS相关
+"""
+# ======================================================================================================================
+# aws api key
+AWS_ARN_S3 = 'arn:aws-cn:s3'
+REGION_NAME = 'cn-northwest-1'
+ACCESS_KEY_ID = 'AKIA2MMWBR4DSFG67DTG'
+SECRET_ACCESS_KEY = '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'
+
+# 存储桶
+PUSH_BUCKET = 'push'                                # 推送存储桶
+
+# redis节点
+REDIS_ADDRESS = 'pushredis.3xavzq.0001.cnw1.cache.amazonaws.com.cn'
+
+APNS_MODE = 'prod'
 
 
-DEBUG_MODE = 'DEV'
-# MODE = 'PRO'
 # 阿里云发邮箱
 # 阿里云发邮箱
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
@@ -46,8 +54,6 @@ TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
 # 验证码超时时间
 # 验证码超时时间
 AuthCode_Expire = 600
 AuthCode_Expire = 600
 
 
-# 根路径
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # uid token key
 # uid token key
 UID_TOKEN_KEY = 'c+565*j@%^'
 UID_TOKEN_KEY = 'c+565*j@%^'
 
 
@@ -56,8 +62,6 @@ OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
-SERVER_TYPE = 'Ansjer.cn_formal_settings'
-
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
@@ -169,6 +173,29 @@ APNS_CONFIG = {
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
     }
     }
 }
 }
-APNS_MODE = 'prod'
-REDIS_ADDRESS = 'pushredis.3xavzq.0001.cnw1.cache.amazonaws.com.cn'
-# REDIS_ADDRESS = '192.168.136.45'
+
+XMPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': 'fXAdAwGDum3FKgQtAiW9hg=='
+}
+
+VIVOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID':'102227506',
+        'Key':'531bf5befece8840d31b654a166db91c',
+        'Secret':'9d6f19ef-5cdb-47ee-9433-d74c7d11fa50'
+    }
+}
+
+OPPOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': {
+        'Key': '882266a2000e4407990932be0b7043f5',
+        'Secret': '1037f29469c8416e87ab9cb8537c68ce'
+    }
+}
+
+MEIZUPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID': 151022,
+        'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
+    }
+}

+ 2 - 2
AnsjerPush/cn_formal_settings.py → AnsjerPush/cn_config/cn_formal_settings.py

@@ -1,6 +1,6 @@
 import os
 import os
 
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 DEBUG = False
 DEBUG = False
 ALLOWED_HOSTS = ["*"]
 ALLOWED_HOSTS = ["*"]
@@ -47,7 +47,7 @@ TEMPLATES = [
     },
     },
 ]
 ]
 
 
-WSGI_APPLICATION = 'AnsjerPush.cn_formal_wsgi.application'
+WSGI_APPLICATION = 'AnsjerPush.wsgi.application'
 
 
 # 业务数据库
 # 业务数据库
 DATABASE_DATA = 'ansjer_server_cn'
 DATABASE_DATA = 'ansjer_server_cn'

+ 1 - 1
AnsjerPush/cn_formal_wsgi.py → AnsjerPush/cn_config/cn_formal_wsgi.py

@@ -11,6 +11,6 @@ import os
 
 
 from django.core.wsgi import get_wsgi_application
 from django.core.wsgi import get_wsgi_application
 
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.cn_formal_settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.cn_config.cn_formal_settings')
 
 
 application = get_wsgi_application()
 application = get_wsgi_application()

+ 25 - 134
AnsjerPush/config.py

@@ -1,22 +1,20 @@
-#!/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
 
 
+import os
+import datetime
+
+# 配置信息
+CONFIG_TEST = 'test'
+CONFIG_CN = 'cn'
+CONFIG_US = 'us'
+CONFIG_EUR = 'eur'
+
+# 配置模式和根路径
+SERVER_TYPE = os.environ.get('DJANGO_SETTINGS_MODULE')
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
 
-DEBUG_MODE = 'DEV'
-# MODE = 'PRO'
 # 阿里云发邮箱
 # 阿里云发邮箱
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
@@ -35,7 +33,6 @@ OFF_LINE_TIME_DELTA = 5
 OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
 OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
 OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
 OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
 # access_token超时
 # access_token超时
-# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
 OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
 OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
 # refresh_token超时
 # refresh_token超时
 OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
 OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
@@ -46,27 +43,23 @@ TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
 # 验证码超时时间
 # 验证码超时时间
 AuthCode_Expire = 600
 AuthCode_Expire = 600
 
 
-# 根路径
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # uid token key
 # uid token key
 UID_TOKEN_KEY = 'c+565*j@%^'
 UID_TOKEN_KEY = 'c+565*j@%^'
-
 # oss param
 # oss param
 OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
 # aws api key
 # aws api key
+AWS_ARN = ['arn:aws-cn:s3', 'arn:aws:s3']
 AWS_ACCESS_KEY_ID = ['AKIA2MMWBR4DSFG67DTG', 'AKIA2E67UIMD45Y3HL53']  # 0国内, 1国外
 AWS_ACCESS_KEY_ID = ['AKIA2MMWBR4DSFG67DTG', 'AKIA2E67UIMD45Y3HL53']  # 0国内, 1国外
 AWS_SECRET_ACCESS_KEY = ['aI9gxcAKPmiGgPy9axrtFKzjYGbvpuytEX4xWweL', 'ckYLg4Lo9ZXJIcJEAKkzf2rWvs8Xth1FCjqiAqUw']
 AWS_SECRET_ACCESS_KEY = ['aI9gxcAKPmiGgPy9axrtFKzjYGbvpuytEX4xWweL', 'ckYLg4Lo9ZXJIcJEAKkzf2rWvs8Xth1FCjqiAqUw']
-AWS_ARN = ['arn:aws-cn:s3', 'arn:aws:s3']
-
 
 
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 DOMAIN_HOST = 'www.dvema.com'
 DOMAIN_HOST = 'www.dvema.com'
-# SERVER_HOST = 'localhost'
+
 PAYPAL_CRD = {
 PAYPAL_CRD = {
     "mode": "live",  # sandbox or live
     "mode": "live",  # sandbox or live
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
     "client_id": "AdSRd6WBn-qLl9OiQHQuNYTDFSx0ZX0RUttqa58au8bPzoGYQUrt8bc6591RmH8_pEAIPijdvVYSVXyI",
@@ -74,111 +67,17 @@ PAYPAL_CRD = {
 }
 }
 DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
 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'},
-    'com.ansjer.customizeda_a': {
-        'Key': 'aad8a02b1c4e82b0ef49c559',
-        'Secret': '8798cc7cd63225f13660421a'},
-    'com.ansjer.customizedc_a': {
-        'Key': 'ecdde95cd272f410ee029139',
-        'Secret': '9ddca1e92bfa331126fd8826'},
-    'com.ansjer.customizedf_a': {
-        'Key': '6ddbfb8fd7a0984dfba74a00',
-        'Secret': '971ee5c5facfbe76cb31a686'},
-}
-# 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',
-    'com.ansjer.customizedc_a': 'AAAAb9YP3rk:APA91bHu8u-CTpcd0g6lKPo0WNVqCi8jZub1cPPbSAY9AucT1HxlF65ZDUko9iG8q2ch17bwu9YWHpK1xI1gHSRXCslLvZlXEmHZC0AG3JKg15XuUvlFKACIajUFV-pOeGRT8tM6-31I',
-    'com.ansjer.customizedf_a': 'AAAAb9YP3rk:APA91bFm06w8b9OKQ0gz0iaWFuRqRIkvgAz6z7Gp3dBU_X-LNGJQd1hc1QR2W7QzBglF8SHtERA45a2lbdLRa5qv7hxfd6W_sJLBK7dA8jklsOQBvy505oUzTwMKWy4TwH-exps9KrhO',
-}
-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.customizede': {
-        'pem_path': 'AnsjerPush/file/apns_pem/customizede.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',
-    },
-    'com.ansjer.customizedh': {
-        'pem_path': 'AnsjerPush/file/apns_pem/customizedh.pem',
-    },
-}
 APNS_MODE = 'dev'
 APNS_MODE = 'dev'
-
-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 *
-elif SERVER_TYPE == 'AnsjerPush.cn_formal_settings':
-    from AnsjerPush.cn_formal_config import *
-elif SERVER_TYPE == 'AnsjerPush.eur_formal_settings':
-    from AnsjerPush.eur_formal_config import *
+if SERVER_TYPE == 'AnsjerPush.dev_config.local_settings':
+    from AnsjerPush.dev_config.local_config import *
+elif SERVER_TYPE == 'AnsjerPush.test_config.test_settings':
+    from AnsjerPush.test_config.test_config import *
+elif SERVER_TYPE == 'AnsjerPush.us_config.formal_settings':
+    from AnsjerPush.us_config.formal_config import *
+elif SERVER_TYPE == 'AnsjerPush.cn_config.cn_formal_settings':
+    from AnsjerPush.cn_config.cn_formal_config import *
+elif SERVER_TYPE == 'AnsjerPush.eur_config.eur_formal_settings':
+    from AnsjerPush.eur_config.eur_formal_config import *
 
 
 APNS_CODE = {
 APNS_CODE = {
     -1: '只库存不推送',
     -1: '只库存不推送',
@@ -231,11 +130,3 @@ APP_BUNDLE_DICT = {
     'com.ansjer.customizeda_a': 'Guardian365',
     'com.ansjer.customizeda_a': 'Guardian365',
     'com.ansjer.customizedc_a': 'PatrolSecure',
     'com.ansjer.customizedc_a': 'PatrolSecure',
 }
 }
-
-# AI识别标签
-AI_IDENTIFICATION_TAGS_DICT = {
-    '1': 'Person',
-    '2': 'Pet',
-    '3': 'Vehicle',
-    '4': 'Package'
-}

+ 8 - 0
AnsjerPush/dev_config/__init__.py

@@ -0,0 +1,8 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : __init__.py.py
+@Time    : 2022/11/29 13:30
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""

+ 27 - 1
AnsjerPush/local_config.py → AnsjerPush/dev_config/local_config.py

@@ -56,7 +56,7 @@ OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
-SERVER_TYPE = 'Ansjer.local_settings'
+SERVER_TYPE = 'Ansjer.dev_config.local_settings'
 
 
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
@@ -154,5 +154,31 @@ APNS_CONFIG = {
         'pem_path': 'AnsjerPush/file/apns_pem/accloud-dev.pem',
         'pem_path': 'AnsjerPush/file/apns_pem/accloud-dev.pem',
     }
     }
 }
 }
+
+XMPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': 'fXAdAwGDum3FKgQtAiW9hg=='
+}
+
+VIVOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID':'102227506',
+        'Key':'531bf5befece8840d31b654a166db91c',
+        'Secret':'9d6f19ef-5cdb-47ee-9433-d74c7d11fa50'
+    }
+}
+
+OPPOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': {
+        'Key': '882266a2000e4407990932be0b7043f5',
+        'Secret': '1037f29469c8416e87ab9cb8537c68ce'
+    }
+}
+
+MEIZUPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID': 151022,
+        'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
+    }
+}
 APNS_MODE = 'dev'
 APNS_MODE = 'dev'
 REDIS_ADDRESS = '127.0.0.1'
 REDIS_ADDRESS = '127.0.0.1'

+ 1 - 1
AnsjerPush/local_settings.py → AnsjerPush/dev_config/local_settings.py

@@ -1,6 +1,6 @@
 import os
 import os
 
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 DEBUG = True
 DEBUG = True
 ALLOWED_HOSTS = ["*"]
 ALLOWED_HOSTS = ["*"]

+ 1 - 1
AnsjerPush/local_wsgi.py → AnsjerPush/dev_config/local_wsgi.py

@@ -11,6 +11,6 @@ import os
 
 
 from django.core.wsgi import get_wsgi_application
 from django.core.wsgi import get_wsgi_application
 
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.local_settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.dev_config.local_settings')
 
 
 application = get_wsgi_application()
 application = get_wsgi_application()

+ 8 - 0
AnsjerPush/eur_config/__init__.py

@@ -0,0 +1,8 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : __init__.py.py
+@Time    : 2022/11/29 13:30
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""

+ 49 - 8
AnsjerPush/eur_formal_config.py → AnsjerPush/eur_config/eur_formal_config.py

@@ -1,9 +1,30 @@
+"""
+独立于config.py的配置文件
+"""
 import os
 import os
 import datetime
 import datetime
 
 
+# 配置信息
+CONFIG_INFO = 'eur'
+
+"""
+AWS相关
+"""
+# ======================================================================================================================
+# aws api key
+AWS_ARN_S3 = 'arn:aws:s3'
+REGION_NAME = 'us-east-1'
+ACCESS_KEY_ID = 'AKIA2E67UIMD45Y3HL53'
+SECRET_ACCESS_KEY = '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'
+
+# 存储桶
+PUSH_BUCKET = 'foreignpush'                                # 推送存储桶
+
+# redis节点
+REDIS_ADDRESS = 'push-redis-001.av1kep.0001.euw1.cache.amazonaws.com'
+
+APNS_MODE = 'prod'
 
 
-DEBUG_MODE = 'DEV'
-# MODE = 'PRO'
 # 阿里云发邮箱
 # 阿里云发邮箱
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
@@ -33,8 +54,6 @@ TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
 # 验证码超时时间
 # 验证码超时时间
 AuthCode_Expire = 600
 AuthCode_Expire = 600
 
 
-# 根路径
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # uid token key
 # uid token key
 UID_TOKEN_KEY = 'c+565*j@%^'
 UID_TOKEN_KEY = 'c+565*j@%^'
 
 
@@ -43,8 +62,6 @@ OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
-SERVER_TYPE = 'Ansjer.eur_formal_settings'
-
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
@@ -156,5 +173,29 @@ APNS_CONFIG = {
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
     }
     }
 }
 }
-APNS_MODE = 'prod'
-REDIS_ADDRESS = 'push-redis-001.av1kep.0001.euw1.cache.amazonaws.com'
+
+XMPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': 'fXAdAwGDum3FKgQtAiW9hg=='
+}
+
+VIVOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID':'102227506',
+        'Key':'531bf5befece8840d31b654a166db91c',
+        'Secret':'9d6f19ef-5cdb-47ee-9433-d74c7d11fa50'
+    }
+}
+
+MEIZUPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID': 151022,
+        'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
+    }
+}
+
+OPPOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': {
+        'Key': '882266a2000e4407990932be0b7043f5',
+        'Secret': '1037f29469c8416e87ab9cb8537c68ce'
+    }
+}

+ 1 - 1
AnsjerPush/eur_formal_settings.py → AnsjerPush/eur_config/eur_formal_settings.py

@@ -1,6 +1,6 @@
 import os
 import os
 
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 DEBUG = False
 DEBUG = False
 ALLOWED_HOSTS = ["*"]
 ALLOWED_HOSTS = ["*"]

+ 1 - 1
AnsjerPush/eur_formal_wsgi.py → AnsjerPush/eur_config/eur_formal_wsgi.py

@@ -11,6 +11,6 @@ import os
 
 
 from django.core.wsgi import get_wsgi_application
 from django.core.wsgi import get_wsgi_application
 
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.eur_formal_settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.eur_config.eur_formal_settings')
 
 
 application = get_wsgi_application()
 application = get_wsgi_application()

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

@@ -1,3 +0,0 @@
------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-----

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

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

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

@@ -1,3 +0,0 @@
------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-----

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

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

BIN
AnsjerPush/file/font/simhei.ttf


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

@@ -1,184 +0,0 @@
-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;

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

@@ -1,69 +0,0 @@
-一.修改记录
-创建时间: 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

BIN
AnsjerPush/file/表文档.doc


+ 8 - 0
AnsjerPush/test_config/__init__.py

@@ -0,0 +1,8 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : __init__.py.py
+@Time    : 2022/11/29 10:35
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""

+ 0 - 0
AnsjerPush/test.py → AnsjerPush/test_config/test.py


+ 48 - 32
AnsjerPush/test_config.py → AnsjerPush/test_config/test_config.py

@@ -1,25 +1,29 @@
-#!/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
+独立于config.py的配置文件
 """
 """
-# 主要静态变量配置文件
-import datetime, os
+import os
+import datetime
 
 
+# 配置信息
+CONFIG_INFO = 'test'
 
 
-# MODE = 'PRO'
-# 阿里云发邮箱
-ALY_SES_ACCESS_NAME = 'message@dvema.com'
-ALY_SES_ACCESS_PAW = 'SMtp123456'
-ALY_SES_ACCESS_REPLYTO = '***'
+"""
+AWS相关
+"""
+# ======================================================================================================================
+# aws api key
+AWS_ARN_S3 = 'arn:aws:s3'
+REGION_NAME = 'us-east-1'
+ACCESS_KEY_ID = 'AKIA2E67UIMD45Y3HL53'
+SECRET_ACCESS_KEY = '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'
+
+# 存储桶
+PUSH_BUCKET = 'foreignpush'                                # 推送存储桶
+
+# redis节点
+REDIS_ADDRESS = '127.0.0.1'
+
+APNS_MODE = 'dev'
 
 
 # 发送邮件邮箱
 # 发送邮件邮箱
 SES_COMPANY_EMAIL = 'user_server@nsst.com'
 SES_COMPANY_EMAIL = 'user_server@nsst.com'
@@ -27,14 +31,11 @@ AWS_SES_ACCESS_ID = 'AKIAJKPU23EU5QWHFPKQ'
 AWS_SES_ACCESS_SECRET = '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'
 AWS_SES_ACCESS_SECRET = 'oYJsF4h95ITWf3bxpPf5uUTvULPrq8DhRaQQzTjf'
 AWS_SES_ACCESS_REGION = 'us-east-1'
 AWS_SES_ACCESS_REGION = 'us-east-1'
 AWS_BUCKET = 'ansjertest'
 AWS_BUCKET = 'ansjertest'
-# 设定离线时间为5分钟
-OFF_LINE_TIME_DELTA = 5
 
 
 # token的secret
 # token的secret
 OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
 OAUTH_ACCESS_TOKEN_SECRET = 'a+jbgnw%@1%zy^=@dn62%'
 OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
 OAUTH_REFRESH_TOKEN_SECRET = 'r+jbgnw%@1%zy^=@dn62%'
 # access_token超时
 # access_token超时
-# OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(hours=1)
 OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
 OAUTH_ACCESS_TOKEN_TIME = datetime.timedelta(days=30)
 # refresh_token超时
 # refresh_token超时
 OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
 OAUTH_REFRESH_TOKEN_TIME = datetime.timedelta(days=30)
@@ -45,8 +46,6 @@ TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
 # 验证码超时时间
 # 验证码超时时间
 AuthCode_Expire = 600
 AuthCode_Expire = 600
 
 
-# 根路径
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # uid token key
 # uid token key
 UID_TOKEN_KEY = 'c+565*j@%^'
 UID_TOKEN_KEY = 'c+565*j@%^'
 
 
@@ -55,18 +54,11 @@ OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
-SERVER_TYPE = 'Ansjer.test_settings'
-
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 DOMAIN_HOST = 'www.dvema.com'
 DOMAIN_HOST = 'www.dvema.com'
 SERVER_HOST = 'localhost'
 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/'
 DETECT_PUSH_DOMAIN = 'http://push.dvema.com/'
 
 
 
 
@@ -158,5 +150,29 @@ APNS_CONFIG = {
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf-dev.pem',
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf-dev.pem',
     }
     }
 }
 }
-APNS_MODE = 'dev'
-REDIS_ADDRESS = '127.0.0.1'
+
+XMPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': 'fXAdAwGDum3FKgQtAiW9hg=='
+}
+
+VIVOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID':'102227506',
+        'Key':'531bf5befece8840d31b654a166db91c',
+        'Secret':'9d6f19ef-5cdb-47ee-9433-d74c7d11fa50'
+    }
+}
+
+OPPOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': {
+        'Key': '882266a2000e4407990932be0b7043f5',
+        'Secret': '1037f29469c8416e87ab9cb8537c68ce'
+    }
+}
+
+MEIZUPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID': 151022,
+        'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
+    }
+}

+ 2 - 1
AnsjerPush/test_settings.py → AnsjerPush/test_config/test_settings.py

@@ -1,6 +1,7 @@
 import os
 import os
 
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+print()
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 DEBUG = True
 DEBUG = True
 ALLOWED_HOSTS = ["*"]
 ALLOWED_HOSTS = ["*"]

+ 1 - 1
AnsjerPush/test_wsgi.py → AnsjerPush/test_config/test_wsgi.py

@@ -11,6 +11,6 @@ import os
 
 
 from django.core.wsgi import get_wsgi_application
 from django.core.wsgi import get_wsgi_application
 
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_config.test_settings')
 
 
 application = get_wsgi_application()
 application = get_wsgi_application()

+ 6 - 6
AnsjerPush/urls.py

@@ -1,18 +1,18 @@
-from django.conf.urls import url
-from django.urls import path
+from django.urls import path, re_path
 
 
 from Controller import DetectController, ShadowController, DetectControllerV2, AiController, gatewayController, \
 from Controller import DetectController, ShadowController, DetectControllerV2, AiController, gatewayController, \
     PowerWarningController
     PowerWarningController
 from Controller.ComboCron import ComboCronPushController
 from Controller.ComboCron import ComboCronPushController
 
 
 urlpatterns = [
 urlpatterns = [
-    path('deviceShadow/update', ShadowController.update_device_shadow),
     path('deviceShadow/generateUTK', ShadowController.generate_utk),
     path('deviceShadow/generateUTK', ShadowController.generate_utk),
+    path('deviceShadow/update', ShadowController.update_device_shadow),
+
     path('notify/push', DetectController.NotificationView.as_view()),
     path('notify/push', DetectController.NotificationView.as_view()),
     path('notifyV2/push', DetectControllerV2.NotificationV2View.as_view()),
     path('notifyV2/push', DetectControllerV2.NotificationV2View.as_view()),
     path('notifyV2/powerWarningPush', PowerWarningController.PowerWarningView.as_view()),
     path('notifyV2/powerWarningPush', PowerWarningController.PowerWarningView.as_view()),
 
 
-    url(r'^AiService/(?P<operation>.*)$', AiController.AiView.as_view()),
-    url(r'^gatewayService/(?P<operation>.*)$', gatewayController.GatewayView.as_view()),
-    url('unicom/device/combo/(?P<operation>.*)$', ComboCronPushController.ComboCronPushView.as_view())
+    re_path(r'^AiService/(?P<operation>.*)$', AiController.AiView.as_view()),
+    re_path(r'^gatewayService/(?P<operation>.*)$', gatewayController.GatewayView.as_view()),
+    re_path('unicom/device/combo/(?P<operation>.*)$', ComboCronPushController.ComboCronPushView.as_view())
 ]
 ]

+ 8 - 0
AnsjerPush/us_config/__init__.py

@@ -0,0 +1,8 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : __init__.py.py
+@Time    : 2022/11/29 10:41
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""

+ 49 - 22
AnsjerPush/formal_config.py → AnsjerPush/us_config/formal_config.py

@@ -1,22 +1,30 @@
-#!/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
+独立于config.py的配置文件
 """
 """
-# 主要静态变量配置文件
-import datetime, os
+import os
+import datetime
 
 
+# 配置信息
+CONFIG_INFO = 'us'
+
+"""
+AWS相关
+"""
+# ======================================================================================================================
+# aws api key
+AWS_ARN_S3 = 'arn:aws:s3'
+REGION_NAME = 'us-east-1'
+ACCESS_KEY_ID = 'AKIA2E67UIMD45Y3HL53'
+SECRET_ACCESS_KEY = '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'
+
+# 存储桶
+PUSH_BUCKET = 'foreignpush'                                # 推送存储桶
+
+# redis节点
+REDIS_ADDRESS = 'pushredis.5tgle2.0001.usw1.cache.amazonaws.com'
+
+APNS_MODE = 'prod'
 
 
-DEBUG_MODE = 'DEV'
-# MODE = 'PRO'
 # 阿里云发邮箱
 # 阿里云发邮箱
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_NAME = 'message@dvema.com'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
 ALY_SES_ACCESS_PAW = 'SMtp123456'
@@ -46,8 +54,6 @@ TX_PHONE_APP_KEY = '7705976ca6e85fe7b86d6bc2d11f7783'
 # 验证码超时时间
 # 验证码超时时间
 AuthCode_Expire = 600
 AuthCode_Expire = 600
 
 
-# 根路径
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # uid token key
 # uid token key
 UID_TOKEN_KEY = 'c+565*j@%^'
 UID_TOKEN_KEY = 'c+565*j@%^'
 
 
@@ -56,8 +62,6 @@ OSS_STS_ACCESS_KEY = 'LTAIyMkGfEdogyL9'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_STS_ACCESS_SECRET = '71uIjpsqVOmF7DAITRyRuc259jHOjO'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 OSS_ROLE_ARN = 'acs:ram::1901342792446414:role/stsoss'
 
 
-SERVER_TYPE = 'Ansjer.formal_settings'
-
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 NGINX_RTMP_STAT = 'http://www.dvema.com/stat'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN = 'http://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
 SERVER_DOMAIN_SSL = 'https://www.dvema.com/'
@@ -169,6 +173,29 @@ APNS_CONFIG = {
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
         'pem_path': 'AnsjerPush/file/apns_pem/commissionf.pem',
     }
     }
 }
 }
-APNS_MODE = 'prod'
-REDIS_ADDRESS = 'pushredis.5tgle2.0001.usw1.cache.amazonaws.com'
-# REDIS_ADDRESS = '192.168.136.45'
+
+XMPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': 'fXAdAwGDum3FKgQtAiW9hg=='
+}
+
+VIVOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID':'102227506',
+        'Key':'531bf5befece8840d31b654a166db91c',
+        'Secret':'9d6f19ef-5cdb-47ee-9433-d74c7d11fa50'
+    }
+}
+
+OPPOPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab': {
+        'Key': '882266a2000e4407990932be0b7043f5',
+        'Secret': '1037f29469c8416e87ab9cb8537c68ce'
+    }
+}
+
+MEIZUPUSH_CONFIG = {
+    'com.ansjer.zccloud_ab':{
+        'ID': 151022,
+        'AppSecret': '890e94e09b7b4aa18278acce82e35c46',
+    }
+}

+ 1 - 1
AnsjerPush/formal_settings.py → AnsjerPush/us_config/formal_settings.py

@@ -1,6 +1,6 @@
 import os
 import os
 
 
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 SECRET_KEY = '$2hf5g$a%_^kk0-l25l$!o5)yc=dvtnfpc8(+$rh4fq4twa_xx'
 DEBUG = False
 DEBUG = False
 ALLOWED_HOSTS = ["*"]
 ALLOWED_HOSTS = ["*"]

+ 1 - 1
AnsjerPush/formal_wsgi.py → AnsjerPush/us_config/formal_wsgi.py

@@ -11,6 +11,6 @@ import os
 
 
 from django.core.wsgi import get_wsgi_application
 from django.core.wsgi import get_wsgi_application
 
 
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.formal_settings')
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.us_config.formal_settings')
 
 
 application = get_wsgi_application()
 application = get_wsgi_application()

+ 1 - 83
Controller/AiController.py

@@ -19,7 +19,7 @@ import jpush
 from boto3.session import Session
 from boto3.session import Session
 from django.views.generic.base import View
 from django.views.generic.base import View
 from pyfcm import FCMNotification
 from pyfcm import FCMNotification
-from AnsjerPush.config import SERVER_TYPE, AI_IDENTIFICATION_TAGS_DICT
+from AnsjerPush.config import SERVER_TYPE
 from AnsjerPush.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, APNS_MODE, APNS_CONFIG, BASE_DIR, \
 from AnsjerPush.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, APNS_MODE, APNS_CONFIG, BASE_DIR, \
     JPUSH_CONFIG, FCM_CONFIG
     JPUSH_CONFIG, FCM_CONFIG
 from Model.models import UidPushModel, AiService
 from Model.models import UidPushModel, AiService
@@ -279,88 +279,6 @@ class AiView(View):
                     os.remove(c_path)
                     os.remove(c_path)
             os.rmdir(path)
             os.rmdir(path)
 
 
-    ## 检查是否有符合条件的标签,并且返回标签坐标位置信息
-    def labelsCoords(self, user_detect_group, rekognition_res, image_size):
-        logger = logging.getLogger('info')
-        labels = rekognition_res['Labels']
-        label_name = []
-        label_list = []
-        logger.info('--------识别到的标签-------')
-        logger.info(labels)
-        all_labels_type = {
-            '1': ['Person', 'Human'],  # 人
-            '2': ['Pet', 'Dog', 'Canine', 'Animal', 'Puppy', 'Cat'],  # 动物
-            '3': ['Vehicle', 'Car', 'Transportation', 'Automobile', 'Bus'],  # 车
-            '4': ['Package', 'Carton', 'Cardboard', 'Package Delivery']  # 包裹
-        }
-        # 找出识别的所有标签
-        for label in labels:
-            label_name.append(label['Name'])
-            for Parents in label['Parents']:
-                label_name.append(Parents['Name'])
-
-        logger.info('标签名------')
-        logger.info(label_name)
-        # 删除用户没有选择的ai识别类型, 并且得出最终识别结果
-        user_detect_list = user_detect_group.split(',')
-        user_detect_list = [i.strip() for i in user_detect_list]
-        conform_label_list = []
-        conform_user_d_group = set()
-        for key, label_type_val in all_labels_type.items():
-            if key in user_detect_list:
-                for label in label_type_val:
-                    if label in label_name:
-                        conform_user_d_group.add(key)
-                        conform_label_list.append(label)
-        # 找出标签边框线位置信息
-        boundingBoxList = []
-        for label in labels:
-            if label['Name'] in conform_label_list:
-                for boundingBox in label['Instances']:
-                    boundingBoxList.append(boundingBox['BoundingBox'])
-
-        # 找出边框位置信息对应的单图位置并重新计算位置比
-        merge_image_height = image_size['height']
-        # merge_image_width = image_size['width']
-        single_height = merge_image_height // image_size['num']
-        new_bounding_box_dict = {}
-        new_bounding_box_dict['file_0'] = []
-        new_bounding_box_dict['file_1'] = []
-        new_bounding_box_dict['file_2'] = []
-        # new_bounding_box_dict['file_3'] = []
-        for k, val in enumerate(boundingBoxList):
-            boundingBoxTop = merge_image_height * val['Top']
-            # 找出当前边框属于哪张图片范围
-            boxDict = {}
-            for i in range(image_size['num']):
-                min = i * single_height  # 第n张图
-                max = (i + 1) * single_height
-                if boundingBoxTop >= min and boundingBoxTop <= max:
-                    # print("属于第{i}张图".format(i=i+1))
-                    boxDict['Width'] = val['Width']
-                    boxDict['Height'] = merge_image_height * val['Height'] / single_height
-                    boxDict['Top'] = ((merge_image_height * val['Top']) - (
-                            i * single_height)) / single_height  # 减去前i张图片的高度
-                    boxDict['Left'] = val['Left']
-                    new_bounding_box_dict["file_{i}".format(i=i)].append(boxDict)
-        # exit(new_bounding_box_list)
-        conform_user_d_group = list(conform_user_d_group)
-        if len(conform_user_d_group) > 1:
-            conform_user_d_group.sort()
-            # 集成识别标签
-            for label_key in conform_user_d_group:
-                label_list.append(AI_IDENTIFICATION_TAGS_DICT[label_key])
-            eventType = ''.join(conform_user_d_group)  # 组合类型
-        else:
-            label_list.append(AI_IDENTIFICATION_TAGS_DICT[conform_user_d_group[0]])
-            eventType = conform_user_d_group[0]
-
-        logger.info('------conform_user_d_group------ {}'.format(conform_user_d_group))
-        logger.info('------label_list------ {}'.format(label_list))
-
-        return {'eventType': eventType, 'label_list': label_list,
-                'new_bounding_box_dict': new_bounding_box_dict}
-
     def upload_s3(self, file_dict, dir_path):
     def upload_s3(self, file_dict, dir_path):
         try:
         try:
             if SERVER_TYPE == "Ansjer.formal_settings" or SERVER_TYPE == 'Ansjer.eur_formal_settings':
             if SERVER_TYPE == "Ansjer.formal_settings" or SERVER_TYPE == 'Ansjer.eur_formal_settings':

+ 22 - 6
Controller/ComboCron/ComboCronPushController.py

@@ -16,7 +16,8 @@ from django.views import View
 from Model.models import UnicomComboOrderInfo, UnicomDeviceInfo, GatewayPush, SysMsgModel, UnicomFlowPush, Device_User
 from Model.models import UnicomComboOrderInfo, UnicomDeviceInfo, GatewayPush, SysMsgModel, UnicomFlowPush, Device_User
 from Object.AliyunSmsObject import AliyunSmsObject
 from Object.AliyunSmsObject import AliyunSmsObject
 from Object.ResponseObject import ResponseObject
 from Object.ResponseObject import ResponseObject
-from Service.GatewayService import GatewayPushService
+from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
+from Service.PushService import PushObject
 
 
 
 
 class ComboCronPushView(View):
 class ComboCronPushView(View):
@@ -102,7 +103,7 @@ class ComboCronPushView(View):
                 app_bundle_id = push_vo['app_bundle_id']
                 app_bundle_id = push_vo['app_bundle_id']
 
 
                 # 获取推送所需数据
                 # 获取推送所需数据
-                msg_title = GatewayPushService.get_msg_title(app_bundle_id, nickname)
+                msg_title = PushObject.get_msg_title(nickname)
                 if lang == 'cn':
                 if lang == 'cn':
                     sys_msg_text = "温馨提示:尊敬的客户,您" + nickname + "设备4G流量套餐将在" + time.strftime("%Y-%m-%d", time.localtime(
                     sys_msg_text = "温馨提示:尊敬的客户,您" + nickname + "设备4G流量套餐将在" + time.strftime("%Y-%m-%d", time.localtime(
                         item['expire_time'])) + "到期"
                         item['expire_time'])) + "到期"
@@ -183,7 +184,7 @@ class ComboCronPushView(View):
                     app_bundle_id = push_vo['app_bundle_id']
                     app_bundle_id = push_vo['app_bundle_id']
 
 
                     # 获取推送所需数据
                     # 获取推送所需数据
-                    msg_title = GatewayPushService.get_msg_title(app_bundle_id, item.serial_no)
+                    msg_title = PushObject.get_msg_title(item.serial_no)
                     sys_msg_text = sys_msg if msg else cls.get_msg_text(item.serial_no, lang, total, usage, usable)
                     sys_msg_text = sys_msg if msg else cls.get_msg_text(item.serial_no, lang, total, usage, usable)
                     kwargs['app_bundle_id'] = app_bundle_id
                     kwargs['app_bundle_id'] = app_bundle_id
                     kwargs['token_val'] = token_val
                     kwargs['token_val'] = token_val
@@ -222,13 +223,28 @@ class ComboCronPushView(View):
         try:
         try:
             # ios apns
             # ios apns
             if push_type == 0:
             if push_type == 0:
-                GatewayPushService.ios_apns_push(**kwargs)
+                PushObject.ios_apns_push(**kwargs)
             # android gcm
             # android gcm
             elif push_type == 1:
             elif push_type == 1:
-                GatewayPushService.android_fcm_push(**kwargs)
+                PushObject.android_fcm_push(**kwargs)
             # android 极光推送
             # android 极光推送
             elif push_type == 2:
             elif push_type == 2:
-                GatewayPushService.android_jpush(**kwargs)
+                PushObject.android_jpush(**kwargs)
+            elif push_type == 3:
+                huawei_push_object = HuaweiPushObject()
+                huawei_push_object.send_push_notify_message(**kwargs)
+            # android 小米推送
+            elif push_type == 4:
+                PushObject.android_xmpush(**kwargs)
+            # android vivo推送
+            elif push_type == 5:
+                PushObject.android_vivopush(**kwargs)
+            # android oppo推送
+            elif push_type == 6:
+                PushObject.android_oppopush(**kwargs)
+            # android 魅族推送
+            elif push_type == 7:
+                PushObject.android_meizupush(**kwargs)
             return True
             return True
         except Exception as e:
         except Exception as e:
             logger.info('流量预警推送异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
             logger.info('流量预警推送异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 178 - 367
Controller/DetectController.py

@@ -1,16 +1,4 @@
-#!/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 json
 import logging
 import logging
 import os
 import os
 import time
 import time
@@ -18,6 +6,7 @@ import time
 import apns2
 import apns2
 import jpush as jpush
 import jpush as jpush
 import oss2
 import oss2
+from django.db import transaction
 from django.http import JsonResponse
 from django.http import JsonResponse
 from django.views.generic.base import View
 from django.views.generic.base import View
 from pyfcm import FCMNotification
 from pyfcm import FCMNotification
@@ -31,6 +20,7 @@ from Object.RedisObject import RedisObject
 from Object.UidTokenObject import UidTokenObject
 from Object.UidTokenObject import UidTokenObject
 from Object.utils import LocalDateTimeUtil
 from Object.utils import LocalDateTimeUtil
 from Service.CommonService import CommonService
 from Service.CommonService import CommonService
+from Service.DevicePushService import DevicePushService
 from Service.EquipmentInfoService import EquipmentInfoService
 from Service.EquipmentInfoService import EquipmentInfoService
 
 
 
 
@@ -46,6 +36,15 @@ class NotificationView(View):
         return self.validation(request.POST)
         return self.validation(request.POST)
 
 
     def validation(self, request_dict):
     def validation(self, request_dict):
+        """
+        设备触发报警消息推送
+        @param request_dict:uidToken 加密uid
+        @param request_dict:etk 加密uid
+        @param request_dict:channel 设备通道号
+        @param request_dict:n_time 设备触发报警时间
+        @param request_dict:event_type 设备事件类型
+        @param request_dict:is_st 文件类型(0:无,1:图片,2:视频)
+        """
         logger = logging.getLogger('info')
         logger = logging.getLogger('info')
         logger.info("旧移动侦测接口参数:{}".format(request_dict))
         logger.info("旧移动侦测接口参数:{}".format(request_dict))
         uidToken = request_dict.get('uidToken', None)
         uidToken = request_dict.get('uidToken', None)
@@ -56,365 +55,177 @@ class NotificationView(View):
         is_st = request_dict.get('is_st', None)
         is_st = request_dict.get('is_st', None)
         if not all([channel, n_time]):
         if not all([channel, n_time]):
             return JsonResponse(status=200, data={'code': 444, 'msg': 'error channel or n_time'})
             return JsonResponse(status=200, data={'code': 444, 'msg': 'error channel or n_time'})
-        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
-        logger.info("旧移动侦测接口的uid:{}".format(uid))
-        pkey = '{uid}_{channel}_{event_type}_ptl'.format(uid=uid, event_type=event_type, channel=channel)
-        ykey = '{uid}_redis_qs'.format(uid=uid)
-        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 = 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:
-            res_data = {'code': 0, 'msg': 'Push it once a minute'}
-            return JsonResponse(status=200, data=res_data)
-
-        # 数据库读取数据
-        if have_ykey:
-            uid_push_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',
-                       'lang', 'm_code', 'tz', 'uid_set__nickname', 'uid_set__detect_interval', 'uid_set__detect_group',
-                       'uid_set__channel', 'uid_set__new_detect_interval')
-
-            uid_push_list = []
-            for qs in uid_push_qs:
-                uid_push_list.append(qs)
-            # 修改redis数据,并设置过期时间为10分钟
-            redisObj.set_data(key=ykey, val=str(uid_push_list), expire=600)
-            if not uid_push_list:
-                res_data = {'code': 404, 'msg': 'error !'}
-                return JsonResponse(status=200, data=res_data)
-
-        if not uid_push_list:
-            res_data = {'code': 0, 'msg': 'uid_push_list not exist'}
-            return JsonResponse(status=200, data=res_data)
-
-        nickname = uid_push_list[0]['uid_set__nickname']
-        detect_interval = uid_push_list[0]['uid_set__detect_interval']
-        detect_group = uid_push_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的话,既推送,又存库
-                if SERVER_TYPE != 'Ansjer.cn_formal_settings':
-                    new_detect_interval = uid_push_list[0]['uid_set__new_detect_interval']
-                    detect_interval = new_detect_interval if new_detect_interval > 0 else detect_interval
-                    detect_interval = 60 if detect_interval < 60 else detect_interval
-                redisObj.set_data(key=dkey, val=1, expire=detect_interval - 5)
-                redisObj.set_data(key=pkey, val=1, expire=60)
-        logger.info('APP消息推送V1接口,是否进行APP推送:{},1为不推送,间隔:{}'.format(detect_med_type, detect_interval))
-        # 旧模式并且没有pkey,重新创建一个
-        if not detect_group and not have_pkey:
-            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,
-        }
-        sys_msg_list = []
-        userID_ids = []
-        do_apns_code = ''
-        do_fcm_code = ''
-        do_jpush_code = ''
-        new_device_info_list = []
-        local_date_time = ''
-        for up in uid_push_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
-            logger.info('推送要的数据:')
-            logger.info(kwag_args)
-            logger.info(detect_med_type)
-            local_date_time = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang='cn')
-            logger.info('<<<<<旧的接口,根据时区计算后日期={},时区={}'.format(local_date_time, tz))
-            local_date_time = local_date_time[0:10]
-            logger.info('<<<<<旧的接口,日期={}'.format(local_date_time))
-
-            # 以下是存库
-            userID_id = up["userID_id"]
-            if userID_id not in userID_ids:
-
+        try:
+            with transaction.atomic():
+                uid = DevicePushService.decode_uid(etk, uidToken)  # 解密uid
+                if len(uid) != 20 and len(uid) != 14:
+                    return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong uid'})
+                logger.info("旧移动侦测接口的uid:{}".format(uid))
+                pkey = '{uid}_{channel}_{event_type}_ptl'.format(uid=uid, event_type=event_type, channel=channel)
+                ykey = '{uid}_redis_qs'.format(uid=uid)
+                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 = 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:
+                    res_data = {'code': 0, 'msg': 'Push it once a minute'}
+                    return JsonResponse(status=200, data=res_data)
+
+                # 数据库读取数据
+                if have_ykey:
+                    uid_push_list = eval(redisObj.get_data(key=ykey))
+                else:
+                    # 从数据库查询出来
+                    uid_push_qs = DevicePushService.query_uid_push(uid)
+                    if not uid_push_qs.exists():
+                        logger.info('消息推送-uid_push 数据不存在')
+                        return JsonResponse(status=200, data={'code': 176, 'msg': 'no uid_push data'})
+                    # 修改redis数据,并设置过期时间为10分钟
+                    uid_push_list = DevicePushService.cache_uid_push(uid_push_qs)
+                    redisObj.set_data(key=ykey, val=str(uid_push_list), expire=600)
+                    if not uid_push_list:
+                        res_data = {'code': 404, 'msg': 'error !'}
+                        return JsonResponse(status=200, data=res_data)
+
+                if not uid_push_list:
+                    res_data = {'code': 0, 'msg': 'uid_push_list not exist'}
+                    return JsonResponse(status=200, data=res_data)
+
+                nickname = uid_push_list[0]['uid_set__nickname']
+                detect_interval = uid_push_list[0]['uid_set__detect_interval']
+                detect_group = uid_push_list[0]['uid_set__detect_group']
+                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的话,既推送,又存库
+                        if SERVER_TYPE != 'Ansjer.cn_config.cn_formal_settings':
+                            new_detect_interval = uid_push_list[0]['uid_set__new_detect_interval']
+                            detect_interval = new_detect_interval if new_detect_interval > 0 else detect_interval
+                            detect_interval = 60 if detect_interval < 60 else detect_interval
+                        redisObj.set_data(key=dkey, val=1, expire=detect_interval - 5)
+                        redisObj.set_data(key=pkey, val=1, expire=60)
+                logger.info('APP消息推送V1接口,是否进行APP推送:{},1为不推送,间隔:{}'.format(detect_med_type, detect_interval))
+                # 旧模式并且没有pkey,重新创建一个
+                if not detect_group and not have_pkey:
+                    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,
+                }
+                params = {'nickname': nickname, 'uid': uid, 'kwag_args': kwag_args, 'is_st': is_st,
+                          'is_sys_msg': is_sys_msg, 'channel': channel, 'event_type': event_type, 'n_time': n_time,
+                          'electricity': '', 'bucket': bucket, 'app_push': ''}
+                #  推送以及报警消息存库
+                result = DevicePushService.save_msg_push(uid_set_push_list=uid_push_list, **params)
+                if result['code_date'] is None:
+                    result['code_date'] = {'do_apns_code': '', 'do_fcm_code': '', 'do_jpush_code': ''}
+                if detect_med_type == 1:
+                    result['code_date']['do_apns_code'] = '只存库不推送'
+                    result['code_date']['do_fcm_code'] = '只存库不推送'
+                    result['code_date']['do_jpush_code'] = '只存库不推送'
                 if is_sys_msg:
                 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))
+                    SysMsgModel.objects.bulk_create(result['sys_msg_list'])
                 else:
                 else:
-                    logger.info('----《旧接口》start------')
-                    new_device_info_list.append(EquipmentInfoService.get_equipment_info_obj(
-                        local_date_time,
-                        device_user_id=userID_id,
-                        event_time=n_time,
-                        event_type=event_type,
-                        device_uid=uid,
-                        device_nick_name=nickname,
-                        channel=channel,
-                        alarm='Motion \tChannel:{channel}'.format(channel=channel),
-                        is_st=is_st,
-                        receive_time=n_time,
-                        add_time=now_time,
-                        storage_location=1,
-                        border_coords='',
-                    ))
-                userID_ids.append(userID_id)
-
-            # 推送
-            if detect_med_type == 2 or detect_med_type == 0:
-                logger.info('准备推送{}'.format(detect_med_type))
-                try:
-                    if push_type == 0:  # ios apns
-                        do_apns_code = self.do_apns(**kwag_args)
-                    elif push_type == 1:  # android gcm
-                        print('do_fcm')
-                        do_fcm_code = self.do_fcm(**kwag_args)
-                    elif push_type == 2:  # android jpush
-                        print('do_jpush')
-                        do_jpush_code = self.do_jpush(**kwag_args)
-                except Exception as e:
-                    logger.info("errLine={errLine}, errMsg={errMsg}".format(errLine=e.__traceback__.tb_lineno,
-                                                                            errMsg=repr(e)))
-                    continue
-
-            if detect_med_type == 1:
-                do_apns_code = '只存库不推送'
-                do_fcm_code = '只存库不推送'
-                do_jpush_code = '只存库不推送'
-        if is_sys_msg:
-            SysMsgModel.objects.bulk_create(sys_msg_list)
-        else:
-            if new_device_info_list and len(new_device_info_list) > 0:
-                # 根据日期获得星期几
-                week = LocalDateTimeUtil.date_to_week(local_date_time)
-                EquipmentInfoService.equipment_info_bulk_create(week, new_device_info_list)
-                logger.info('----《旧接口》设备信息分表批量保存end')
-        if is_st == '0' or is_st == '2':
-            print("is_st=0or2")
-            for up in uid_push_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
-                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'})
-
-        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 uid_push_list:
-                up['do_apns_code'] = do_apns_code
-                up['do_fcm_code'] = do_fcm_code
-                up['do_jpush_code'] = do_jpush_code
-                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']
-            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 uid_push_list:
-                up['do_apns_code'] = do_apns_code
-                up['do_fcm_code'] = do_fcm_code
-                up['do_jpush_code'] = do_jpush_code
-                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']
-
-            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
+                    if result['new_device_info_list'] and len(result['new_device_info_list']) > 0:
+                        # 根据日期获得星期几
+                        week = LocalDateTimeUtil.date_to_week(result['local_date_time'])
+                        EquipmentInfoService.equipment_info_bulk_create(week, result['new_device_info_list'])
+                        logger.info('----《旧接口》设备信息分表批量保存end')
+                if is_st == '0' or is_st == '2':
+                    print("is_st=0or2")
+                    for up in uid_push_list:
+                        if up['push_type'] == 0:  # ios apns
+                            up['do_apns_code'] = result['code_date']['do_apns_code']
+                        elif up['push_type'] == 1:  # android gcm
+                            up['do_fcm_code'] = result['code_date']['do_fcm_code']
+                        elif up['push_type'] == 2:  # android jpush
+                            up['do_jpush_code'] = result['code_date']['do_jpush_code']
+                        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'})
+
+                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 uid_push_list:
+                        up['do_apns_code'] = result['code_date']['do_apns_code']
+                        up['do_fcm_code'] = result['code_date']['do_fcm_code']
+                        up['do_jpush_code'] = result['code_date']['do_jpush_code']
+                        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']
+                    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 uid_push_list:
+                        up['do_apns_code'] = result['code_date']['do_apns_code']
+                        up['do_fcm_code'] = result['code_date']['do_fcm_code']
+                        up['do_jpush_code'] = result['code_date']['do_jpush_code']
+                        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']
+
+                    res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3'}
+                    return JsonResponse(status=200, data=res_data)
+        except Exception as e:
+            logger.info('消息推送-异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+            data = {
+                'errLine': e.__traceback__.tb_lineno,
+                'errMsg': repr(e),
+            }
+            return JsonResponse(status=200, data=json.dumps(data), safe=False)
 
 
     def is_sys_msg(self, event_type):
     def is_sys_msg(self, event_type):
         event_type_list = [702, 703, 704]
         event_type_list = [702, 703, 704]
         if event_type in event_type_list:
         if event_type in event_type_list:
             return True
             return True
         return False
         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)
-        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']
-        _jpush = jpush.JPush(app_key, master_secret)
-        push = _jpush.create_push()
-        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_
-        try:
-            res = push.send()
-            print(res)
-            status_code = res.status_code
-        except Exception as e:
-            logger = logging.getLogger('info')
-            logger.info(e)
-            status_code = 100
-        return status_code
-
-    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
-                                                   })
-        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": "", "uid": uid, "zpush": "1", "channel": channel}
-            alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
-            payload = apns2.Payload(alert=alert, custom=push_data, sound="default")
-            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
-            res = cli.push(n=n, device_token=token_val, topic=appBundleId)
-            if res.status_code == 200:
-                return res.status_code
-            else:
-                return res.status_code
-        except (ValueError, ArithmeticError):
-            return 'The program has a numeric format exception, one of the arithmetic exceptions'
-        except Exception as e:
-            return repr(e)

+ 90 - 289
Controller/DetectControllerV2.py

@@ -1,29 +1,23 @@
 import json
 import json
 import logging
 import logging
 import os
 import os
-import threading
-import time
 
 
 import apns2
 import apns2
 import boto3
 import boto3
 import botocore
 import botocore
 import jpush as jpush
 import jpush as jpush
 from botocore import client
 from botocore import client
+from django.db import transaction
 from django.http import JsonResponse
 from django.http import JsonResponse
 from django.views.generic.base import View
 from django.views.generic.base import View
 from pyfcm import FCMNotification
 from pyfcm import FCMNotification
 
 
 from AnsjerPush.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
 from AnsjerPush.config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
 from AnsjerPush.config import JPUSH_CONFIG, FCM_CONFIG, APNS_CONFIG, BASE_DIR, APNS_MODE
 from AnsjerPush.config import JPUSH_CONFIG, FCM_CONFIG, APNS_CONFIG, BASE_DIR, APNS_MODE
-from AnsjerPush.config import SERVER_TYPE
-from Model.models import UidPushModel, SysMsgModel
-from Object.ETkObject import ETkObject
 from Object.RedisObject import RedisObject
 from Object.RedisObject import RedisObject
-from Object.UidTokenObject import UidTokenObject
-from Object.utils import LocalDateTimeUtil
 from Service.CommonService import CommonService
 from Service.CommonService import CommonService
-from Service.EquipmentInfoService import EquipmentInfoService
-from Service.GatewayService import GatewayPushService
+from Service.DevicePushService import DevicePushService
+from Service.PushService import PushObject
 
 
 
 
 # 移动侦测V2接口
 # 移动侦测V2接口
@@ -38,6 +32,17 @@ class NotificationV2View(View):
         return self.validation(request.POST)
         return self.validation(request.POST)
 
 
     def validation(self, request_dict):
     def validation(self, request_dict):
+        """
+        设备触发报警消息推送
+        @param request_dict:uidToken 加密uid
+        @param request_dict:etk 加密uid
+        @param request_dict:channel 设备通道号
+        @param request_dict:n_time 设备触发报警时间
+        @param request_dict:event_type 设备事件类型
+        @param request_dict:is_st 文件类型(0:无,1:图片,2:视频)
+        @param request_dict:region 文件存储区域(1:国外,2国内)
+        @param request_dict:electricity 电量值
+        """
         logger = logging.getLogger('info')
         logger = logging.getLogger('info')
         logger.info("移动侦测V2接口参数:{}".format(request_dict))
         logger.info("移动侦测V2接口参数:{}".format(request_dict))
         uidToken = request_dict.get('uidToken', None)
         uidToken = request_dict.get('uidToken', None)
@@ -57,252 +62,71 @@ class NotificationV2View(View):
             return JsonResponse(status=200, data={'code': 404, 'msg': 'no region or is_st'})
             return JsonResponse(status=200, data={'code': 404, 'msg': 'no region or is_st'})
 
 
         try:
         try:
-            is_st = int(is_st)
-            region = int(region)
-
-            # 解密获取uid
-            if etk:
-                eto = ETkObject(etk)
-                uid = eto.uid
-            else:
-                uto = UidTokenObject(uidToken)
-                uid = uto.UID
-            # uid = request_dict.get('uid', None)     # 调试
-            # 判断uid长度
-            if len(uid) != 20 and len(uid) != 14:
-                return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong uid'})
-            logger.info('调用推送接口的uid:{}'.format(uid))
-
-            pkey = '{uid}_{channel}_{event_type}_ptl'.format(uid=uid, channel=channel, event_type=event_type)
-            ykey = '{uid}_redis_qs'.format(uid=uid)
-            is_sys_msg = self.is_sys_msg(int(event_type))
-            if is_sys_msg:
-                dkey = '{uid}_{channel}_{event_type}_flag'.format(uid=uid, channel=channel, event_type=event_type)
-            else:
-                dkey = '{uid}_{channel}_flag'.format(uid=uid, channel=channel)
-
-            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)  # 推送消息时间间隔
-            logger.info('ykey:{}, pkey: {}, dkey: {}'.format(have_ykey, have_pkey, have_dkey))
-            # 一分钟内不推送
-            if have_pkey:
-                return JsonResponse(status=200, data={'code': 0, 'msg': 'Push again in one minute'})
-            redisObj.set_data(key=pkey, val=1, expire=60)
-
-            # 查询推送数据
-            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', 'uid_set__ai_type', 'uid_set__new_detect_interval')
-            if not uid_push_qs.exists():
-                logger.info('uid_push 数据不存在')
-                return JsonResponse(status=200, data={'code': 176, 'msg': 'no uid_push data'})
-            ai_type = uid_push_qs.first()['uid_set__ai_type']
-            event_type = self.get_combo_msg_type(ai_type, int(event_type))
-            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)
-
-            nickname = redis_list[0]['uid_set__nickname']
-            detect_interval = redis_list[0]['uid_set__detect_interval']
-            if not nickname:
-                nickname = uid
-            logger.info('{}推送nickname:{}'.format(uid, nickname))
-            if not have_dkey:
-                # 设置推送消息的时间间隔
-                if SERVER_TYPE != 'Ansjer.cn_formal_settings':
-                    new_detect_interval = redis_list[0]['uid_set__new_detect_interval']
-                    detect_interval = new_detect_interval if new_detect_interval > 0 else detect_interval
-                    detect_interval = 60 if detect_interval < 60 else detect_interval
-                redisObj.set_data(key=dkey, val=1, expire=detect_interval - 5)
-                logger.info('APP消息推送间隔:{}s'.format(detect_interval))
-
-            if is_st == 1 or is_st == 3:  # 使用aws s3
-                aws_s3_client = s3_client(region=region)
-                bucket = 'foreignpush' if region == 1 else 'push'
-            kwag_args = {
-                'uid': uid,
-                'channel': channel,
-                'event_type': event_type,
-                'n_time': n_time,
-            }
-            sys_msg_list = []
-            userID_ids = []
-            do_apns_code = ''
-            do_fcm_code = ''
-            do_jpush_code = ''
-            logger.info('进入手机推送------')
-            logger.info('uid={}'.format(uid))
-            logger.info(redis_list)
-            new_device_info_list = []
-            local_date_time = ''
-            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)
-                logger.info('{}推送标题{}'.format(uid, msg_title))
-                # 发送内容
-                msg_text = self.get_msg_text(channel=channel, n_time=n_time, lang=lang, tz=tz,
-                                             event_type=event_type, electricity=electricity)
-                kwag_args['appBundleId'] = appBundleId
-                kwag_args['token_val'] = token_val
-                kwag_args['msg_title'] = msg_title
-                kwag_args['msg_text'] = msg_text
-                logger.info('推送要的数据: {}'.format(kwag_args))
-                local_date_time = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang='cn')
-                logger.info('<<<<<根据时区计算后日期={},时区={}'.format(local_date_time, tz))
-                local_date_time = local_date_time[0:10]
-                logger.info('<<<<<切片后的日期={}'.format(local_date_time))
-                # 以下是存库
-                userID_id = up["userID_id"]
-                if userID_id not in userID_ids:
-                    now_time = int(time.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, electricity=electricity, 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))
-                    else:
-                        # start 根据设备侦测时间为准进行分表存储数据
-                        logger.info('分表存数据start------')
-                        new_device_info_list.append(EquipmentInfoService.get_equipment_info_obj(
-                            local_date_time,
-                            device_user_id=userID_id,
-                            event_time=n_time,
-                            event_type=event_type,
-                            device_uid=uid,
-                            device_nick_name=nickname,
-                            channel=channel,
-                            alarm='Motion \tChannel:{channel}'.format(channel=channel),
-                            is_st=is_st,
-                            receive_time=n_time,
-                            add_time=now_time,
-                            storage_location=2,
-                            border_coords='',
-                        ))
-                        # end
-                    userID_ids.append(userID_id)
-                try:
-                    # 推送消息
-                    if not have_dkey:
-                        logger.info('APP准备推送:{}, {}'.format(uid, request_dict))
-                        if (is_st == 1 or is_st == 3) and (push_type == 0 or push_type == 1):  # 推送显示图片
-                            if is_st == 1:
-                                key = '{}/{}/{}.jpeg'.format(uid, channel, n_time)
-                            else:
-                                key = '{}/{}/{}_0.jpeg'.format(uid, channel, n_time)
-                            push_thread = threading.Thread(target=self.push_thread_test, args=(
-                                push_type, aws_s3_client, bucket, key, uid, appBundleId, token_val, event_type, n_time,
-                                msg_title, msg_text, channel))
-                            push_thread.start()
-                        else:
-                            if push_type == 0:  # ios apns
-                                do_apns_code = self.do_apns(**kwag_args)
-                            elif push_type == 1:  # android gcm
-                                do_fcm_code = self.do_fcm(**kwag_args)
-                            elif push_type == 2:  # android jpush
-                                do_jpush_code = self.do_jpush(**kwag_args)
-                except Exception as e:
-                    logger.info(
-                        "errLine={errLine}, errMsg={errMsg}".format(errLine=e.__traceback__.tb_lineno, errMsg=repr(e)))
-                    continue
-            if is_sys_msg:
-                SysMsgModel.objects.bulk_create(sys_msg_list)
-            else:
-                # new 分表批量存储 设备信息
-                if new_device_info_list and len(new_device_info_list) > 0:
-                    # 根据日期获得星期几
-                    week = LocalDateTimeUtil.date_to_week(local_date_time)
-                    EquipmentInfoService.equipment_info_bulk_create(week, new_device_info_list)
-                    logger.info('设备信息分表批量保存end------')
-
-            if is_st == 0 or is_st == 2:
-                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
-                    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:
-                thumbspng = '{uid}/{channel}/{filename}.jpeg'.format(uid=uid, channel=channel, filename=n_time)
-                Params = {'Key': thumbspng}
-                if region == 2:  # 2:国内
-                    Params['Bucket'] = 'push'
-                else:  # 1:国外
-                    Params['Bucket'] = 'foreignpush'
-                response_url = generate_s3_url(aws_s3_client, Params)
-                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
-                    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']
-                res_data = {'code': 0, 'img_push': response_url, 'msg': 'success'}
-                return JsonResponse(status=200, data=res_data)
-
-            elif is_st == 3:
-                img_url_list = []
-                if region == 2:  # 2:国内
-                    Params = {'Bucket': 'push'}
-                else:  # 1:国外
-                    Params = {'Bucket': 'foreignpush'}
-                for i in range(is_st):
-                    thumbspng = '{uid}/{channel}/{filename}_{st}.jpeg'. \
-                        format(uid=uid, channel=channel, filename=n_time, st=i)
-                    Params['Key'] = thumbspng
-                    response_url = generate_s3_url(aws_s3_client, Params)
-                    img_url_list.append(response_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
-                    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']
-                res_data = {'code': 0, 'img_url_list': img_url_list, 'msg': 'success 3'}
-                return JsonResponse(status=200, data=res_data)
+            with transaction.atomic():
+                is_st = int(is_st)
+                region = int(region)
+                uid = DevicePushService.decode_uid(etk, uidToken)  # 解密uid
+                if len(uid) != 20 and len(uid) != 14:
+                    return JsonResponse(status=200, data={'code': 404, 'msg': 'wrong uid'})
+                req_limiting = '{uid}_{channel}_{event_type}_ptl' \
+                    .format(uid=uid, channel=channel, event_type=event_type)
+                is_sys_msg = self.is_sys_msg(int(event_type))  # 判断事件类型是否是系统消息
+                if is_sys_msg:
+                    push_interval = '{uid}_{channel}_{event_type}_flag' \
+                        .format(uid=uid, channel=channel, event_type=event_type)
+                else:
+                    push_interval = '{uid}_{channel}_flag'.format(uid=uid, channel=channel)
+                redisObj = RedisObject(db=6)
+                cache_req_limiting = redisObj.get_data(key=req_limiting)  # 获取请求限流缓存数据
+                cache_app_push = redisObj.get_data(key=push_interval)  # 获取APP推送消息时间间隔缓存数据
+                logger.info('消息推送- 限流key: {}, 推送间隔key: {}'.
+                            format(cache_req_limiting, cache_app_push))
+                if cache_req_limiting:  # 限流存在则直接返回
+                    return JsonResponse(status=200, data={'code': 0, 'msg': 'Push again in one minute'})
+                redisObj.set_data(key=req_limiting, val=1, expire=60)  # 当缓存不存在限流数据 重新设置一分钟请求一次
+                uid_push_qs = DevicePushService.query_uid_push(uid)  # 查询uid_set与push数据列表
+                if not uid_push_qs.exists():
+                    logger.info('消息推送-uid_push 数据不存在')
+                    return JsonResponse(status=200, data={'code': 176, 'msg': 'no uid_push data'})
+                ai_type = uid_push_qs.first()['uid_set__ai_type']
+                event_type = self.get_combo_msg_type(ai_type, int(event_type))  # 解析消息事件类型看是否多类型组合
+                # 将uid_set以及uid_push 转数组列表
+                uid_set_push_list = DevicePushService.cache_uid_push(uid_push_qs)
+                nickname = uid_set_push_list[0]['uid_set__nickname']
+                nickname = uid if not nickname else nickname
+                # APP消息提醒推送间隔
+                detect_interval = uid_set_push_list[0]['uid_set__detect_interval']
+                if not cache_app_push:
+                    # 缓存APP提醒推送间隔 默认1分钟提醒一次
+                    DevicePushService.cache_push_detect_interval(redisObj, push_interval, detect_interval,
+                                                                 uid_set_push_list[0]['uid_set__new_detect_interval'])
+                bucket = ''
+                aws_s3_client = ''
+                if is_st == 1 or is_st == 3:  # 使用aws s3
+                    aws_s3_client = s3_client(region=region)
+                    bucket = 'foreignpush' if region == 1 else 'push'
+                kwag_args = {
+                    'uid': uid,
+                    'channel': channel,
+                    'event_type': event_type,
+                    'n_time': n_time,
+                }
+                params = {'nickname': nickname, 'uid': uid, 'kwag_args': kwag_args, 'is_st': is_st, 'region': region,
+                          'is_sys_msg': is_sys_msg, 'channel': channel, 'event_type': event_type, 'n_time': n_time,
+                          'electricity': electricity, 'bucket': bucket, 'aws_s3_client': aws_s3_client,
+                          'app_push': cache_app_push}
+                # APP消息推送与获取报警消息数据列表
+                result = DevicePushService.save_msg_push(uid_set_push_list, **params)
+                # 批量系统消息&报警消息数据存库
+                DevicePushService.save_sys_msg(is_sys_msg, result['local_date_time'],
+                                               result['sys_msg_list'], result['new_device_info_list'])
+                params['aws_s3_client'] = aws_s3_client
+                params['uid_set_push_list'] = uid_set_push_list
+                params['code_dict'] = result
+                result_dict = DevicePushService.get_push_url(**params)  # 获取S3对象上传链接
+                return JsonResponse(status=200, data=result_dict)
         except Exception as e:
         except Exception as e:
-            logger.info('移动侦测接口异常: {}'.format(e))
-            logger.info('错误文件', e.__traceback__.tb_frame.f_globals['__file__'])
-            logger.info('错误行号', e.__traceback__.tb_lineno)
+            logger.info('消息推送-异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
             data = {
             data = {
                 'errLine': e.__traceback__.tb_lineno,
                 'errLine': e.__traceback__.tb_lineno,
                 'errMsg': repr(e),
                 'errMsg': repr(e),
@@ -362,34 +186,20 @@ class NotificationV2View(View):
                                                              ExpiresIn=300)
                                                              ExpiresIn=300)
             logger.info('推送图片url:{}'.format(image_url))
             logger.info('推送图片url:{}'.format(image_url))
             if push_type == 0:
             if push_type == 0:
-                GatewayPushService.ios_apns_push(uid, appBundleId, token_val, n_time, event_type, msg_title, msg_text,
-                                                 uid, channel, image_url)
+                PushObject.ios_apns_push(uid, appBundleId, token_val, n_time, event_type, msg_title, msg_text,
+                                         uid, channel, image_url)
             elif push_type == 1:
             elif push_type == 1:
-                GatewayPushService.android_fcm_push(uid, appBundleId, token_val, n_time, event_type, msg_title,
-                                                    msg_text, uid, channel, image_url)
+                PushObject.android_fcm_push(uid, appBundleId, token_val, n_time, event_type, msg_title,
+                                            msg_text, uid, channel, image_url)
         except Exception as e:
         except Exception as e:
             logger.info('推送图片测试异常:{}'.format(e))
             logger.info('推送图片测试异常:{}'.format(e))
 
 
-    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):
+    @staticmethod
+    def is_sys_msg(event_type):
+        """
+        判断是否属于系统消息
+        @return: True | False
+        """
         event_type_list = [702, 703, 704]
         event_type_list = [702, 703, 704]
         if event_type in event_type_list:
         if event_type in event_type_list:
             return True
             return True
@@ -400,7 +210,7 @@ class NotificationV2View(View):
         etype = int(event_type)
         etype = int(event_type)
         if lang == 'cn':
         if lang == 'cn':
             if etype == 704:
             if etype == 704:
-                msg_type = '剩余电量:' + electricity
+                msg_type = '剩余电量 ' + electricity
             elif etype == 702:
             elif etype == 702:
                 msg_type = '摄像头休眠'
                 msg_type = '摄像头休眠'
             elif etype == 703:
             elif etype == 703:
@@ -413,7 +223,7 @@ class NotificationV2View(View):
                 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:
         else:
             if etype == 704:
             if etype == 704:
-                msg_type = 'Battery remaining:' + electricity
+                msg_type = 'Battery remaining ' + electricity
             elif etype == 702:
             elif etype == 702:
                 msg_type = 'Camera sleep'
                 msg_type = 'Camera sleep'
             elif etype == 703:
             elif etype == 703:
@@ -523,12 +333,3 @@ def s3_client(region):
             region_name='us-east-1'
             region_name='us-east-1'
         )
         )
     return aws_s3_client
     return aws_s3_client
-
-
-def generate_s3_url(aws_s3_client, Params):
-    response_url = aws_s3_client.generate_presigned_url(
-        ClientMethod='put_object',
-        Params=Params,
-        ExpiresIn=3600
-    )
-    return response_url

+ 69 - 177
Controller/PowerWarningController.py

@@ -5,24 +5,19 @@
 @File :PowerWarningController.py
 @File :PowerWarningController.py
 """
 """
 import logging
 import logging
-import os
 import time
 import time
 
 
-import apns2
-import jpush as jpush
 from django.http import JsonResponse
 from django.http import JsonResponse
 from django.views.generic.base import View
 from django.views.generic.base import View
-from pyfcm import FCMNotification
 
 
-from AnsjerPush.config import JPUSH_CONFIG, FCM_CONFIG, APNS_CONFIG, BASE_DIR, APNS_MODE
-from Model.models import UidPushModel, SysMsgModel
+from Model.models import UidPushModel, SysMsgModel, Device_Info
 from Object.RedisObject import RedisObject
 from Object.RedisObject import RedisObject
-from Service.CommonService import CommonService
+from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
+from Service.PushService import PushObject
 
 
 
 
-# 低电量推送接口
 class PowerWarningView(View):
 class PowerWarningView(View):
-
+    # 低电量推送接口
     def get(self, request, *args, **kwargs):
     def get(self, request, *args, **kwargs):
         request.encoding = 'utf-8'
         request.encoding = 'utf-8'
         return self.validation(request.GET)
         return self.validation(request.GET)
@@ -31,14 +26,15 @@ class PowerWarningView(View):
         request.encoding = 'utf-8'
         request.encoding = 'utf-8'
         return self.validation(request.POST)
         return self.validation(request.POST)
 
 
-    def validation(self, request_dict):
+    @staticmethod
+    def validation(request_dict):
         logger = logging.getLogger('info')
         logger = logging.getLogger('info')
         uid = request_dict.get('uid', None)
         uid = request_dict.get('uid', None)
 
 
         # 限制每6小时推一次
         # 限制每6小时推一次
-        redisObj = RedisObject()
-        is_limit = redisObj.CONN.setnx(uid+'limit_power_warning', 1)
-        redisObj.CONN.expire(uid+'limit_power_warning', 6*60*60)
+        redis_obj = RedisObject()
+        is_limit = redis_obj.CONN.setnx(uid + 'limit_power_warning', 1)
+        redis_obj.CONN.expire(uid + 'limit_power_warning', 6 * 60 * 60)
         if not is_limit:
         if not is_limit:
             return JsonResponse(status=200, data={'code': 0, 'msg': 'push limited!'})
             return JsonResponse(status=200, data={'code': 0, 'msg': 'push limited!'})
 
 
@@ -47,190 +43,86 @@ class PowerWarningView(View):
         logger.info('调用低电量推送接口的uid: {},electricity: {}'.format(uid, electricity))
         logger.info('调用低电量推送接口的uid: {},electricity: {}'.format(uid, electricity))
         try:
         try:
             uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid). \
             uid_push_qs = UidPushModel.objects.filter(uid_set__uid=uid). \
-                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')
+                values('push_type', 'appBundleId', 'token_val', 'lang', 'tz', 'userID_id')
             if not uid_push_qs.exists():
             if not uid_push_qs.exists():
                 res_data = {'code': 173, 'msg': 'uid push data not exit!'}
                 res_data = {'code': 173, 'msg': 'uid push data not exit!'}
                 return JsonResponse(status=200, data=res_data)
                 return JsonResponse(status=200, data=res_data)
 
 
-            # 新建一个list接收数据
-            redis_list = []
-            # 把数据库数据追加进redis_list
-            for qs in uid_push_qs:
-                redis_list.append(qs)
-
-            if not redis_list:
-                res_data = {'code': 0, 'msg': 'no redis_list success!'}
-                return JsonResponse(status=200, data=res_data)
-
-            nickname = redis_list[0]['uid_set__nickname']
-            if not nickname:
-                nickname = uid
+            uid_push_list = [uid_push for uid_push in uid_push_qs]
+            # 查询设备数据
+            device_info_qs = Device_Info.objects.filter(UID=uid).first()
+            nickname = uid if device_info_qs is None else device_info_qs.NickName
 
 
             now_time = int(time.time())
             now_time = int(time.time())
-            channel = channel
             event_type = 704
             event_type = 704
             sys_msg_list = []
             sys_msg_list = []
-            userID_ids = []
-            kwag_args = {
-                'uid': uid,
-                'channel': channel,
-                'event_type': event_type,
-                'n_time': now_time,
-            }
-
-            for up in redis_list:
-                push_type = up['push_type']
-                appBundleId = up['appBundleId']
-                token_val = up['token_val']
-                lang = up['lang']
-                tz = up['tz']
+            user_id_list = []
+
+            for uid_push in uid_push_list:
+                push_type = uid_push['push_type']
+                app_bundle_id = uid_push['appBundleId']
+                token_val = uid_push['token_val']
+                lang = uid_push['lang']
+                tz = uid_push['tz']
                 if tz is None or tz == '':
                 if tz is None or tz == '':
                     tz = 0
                     tz = 0
-                # 发送标题
-                msg_title = self.get_msg_title(appBundleId=appBundleId, nickname=nickname)
-                # 发送内容
-                msg_text = self.get_msg_text(channel=channel, n_time=now_time, lang=lang, tz=tz,
-                                             event_type=event_type, electricity=electricity)
-                kwag_args['appBundleId'] = appBundleId
-                kwag_args['token_val'] = token_val
-                kwag_args['msg_title'] = msg_title
-                kwag_args['msg_text'] = msg_text
-
-                if push_type == 0:  # ios apns
-                    self.do_apns(**kwag_args)
-                elif push_type == 1:  # android gcm
-                    self.do_fcm(**kwag_args)
-                elif push_type == 2:  # android jpush
-                    self.do_jpush(**kwag_args)
 
 
-                # 以下是存库
-                userID_id = up["userID_id"]
-                if userID_id not in userID_ids:
-                    sys_msg_text = self.get_msg_text(channel=channel, n_time=now_time, lang=lang, tz=tz,
-                                                     event_type=event_type, is_sys=1, electricity=electricity)
+                # 推送标题和推送内容
+                msg_title = PushObject.get_msg_title(nickname=nickname)
+                msg_text = PushObject.get_low_power_msg_text(channel=channel, n_time=now_time, lang=lang, tz=tz,
+                                                             electricity=electricity)
+
+                kwargs = {
+                    'nickname': nickname,
+                    'app_bundle_id': app_bundle_id,
+                    'token_val': token_val,
+                    'n_time': now_time,
+                    'event_type': event_type,
+                    'msg_title': msg_title,
+                    'msg_text': msg_text,
+                    'uid': uid,
+                    'channel': channel,
+                }
+
+                try:
+                    # 推送消息
+                    if push_type == 0:  # ios apns
+                        PushObject.ios_apns_push(**kwargs)
+                    elif push_type == 1:  # android gcm
+                        PushObject.android_fcm_push(**kwargs)
+                    elif push_type == 2:  # android jpush
+                        PushObject.android_jpush(**kwargs)
+                    elif push_type == 3:
+                        huawei_push_object = HuaweiPushObject()
+                        huawei_push_object.send_push_notify_message(**kwargs)
+                    elif push_type == 4:  # android 小米推送
+                        PushObject.android_xmpush(**kwargs)
+                    elif push_type == 5:  # android vivo推送
+                        PushObject.android_vivopush(**kwargs)
+                    elif push_type == 6:  # android oppo推送
+                        PushObject.android_oppopush(**kwargs)
+                    elif push_type == 7:  # android 魅族推送
+                        PushObject.android_meizupush(**kwargs)
+                except Exception as e:
+                    logger.info('低电量推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+                    continue
+
+                # 保存系统消息
+                user_id = uid_push['userID_id']
+                if user_id not in user_id_list:
+                    sys_msg_text = PushObject.get_low_power_msg_text(channel=channel, n_time=now_time, lang=lang, tz=tz,
+                                                                     electricity=electricity, is_sys=1)
                     sys_msg_list.append(SysMsgModel(
                     sys_msg_list.append(SysMsgModel(
-                        userID_id=userID_id,
+                        userID_id=user_id,
                         msg=sys_msg_text,
                         msg=sys_msg_text,
                         addTime=now_time,
                         addTime=now_time,
                         updTime=now_time,
                         updTime=now_time,
                         uid=uid,
                         uid=uid,
                         eventType=event_type,
                         eventType=event_type,
                     ))
                     ))
-                    userID_ids.append(userID_id)
+                    user_id_list.append(user_id)
             SysMsgModel.objects.bulk_create(sys_msg_list)
             SysMsgModel.objects.bulk_create(sys_msg_list)
             return JsonResponse(status=200, data={'code': 0})
             return JsonResponse(status=200, data={'code': 0})
         except Exception as e:
         except Exception as e:
             logger.info('低电量推送接口异常: {}'.format(e))
             logger.info('低电量推送接口异常: {}'.format(e))
             return JsonResponse(status=500, data={'msg': 'power warning error'})
             return JsonResponse(status=500, data={'msg': 'power warning error'})
-
-    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, electricity, 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 = '剩余电量:' + electricity
-            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 = 'Battery remaining:' + electricity
-            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']
-        _jpush = jpush.JPush(app_key, master_secret)
-        push = _jpush.create_push()
-        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()
-        return res.status_code
-
-    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
-                                                   })
-        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": "", "uid": uid, "zpush": "1", "channel": channel}
-            alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
-            payload = apns2.Payload(alert=alert, custom=push_data, sound="default")
-            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
-            res = cli.push(n=n, device_token=token_val, topic=appBundleId)
-            if res.status_code == 200:
-                return res.status_code
-            else:
-                return res.status_code
-        except (ValueError, ArithmeticError):
-            return 'The program has a numeric format exception, one of the arithmetic exceptions'
-        except Exception as e:
-            return repr(e)

+ 21 - 3
Controller/ShadowController.py

@@ -14,6 +14,15 @@ from Service.CommonService import CommonService
 
 
 
 
 def generate_utk(request):
 def generate_utk(request):
+    """
+    uid生成etk
+    app测试使用
+    @param request: 请求
+    @request username: username
+    @request password: password
+    @request uid: uid
+    @return : etk
+    """
     request.encoding = 'utf-8'
     request.encoding = 'utf-8'
     response = ResponseObject()
     response = ResponseObject()
     if request.method == 'GET':
     if request.method == 'GET':
@@ -28,7 +37,7 @@ def generate_utk(request):
     if username and password:
     if username and password:
         if username == 'debug_user' and password == 'debug_password':
         if username == 'debug_user' and password == 'debug_password':
             etkObj = ETkObject(etk='')
             etkObj = ETkObject(etk='')
-            etk = etkObj.encrypt(uid)
+            etk = etkObj.encrypt_uid(uid)
             return response.json(0, {'etk': etk})
             return response.json(0, {'etk': etk})
         else:
         else:
             return response.json(404)
             return response.json(404)
@@ -36,8 +45,12 @@ def generate_utk(request):
         return response.json(444, 'username password')
         return response.json(444, 'username password')
 
 
 
 
-# 更新设备影子
 def update_device_shadow(request):
 def update_device_shadow(request):
+    """
+    设备生成或更新(复位时)设备影子
+    @param request: 请求
+    @return : JsonResponse
+    """
     request.encoding = 'utf-8'
     request.encoding = 'utf-8'
     if request.method == 'POST':
     if request.method == 'POST':
         request_dict = request.POST
         request_dict = request.POST
@@ -49,7 +62,7 @@ def update_device_shadow(request):
     logger = logging.getLogger('info')
     logger = logging.getLogger('info')
     logger.info('---更新设备影子---, 使用配置:{}, 参数:{}'.format(SERVER_TYPE, request_dict.dict()))
     logger.info('---更新设备影子---, 使用配置:{}, 参数:{}'.format(SERVER_TYPE, request_dict.dict()))
     # 如果为美国配置,异步请求更新国内和欧洲数据
     # 如果为美国配置,异步请求更新国内和欧洲数据
-    if SERVER_TYPE == 'Ansjer.formal_settings':
+    if SERVER_TYPE == 'Ansjer.us_config.formal_settings':
         domain_name_list = ['push.zositechc.cn', 'push.zositeche.com']
         domain_name_list = ['push.zositechc.cn', 'push.zositeche.com']
         request_thread = threading.Thread(target=do_request_thread, args=(domain_name_list, request_dict.dict()))
         request_thread = threading.Thread(target=do_request_thread, args=(domain_name_list, request_dict.dict()))
         request_thread.start()
         request_thread.start()
@@ -186,6 +199,11 @@ def update_device_shadow(request):
 
 
 
 
 def do_request_thread(domain_name_list, data):
 def do_request_thread(domain_name_list, data):
+    """
+    请求线程
+    @param domain_name_list: 域名列表
+    @param data: 请求数据
+    """
     for domain_name in domain_name_list:
     for domain_name in domain_name_list:
         url = 'http://{}/deviceShadow/update'.format(domain_name)
         url = 'http://{}/deviceShadow/update'.format(domain_name)
         requests.post(url=url, data=data, timeout=2)
         requests.post(url=url, data=data, timeout=2)

+ 76 - 21
Controller/gatewayController.py

@@ -15,7 +15,10 @@ from Object.ResponseObject import ResponseObject
 from Object.utils import LocalDateTimeUtil
 from Object.utils import LocalDateTimeUtil
 from Service.CommonService import CommonService
 from Service.CommonService import CommonService
 from Service.EquipmentInfoService import EquipmentInfoService
 from Service.EquipmentInfoService import EquipmentInfoService
-from Service.GatewayService import GatewayPushService
+from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
+from Service.PushService import PushObject
+
+LOGGER = logging.getLogger('info')
 
 
 
 
 class GatewayView(View):
 class GatewayView(View):
@@ -35,6 +38,8 @@ class GatewayView(View):
             return self.gateway_push(request_dict, response)
             return self.gateway_push(request_dict, response)
         elif operation == 'sceneLogPush':  # 场景日志推送
         elif operation == 'sceneLogPush':  # 场景日志推送
             return self.scene_log_push(request_dict, response)
             return self.scene_log_push(request_dict, response)
+        elif operation == 'socketPush':  # 插座推送
+            return self.socket_msg_push(request_dict, response)
         else:
         else:
             return response.json(414)
             return response.json(414)
 
 
@@ -51,7 +56,6 @@ class GatewayView(View):
         @request_dict alarm: 消息内容
         @request_dict alarm: 消息内容
         @request_dict defense: 防御状态,0:撤防,1:防御
         @request_dict defense: 防御状态,0:撤防,1:防御
         @request_dict sensor_status: 拆动状态,拆动时传参
         @request_dict sensor_status: 拆动状态,拆动时传参
-        @request_dict sensor_low_power: 低电量
         @param response: 响应对象
         @param response: 响应对象
         @return: response
         @return: response
         """
         """
@@ -158,8 +162,8 @@ class GatewayView(View):
                     tz = gateway_push['tz'] if gateway_push['tz'] else 0
                     tz = gateway_push['tz'] if gateway_push['tz'] else 0
 
 
                     # 获取推送所需数据
                     # 获取推送所需数据
-                    msg_title = GatewayPushService.get_msg_title(app_bundle_id, nickname)
-                    msg_text = GatewayPushService.get_msg_text(n_time, tz, lang, alarm)
+                    msg_title = PushObject.get_msg_title(nickname)
+                    msg_text = PushObject.get_gateway_msg_text(n_time, tz, lang, alarm)
 
 
                     kwargs['msg_title'] = msg_title
                     kwargs['msg_title'] = msg_title
                     kwargs['msg_text'] = msg_text
                     kwargs['msg_text'] = msg_text
@@ -169,11 +173,22 @@ class GatewayView(View):
                     try:
                     try:
                         # 推送消息
                         # 推送消息
                         if push_type == 0:  # ios apns
                         if push_type == 0:  # ios apns
-                            GatewayPushService.ios_apns_push(**kwargs)
+                            PushObject.ios_apns_push(**kwargs)
                         elif push_type == 1:  # android gcm
                         elif push_type == 1:  # android gcm
-                            GatewayPushService.android_fcm_push(**kwargs)
+                            PushObject.android_fcm_push(**kwargs)
                         elif push_type == 2:  # android 极光推送
                         elif push_type == 2:  # android 极光推送
-                            GatewayPushService.android_jpush(**kwargs)
+                            PushObject.android_jpush(**kwargs)
+                        elif push_type == 3:
+                            huawei_push_object = HuaweiPushObject()
+                            huawei_push_object.send_push_notify_message(**kwargs)
+                        elif push_type == 4:  # android 小米推送
+                            PushObject.android_xmpush(**kwargs)
+                        elif push_type == 5:  # android vivo推送
+                            PushObject.android_vivopush(**kwargs)
+                        elif push_type == 6:  # android oppo推送
+                            PushObject.android_oppopush(**kwargs)
+                        elif push_type == 7:  # android 魅族推送
+                            PushObject.android_meizupush(**kwargs)
                     except Exception as e:
                     except Exception as e:
                         logger.info('网关推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                         logger.info('网关推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                         continue
                         continue
@@ -186,14 +201,10 @@ class GatewayView(View):
     @staticmethod
     @staticmethod
     def scene_log_push(request_dict, response):
     def scene_log_push(request_dict, response):
         """
         """
-        网关推送
+        网关智能场景日志推送
         @param request_dict: 请求参数
         @param request_dict: 请求参数
-        @request_dict serial_number: 序列号
-        @request_dict ieee_addr: 长地址
-        @request_dict src_addr: 短地址
-        @request_dict sensor_type: 传感器类型
-        @request_dict event_type: 事件类型
-        @request_dict alarm: 消息内容
+        @request_dict sceneId: 场景id
+        @request_dict status: 状态
         @param response: 响应对象
         @param response: 响应对象
         @return: response
         @return: response
         """
         """
@@ -209,13 +220,18 @@ class GatewayView(View):
         if not smart_scene_qs.exists():
         if not smart_scene_qs.exists():
             return response.json(173)
             return response.json(173)
 
 
-        nickname = ''
         scene_name = smart_scene_qs[0]['scene_name']
         scene_name = smart_scene_qs[0]['scene_name']
         tasks = smart_scene_qs[0]['tasks']
         tasks = smart_scene_qs[0]['tasks']
         device_id = smart_scene_qs[0]['device_id']
         device_id = smart_scene_qs[0]['device_id']
         sub_device_id = smart_scene_qs[0]['sub_device_id']
         sub_device_id = smart_scene_qs[0]['sub_device_id']
         n_time = int(time.time())
         n_time = int(time.time())
         user_id = smart_scene_qs[0]['user_id']
         user_id = smart_scene_qs[0]['user_id']
+        if sub_device_id:
+            gateway_sub_device_qs = GatewaySubDevice.objects.filter(id=sub_device_id).values('nickname')
+            nickname = gateway_sub_device_qs[0]['nickname'] if gateway_sub_device_qs.exists() else ''
+        else:
+            device_qs = Device_Info.objects.filter(id=device_id).values('NickName')
+            nickname = device_qs[0]['NickName'] if device_qs.exists() else ''
         log_dict = {
         log_dict = {
             'scene_id': scene_id,
             'scene_id': scene_id,
             'scene_name': scene_name,
             'scene_name': scene_name,
@@ -249,10 +265,8 @@ class GatewayView(View):
                         app_bundle_id = gateway_push['app_bundle_id']
                         app_bundle_id = gateway_push['app_bundle_id']
                         push_type = gateway_push['push_type']
                         push_type = gateway_push['push_type']
                         token_val = gateway_push['token_val']
                         token_val = gateway_push['token_val']
-                        lang = gateway_push['lang']
-                        tz = gateway_push['tz'] if gateway_push['tz'] else 0
 
 
-                        kwargs['msg_title'] = nickname
+                        kwargs['msg_title'] = PushObject.get_msg_title(nickname)
                         kwargs['msg_text'] = event_info
                         kwargs['msg_text'] = event_info
                         kwargs['app_bundle_id'] = app_bundle_id
                         kwargs['app_bundle_id'] = app_bundle_id
                         kwargs['token_val'] = token_val
                         kwargs['token_val'] = token_val
@@ -260,11 +274,22 @@ class GatewayView(View):
                         try:
                         try:
                             # 推送消息
                             # 推送消息
                             if push_type == 0:  # ios apns
                             if push_type == 0:  # ios apns
-                                GatewayPushService.ios_apns_push(**kwargs)
+                                PushObject.ios_apns_push(**kwargs)
                             elif push_type == 1:  # android gcm
                             elif push_type == 1:  # android gcm
-                                GatewayPushService.android_fcm_push(**kwargs)
+                                PushObject.android_fcm_push(**kwargs)
                             elif push_type == 2:  # android 极光推送
                             elif push_type == 2:  # android 极光推送
-                                GatewayPushService.android_jpush(**kwargs)
+                                PushObject.android_jpush(**kwargs)
+                            elif push_type == 3:
+                                huawei_push_object = HuaweiPushObject()
+                                huawei_push_object.send_push_notify_message(**kwargs)
+                            elif push_type == 4:  # android 小米推送
+                                PushObject.android_xmpush(**kwargs)
+                            elif push_type == 5:  # android vivo推送
+                                PushObject.android_vivopush(**kwargs)
+                            elif push_type == 6:  # android oppo推送
+                                PushObject.android_oppopush(**kwargs)
+                            elif push_type == 7:  # android 魅族推送
+                                PushObject.android_meizupush(**kwargs)
                         except Exception as e:
                         except Exception as e:
                             logger.info('场景日志推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                             logger.info('场景日志推送消息异常,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                             continue
                             continue
@@ -272,3 +297,33 @@ class GatewayView(View):
         except Exception as e:
         except Exception as e:
             logger.info('---场景日志推送接口异常--- {}'.format(repr(e)))
             logger.info('---场景日志推送接口异常--- {}'.format(repr(e)))
             return response.json(500, repr(e))
             return response.json(500, repr(e))
+
+    @classmethod
+    def socket_msg_push(cls, request_dict, response):
+        """
+        智能插座开关状态推送
+        """
+        t = time.time()
+        logger = logging.getLogger('info')
+        try:
+            serial_number = request_dict.get('serialNumber', None)
+            status = request_dict.get('status', None)
+            if not all([serial_number, status]):
+                return response.json(444)
+            now_time = int(time.time())
+            LOGGER.info('已订阅成功接收:事件类型{},状态:{}'.format(serial_number, status))
+            # socket_info_qs = SocketInfo.objects.filter(serial_number=serial_number).values('device_id')
+            # if not socket_info_qs.exists():
+            #     return response.json(173)
+            # 获取主用户设备id
+            log_dict = {
+                'status': int(status),
+                'device_id': serial_number,
+                'created_time': now_time,
+            }
+            SceneLog.objects.create(**log_dict)
+            LOGGER.info(f'coast:{time.time() - t:.4f}s')
+            return response.json(0)
+        except Exception as e:
+            logger.info('---插座开关日志推送接口异常--- {}'.format(repr(e)))
+            return response.json(500, repr(e))

+ 2 - 12
Model/models.py

@@ -1,8 +1,6 @@
 from itertools import chain
 from itertools import chain
 from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
 from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
 from django.db import models
 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 six import python_2_unicode_compatible
 from imagekit.models import ProcessedImageField
 from imagekit.models import ProcessedImageField
 
 
@@ -66,10 +64,6 @@ class Permissions(models.Model):
     description = models.CharField(blank=True, null=True, max_length=128, verbose_name=u'描述信息', default='')
     description = models.CharField(blank=True, null=True, max_length=128, verbose_name=u'描述信息', default='')
     objects = PermissionsManager()
     objects = PermissionsManager()
 
 
-    def __str__(self):
-        return "%s" % (
-            six.text_type(self.description))
-
     class Meta:
     class Meta:
         ordering = ['permName']
         ordering = ['permName']
         db_table = 'permissions'
         db_table = 'permissions'
@@ -209,7 +203,6 @@ class Device_User(AbstractBaseUser):
 
 
     def get_all_permission(self):
     def get_all_permission(self):
         roles = self.role.all()
         roles = self.role.all()
-        perms = self.permission.all()
 
 
         permslist = []
         permslist = []
         for role in roles:
         for role in roles:
@@ -219,9 +212,6 @@ class Device_User(AbstractBaseUser):
                     permslist.append(perm.permName)
                     permslist.append(perm.permName)
                 return permslist
                 return permslist
 
 
-        for perm in perms:
-            permslist.append(perm.permName)
-
         permSet = set(permslist)
         permSet = set(permslist)
         for role in roles:
         for role in roles:
             permlist_tmp = []
             permlist_tmp = []
@@ -1182,7 +1172,7 @@ class UidPushModel(models.Model):
     uid_set = models.ForeignKey(UidSetModel, to_field='id', 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')
     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:安卓')
     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,极光
+    push_type = models.IntegerField(default=0, verbose_name=u'推送类型')  # 0: apns, 1: 安卓gcm, 2: 极光, 3:华为, 4:小米, 5:vivo, 6:oppo, 7:魅族
     token_val = models.CharField(default='', max_length=160, verbose_name=u'设备验证令牌')
     token_val = models.CharField(default='', max_length=160, verbose_name=u'设备验证令牌')
     m_code = models.CharField(default='', max_length=64, verbose_name='手机唯一标识')
     m_code = models.CharField(default='', max_length=64, verbose_name='手机唯一标识')
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
@@ -1202,7 +1192,7 @@ class GatewayPush(models.Model):
     user_id = models.CharField(default='', max_length=32, db_index=True, verbose_name=u'用户id')
     user_id = models.CharField(default='', max_length=32, db_index=True, verbose_name=u'用户id')
     app_bundle_id = models.CharField(default='', max_length=32, verbose_name=u'app包id')
     app_bundle_id = models.CharField(default='', max_length=32, verbose_name=u'app包id')
     app_type = models.IntegerField(default=0, verbose_name=u'app类型')  # 1: ios, 2: 安卓
     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: 极光
+    push_type = models.IntegerField(default=0, verbose_name=u'推送类型')  # 0: apns, 1: 安卓gcm, 2: 极光, 3:华为, 4:小米, 5:vivo, 6:oppo, 7:魅族
     token_val = models.CharField(default='', max_length=500, verbose_name=u'设备验证令牌')
     token_val = models.CharField(default='', max_length=500, verbose_name=u'设备验证令牌')
     m_code = models.CharField(default='', max_length=64, db_index=True, verbose_name='手机唯一标识')
     m_code = models.CharField(default='', max_length=64, db_index=True, verbose_name='手机唯一标识')
     lang = models.CharField(default='en', max_length=8, verbose_name='推送语言')
     lang = models.CharField(default='en', max_length=8, verbose_name='推送语言')

+ 12 - 42
Object/ETkObject.py

@@ -1,17 +1,3 @@
-#!/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 base64
 import urllib.parse
 import urllib.parse
 from random import Random
 from random import Random
@@ -20,52 +6,36 @@ from random import Random
 class ETkObject(object):
 class ETkObject(object):
     def __init__(self, etk):
     def __init__(self, etk):
         self.uid = ''
         self.uid = ''
-        self.parseUid(etk)
+        self.decrypt_uid(etk)
 
 
-    def parseUid(self, etk):
+    def decrypt_uid(self, etk):
         try:
         try:
             c = base64.b64decode(etk)
             c = base64.b64decode(etk)
             c = c[2:-2]
             c = c[2:-2]
             c = urllib.parse.unquote(c.decode('utf-8'))
             c = urllib.parse.unquote(c.decode('utf-8'))
             c = base64.b64decode(c)
             c = base64.b64decode(c)
             uid = c.decode('utf-8')
             uid = c.decode('utf-8')
-            if len(uid) == 20:
-                self.uid = uid
-                print(uid)
-            elif len(uid) == 14:
-                self.uid = uid
-                print(uid)
+            assert len(uid) == 20 or len(uid) == 14
+            self.uid = uid
         except Exception as e:
         except Exception as e:
             print(repr(e))
             print(repr(e))
 
 
-    def encrypt(self,data):
+    def encrypt_uid(self, data):
         s = data.encode()
         s = data.encode()
         s = base64.b64encode(s)
         s = base64.b64encode(s)
-        startStr = self.randomParam()
-        endStr = self.randomParam()
-        s = '{startStr}{s}{endStr}'.format(startStr=startStr,s=s.decode('utf-8'),endStr=endStr)
+        startStr = self.generate_random()
+        endStr = self.generate_random()
+        s = '{startStr}{s}{endStr}'.format(startStr=startStr, s=s.decode('utf-8'), endStr=endStr)
         s = base64.b64encode(s.encode())
         s = base64.b64encode(s.encode())
-        s=s.decode('utf-8')
+        s = s.decode('utf-8')
         return s
         return s
 
 
-    def randomParam(self):
-        characterSet = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsT' \
-                       'tUuVvWwXxYyZz0123456789'
+    @staticmethod
+    def generate_random():
+        characterSet = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
         length = len(characterSet) - 1
         length = len(characterSet) - 1
-
         random = Random()
         random = Random()
         ss = ''
         ss = ''
         for index in range(2):
         for index in range(2):
             ss += characterSet[random.randint(0, length)]
             ss += characterSet[random.randint(0, length)]
         return ss
         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)
-

+ 20 - 12
Object/ResponseObject.py

@@ -6,7 +6,9 @@ class ResponseObject(object):
     def __init__(self, lang='en'):
     def __init__(self, lang='en'):
         self.lang = lang
         self.lang = lang
 
 
-    def data(self, code, res={}):
+    def data(self, code, res=None):
+        if res is None:
+            res = {}
         data_en = {
         data_en = {
             0: 'Success',
             0: 'Success',
             5: 'Please try again one minute later!',
             5: 'Please try again one minute later!',
@@ -18,16 +20,16 @@ class ResponseObject(object):
             48: 'System object error!',
             48: 'System object error!',
             89: 'Already send the code, please check it or get it again after 10m',
             89: 'Already send the code, please check it or get it again after 10m',
             90: 'please check code or get it again after 1m',
             90: 'please check code or get it again after 1m',
-            99: 'Mail doesn\'t exist!',
+            99: 'Mail does not exist!',
             100: 'Phone format error!',
             100: 'Phone format error!',
             101: 'Phone already existed!',
             101: 'Phone already existed!',
-            102: 'Phone doesn\'t exist!',
+            102: 'Phone does not exist!',
             103: 'Mail already existed!',
             103: 'Mail already existed!',
-            104: 'Account doesn\'t exist!',
+            104: 'Account does not exist!',
             105: 'Email format error!',
             105: 'Email format error!',
             107: 'The username not conform to the rules!',
             107: 'The username not conform to the rules!',
             109: 'The password not conform to the rules!',
             109: 'The password not conform to the rules!',
-            110: 'user doesn\'t activated',
+            110: 'user does not activated',
             111: 'Error password',
             111: 'Error password',
             119: 'The qr code has expired',
             119: 'The qr code has expired',
             120: 'The code has expired',
             120: 'The code has expired',
@@ -111,6 +113,7 @@ class ResponseObject(object):
             906: '文件操作错误',
             906: '文件操作错误',
             907: '文件不存在!',
             907: '文件不存在!',
         }
         }
+
         if self.lang == 'cn':
         if self.lang == 'cn':
             msg = data_cn
             msg = data_cn
         elif self.lang == 'zh-Hans':
         elif self.lang == 'zh-Hans':
@@ -119,16 +122,21 @@ class ResponseObject(object):
             msg = data_cn
             msg = data_cn
         else:
         else:
             msg = data_en
             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={}):
+        reason = msg.get(code)
+        if reason is None:
+            reason = 'code不存在'
+
+        return {'result_code': code, 'reason': reason, 'result': res, 'error_code': code}
+
+    def formal(self, code, res=None):
+        if res is None:
+            res = {}
         formal_data = self.data(code, res)
         formal_data = self.data(code, res)
         return json.dumps(formal_data, ensure_ascii=False)
         return json.dumps(formal_data, ensure_ascii=False)
 
 
-    def json(self, code, res={}):
+    def json(self, code, res=None):
+        if res is None:
+            res = {}
         result = self.formal(code, res)
         result = self.formal(code, res)
         return HttpResponse(result)
         return HttpResponse(result)

+ 66 - 2
Service/CommonService.py

@@ -1,15 +1,18 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 import datetime
 import datetime
+import os
 import time
 import time
+import hashlib
 from pathlib import Path
 from pathlib import Path
 from random import Random
 from random import Random
 import ipdb
 import ipdb
 import simplejson as json
 import simplejson as json
+from boto3 import Session
 from django.core import serializers
 from django.core import serializers
 from django.utils import timezone
 from django.utils import timezone
 from pyipip import IPIPDatabase
 from pyipip import IPIPDatabase
 
 
-from AnsjerPush.config import BASE_DIR
+from AnsjerPush.config import BASE_DIR, ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION_NAME, PUSH_BUCKET
 
 
 
 
 # 复用性且公用较高封装代码在这
 # 复用性且公用较高封装代码在这
@@ -217,4 +220,65 @@ class CommonService:
         file.write("uid:" + uid + "; " + "; tz:" + tz)
         file.write("uid:" + uid + "; " + "; tz:" + tz)
         file.write('\n')
         file.write('\n')
         file.flush()
         file.flush()
-        file.close()
+        file.close()
+
+    @classmethod
+    def upload_images(cls, file_dict, dir_path):
+        """
+        上传图片到S3,并删除本地图片
+        @param file_dict: S3图片路径
+        @param dir_path: 本地图片路径
+        @return: boolean
+        """
+        try:
+            s3 = Session(
+                aws_access_key_id=ACCESS_KEY_ID,
+                aws_secret_access_key=SECRET_ACCESS_KEY,
+                region_name=REGION_NAME
+            ).resource('s3')
+
+            for file_path, upload_path in file_dict.items():
+                upload_data = open(file_path, 'rb')
+                s3.Bucket(PUSH_BUCKET).put_object(Key=upload_path, Body=upload_data)
+
+            # 删除图片
+            cls.del_path(dir_path)
+            cls.del_path(dir_path + '.jpg')
+            return True
+        except Exception as e:
+            print(repr(e))
+            return False
+
+    @classmethod
+    def del_path(cls, path):
+        """
+        删除目录文件
+        @param path: 文件路径
+        @return: None
+        """
+        if not os.path.exists(path):
+            return
+        if os.path.isfile(path):
+            os.remove(path)
+        else:
+            items = os.listdir(path)
+            for f in items:
+                c_path = os.path.join(path, f)
+                if os.path.isdir(c_path):
+                    cls.del_path(c_path)
+                else:
+                    os.remove(c_path)
+            os.rmdir(path)
+
+    @staticmethod
+    def getMD5Sign(data, key):
+        '''
+        魅族MD5签名
+        '''
+        dataList = []
+        for k in sorted(data):
+            dataList.append("%s=%s" % (k, data[k]))
+        data = (''.join(dataList))
+        data = data + key
+        sign = hashlib.md5(data.encode(encoding="utf-8")).hexdigest()
+        return sign

+ 542 - 0
Service/DevicePushService.py

@@ -0,0 +1,542 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : DevicePushService.py
+@Time    : 2022/11/23 11:40
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+import hashlib
+import json
+import logging
+import os
+import threading
+import time
+
+import apns2
+import jpush as jpush
+import requests
+from pyfcm import FCMNotification
+
+from AnsjerPush.config import JPUSH_CONFIG, FCM_CONFIG, APNS_CONFIG, BASE_DIR, APNS_MODE, APP_BUNDLE_DICT, \
+    XMPUSH_CONFIG, VIVOPUSH_CONFIG, OPPOPUSH_CONFIG, MEIZUPUSH_CONFIG
+from AnsjerPush.config import SERVER_TYPE
+from Model.models import UidPushModel, SysMsgModel
+from Object.ETkObject import ETkObject
+from Object.RedisObject import RedisObject
+from Object.UidTokenObject import UidTokenObject
+from Object.utils import LocalDateTimeUtil
+from Service.CommonService import CommonService
+from Service.EquipmentInfoService import EquipmentInfoService
+from Service.HuaweiPushService.HuaweiPushService import HuaweiPushObject
+from Service.PushService import PushObject
+from Service.VivoPushService.push_admin.APIMessage import PushMessage
+from Service.VivoPushService.push_admin.APISender import APISender
+
+LOGGING = logging.getLogger('info')
+
+
+class DevicePushService:
+
+    @staticmethod
+    def decode_uid(etk, uidToken):
+        """
+        解密UID,优先解密etk 否则判断uidToken
+        """
+        # 解密获取uid
+        if etk:
+            eto = ETkObject(etk)
+            uid = eto.uid
+        else:
+            uto = UidTokenObject(uidToken)
+            uid = uto.UID
+        LOGGING.info('消息推送-当前UID:{}'.format(uid))
+        return uid
+
+    @classmethod
+    def query_uid_push(cls, uid):
+        """
+        查询uid_set与push数据列表
+        """
+        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', 'uid_set__ai_type', 'uid_set__new_detect_interval')
+        return uid_push_qs
+
+    @staticmethod
+    def cache_uid_push(uid_push_qs):
+        """
+        将uid_push 信息进行缓存
+        @param uid_push_qs: uid_set & uid_push 列表对象
+        @return: uid_set_list
+        """
+        uid_set_list = []
+        for qs in uid_push_qs:
+            uid_set_list.append(qs)
+        # redis_obj.set_data(key=name, val=str(redis_list), expire=expire)
+        return uid_set_list
+
+    @staticmethod
+    def cache_push_detect_interval(redis_obj, name, detect_interval, new_detect_interval):
+        """
+        缓存设置推送消息的时间间隔
+        @param redis_obj: redis对象
+        @param name: redis key
+        @param detect_interval: 原推送时间间隔
+        @param new_detect_interval: 新推送时间间隔
+        """
+        if SERVER_TYPE != 'Ansjer.cn_config.cn_formal_settings':
+            detect_interval = new_detect_interval if new_detect_interval > 0 else detect_interval
+            detect_interval = 60 if detect_interval < 60 else detect_interval
+        redis_obj.set_data(key=name, val=1, expire=detect_interval - 5)
+        LOGGING.info('消息推送-缓存设置APP推送间隔:{}s'.format(detect_interval))
+
+    @classmethod
+    def save_msg_push(cls, uid_set_push_list, **params):
+        """
+        APP消息推送以及报警消息存库
+        @nickname 设备名称
+        @channel 通道
+        @event_type 事件类型
+        """
+        new_device_info_list = []
+        sys_msg_list = []
+        userID_ids = []
+        kwag_args = params['kwag_args']
+        code_data = {'do_apns_code': '', 'do_fcm_code': '', 'do_jpush_code': ''}
+        local_date_time = ''
+        push_permission = True
+        for up in uid_set_push_list:
+            appBundleId = up['appBundleId']
+            token_val = up['token_val']
+            lang = up['lang']
+            tz = up['tz']
+            if tz is None or tz == '':
+                tz = 0
+            # 发送标题
+            msg_title = cls.get_msg_title(nickname=params['nickname'])
+            # 发送内容
+            msg_text = cls.get_msg_text(channel=params['channel'], n_time=params['n_time'], lang=lang,
+                                        tz=tz, event_type=params['event_type'],
+                                        electricity=params['electricity'])
+            kwag_args['appBundleId'] = appBundleId
+            kwag_args['token_val'] = token_val
+            kwag_args['msg_title'] = msg_title
+            kwag_args['msg_text'] = msg_text
+            LOGGING.info('推送要的数据: {}'.format(kwag_args))
+            local_date_time = CommonService.get_now_time_str(n_time=params['n_time'], tz=tz, lang='cn')
+            LOGGING.info('<<<<<根据时区计算后日期={},时区={}'.format(local_date_time, tz))
+            local_date_time = local_date_time[0:10]
+            LOGGING.info('<<<<<切片后的日期={}'.format(local_date_time))
+            # 以下是存库
+            userID_id = up["userID_id"]
+            if userID_id not in userID_ids:
+                now_time = int(time.time())
+                if params['is_sys_msg']:
+                    sys_msg_text = cls.get_msg_text(channel=params['channel'], n_time=params['n_time'], lang=lang,
+                                                    tz=tz,
+                                                    event_type=params['event_type'], electricity=params['electricity'],
+                                                    is_sys=1)
+                    sys_msg_list.append(SysMsgModel(userID_id=userID_id, msg=sys_msg_text, addTime=now_time,
+                                                    updTime=now_time, uid=params['uid'],
+                                                    eventType=params['event_type']))
+                else:
+                    LOGGING.info('分表存数据start------')
+                    params['userID_id'] = userID_id
+                    push_permission = DevicePushService.check_share_permission(userID_id, params['channel'],
+                                                                               params['uid'])
+                    if push_permission:
+                        new_device_info_list.append(cls.created_device_vo(local_date_time, **params))
+                userID_ids.append(userID_id)
+            params['appBundleId'] = appBundleId
+            params['token_val'] = token_val
+            params['lang'] = lang
+            params['tz'] = tz
+            params['kwag_args'] = kwag_args
+            code_data = cls.send_app_msg_push(up['push_type'], **params) if push_permission else code_data
+        return {'code_date': code_data, 'new_device_info_list': new_device_info_list, 'sys_msg_list': sys_msg_list,
+                'local_date_time': local_date_time}
+
+    @classmethod
+    def send_app_msg_push(cls, push_type, **param):
+        """
+        发送app消息推送
+        """
+        try:
+            kwargs = param['kwag_args']
+            result = {'do_apns_code': '', 'do_fcm_code': '', 'do_jpush_code': '', 'do_xmpush_code': '',
+                      'do_vivopush_code': '', 'do_meizupush_code': '', 'do_oppopush_code': ''}
+            # 判断是否进行APP消息推送,如app_push不为空,则不进行推送
+            if not param['app_push']:
+                LOGGING.info('APP准备推送:{}, {}'.format(param['uid'], param))
+                # 推送显示图片
+                if (param['is_st'] == 1 or param['is_st'] == 3) and \
+                        (push_type == 0 or push_type == 1 or push_type == 3):
+                    if param['is_st'] == 1:
+                        key = '{}/{}/{}.jpeg'.format(param['uid'], param['channel'], param['n_time'])
+                    else:
+                        key = '{}/{}/{}_0.jpeg'.format(param['uid'], param['channel'], param['n_time'])
+                    push_thread = threading.Thread(target=cls.async_send_picture_push, args=(
+                        push_type, param['aws_s3_client'], param['bucket'], key, param['uid'], param['appBundleId'],
+                        param['token_val'], param['event_type'], param['n_time'],
+                        param['kwag_args']['msg_title'], param['kwag_args']['msg_text'], param['channel']))
+                    push_thread.start()
+                else:
+                    if push_type == 0:  # ios apns
+                        result['do_apns_code'] = cls.do_apns(**kwargs)
+                    elif push_type == 1:  # android gcm
+                        result['do_fcm_code'] = cls.do_fcm(**kwargs)
+                    elif push_type == 2:  # android jpush
+                        result['do_jpush_code'] = cls.do_jpush(**kwargs)
+                    elif push_type == 3:
+                        huawei_push_object = HuaweiPushObject()
+                        huawei_push_object.send_push_notify_message(**kwargs)
+                    elif push_type == 4:  # android xmpush
+                        result['do_xmpush_code'] = cls.do_xmpush(**kwargs)
+                    elif push_type == 5:  # android vivopush
+                        result['do_vivopush_code'] = PushObject.android_vivopush(**kwargs)
+                    elif push_type == 6:  # android oppopush
+                        result['do_oppopush_code'] = cls.do_oppopush(**kwargs)
+                    elif push_type == 7:  # android meizupush
+                        result['do_meizupush_code'] = PushObject.android_meizupush(**kwargs)
+            return result
+        except Exception as e:
+            LOGGING.info('异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+            return None
+
+    @classmethod
+    def save_sys_msg(cls, is_sys_msg, local_date_time, sys_msg_list, new_device_info_list):
+        """
+        保存系统消息&设备推送消息存库
+        """
+        if is_sys_msg:
+            SysMsgModel.objects.bulk_create(sys_msg_list)
+        else:
+            # new 分表批量存储 设备信息
+            if new_device_info_list and len(new_device_info_list) > 0:
+                # 根据日期获得星期几
+                week = LocalDateTimeUtil.date_to_week(local_date_time)
+                EquipmentInfoService.equipment_info_bulk_create(week, new_device_info_list)
+                LOGGING.info('设备信息分表批量保存end------')
+        return True
+
+    @classmethod
+    def created_device_vo(cls, local_date_time, **params):
+        """
+        获取设备推送表对象
+        """
+        return EquipmentInfoService.get_equipment_info_obj(
+            local_date_time,
+            device_user_id=params['userID_id'],
+            event_time=params['n_time'],
+            event_type=params['event_type'],
+            device_uid=params['uid'],
+            device_nick_name=params['nickname'],
+            channel=params['channel'],
+            alarm='Motion \tChannel:{channel}'.format(channel=params['channel']),
+            is_st=params['is_st'],
+            receive_time=params['n_time'],
+            add_time=int(time.time()),
+            storage_location=2,
+            border_coords='',
+        )
+
+    @staticmethod
+    def get_msg_title(nickname):
+        """
+        获取消息标题
+        """""
+        return nickname
+
+    @staticmethod
+    def get_msg_text(channel, n_time, lang, tz, event_type, electricity='', 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 = '剩余电量 ' + electricity
+            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 = 'Battery remaining ' + electricity
+            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
+
+    @staticmethod
+    def do_jpush(uid, channel, appBundleId, token_val, event_type, n_time,
+                 msg_title, msg_text):
+        """
+        android 国内极光APP消息提醒推送
+        """
+        app_key = JPUSH_CONFIG[appBundleId]['Key']
+        master_secret = JPUSH_CONFIG[appBundleId]['Secret']
+        _jpush = jpush.JPush(app_key, master_secret)
+        push = _jpush.create_push()
+        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
+
+    @staticmethod
+    def do_fcm(uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
+        """
+        android 谷歌APP消息提醒推送
+        """
+        try:
+            serverKey = FCM_CONFIG[appBundleId]
+        except Exception as e:
+            LOGGING.info('异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(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
+                                                   })
+        return result
+
+    @staticmethod
+    def do_apns(uid, channel, appBundleId, token_val, event_type, n_time, msg_title,
+                msg_text):
+        """
+        ios 消息提醒推送
+        """
+        LOGGING.info("进来do_apns函数了")
+        LOGGING.info(token_val)
+        LOGGING.info(APNS_MODE)
+        LOGGING.info(os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
+        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": "", "uid": uid, "zpush": "1", "channel": channel}
+            alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
+            payload = apns2.Payload(alert=alert, custom=push_data, sound="default")
+
+            # 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)
+            LOGGING.info("apns_推送状态:")
+            LOGGING.info(res.status_code)
+
+            if res.status_code == 200:
+                return res.status_code
+            else:
+                print('apns push fail')
+                print(res.reason)
+                LOGGING.info('apns push fail')
+                LOGGING.info(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))
+            print('do_apns函数错误行号', e.__traceback__.tb_lineno)
+            LOGGING.info('异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+            return repr(e)
+
+    @staticmethod
+    def do_xmpush(uid, channel, appBundleId, token_val, event_type, n_time,
+                  msg_title, msg_text):
+        """
+        android 国内小米APP消息提醒推送
+        """
+        url = 'https://api.xmpush.xiaomi.com/v3/message/regid'
+        app_secret = XMPUSH_CONFIG[appBundleId]
+        payload = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                   'received_at': n_time, 'event_time': n_time, 'event_type': event_type,
+                   'uid': uid, 'channel': channel
+                   }
+        data = {
+            'title': msg_title,
+            'description': msg_text,
+            'payload': 'payload',
+            'restricted_package_name': appBundleId,
+            'registration_id': token_val,
+        }
+
+        headers = {
+            'Authorization': 'key={}'.format(app_secret)
+        }
+        response = requests.post(url, data=data, headers=headers)
+        if response.status_code == 200:
+            return response.json()
+
+    @classmethod
+    def do_oppopush(cls, uid, channel, appBundleId, token_val, event_type, n_time,
+                    msg_title, msg_text):
+        """
+        android 国内oppo APP消息提醒推送
+        """
+        app_key = OPPOPUSH_CONFIG[appBundleId]['Key']
+        master_secret = OPPOPUSH_CONFIG[appBundleId]['Secret']
+        url = 'https://api.push.oppomobile.com/'
+        now_time = str(round(time.time() * 1000))
+        # 1、实例化一个sha256对象
+        sha256 = hashlib.sha256()
+        # 2、调用update方法进行加密
+        sha256.update((app_key + now_time + master_secret).encode('utf-8'))
+        # 3、调用hexdigest方法,获取加密结果
+        sign = sha256.hexdigest()
+        # 获取auth_token
+        get_token_url = url + 'server/v1/auth'
+        post_data = {
+            'app_key': app_key,
+            'sign': sign,
+            'timestamp': now_time
+        }
+        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+        response = requests.post(get_token_url, data=post_data, headers=headers)
+        result = response.json()
+        # 发送推送
+        push_url = url + 'server/v1/message/notification/unicast'
+        extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                      'received_at': n_time, 'event_time': n_time, 'event_type': event_type,
+                      'uid': uid, 'channel': channel}
+        message = {
+            "target_type": 2,
+            "target_value": token_val,
+            "notification": {
+                "title": msg_title,
+                "content": msg_text,
+            }
+        }
+        push_data = {
+            'auth_token': result['data']['auth_token'],
+            'message': json.dumps(message)
+        }
+
+        response = requests.post(push_url, data=push_data, headers=headers)
+        if response.status_code == 200:
+            return response.json()
+
+    @classmethod
+    def async_send_picture_push(cls, push_type, aws_s3_client, bucket, key, uid, appBundleId,
+                                token_val, event_type, n_time, msg_title, msg_text, channel):
+        """
+        异步APP图片推送
+        """
+        try:
+            image_url = aws_s3_client.generate_presigned_url('get_object',
+                                                             Params={'Bucket': bucket, 'Key': key},
+                                                             ExpiresIn=3600)
+            LOGGING.info('推送图片url:{}'.format(image_url))
+            if push_type == 0:
+                PushObject.ios_apns_push(uid, appBundleId, token_val, n_time, event_type, msg_title, msg_text,
+                                         uid, channel, image_url)
+            elif push_type == 1:
+                PushObject.android_fcm_push(uid, appBundleId, token_val, n_time, event_type, msg_title,
+                                            msg_text, uid, channel, image_url)
+            elif push_type == 3:
+                huawei_push_object = HuaweiPushObject()
+                huawei_push_object.send_push_notify_message(token_val=token_val, msg_title=msg_title, msg_text=msg_text,
+                                                            image_url=image_url)
+
+        except Exception as e:
+            LOGGING.info('异常详情,errLine:{}, errMsg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def get_push_url(**params):
+        """
+        获取推送URL,设备根本当前返回结果进行数据上传
+        @return: re_data
+        """
+        re_data = {'code': 0, 'msg': 'success'}
+        if params['is_st'] == 0 or params['is_st'] == 2:
+            re_data['msg'] = 'success 0 or 2'
+            for up in params['uid_set_push_list']:
+                if up['push_type'] == 0:  # ios apns
+                    up['do_apns_code'] = params['code_dict']['code_date']['do_apns_code']
+                elif up['push_type'] == 1:  # android gcm
+                    up['do_fcm_code'] = params['code_dict']['code_date']['do_fcm_code']
+                elif up['push_type'] == 2:  # android jpush
+                    up['do_jpush_code'] = params['code_dict']['code_date']['do_jpush_code']
+                elif up['push_type'] == 4:  # android jpush
+                    up['do_xmpush_code'] = params['code_dict']['code_date']['do_xmpush_code']
+                elif up['push_type'] == 5:  # android jpush
+                    up['do_vivopush_code'] = params['code_dict']['code_date']['do_vivopush_code']
+                elif up['push_type'] == 7:  # android jpush
+                    up['do_meizupush_code'] = params['code_dict']['code_date']['do_meizupush_code']
+                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']
+            re_data['re_list'] = params['uid_set_push_list']
+        elif params['is_st'] == 1:
+            key_name = '{uid}/{channel}/{filename}.jpeg' \
+                .format(uid=params['uid'], channel=params['channel'], filename=params['n_time'])
+            re_args = {'Key': key_name}
+            if params['region'] == 2:  # 2:国内
+                re_args['Bucket'] = 'push'
+            else:  # 1:国外
+                re_args['Bucket'] = 'foreignpush'
+            response_url = DevicePushService.generate_s3_url(params['aws_s3_client'], re_args)
+            re_data['img_push'] = response_url
+        elif params['is_st'] == 3:
+            img_url_list = []
+            if params['region'] == 2:  # 2:国内
+                re_args = {'Bucket': 'push'}
+            else:  # 1:国外
+                re_args = {'Bucket': 'foreignpush'}
+            for i in range(params['is_st']):
+                key_name = '{uid}/{channel}/{filename}_{st}.jpeg'. \
+                    format(uid=params['uid'], channel=params['channel'], filename=params['n_time'], st=i)
+                re_args['Key'] = key_name
+                response_url = DevicePushService.generate_s3_url(params['aws_s3_client'], re_args)
+                img_url_list.append(response_url)
+            re_data['img_url_list'] = img_url_list
+            re_data['msg'] = 'success 3'
+        return re_data
+
+    @staticmethod
+    def generate_s3_url(aws_s3_client, params):
+        """
+        获取S3对象URL
+        """
+        response_url = aws_s3_client.generate_presigned_url(
+            ClientMethod='put_object',
+            Params=params,
+            ExpiresIn=3600
+        )
+        return response_url

+ 0 - 107
Service/GatewayService.py

@@ -1,107 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-@Time : 2022/5/19 11:43
-@Auth : Locky
-@File :GatewayService.py
-@IDE :PyCharm
-"""
-import logging
-import os
-
-import apns2
-import jpush
-from pyfcm import FCMNotification
-
-from AnsjerPush.config import APP_BUNDLE_DICT, APNS_MODE, BASE_DIR, APNS_CONFIG, FCM_CONFIG, JPUSH_CONFIG
-# 网关推送类
-from Service.CommonService import CommonService
-
-
-class GatewayPushService:
-    # 获取推送消息标题
-    @staticmethod
-    def get_msg_title(app_bundle_id, nickname):
-        if app_bundle_id in APP_BUNDLE_DICT.keys():
-            return APP_BUNDLE_DICT[app_bundle_id] + '(' + nickname + ')'
-        else:
-            return nickname
-
-    # 获取推送消息内容
-    @staticmethod
-    def get_msg_text(n_time, tz, lang, alarm):
-        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
-        if lang == 'cn':
-            msg_text = '{} 日期:{}'.format(alarm, n_date)
-        else:
-            msg_text = '{} date:{}'.format(alarm, n_date)
-        return msg_text
-
-    # ios apns 推送
-    @staticmethod
-    def ios_apns_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
-                      uid='', channel='1', launch_image=None):
-        logger = logging.getLogger('info')
-        try:
-            pem_path = os.path.join(BASE_DIR, APNS_CONFIG[app_bundle_id]['pem_path'])
-            logger.info('apns推送app_bundle_id:{}, pem_path:{}'.format(app_bundle_id, pem_path))
-            cli = apns2.APNSClient(mode=APNS_MODE, client_cert=pem_path)
-            alert = apns2.PayloadAlert(title=msg_title, body=msg_text, launch_image=launch_image)
-            push_data = {'alert': 'Motion', 'msg': '', 'sound': '', 'zpush': '1', 'uid': uid, 'channel': channel,
-                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
-                         'image_url': launch_image
-                         }
-            payload = apns2.Payload(alert=alert, custom=push_data, sound='default', category='myCategory',
-                                    mutable_content=True)
-            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
-            res = cli.push(n=n, device_token=token_val, topic=app_bundle_id)
-            assert res.status_code == 200
-        except Exception as e:
-            logger.info('--->IOS推送异常{}'.format(repr(e)))
-            return repr(e)
-
-    # android fcm 推送
-    @staticmethod
-    def android_fcm_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
-                         uid='', channel='1', image=''):
-        logger = logging.getLogger('info')
-        try:
-            serverKey = FCM_CONFIG[app_bundle_id]
-            push_service = FCMNotification(api_key=serverKey)
-            push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
-                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
-                         'uid': uid, 'channel': channel
-                         }
-            result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
-                                                       message_body=msg_text, data_message=push_data,
-                                                       extra_kwargs={'default_sound': True,
-                                                                     'default_vibrate_timings': True,
-                                                                     'default_light_settings': True,
-                                                                     }
-                                                       )
-            logger.info('fcm推送结果:{}'.format(result))
-            return result
-        except Exception as e:
-            return repr(e)
-
-    # android 极光 推送
-    @staticmethod
-    def android_jpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text):
-        try:
-            app_key = JPUSH_CONFIG[app_bundle_id]['Key']
-            master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
-            # 换成各自的app_key和master_secret
-            _jpush = jpush.JPush(app_key, master_secret)
-            push = _jpush.create_push()
-            push.audience = jpush.registration_id(token_val)
-            push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
-                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname
-                         }
-            android = jpush.android(title=msg_title, big_text=msg_text, alert=msg_text, extras=push_data,
-                                    priority=1, style=1, alert_type=7
-                                    )
-            push.notification = jpush.notification(android=android)
-            push.platform = jpush.all_
-            res = push.send()
-            assert res.status_code == 200
-        except Exception as e:
-            return repr(e)

+ 97 - 0
Service/HuaweiPushService/HuaweiPushService.py

@@ -0,0 +1,97 @@
+import json
+import logging
+
+from Service.HuaweiPushService import push_admin
+from Service.HuaweiPushService.push_admin import messaging
+
+
+class HuaweiPushObject:
+    # 华为推送服务类
+
+    def __init__(self):
+        self.app_id = '101064781'
+        self.app_secret = '29d5c5367208e35079f14779597b8f6bcc28ee39091546ed577862231fdd0fdd'
+        self.init_app()
+
+    def init_app(self):
+        """init sdk app"""
+        push_admin.initialize_app(self.app_id, self.app_secret)
+
+    def send_push_notify_message(self, token_val, msg_title, msg_text, image_url=None):
+        """
+        发送推送消息
+        @param token_val: 手机推送token
+        @param msg_title: 标题
+        @param msg_text: 内容
+        @param image_url: 图片链接
+        @return:
+        """
+        logger = logging.getLogger('info')
+        logger.info('华为推送参数:{}, {}, {}, {}'.format(token_val, msg_title, msg_text, image_url))
+
+        msg_title = '设备昵称: {}'.format(msg_title)
+        notification = messaging.Notification(
+            title=msg_title,
+            body=msg_text,
+            image=image_url
+        )
+        # 推送通知内容配置
+        android_notification = self.android_notification(msg_title, msg_text)
+        # 安卓配置
+        android = messaging.AndroidConfig(
+            collapse_key=-1,
+            urgency=messaging.AndroidConfig.NORMAL_PRIORITY,
+            ttl='10000s',
+            bi_tag='the_sample_bi_tag_for_receipt_service',
+            notification=android_notification,
+            category='DEVICE_REMINDER'
+        )
+
+        message = messaging.Message(
+            notification=notification,
+            android=android,
+            token=[token_val]
+        )
+
+        try:
+            # Case 1: Local CA sample code
+            # response = messaging.send_message(message, verify_peer='../Push-CA-Root.pem')
+            # Case 2: No verification of HTTPS's certificate
+            # response = messaging.send_message(message)
+            # Case 3: use certifi Library
+            import certifi
+            response = messaging.send_message(message, verify_peer=certifi.where())
+            logger.info('华为推送响应: {}'.format(json.dumps(vars(response))))
+            assert (response.code == '80000000')
+        except Exception as e:
+            logger.info('华为推送异常: {}'.format(repr(e)))
+
+    @staticmethod
+    def android_notification(msg_title, msg_text):
+        return messaging.AndroidNotification(
+            icon='/raw/ic_launcher2',
+            color='#AACCDD',
+            sound='/raw/shake',
+            default_sound=True,
+            click_action=messaging.AndroidClickAction(
+                action_type=1,
+                intent='intent://com.huawei.codelabpush/deeplink?#Intent;scheme=pushscheme;launchFlags=0x4000000;i.age=180;S.name=abc;end'),
+            body_loc_key='M.String.body',
+            body_loc_args=('boy', 'dog'),
+            title_loc_key='M.String.title',
+            title_loc_args=['Girl', 'Cat'],
+            channel_id='1',
+            notify_summary='',
+            multi_lang_key={'title_key': {'en': 'value1'}, 'body_key': {'en': 'value2'}},
+            style=1,
+            big_title=msg_title,
+            big_body=msg_text,
+            auto_clear=86400000,
+            importance=messaging.AndroidNotification.PRIORITY_HIGH,
+            light_settings=messaging.AndroidLightSettings(color=messaging.AndroidLightSettingsColor(
+                alpha=0, red=0, green=1, blue=1), light_on_duration='3.5', light_off_duration='5S'),
+            badge=messaging.AndroidBadgeNotification(
+                add_num=1, clazz='Classic'),
+            visibility=messaging.AndroidNotification.PUBLIC,
+            foreground_show=True
+        )

+ 71 - 0
Service/HuaweiPushService/push_admin/__init__.py

@@ -0,0 +1,71 @@
+# -*-coding:utf-8-*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Huawei Admin SDK for Python."""
+
+import threading
+from Service.HuaweiPushService.push_admin import _app
+
+_apps = {}
+_apps_lock = threading.RLock()
+_DEFAULT_APP_NAME = 'DEFAULT'
+
+
+def initialize_app(appid_at, appsecret_at, appid_push=None, token_server='https://oauth-login.cloud.huawei.com/oauth2/v3/token',
+                   push_open_url='https://push-api.cloud.huawei.com'):
+    """
+        Initializes and returns a new App instance.
+        :param appid_at: appid parameters obtained by developer alliance applying for Push service
+        :param appsecret_at: appsecret parameters obtained by developer alliance applying for Push service
+        :param appid_push: the application Id in the URL
+        :param token_server: Oauth server URL
+        :param push_open_url: push open API URL
+    """
+    app = _app.App(appid_at, appsecret_at, appid_push, token_server=token_server, push_open_url=push_open_url)
+
+    with _apps_lock:
+        if appid_at not in _apps:
+            _apps[appid_at] = app
+
+        """set default app instance"""
+        if _apps.get(_DEFAULT_APP_NAME) is None:
+            _apps[_DEFAULT_APP_NAME] = app
+
+
+def get_app(appid=None):
+    """
+        get app instance
+        :param appid: appid parameters obtained by developer alliance applying for Push service
+        :return: app instance
+        Raise: ValueError
+    """
+    if appid is None:
+        with _apps_lock:
+            app = _apps.get(_DEFAULT_APP_NAME)
+            if app is None:
+                raise ValueError('The default Huawei app is not exists. '
+                                 'This means you need to call initialize_app() it.')
+            return app
+
+    with _apps_lock:
+        if appid not in _apps:
+            raise ValueError('Huawei app id[{0}] is not exists. '
+                             'This means you need to call initialize_app() it.'.format(appid))
+
+        app = _apps.get(appid)
+        if app is None:
+            raise ValueError('The app id[{0}] is None.'.format(appid))
+        return app

+ 190 - 0
Service/HuaweiPushService/push_admin/_app.py

@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import time
+import urllib
+import urllib.parse
+
+from Service.HuaweiPushService.push_admin import _http
+from Service.HuaweiPushService.push_admin import _message_serializer
+
+
+class App(object):
+    """application for HW Cloud Message(HCM)"""
+
+    JSON_ENCODER = _message_serializer.MessageSerializer()
+
+    @classmethod
+    def _send_to_server(cls, headers, body, url, verify_peer=False):
+        try:
+            msg_body = json.dumps(body)
+            response = _http.post(url, msg_body, headers, verify_peer)
+
+            if response.status_code is not 200:
+                raise ApiCallError('http status code is {0} in send.'.format(response.status_code))
+
+            # json text to dict
+            resp_dict = json.loads(response.text)
+            return resp_dict
+
+        except Exception as e:
+            raise ApiCallError('caught exception when send. {0}'.format(e))
+
+    def __init__(self, appid_at, app_secret_at, appid_push, token_server='https://oauth-login.cloud.huawei.com/oauth2/v3/token',
+                 push_open_url='https://push-api.cloud.huawei.com'):
+        """class init"""
+        self.app_id_at = appid_at
+        self.app_secret_at = app_secret_at
+        if appid_push is None:
+            self.appid_push = appid_at
+        else:
+            self.appid_push = appid_push
+        self.token_expired_time = 0
+        self.access_token = None
+        self.token_server = token_server
+        self.push_open_url = push_open_url
+        self.hw_push_server = self.push_open_url + "/v1/{0}/messages:send"
+        self.hw_push_topic_sub_server = self.push_open_url + "/v1/{0}/topic:subscribe"
+        self.hw_push_topic_unsub_server = self.push_open_url + "/v1/{0}/topic:unsubscribe"
+        self.hw_push_topic_query_server = self.push_open_url + "/v1/{0}/topic:list"
+
+    def _refresh_token(self, verify_peer=False):
+        """refresh access token
+        :param verify_peer: (optional) Either a boolean, in which case it controls whether we verify
+            the server's TLS certificate, or a string, in which case it must be a path
+            to a CA bundle to use. Defaults to ``True``.
+        """
+        headers = dict()
+        headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'
+
+        params = dict()
+        params['grant_type'] = 'client_credentials'
+        params['client_secret'] = self.app_secret_at
+        params['client_id'] = self.app_id_at
+
+        msg_body = urllib.parse.urlencode(params)
+
+        try:
+            response = _http.post(self.token_server, msg_body, headers, verify_peer=verify_peer)
+
+            if response.status_code is not 200:
+                return False, 'http status code is {0} in get access token'.format(response.status_code)
+
+            """ json string to directory """
+            response_body = json.loads(response.text)
+
+            self.access_token = response_body.get('access_token')
+            self.token_expired_time = int(round(time.time() * 1000)) + (int(response_body.get('expires_in')) - 5 * 60) * 1000
+
+            return True, None
+        except Exception as e:
+            raise ApiCallError(format(repr(e)))
+
+    def _is_token_expired(self):
+        """is access token expired"""
+        if self.access_token is None:
+            """ need refresh token """
+            return True
+        return int(round(time.time() * 1000)) >= self.token_expired_time
+
+    def _update_token(self, verify_peer=False):
+        """
+        :param verify_peer: (optional) Either a boolean, in which case it controls whether we verify
+            the server's TLS certificate, or a string, in which case it must be a path
+            to a CA bundle to use. Defaults to ``True``.
+        :return:
+        """
+        if self._is_token_expired() is True:
+            result, reason = self._refresh_token(verify_peer)
+            if result is False:
+                raise ApiCallError(reason)
+
+    def _create_header(self):
+        headers = dict()
+        headers['Content-Type'] = 'application/json;charset=utf-8'
+        headers['Authorization'] = 'Bearer {0}'.format(self.access_token)
+        return headers
+
+    def send(self, message, validate_only, **kwargs):
+        """
+            Sends the given message Huawei Cloud Messaging (HCM)
+            :param message: JSON format message
+            :param validate_only: validate message format or not
+            :param kwargs:
+                   verify_peer: HTTPS server identity verification, use library 'certifi'
+            :return:
+                response dict: response body dict
+            :raise:
+                ApiCallError: failure reason
+        """
+        verify_peer = kwargs['verify_peer']
+        self._update_token(verify_peer)
+        headers = self._create_header()
+        url = self.hw_push_server.format(self.appid_push)
+        msg_body_dict = dict()
+        msg_body_dict['validate_only'] = validate_only
+        msg_body_dict['message'] = App.JSON_ENCODER.default(message)
+
+        return App._send_to_server(headers, msg_body_dict, url, verify_peer)
+
+    def subscribe_topic(self, topic, token_list):
+        """
+        :param topic: The specific topic
+        :param token_list: The token list to be added
+        :return:
+        """
+        self._update_token()
+        headers = self._create_header()
+        url = self.hw_push_topic_sub_server.format(self.appid_push)
+        msg_body_dict = {'topic': topic, 'tokenArray': token_list}
+        return App._send_to_server(headers, msg_body_dict, url)
+
+    def unsubscribe_topic(self, topic, token_list):
+        """
+
+        :param topic: The specific topic
+        :param token_list: The token list to be deleted
+        :return:
+        """
+        self._update_token()
+        headers = self._create_header()
+        url = self.hw_push_topic_unsub_server.format(self.appid_push)
+        msg_body_dict = {'topic': topic, 'tokenArray': token_list}
+        return App._send_to_server(headers, msg_body_dict, url)
+
+    def query_subscribe_list(self, token):
+        """
+        :param token:  The specific token
+        :return:
+        """
+        self._update_token()
+        headers = self._create_header()
+        url = self.hw_push_topic_query_server.format(self.appid_push)
+        msg_body_dict = {'token': token}
+        return App._send_to_server(headers, msg_body_dict, url)
+
+
+class ApiCallError(Exception):
+    """Represents an Exception encountered while invoking the HCM API.
+
+    Attributes:
+        message: A error message string.
+        detail: Original low-level exception.
+    """
+    def __init__(self, message, detail=None):
+        Exception.__init__(self, message)
+        self.detail = detail

+ 55 - 0
Service/HuaweiPushService/push_admin/_http.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import requests
+
+
+def post(url, req_body, headers=None, verify_peer=False):
+    """ post http request to slb service
+        :param url: url path
+        :param req_body: http request body
+        :param headers: http headers
+        :param verify_peer:  (optional) Either a boolean, in which case it controls whether we verify
+            the server's TLS certificate, or a string, in which case it must be a path
+            to a CA bundle to use. Defaults to ``True``.
+        :return:
+            success return response
+            fali return None
+    """
+    try:
+        response = requests.post(url, data=req_body, headers=headers, timeout=10, verify=verify_peer)
+        return response
+
+    except Exception as e:
+        raise ValueError('caught exception when post {0}. {1}'.format(url, e))
+
+
+def _format_http_text(method, url, headers, body):
+    """
+    print http head and body for request or response
+
+    For examples: _format_http_text('', title, response.headers, response.text)
+    """
+    result = method + ' ' + url + '\n'
+
+    if headers is not None:
+        for key, value in headers.items():
+            result = result + key + ': ' + value + '\n'
+
+    result = result + body
+    return result
+
+

+ 613 - 0
Service/HuaweiPushService/push_admin/_message_serializer.py

@@ -0,0 +1,613 @@
+# -*-coding:utf-8-*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from Service.HuaweiPushService.push_admin import _messages
+import six
+
+
+class MessageSerializer(json.JSONEncoder):
+    """
+    Use https://docs.python.org/3/library/json.html to do serialization
+
+    The serializer should serialize the following messages:
+    _messages.Message
+    _messages.Notification
+    _messages.ApnsConfig
+    _messages.WebPushConfig
+    _messages.WebPushNotification
+    _messages.WebPushNotificationAction
+    _messages.WebPushHMSOptions
+    _messages.AndroidConfig
+    _messages.AndroidNotification
+    _messages.AndroidClickAction
+    _messages.BadgeNotification
+    """
+    def default(self, message):
+        """
+        :param message: The push message
+        :return: formatted push messages
+        """
+        result = {
+            'data': message.data,
+            'notification': MessageSerializer.encode_notification(message.notification),
+            'android': MessageSerializer.encode_android_config(message.android),
+            'apns': MessageSerializer.encode_apns_config(message.apns),
+            'webpush': MessageSerializer.encode_webpush_config(message.web_push),
+            'token': message.token,
+            'topic': message.topic,
+            'condition': message.condition
+        }
+        result = MessageSerializer.remove_null_values(result)
+        return result
+
+    @classmethod
+    def remove_null_values(cls, dict_value):
+        return {k: v for k, v in dict_value.items() if v not in [None, [], {}]}
+
+    @classmethod
+    def encode_notification(cls, notification):
+        """
+            An example:
+           {
+              "title":"Big News",
+              "body":"This is a Big News!",
+              "image":"https://res.vmallres.com/pimages//common/config/logo/SXppnESYv4K11DBxDFc2_0.png"
+            }
+        :param notification:
+        :return:
+        """
+        if notification is None:
+            return None
+
+        if not isinstance(notification, _messages.Notification):
+            raise ValueError('Message.notification must be an instance of Notification class.')
+
+        result = {
+            'title': notification.title,
+            'body': notification.body,
+            'image': notification.image
+        }
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_android_config(cls, android_config):
+        """
+        An example:
+        {
+          "android":{
+            "collapse_key":-1,
+            "urgency":"HIGH",
+            "ttl":"1448s",
+            "bi_tag":"Trump",
+            "fast_app_target":1,
+            "notification": {}
+        }
+        :param android_config:
+        :return:
+        """
+        if android_config is None:
+            return None
+
+        if not isinstance(android_config, _messages.AndroidConfig):
+            raise ValueError('Message.android must be an instance of AndroidConfig class.')
+
+        result = {
+            'collapse_key': android_config.collapse_key,
+            'urgency': android_config.urgency,
+            'ttl': android_config.ttl,
+            'bi_tag': android_config.bi_tag,
+            'fast_app_target': android_config.fast_app_target,
+            'data': android_config.data,
+            'notification': MessageSerializer.encode_android_notification(android_config.notification)
+        }
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_android_notification(cls, notification):
+        """
+           "notification":{
+                "title":"Noti in Noti title",
+                "body":"Noti in Noti body",
+                "icon":"https://res.vmallres.com/pimages//common/config/logo/SXppnESYv4K11DBxDFc2.png",
+                "color":"#AACCDD",
+                "default_sound":true,
+                "tag":"tagBoom",
+                "importance":"PRIORITY_HIGH",
+                "click_action":{
+                    "type":2,
+                    "url":"https://www.huawei.com"
+                },
+                "body_loc_key":"M.String.body",
+                "body_loc_args":[
+                    "Boy",
+                    "Dog"
+                ],
+                "title_loc_key":"M.String.title",
+                "title_loc_args":[
+                    "Girl",
+                    "Cat"
+                ],
+                "channel_id":"RingRing",
+                "notify_summary":"Some Summary",
+                "style":2,
+                "big_title":"Big Boom Title",
+                "big_body":"Big Boom Body",
+                "notify_id":486,
+                "group":"Espace",
+                "badge":{
+                    "add_num":99,
+                    "set_num":99,
+                    "class":"Classic"
+                },
+                "ticker":"i am a ticker",
+                "auto_cancel":false,
+                "when":"2019-11-05",
+                "use_default_vibrate":true,
+                "use_default_light":false,
+                "visibility":"PUBLIC",
+                "vibrate_config":["1.5","2","3"],
+                "light_settings":{
+                    "color":{
+                        "alpha":0,
+                        "red":0,
+                        "green":1,
+                        "blue":1
+                    },
+                    "light_on_duration":"3.5",
+                    "light_off_duration":"5S"
+                },
+                "foreground_show":true
+              }
+        :param notification:
+        :return:
+        """
+        if notification is None:
+            return None
+
+        if not isinstance(notification, _messages.AndroidNotification):
+            raise ValueError('Message.AndroidConfig.notification must be an instance of AndroidNotification class.')
+
+        result = {
+            "title": notification.title,
+            "body": notification.body,
+            "icon": notification.icon,
+            "color": notification.color,
+            "sound": notification.sound,
+            "default_sound": notification.default_sound,
+            "tag": notification.tag,
+            "importance": notification.importance,
+            "multi_lang_key": notification.multi_lang_key,
+            "click_action": MessageSerializer.encode_android_click_action(notification.click_action),
+            "body_loc_key": notification.body_loc_key,
+            "body_loc_args": notification.body_loc_args,
+            "title_loc_key": notification.title_loc_key,
+            "title_loc_args": notification.title_loc_args,
+            "channel_id": notification.channel_id,
+            "notify_summary": notification.notify_summary,
+            "image": notification.image,
+            "style": notification.style,
+            "big_title": notification.big_title,
+            "big_body": notification.big_body,
+            "notify_id": notification.notify_id,
+            "group": notification.group,
+            "badge": MessageSerializer.encode_android_badge(notification.badge),
+            "ticker": notification.ticker,
+            "auto_cancel": notification.auto_cancel,
+            "when": notification.when,
+            "use_default_vibrate": notification.use_default_vibrate,
+            "use_default_light": notification.use_default_light,
+            "visibility": notification.visibility,
+            "vibrate_config": notification.vibrate_config,
+            "light_settings": MessageSerializer.encode_android_light_settings(notification.light_settings),
+            "foreground_show": notification.foreground_show
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_android_click_action(cls, click_action):
+        """
+            "click_action":{
+                    "type":2,
+                    "url":"https://www.huawei.com"
+             }
+
+             "click_action":{
+                    "type":1,
+                    "intent":"https://www.huawei.com",
+                    "action":""
+             }
+
+        :param click_action: _messages.AndroidClickAction
+        :return:
+        """
+        if click_action is None:
+            return None
+
+        if not isinstance(click_action, _messages.AndroidClickAction):
+            raise ValueError('Message.AndroidConfig.AndroidNotification.click_action must be an instance\
+                             of AndroidClickAction class.')
+
+        result = {
+            "type": click_action.action_type,
+            "intent": click_action.intent,
+            "url": click_action.url,
+            "action": click_action.action
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_android_badge(cls, badge):
+        """
+        refer to: _messages.AndroidBadgeNotification
+
+        "badge":{
+                    "add_num":99,
+                    "set_num":99,
+                    "class":"Classic"
+                }
+
+        :param badge:
+        :return:
+        """
+        if badge is None:
+            return None
+
+        if not isinstance(badge, _messages.AndroidBadgeNotification):
+            raise ValueError('Message.AndroidConfig.AndroidNotification.badge must be an instance\
+                             of AndroidBadgeNotification class.')
+
+        result = {
+            "add_num": badge.add_num,
+            "set_num": badge.set_num,
+            "class": badge.clazz
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_android_light_settings(cls, android_light_settings):
+        """
+        refer to: _messages.AndroidLightSettings
+
+        "light_settings":{
+                "color":{
+                        "alpha":0,
+                        "red":0,
+                        "green":1,
+                        "blue":1
+                },
+                "light_on_duration":"3.5",
+                "light_off_duration":"5S"
+         }
+
+        :param android_light_settings:  _messages.AndroidLightSettings
+        :return:
+        """
+        if android_light_settings is None:
+            return None
+
+        if not isinstance(android_light_settings, _messages.AndroidLightSettings):
+            raise ValueError('Message.AndroidConfig.AndroidNotification.android_light_settings must be an instance\
+                             of AndroidLightSettings class.')
+
+        result = {
+            "color": MessageSerializer.encode_android_light_settings_color(android_light_settings.color),
+            "light_on_duration": android_light_settings.light_on_duration,
+            "light_off_duration": android_light_settings.light_off_duration
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_android_light_settings_color(cls, color):
+        """
+        "color":{
+                        "alpha":0,
+                        "red":0,
+                        "green":1,
+                        "blue":1
+        }
+
+        :param color: _messages.AndroidLightSettingsColor
+        :return:
+        """
+        if color is None:
+            return None
+
+        if not isinstance(color, _messages.AndroidLightSettingsColor):
+            raise ValueError('Message.AndroidConfig.AndroidNotification.android_light_settings.color must be an instance\
+                             of AndroidLightSettingsColor class.')
+        result = {
+            "alpha": color.alpha,
+            "red": color.red,
+            "green": color.green,
+            "blue": color.blue
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_webpush_config(cls, webpush_config):
+        """
+        "webpush":{
+            "headers":{
+                ...
+            },
+            "notification":{
+                ...
+            },
+            "hms_options":{
+                ...
+            }
+         }
+        :param webpush_config: refer to _messages.WebPushConfig
+        :return:
+        """
+        if webpush_config is None:
+            return None
+
+        if not isinstance(webpush_config, _messages.WebPushConfig):
+            raise ValueError('Message.webpush must be an instance of WebPushConfig class.')
+
+        result = {
+            "headers": MessageSerializer.encode_webpush_config_headers(webpush_config.headers),
+            "notification": MessageSerializer.encode_webpush_config_notification(webpush_config.notification),
+            "hms_options": MessageSerializer.encode_webpush_config_hms_options(webpush_config.hms_options),
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_webpush_config_headers(cls, webpush_headers):
+        """
+        "headers":{
+                "ttl":"990",
+                "urgency":"very-low",
+                "topic":"12313ceshi"
+            }
+
+        :param webpush_headers: _messages.WebPushHeader
+        :return:
+        """
+        if webpush_headers is None:
+            return None
+
+        if not isinstance(webpush_headers, _messages.WebPushHeader):
+            raise ValueError('Message.webpush.headers must be an instance of WebPushHeader class.')
+
+        result = {
+            "ttl": webpush_headers.ttl,
+            "urgency": webpush_headers.urgency,
+            "topic": webpush_headers.topic,
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_webpush_config_notification(cls, webpush_notification):
+        """
+        "notification":{
+                "title":"notication string",
+                "body":"web push body",
+                "actions":[
+                    {
+                        "action":"",
+                        "icon":"https://res.vmallres.com/pimages//common/config/logo/SXppnESYv4K11DBxDFc2.png",
+                        "title":"string"
+                    }
+                ],
+                "badge":"string",
+                "dir":"auto",
+                "icon":"https://res.vmallres.com/pimages//common/config/logo/SXppnESYv4K11DBxDFc2.png",
+                "image":"string",
+                "lang":"string",
+                "renotify":true,
+                "requireInteraction":true,
+                "silent":true,
+                "tag":"string",
+                "timestamp":1545201266,
+                "vibrate":[1,2,3]
+            }
+
+        :param webpush_notification: refer to _messages.WebPushNotification
+        :return:
+        """
+        if webpush_notification is None:
+            return None
+
+        if not isinstance(webpush_notification, _messages.WebPushNotification):
+            raise ValueError('Message.webpush.notification must be an instance of WebPushNotification class.')
+
+        result = {
+            "title": webpush_notification.title,
+            "body": webpush_notification.body,
+            "actions": [MessageSerializer.encode_webpush_notification_action(_)
+                        for _ in webpush_notification.actions],
+            "badge": webpush_notification.badge,
+            "dir": webpush_notification.dir,
+            "icon": webpush_notification.icon,
+            "image": webpush_notification.image,
+            "lang": webpush_notification.lang,
+            "renotify": webpush_notification.renotify,
+            "require_interaction": webpush_notification.require_interaction,
+            "silent": webpush_notification.silent,
+            "tag": webpush_notification.tag,
+            "timestamp": webpush_notification.timestamp,
+            "vibrate": webpush_notification.vibrate
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_webpush_notification_action(cls, webpush_notification_action):
+        """
+        "actions":[
+                    {
+                        "action":"",
+                        "icon":"https://res.vmallres.com/pimages//common/config/logo/SXppnESYv4K11DBxDFc2.png",
+                        "title":"string"
+                    }
+                ],
+        :param webpush_notification_action: refer to _messages.WebPushNotificationAction
+        :return:
+        """
+        if webpush_notification_action is None:
+            return None
+
+        if not isinstance(webpush_notification_action, _messages.WebPushNotificationAction):
+            raise ValueError('Message.webpush.notification.action must be an instance of \
+                            WebPushNotificationAction class.')
+
+        result = {
+            "action": webpush_notification_action.action,
+            "icon": webpush_notification_action.icon,
+            "title": webpush_notification_action.title
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_webpush_config_hms_options(cls, webpush_hms_options):
+        """
+        "hms_options":{
+                "link":"https://www.huawei.com/"
+         }
+
+        :param webpush_hms_options: refer to _messages.WebPushHMSOptions
+        :return:
+        """
+        if webpush_hms_options is None:
+            return None
+
+        if not isinstance(webpush_hms_options, _messages.WebPushHMSOptions):
+            raise ValueError('Message.webpush.hmsoptions must be an instance of \
+                            WebPushHMSOptions class.')
+
+        result = {
+            "link": webpush_hms_options.link
+        }
+        result = cls.remove_null_values(result)
+        return result
+
+    @classmethod
+    def encode_apns_config(cls, apns_config):
+        """
+        Encode APNs config into JSON
+        :param apns_config:
+        :return:
+        """
+        if apns_config is None:
+            return None
+        if not isinstance(apns_config, _messages.APNsConfig):
+            raise ValueError('Message.apns_config must be an instance of _messages.APNsConfig class.')
+
+        result = {
+            'headers': apns_config.headers,
+            'payload': cls.encode_apns_payload(apns_config.payload),
+            'hms_options': cls.encode_apns_hms_options(apns_config.apns_hms_options)
+        }
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_apns_payload(cls, apns_payload):
+        """Encodes an ``APNSPayload`` instance into JSON."""
+        if apns_payload is None:
+            return None
+        if not isinstance(apns_payload, _messages.APNsPayload):
+            raise ValueError('APNSConfig.payload must be an instance of _messages.APNsPayload class.')
+        result = {
+            'aps': cls.encode_apns_payload_aps(apns_payload.aps)
+        }
+        for key, value in apns_payload.custom_data.items():
+            result[key] = value
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_apns_payload_aps(cls, apns_payload_aps):
+        """Encodes an ``Aps`` instance into JSON."""
+        if not isinstance(apns_payload_aps, _messages.APNsAps):
+            raise ValueError('APNSPayload.aps must be an instance of _messages.APNsAps class.')
+
+        result = {
+            'alert': cls.encode_apns_payload_alert(apns_payload_aps.alert),
+            'badge': apns_payload_aps.badge,
+            'sound': apns_payload_aps.sound,
+            'category': apns_payload_aps.category,
+            'thread-id': apns_payload_aps.thread_id
+        }
+
+        if apns_payload_aps.content_available is True:
+            result['content-available'] = 1
+        if apns_payload_aps.mutable_content is True:
+            result['mutable-content'] = 1
+        if apns_payload_aps.custom_data is not None:
+            if not isinstance(apns_payload_aps.custom_data, dict):
+                raise ValueError('Aps.custom_data must be a dict.')
+            for key, val in apns_payload_aps.custom_data.items():
+                if key in result:
+                    raise ValueError('Multiple specifications for {0} in Aps.'.format(key))
+                result[key] = val
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_apns_payload_alert(cls, apns_payload_alert):
+        """Encodes an ``ApsAlert`` instance into JSON."""
+        if apns_payload_alert is None:
+            return None
+        if isinstance(apns_payload_alert, six.string_types):
+            return apns_payload_alert
+        if not isinstance(apns_payload_alert, _messages.APNsAlert):
+            raise ValueError('Aps.alert must be a string or an instance of _messages.APNsAlert class.')
+        result = {
+            'title': apns_payload_alert.title,
+            'body': apns_payload_alert.body,
+            'title-loc-key': apns_payload_alert.title_loc_key,
+            'title-loc-args': apns_payload_alert.title_loc_args,
+            'loc-key': apns_payload_alert.loc_key,
+            'loc-args': apns_payload_alert.loc_args,
+            'action-loc-key': apns_payload_alert.action_loc_key,
+            'launch-image': apns_payload_alert.launch_image
+        }
+        if result.get('loc-args') and not result.get('loc-key'):
+            raise ValueError(
+                'ApsAlert.loc_key is required when specifying loc_args.')
+        if result.get('title-loc-args') and not result.get('title-loc-key'):
+            raise ValueError(
+                'ApsAlert.title_loc_key is required when specifying title_loc_args.')
+        if apns_payload_alert.custom_data is not None:
+            if not isinstance(apns_payload_alert.custom_data, dict):
+                raise ValueError('ApsAlert.custom_data must be a dict.')
+            for key, val in apns_payload_alert.custom_data.items():
+                result[key] = val
+        return cls.remove_null_values(result)
+
+    @classmethod
+    def encode_apns_hms_options(cls, apns_hms_options):
+        """
+        :param apns_hms_options:
+        """
+        if apns_hms_options is None:
+            return None
+        if not isinstance(apns_hms_options, _messages.APNsHMSOptions):
+            raise ValueError('Aps.alert must be a string or an instance of _messages.APNsHMSOptions class.')
+
+        result = {
+            'target_user_type': apns_hms_options.target_user_type,
+        }
+        return cls.remove_null_values(result)

+ 915 - 0
Service/HuaweiPushService/push_admin/_messages.py

@@ -0,0 +1,915 @@
+# -*-coding:utf-8-*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import numbers
+import re
+import six
+
+
+class Message(object):
+    """A message that can be sent Huawei Cloud Messaging.
+
+    Args:
+        data: A string value.
+        notification: An instance of ``messaging.Notification`` (optional).
+        android: An instance of ``messaging.Android`` (optional).
+        apns: APSN related message definition
+        web_push: Web Push related message definition
+        token: token list, must be tuple (optional).
+        topic: message topic, must be string (optional).
+        condition: message condition, must be string (optional).
+    """
+    def __init__(self, data=None, notification=None, android=None, apns=None, web_push=None, token=None,
+                 topic=None, condition=None):
+        MessageValidator.check_message(data, notification, android, apns, web_push, token, topic, condition)
+        self.data = data
+        self.notification = notification
+        self.android = android
+        self.apns = apns
+        self.web_push = web_push
+        self.token = token
+        self.topic = topic
+        self.condition = condition
+
+
+class Notification(object):
+    """A notification that can be included in a message.
+
+    Args:
+        title: Title of the notification (optional).
+        body: Body of the notification (optional).
+    """
+    def __init__(self, title=None, body=None, image=None):
+        MessageValidator.check_notification(title, body, image)
+        self.title = title
+        self.body = body
+        self.image = image
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+
+class APNsConfig(object):
+    """
+    Please refer to the Apple APNS API reference:
+    https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/\
+    CommunicatingwithAPNs.html
+    """
+    def __init__(self, headers=None, payload=None, apns_hms_options=None):
+        MessageValidator.check_apns_config(headers=headers, payload=payload, apns_hms_options=apns_hms_options)
+        self.headers = headers
+        self.payload = payload
+        self.apns_hms_options = apns_hms_options
+
+
+class APNsHeader(object):
+    """
+    authorization
+    apns-id
+    apns-expiration
+    apns-priority
+    apns-topic
+    apns-collapse-id
+    """
+    HEAD_AUTHORIZATION = "authorization"
+    HEAD_APNs_ID = "apns-id"
+    HEAD_APNs_EXPIRATION = "apns-expiration"
+    HEAD_APNs_PRIORITY = "apns-priority"
+    HEAD_APNs_TOPIC = "pns-topic"
+    HEAD_APNs_COLLAPSE_ID = "apns-collapse-id"
+
+
+class APNsPayload(object):
+    """
+     APNs payload definition
+    """
+    def __init__(self, aps, **kwargs):
+        MessageValidator.check_apns_payload(aps=aps)
+        self.aps = aps
+        self.custom_data = kwargs
+
+
+class APNsAps(object):
+    """
+    APNs aps definition: https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual\
+                        /RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1
+
+    one sample is as follows:
+
+    {
+        "aps" : {
+            "alert" : {
+                "title" : "Game Request",
+                "body" : "Bob wants to play poker",
+                "action-loc-key" : "PLAY"
+                "loc-key" : "GAME_PLAY_REQUEST_FORMAT",
+                "loc-args" : [ "Jenna", "Frank"],
+                "content-available" : 1
+            },
+            "badge" : 5,
+            "sound" : "bingbong.aiff",
+        },
+        "acme1" : "bar",
+        "acme2" : [ "bang",  "whiz" ]
+    }
+    """
+    def __init__(self, alert=None, badge=None, sound=None, content_available=None, category=None,
+                 thread_id=None, mutable_content=None, custom_data=None):
+        MessageValidator.check_apns_payload_aps(alert=alert, badge=badge, sound=sound,
+                                                content_available=content_available, category=category,
+                                                thread_id=thread_id, mutable_content=mutable_content,
+                                                custom_data=custom_data)
+        self.alert = alert
+        self.badge = badge
+        self.sound = sound
+        self.content_available = content_available
+        self.category = category
+        self.thread_id = thread_id
+        self.mutable_content = mutable_content
+        self.custom_data = custom_data
+
+
+class APNsAlert(object):
+    """An alert that can be included in ``messaging.Aps``.
+
+    Args:
+
+    """
+
+    def __init__(self, title=None, body=None, loc_key=None, loc_args=None,
+                 title_loc_key=None, title_loc_args=None, action_loc_key=None, launch_image=None,
+                 custom_data=None):
+        MessageValidator.check_apns_payload_aps_alert(title=title, body=body, loc_key=loc_key, loc_args=loc_args,
+                                                      title_loc_key=title_loc_key, title_loc_args=title_loc_args,
+                                                      action_loc_key=action_loc_key, launch_image=launch_image,
+                                                      custom_data=custom_data)
+        self.title = title
+        self.body = body
+        self.loc_key = loc_key
+        self.loc_args = loc_args
+        self.title_loc_key = title_loc_key
+        self.title_loc_args = title_loc_args
+        self.action_loc_key = action_loc_key
+        self.launch_image = launch_image
+        self.custom_data = custom_data
+
+
+class APNsHMSOptions(object):
+    """Options for features provided by the FCM SDK for iOS.
+
+    Args:
+        target_user_type: Developer or Commercial enviroment
+    """
+    def __init__(self, target_user_type=None):
+        MessageValidator.check_apns_hms_options(target_user_type=target_user_type)
+        self.target_user_type = target_user_type
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+
+class WebPushConfig(object):
+    """
+        Web push-specific options that can be included in a message.
+        For Web Push Specification Reference: https://tools.ietf.org/html/rfc8030#section-5
+        For mozilla implementation: https://developer.mozilla.org/en-US/docs/Web/API/notification
+    """
+    TTL_HEADER = "ttl"
+    URGENCY_HEADER = "urgency"
+    TOPIC_HEADER = "topic"
+
+    def __init__(self, headers=None, data=None, notification=None, hms_options=None):
+        """
+
+        :param headers: A dictionary of headers (optional). Refer `Web push Specification`_ for supported headers.
+        :param notification:  A ``messaging.WebPushNotification`` to be included in the message (optional).
+        :param hms_options:  A ``WebPushHMSOptions`` instance to be included in the message(optional).
+        """
+        MessageValidator.check_webpush_config(headers, data, notification, hms_options)
+        """ Refer to https://tools.ietf.org/html/rfc7240 """
+        self.headers = headers
+        """ message deliver to the end application directly """
+        self.data = data
+        """ Refer to  WebPushNotification """
+        self.notification = notification
+        """ Refer to WebPushHMSOptions"""
+        self.hms_options = hms_options
+
+
+class WebPushHeader(object):
+    """
+     Web Push Header, refer to: https://tools.ietf.org/html/rfc7240
+    """
+    def __init__(self, ttl=None, urgency=None, topic=None):
+        MessageValidator.check_webpush_header(ttl, urgency, topic)
+        self.ttl = ttl
+        self.urgency = urgency
+        self.topic = topic
+
+
+class WebPushNotification(object):
+    """
+     Web Push Notification
+    """
+    def __init__(self, title=None, body=None, icon=None, actions=None, badge=None, data=None, dir=None,
+                 image=None, lang=None, renotify=None, require_interaction=None, silent=None, tag=None,
+                 timestamp=None, vibrate=None):
+        MessageValidator.check_webpush_notification(title=title, body=body, icon=icon, actions=actions, badge=badge,
+                                                    data=data, dir=dir, image=image, lang=lang,
+                                                    renotify=renotify, require_interaction=require_interaction,
+                                                    silent=silent, tag=tag, timestamp=timestamp, vibrate=vibrate)
+        self.title = title
+        self.body = body
+        """ Refer to WebPushNotificationAction """
+        self.actions = actions
+        self.badge = badge
+        self.data = data
+        self.dir = dir
+        self.icon = icon
+        self.image = image
+        self.lang = lang
+        self.renotify = renotify
+        self.require_interaction = require_interaction
+        self.silent = silent
+        self.tag = tag
+        self.timestamp = timestamp
+        self.vibrate = vibrate
+
+
+class WebPushNotificationAction(object):
+    """
+    The action for web push notification
+    """
+    def __init__(self, action=None, title=None, icon=None):
+        """
+
+        :param action:
+        :param title:
+        :param icon:
+        """
+        MessageValidator.check_webpush_notification_action(action=action, title=title, icon=icon)
+        self.action = action
+        self.icon = icon
+        self.title = title
+
+
+class WebPushHMSOptions(object):
+    """
+    optional link option
+    """
+    def __init__(self, link=None):
+        MessageValidator.check_webpush_hms_options(link)
+        self.link = link
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+
+class AndroidConfig(object):
+
+    HIGH_PRIORITY = "HIGH"
+    NORMAL_PRIORITY = "NORMAL"
+
+    """
+    Android-specific options that can be included in a message.
+    """
+    def __init__(self, collapse_key=None, urgency='NORMAL', ttl=None, bi_tag=None
+                 , fast_app_target=None, notification=None, data=None, category=None):
+        MessageValidator.check_android_config(collapse_key, urgency, ttl, bi_tag, fast_app_target
+                                              , notification, data)
+        self.collapse_key = collapse_key
+        self.urgency = urgency
+        self.ttl = ttl
+        self.bi_tag = bi_tag
+        self.fast_app_target = fast_app_target
+        self.notification = notification
+        self.data = data
+        self.category = category
+
+
+class AndroidNotification(object):
+
+    PRIORITY_LOW = "LOW"
+    PRIORITY_DEFAULT = "NORMAL"
+    PRIORITY_HIGH = "HIGH"
+    VISIBILITY_UNSPECIFIED = "VISIBILITY_UNSPECIFIED"
+    PRIVATE = "PRIVATE"
+    PUBLIC = "PUBLIC"
+    SECRET = "SECRET"
+
+    """
+    Android-specific notification parameters.
+    """
+
+    def __init__(self, title=None, body=None, icon=None, color=None, sound=None, default_sound=None, tag=None,
+                 click_action=None, body_loc_key=None, body_loc_args=None, title_loc_key=None,
+                 title_loc_args=None, multi_lang_key=None, channel_id=None, notify_summary=None, image=None,
+                 style=None, big_title=None, big_body=None, auto_clear=None, notify_id=None, group=None, badge=None,
+                 ticker=None, auto_cancel=None, when=None, importance=None, use_default_vibrate=True,
+                 use_default_light=True, vibrate_config=None, visibility=None, light_settings=None, foreground_show=False):
+
+        MessageValidator.check_android(title=title, body=body, icon=icon, color=color, sound=sound,
+                                       default_sound=default_sound, tag=tag, click_action=click_action,
+                                       body_loc_key=body_loc_key, body_loc_args=body_loc_args,
+                                       title_loc_key=title_loc_key, title_loc_args=title_loc_args,
+                                       multi_lang_key=multi_lang_key, channel_id=channel_id,
+                                       notify_summary=notify_summary,
+                                       image=image, style=style, big_title=big_title, big_body=big_body,
+                                       auto_clear=auto_clear, notify_id=notify_id,
+                                       group=group, badge=badge, ticker=ticker, auto_cancel=auto_cancel, when=when,
+                                       importance=importance,
+                                       use_default_vibrate=use_default_vibrate,
+                                       use_default_light=use_default_light, vibrate_config=vibrate_config,
+                                       visibility=visibility, light_settings=light_settings,
+                                       foreground_show=foreground_show)
+        self.title = title
+        self.body = body
+        self.icon = icon
+        self.color = color
+        self.sound = sound
+        self.default_sound = default_sound
+        self.tag = tag
+        self.click_action = click_action
+        self.body_loc_key = body_loc_key
+        self.body_loc_args = body_loc_args
+        self.title_loc_key = title_loc_key
+        self.title_loc_args = title_loc_args
+        self.multi_lang_key = multi_lang_key
+        self.channel_id = channel_id
+        self.notify_summary = notify_summary
+        self.image = image
+        self.style = style
+        self.big_title = big_title
+        self.big_body = big_body
+        self.auto_clear = auto_clear
+        self.notify_id = notify_id
+        self.group = group
+        self.badge = badge
+        self.ticker = ticker
+        self.auto_cancel = auto_cancel
+        self.when = when
+        self.importance = importance
+        self.use_default_vibrate = use_default_vibrate
+        self.use_default_light = use_default_light
+        self.vibrate_config = vibrate_config
+        self.visibility = visibility
+        self.light_settings = light_settings
+        self.foreground_show = foreground_show
+
+
+class AndroidClickAction(object):
+    """A ClickAction that can be included in a message.android.notification.
+
+    Args:
+        action_type: type of the android.notification (optional).
+        intent: intent of the android.notification (optional).
+        url: url of the android.notification (optional).
+        action: action definition for push message
+                1: to specific activity of application
+                2: specific URL
+                3: to specific application
+    """
+    def __init__(self, action_type=None, intent=None, action=None, url=None):
+        MessageValidator.check_click_action(action_type=action_type, intent=intent, action=action, url=url)
+        self.action_type = action_type
+        self.intent = intent
+        self.action = action
+        self.url = url
+
+class AndroidBadgeNotification(object):
+    """A BadgeNotification that can be included in a message.android.notification.
+
+    Args:
+        add_num: message number of badge notification in the android.notification (optional).
+        set_num: set the specific number of badge notification (optional).
+        clazz: message class of badge notification in the android.notification (optional).
+    """
+    def __init__(self, add_num=None, set_num=None, clazz=None):
+        MessageValidator.check_badge_notification(add_num=add_num, set_num=set_num, clazz=clazz)
+        self.add_num = add_num
+        self.set_num = set_num
+        self.clazz = clazz
+
+
+class AndroidLightSettings(object):
+    """
+        light_settings":{
+            "color":{
+                "alpha":0,
+                "red":0,
+                "green":1,
+                "blue":1
+            },
+            "light_on_duration":"3.5",
+            "light_off_duration":"5S"
+        }
+    """
+    def __init__(self, color=None, light_on_duration=None, light_off_duration=None):
+        MessageValidator.check_light_settings(color=color, light_on_duration=light_on_duration, light_off_duration=light_off_duration)
+        self.color = color
+        self.light_on_duration = light_on_duration
+        self.light_off_duration = light_off_duration
+
+
+class AndroidLightSettingsColor(object):
+    """
+        "color":{
+                "alpha":0,
+                "red":0,
+                "green":1,
+                "blue":1
+            }
+    """
+    def __init__(self, alpha=None, red=None, green=None, blue=None):
+        MessageValidator.check_light_settings_color(alpha=alpha, red=red, green=green, blue=blue)
+        self.alpha = alpha
+        self.red = red
+        self.green = green
+        self.blue = blue
+
+# --------------------------------------------------------------------------------------------------------------------
+
+
+class MessageValidator(object):
+    """
+        message validation utilities.
+        Methods provided in this class raise ValueErrors if any validations fail.
+    """
+    @classmethod
+    def check_https_url(cls, hint, value):
+        cls.check_string(hint, value)
+        if value is not None and not re.match(r"^https:/{2}\w.+$", value):
+            raise ValueError('{0} must be a valid https url.'.format(hint))
+
+    @classmethod
+    def check_string(cls, hint, value, non_empty=False):
+        """Checks if the given value is a string."""
+        if value is None:
+            return None
+        if not isinstance(value, six.string_types):
+            if non_empty:
+                raise ValueError('{0} must be a non-empty string.'.format(hint))
+            else:
+                raise ValueError('{0} must be a string.'.format(hint))
+        if non_empty and not value:
+            raise ValueError('{0} must be a non-empty string.'.format(hint))
+        return value
+
+    @classmethod
+    def assert_string_values(cls, hint, value, *args):
+        """
+        Check the class value should be an instance of string, and related values should be within *args
+        :param hint: prompt message
+        :param value: the real value
+        :param args: the specific value list
+        :return:
+        """
+        if value is None:
+            return None
+        if not isinstance(value, six.string_types):
+                raise ValueError('{0} must be a string.'.format(hint))
+        for v in args:
+            if value.__eq__(v):
+                return value
+
+        raise ValueError('{} must be a value within{}.'.format(hint, args))
+
+    @classmethod
+    def check_string_list(cls, label, value):
+        """Checks if the given value is a list comprised only of strings."""
+        if value is None or value == []:
+            return None
+        if not isinstance(value, list):
+            raise ValueError('{0} must be a list of strings.'.format(label))
+        non_str = [k for k in value if not isinstance(k, six.string_types)]
+        if non_str:
+            raise ValueError('{0} must not contain non-string values.'.format(label))
+        return value
+
+    @classmethod
+    def check_boolean(cls, hint, value):
+        """Checks if the given value is a string."""
+        if value is None:
+            return None
+        if not isinstance(value, bool):
+            raise ValueError('{0} must be a boolean.'.format(hint))
+        return value
+
+    @classmethod
+    def count_boolean(cls, *args):
+        count = 0
+        for v in args:
+            if v:
+                count += 1
+        return count
+
+    @classmethod
+    def check_not_all_none(cls, hint, *args):
+        total_size = len(args)
+        count = 0
+        for data in args:
+            if data is None:
+                count += 1
+        if total_size == count:
+            raise ValueError(hint)
+
+    @classmethod
+    def check_type(cls, class_obj, class_type, hint):
+        if (class_obj is not None) and (not isinstance(class_obj, class_type)):
+            raise ValueError(hint)
+
+    @classmethod
+    def check_type_list(cls, label, value, cls_type):
+        """Checks if the given value is a list comprised only of numbers."""
+        if value is None or value == []:
+            return None
+        if not isinstance(value, list):
+            raise ValueError('{0} must be a list of {1}.'.format(label, cls_type))
+        non_number = [k for k in value if not isinstance(k, cls_type)]
+        if non_number:
+            raise ValueError('{0} must not contain non-{1} values.'.format(label, cls_type))
+        return value
+
+    @classmethod
+    def check_number(cls, label, value):
+        if value is None:
+            return None
+        if not isinstance(value, numbers.Number):
+            raise ValueError('{0} must be a number.'.format(label))
+        return value
+
+    @classmethod
+    def check_number_span(cls, label, value, min, max):
+        if value is None:
+            return None
+        if not isinstance(value, numbers.Number):
+            raise ValueError('{0} must be a number.'.format(label))
+        if value < min or value > max:
+            raise ValueError('{0} must be within {1} to {2}.'.format(label, min, max))
+        return value
+
+    @classmethod
+    def assert_integer_values(cls, hint, value, *args):
+        """
+        Check the class value should be an instance of string, and related values should be within *args
+        :param hint: prompt message
+        :param value: the real value
+        :param args: the specific value list
+        :return:
+        """
+        if value is None:
+            return None
+        if not isinstance(value, six.integer_types):
+            raise ValueError('{0} must be a integer.'.format(hint))
+        for v in args:
+            if value == v:
+                return value
+
+        raise ValueError('{} must be a value within{}.'.format(hint, args))
+
+    @classmethod
+    def check_number_list(cls, label, value):
+        if value is None or value == []:
+            return None
+        if not isinstance(value, list):
+            raise ValueError('{0} must be a list of numbers.'.format(label))
+        non_number = [k for k in value if not isinstance(k, numbers.Number)]
+        if non_number:
+            raise ValueError('{0} must not contain non-number values.'.format(label))
+        return value
+
+    @classmethod
+    def check_string_dict(cls, label, value):
+        if value is None or value == {}:
+            return None
+        if not isinstance(value, dict):
+            raise ValueError('{0} must be a dictionary.'.format(label))
+        non_str = [k for k in value if not isinstance(k, six.string_types)]
+        if non_str:
+            raise ValueError('{0} must not contain non-string keys.'.format(label))
+        return value
+
+    # ------------------------------------------------------------------------------------------------------------------
+
+    @classmethod
+    def check_message(cls, data, notification, android, apns, web_push, token, topic, condition):
+        """
+        Check whether the message parameter is valid or not
+
+        :param data:
+        :param notification:
+        :param android:
+        :param apns:
+        :param web_push:
+        :param token:
+        :param topic:
+        :param condition:
+        :return:
+        """
+        # data must be string
+        cls.check_string(hint="Message.data", value=data)
+
+        # notification
+        if (notification is not None) and (not isinstance(notification, Notification)):
+            raise ValueError('notification must be an instance of Notification class')
+
+        # android / APNs / Web Push
+        # if notification message(data is None), one of android / APNs / Web Push must be present
+        if data is None:
+            cls.check_not_all_none('Message.data is None, one of Message.android/Message.apns/Message.webpush \
+            must be present', android, apns, web_push)
+
+        cls.check_type(android, AndroidConfig, 'android must be an instance of AndroidConfig class')
+        cls.check_type(apns, APNsConfig, 'apns must be an instance of APNsConfig class')
+        cls.check_type(web_push, WebPushConfig, 'web_push must be an instance of WebPushConfig class')
+
+        """token, topic, condition"""
+        # [token, topic, condition] only one not None
+        target_count = cls.count_boolean(token is not None, topic is not None, condition is not None)
+        if target_count != 1:
+            raise ValueError('Exactly one of token, topic or condition must be specified.')
+
+        # token must be tuple or list
+        if token is not None:
+            if not isinstance(token, tuple) and not isinstance(token, list):
+                raise ValueError('token must be a tuple or a list')
+            if len(token) > 1000:
+                raise ValueError('token must not contain more than 1000 tokens')
+
+        cls.check_string(hint="Message.topic", value=topic)
+        cls.check_string(hint="Message.condition", value=condition)
+
+    @classmethod
+    def check_notification(cls, title, body, image):
+        cls.check_string(hint="Notification.title", value=title)
+        cls.check_string(hint="Notification.body", value=body)
+        cls.check_https_url(hint="Notification.image", value=image)
+
+    @classmethod
+    def check_android_config(cls, collapse_key, urgency, ttl, bi_tag, fast_app_target, notification, data):
+        # collapse_key
+        cls.check_number('AndroidConfig.collapse_key', collapse_key)
+        # urgency
+        cls.assert_string_values("AndroidConfig.urgency", urgency, AndroidConfig.HIGH_PRIORITY,
+                                 AndroidConfig.NORMAL_PRIORITY)
+        # ttl
+        cls.check_string(hint="AndroidConfig.ttl", value=ttl)
+        # bi_tag
+        cls.check_string(hint="AndroidConfig.bi_tag", value=bi_tag)
+        # fast_app_target
+        cls.check_number_span("AndroidConfig.fast_app_target", fast_app_target, 1, 2)
+        # notification
+        cls.check_type(notification, AndroidNotification,
+                       hint='notification must be an instance of AndroidNotification')
+        # data
+        cls.check_string(hint="AndroidConfig.data", value=data)
+
+    @classmethod
+    def check_android(cls, title, body, icon, color, sound, default_sound, tag, click_action, body_loc_key, body_loc_args,
+                      title_loc_key, title_loc_args, multi_lang_key, channel_id, notify_summary, image,
+                      style, big_title, big_body, auto_clear, notify_id, group, badge,
+                      ticker, auto_cancel, when, importance,
+                      use_default_vibrate, use_default_light, vibrate_config, visibility, light_settings,
+                      foreground_show):
+        # title
+        cls.check_string(hint="AndroidNotification.title", value=title)
+        # body
+        cls.check_string(hint="AndroidNotification.body", value=body)
+        # icon
+        cls.check_string(hint="AndroidNotification.icon", value=icon)
+        # color
+        cls.check_string(hint="AndroidNotification.color", value=color)
+        # sound
+        cls.check_string(hint="AndroidNotification.sound", value=sound)
+        # default_sound
+        cls.check_boolean(hint="AndroidNotification.default_sound", value=default_sound)
+        # tag
+        cls.check_string(hint="AndroidNotification.tag", value=tag)
+        # click_action
+        cls.check_type(click_action, AndroidClickAction,
+                       hint='click_action must be an instance of AndroidClickAction')
+        # body_loc_key
+        cls.check_string(hint="AndroidNotification.body_loc_key", value=body_loc_key)
+        # body_loc_args
+        if (body_loc_args is not None) and (not isinstance(body_loc_args, tuple)) and (not isinstance(body_loc_args, list)):
+            raise ValueError('AndroidNotification.body_loc_args must be an instance of tuple or list')
+        # title_loc_key
+        cls.check_string(hint="AndroidNotification.title_loc_key", value=title_loc_key)
+        # title_loc_args
+        if (title_loc_args is not None) and (not isinstance(title_loc_args, tuple) and not isinstance(title_loc_args, list)):
+            raise ValueError('AndroidNotification.title_loc_args must be an instance of tuple or list')
+        # multi_lang_key
+        if multi_lang_key is not None:
+            if not isinstance(multi_lang_key, dict):
+                raise ValueError('AndroidNotification.multi_lang_key must be a dict.')
+        # channel_id
+        cls.check_string(hint="AndroidNotification.channel_id", value=channel_id)
+        # notify_summary
+        cls.check_string(hint="AndroidNotification.notify_summary", value=notify_summary)
+        #
+        # image
+        cls.check_https_url(hint="AndroidNotification.image", value=image)
+        # style
+        if style is not None:
+            if style not in [0, 1, 2]:
+                raise ValueError('AndroidNotification.style must in [0, 1, 2]')
+            # big_title, big_body
+            if style == 1:
+                if (big_title is None) or (not isinstance(big_title, str)):
+                    raise ValueError('AndroidNotification.big_title must be valid string when style is 1')
+                if (big_body is None) and (not isinstance(big_body, str)):
+                    raise ValueError('AndroidNotification.big_body must be valid string when style is 1')
+        # auto_clear
+        cls.check_number(label='AndroidNotification.auto_clear ', value=auto_clear)
+        # notify_id
+        cls.check_number(label='AndroidNotification.notify_id ', value=notify_id)
+        # group
+        cls.check_string(hint="AndroidNotification.group", value=group)
+        # badge
+        cls.check_type(badge, AndroidBadgeNotification, "badge should be an instance of AndroidBadgeNotification")
+        # ticker
+        cls.check_string(hint="AndroidNotification.ticker", value=ticker)
+        # auto_cancel
+        cls.check_boolean(hint="AndroidNotification.auto_cancel", value=auto_cancel)
+        # when
+        cls.check_string(hint="AndroidNotification.when", value=when)
+        # importance
+        cls.assert_string_values("AndroidNotification.importance", importance,
+                                 AndroidNotification.PRIORITY_DEFAULT,
+                                 AndroidNotification.PRIORITY_HIGH, AndroidNotification.PRIORITY_LOW)
+        # use_default_vibrate
+        cls.check_boolean(hint="AndroidNotification.use_default_vibrate", value=use_default_vibrate)
+        # use_default_light
+        cls.check_boolean(hint="AndroidNotification.use_default_light", value=use_default_light)
+        # vibrate_config
+        cls.check_string_list(label="AndroidNotification.vibrate_config", value=vibrate_config)
+        # visibility
+        cls.assert_string_values("AndroidNotification.visibility", visibility,
+                                 AndroidNotification.PRIVATE,
+                                 AndroidNotification.PUBLIC, AndroidNotification.SECRET,
+                                 AndroidNotification.VISIBILITY_UNSPECIFIED)
+        # light_settings
+        cls.check_type(light_settings, AndroidLightSettings, "light_settings should be an instance of AndroidLightSettings")
+        # foreground_show
+        cls.check_boolean(hint="AndroidNotification.foreground_show", value=foreground_show)
+
+    @classmethod
+    def check_badge_notification(cls, add_num, set_num, clazz):
+        # add_num must be int
+        cls.check_number_span(label="AndroidBadgeNotification.add_num", value=add_num, min=0, max=100)
+        # set_num must be int
+        cls.check_number_span(label="AndroidBadgeNotification.set_num", value=set_num, min=0, max=100)
+        # clazz
+        cls.check_string(hint="AndroidBadgeNotification.clazz", value=clazz)
+
+    @classmethod
+    def check_click_action(cls, action_type, intent, action, url):
+        # type must be in [1, 4]
+        if (action_type is None) or (action_type not in [1, 2, 3, 4]):
+            raise ValueError('ClickAction.type must be in [1, 2, 3, 4]')
+
+        # intent, if type is 1, intent or action must be present or both
+        if action_type == 1:
+            count = cls.count_boolean(isinstance(intent, str), isinstance(action, str))
+            if count <= 0:
+                raise ValueError('ClickAction.intent or ClickAction.action must be present or both when click_type is 1')
+
+        # url, if type is 2, url must
+        if action_type == 2:
+            if not isinstance(url, str):
+                raise ValueError('ClickAction.url must when ClickAction.type is 2')
+            if not url.upper().startswith('HTTPS'):
+                raise ValueError('ClickAction.url must be https prefix when ClickAction.type is 2')
+
+    @classmethod
+    def check_light_settings(cls, color, light_on_duration, light_off_duration):
+        cls.check_type(color, AndroidLightSettingsColor, "color must be an instance of AndroidLightSettingsColor")
+        cls.check_string(hint="AndroidLightSettings.light_on_duration", value=light_on_duration)
+        cls.check_string(hint="AndroidLightSettings.light_off_duration", value=light_off_duration)
+
+    @classmethod
+    def check_light_settings_color(cls, alpha, red, green, blue):
+        cls.check_number("AndroidLightSettingsColor.alpha", alpha)
+        cls.check_number("AndroidLightSettingsColor.red", red)
+        cls.check_number("AndroidLightSettingsColor.green", green)
+        cls.check_number("AndroidLightSettingsColor.blue", blue)
+
+    @classmethod
+    def check_webpush_config(cls, headers, data, notification, hms_options):
+        # headers
+        cls.check_type(headers, WebPushHeader, "headers must be an instance of WebPushHeader")
+        cls.check_string(hint="WebPushConfig.headers", value=data)
+        cls.check_type(notification, WebPushNotification, "notification must be an instance of WebPushNotification")
+        cls.check_type(hms_options, WebPushHMSOptions, "hms_options must be an instance of WebPushHMSOptions")
+
+    @classmethod
+    def check_webpush_header(cls, ttl=None, urgency=None, topic=None):
+        cls.check_string(hint="WebPushHeader.ttl", value=ttl)
+        cls.check_string(hint="WebPushHeader.urgency", value=urgency)
+        cls.check_string(hint="WebPushHeader.topic", value=topic)
+
+    @classmethod
+    def check_webpush_notification(cls, title=None, body=None, icon=None, actions=None, badge=None,
+                                   data=None, dir=None, image=None, lang=None, renotify=None,
+                                   require_interaction=None, silent=None, tag=None, timestamp=None, vibrate=None):
+        cls.check_string(hint="WebPushNotification.title", value=title)
+        cls.check_string(hint="WebPushNotification.body", value=body)
+        cls.check_string(hint="WebPushNotification.icon", value=icon)
+        cls.check_string(hint="WebPushNotification.data", value=data)
+        cls.check_type_list("WebPushNotificationAction.actions", actions, WebPushNotificationAction)
+        cls.check_string(hint="WebPushNotification.image", value=image)
+        cls.check_string(hint="WebPushNotification.lang", value=lang)
+        cls.check_string(hint="WebPushNotification.tag", value=tag)
+        cls.check_string(hint="WebPushNotification.badge", value=badge)
+        cls.assert_string_values("WebPushNotification.dir", dir, "auto", "ltr", "rtl")
+        cls.check_number_list("WebPushNotification.vibrate", vibrate)
+        cls.check_boolean("WebPushNotification.renotify", renotify)
+        cls.check_boolean("WebPushNotification.require_interaction", require_interaction)
+        cls.check_boolean("WebPushNotification.silent", silent)
+        cls.check_number("WebPushNotification.timestamp", timestamp)
+
+    @classmethod
+    def check_webpush_notification_action(cls, action=None, title=None, icon=None):
+        cls.check_string(hint="WebPushNotificationAction.action", value=action)
+        cls.check_string(hint="WebPushNotificationAction.title", value=title)
+        cls.check_string(hint="WebPushNotificationAction.icon", value=icon)
+
+    @classmethod
+    def check_webpush_hms_options(cls, link):
+        cls.check_string(hint="WebPushHMSOptions.link", value=link)
+
+    @classmethod
+    def check_apns_config(cls, headers=None, payload=None, apns_hms_options=None):
+        cls.check_string_dict("APNsConfig.headers", headers)
+        cls.check_type(payload, APNsPayload, "payload must be an instance of APNsPayload")
+        cls.check_type(apns_hms_options, APNsHMSOptions, "apns_hms_options must be an instance of APNsHMSOptions")
+
+    @classmethod
+    def check_apns_payload(cls, aps=None):
+        cls.check_type(aps, APNsAps, "aps must be an instance of APNsAps")
+        pass
+
+    @classmethod
+    def check_apns_payload_aps(cls, alert, badge, sound, content_available, category, thread_id, mutable_content,
+                               custom_data):
+        # alert: Dictionary or String
+        if alert is not None:
+            if not isinstance(alert, six.string_types):
+                cls.check_type(alert, APNsAlert, "alert must be an instance of String or APNsAlert class")
+        # badge: Number
+        cls.check_number("APNsAps.badge", badge)
+        # sound: String
+        cls.check_string("APNsAps.sound", sound)
+        # content_available: number
+        cls.check_number("APNsAps.content_available", content_available)
+        # category: String
+        cls.check_string("APNsAps.category", category)
+        # thread_id: String
+        cls.check_string("APNsAps.thread_id", thread_id)
+        # mutable_content
+        cls.check_boolean("APNsAps.mutable_content", mutable_content)
+        # custom_data
+        if custom_data is not None:
+            if not isinstance(custom_data, dict):
+                raise ValueError('APNsAps.custom_data must be a dict.')
+
+    @classmethod
+    def check_apns_payload_aps_alert(cls, title, body, loc_key, loc_args, title_loc_key, title_loc_args, action_loc_key,
+                                     launch_image, custom_data):
+        # title: String
+        cls.check_string("APNsAlert.title", title)
+        # body: String
+        cls.check_string("APNsAlert.body", body)
+        # loc_key: String
+        cls.check_string("APNsAlert.loc_key", loc_key)
+        # loc_args: Array of strings
+        cls.check_string_list("APNsAlert.loc_args", loc_args)
+        # title_loc_key: String or null
+        cls.check_string("APNsAlert.title_loc_key", title_loc_key)
+        # title_loc_args: Array of strings or null
+        cls.check_string_list("APNsAlert.title_loc_args", title_loc_args)
+        # action_loc_key: String or null
+        cls.check_string("APNsAlert.action_loc_key", action_loc_key)
+        # launch_image: String
+        cls.check_string("APNsAlert.launch_image", launch_image)
+        # custom_data
+        if custom_data is not None:
+            if not isinstance(custom_data, dict):
+                raise ValueError('APNsAlert.custom_data must be a dict.')
+
+    @classmethod
+    def check_apns_hms_options(cls, target_user_type):
+        cls.assert_integer_values("APNsHMSOptions.target_user_type", target_user_type, 1, 2, 3)

+ 224 - 0
Service/HuaweiPushService/push_admin/messaging.py

@@ -0,0 +1,224 @@
+# -*-coding:utf-8-*-
+#
+# Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from Service.HuaweiPushService.push_admin import _messages, _app
+from Service.HuaweiPushService import push_admin
+
+"""HUAWEI Cloud Messaging module."""
+
+""" General Data structure """
+Message = _messages.Message
+Notification = _messages.Notification
+
+""" Web Push related data structure """
+WebPushConfig = _messages.WebPushConfig
+WebPushHeader = _messages.WebPushHeader
+WebPushNotification = _messages.WebPushNotification
+WebPushNotificationAction = _messages.WebPushNotificationAction
+WebPushHMSOptions = _messages.WebPushHMSOptions
+
+""" Android Push related data structure """
+AndroidConfig = _messages.AndroidConfig
+AndroidNotification = _messages.AndroidNotification
+AndroidClickAction = _messages.AndroidClickAction
+AndroidBadgeNotification = _messages.AndroidBadgeNotification
+AndroidLightSettings = _messages.AndroidLightSettings
+AndroidLightSettingsColor = _messages.AndroidLightSettingsColor
+
+""" APNS Push related data structure"""
+APNsConfig = _messages.APNsConfig
+APNsHeader = _messages.APNsHeader
+APNsPayload = _messages.APNsPayload
+APNsAps = _messages.APNsAps
+APNsAlert = _messages.APNsAlert
+APNsHMSOptions = _messages.APNsHMSOptions
+
+"""Common exception definition"""
+ApiCallError = _app.ApiCallError
+
+
+def send_message(message, validate_only=False, app_id=None, verify_peer=False):
+    """
+        Sends the given message Huawei Cloud Messaging (HCM)
+        :param message: An instance of ``messaging.Message``.
+        :param validate_only: A boolean indicating whether to run the operation in dry run mode (optional).
+        :param app_id: app id parameters obtained by developer alliance applying for Push service (optional).
+        :param verify_peer: (optional) Either a boolean, in which case it controls whether we verify
+            the server's TLS certificate, or a string, in which case it must be a path
+            to a CA bundle to use. Defaults to ``True``.
+        :return: SendResponse
+        Raises:
+            ApiCallError: If an error occurs while sending the message to the HCM service.
+    """
+    try:
+        response = push_admin.get_app(app_id).send(message, validate_only, verify_peer=verify_peer)
+        return SendResponse(response)
+    except Exception as e:
+        raise ApiCallError(repr(e))
+
+
+def subscribe_topic(topic, token_list, app_id=None):
+    """
+    :param topic: The specific topic
+    :param token_list: The token list to be added
+    :param app_id: application ID
+    """
+    try:
+        response = push_admin.get_app(app_id).subscribe_topic(topic, token_list)
+        return TopicSubscribeResponse(response)
+    except Exception as e:
+        raise ApiCallError(repr(e))
+
+
+def unsubscribe_topic(topic, token_list, app_id=None):
+    """
+    :param topic: The specific topic
+    :param token_list: The token list to be deleted
+    :param app_id: application ID
+    """
+    try:
+        response = push_admin.get_app(app_id).unsubscribe_topic(topic, token_list)
+        return TopicSubscribeResponse(response)
+    except Exception as e:
+        raise ApiCallError(repr(e))
+
+
+def list_topics(token, app_id=None):
+    """
+    :param token: The token to be queried
+    :param app_id: application ID
+    """
+    try:
+        response = push_admin.get_app(app_id).query_subscribe_list(token)
+        return TopicQueryResponse(response)
+    except Exception as e:
+        raise ApiCallError(repr(e))
+
+
+class SendResponse(object):
+    """
+        The response received from an send request to the HCM API.
+        response: received http response body text from HCM.
+    """
+    def __init__(self, response=None):
+        try:
+            self._code = response['code']
+            self._msg = response['msg']
+            self._requestId = response['requestId']
+        except Exception as e:
+            raise ValueError(format(repr(e)))
+
+    @property
+    def code(self):
+        """errcode"""
+        return self._code
+
+    @property
+    def reason(self):
+        """the description of errcode"""
+        return self._msg
+
+    @property
+    def requestId(self):
+        """A message ID string that uniquely identifies the message."""
+        return self._requestId
+
+
+class BaseTopicResponse(object):
+    """
+    {
+       "msg": "Success",
+       "code": "80000000",
+       "requestId": "157466304904000004000701"
+     }
+    """
+    def __init__(self, json_rsp=None):
+        if json_rsp is None:
+            self._msg = ""
+            self._code = ""
+            self._requestId = ""
+        else:
+            self._msg = json_rsp['msg']
+            self._code = json_rsp['code']
+            self._requestId = json_rsp['requestId']
+
+    @property
+    def msg(self):
+        return self._msg
+
+    @property
+    def code(self):
+        return self._code
+
+    @property
+    def requestId(self):
+        return self._requestId
+
+
+class TopicSubscribeResponse(BaseTopicResponse):
+    """
+     {
+       "msg": "Success",
+       "code": "80000000",
+       "requestId": "157466304904000004000701",
+       "successCount": 2,
+       "failureCount": 0,
+       "errors": []
+     }
+    """
+    def __init__(self, json_rsp=None):
+        super(TopicSubscribeResponse, self).__init__(json_rsp=json_rsp)
+        if json_rsp is None:
+            self._successCount = 0
+            self._failureCount = 0
+            self._errors = []
+        else:
+            self._successCount = json_rsp['successCount']
+            self._failureCount = json_rsp['failureCount']
+            self._errors = json_rsp['errors']
+
+    @property
+    def successCount(self):
+        return self._successCount
+
+    @property
+    def failureCount(self):
+        return self._failureCount
+
+    @property
+    def errors(self):
+        return self._errors
+
+
+class TopicQueryResponse(BaseTopicResponse):
+    """
+         {
+           "msg": "success",
+           "code": "80000000",
+           "requestId": "157466350121600008000701",
+           "topics": [
+                       { "name": "sports",
+                         "addDate": "2019-11-25"
+                         } ]
+         }
+    """
+    def __init__(self, json_rsp=None):
+        super(TopicQueryResponse, self).__init__(json_rsp)
+        self._topics = json_rsp['topics']
+
+    @property
+    def topics(self):
+        return self._topics

+ 433 - 0
Service/PushService.py

@@ -0,0 +1,433 @@
+# -*- coding: utf-8 -*-
+"""
+@Time : 2022/5/19 11:43
+@Auth : Locky
+@File :PushService.py
+@IDE :PyCharm
+"""
+import hashlib
+import json
+import logging
+import os
+import time
+
+import apns2
+import jpush
+import requests
+from pyfcm import FCMNotification
+
+from AnsjerPush.config import APP_BUNDLE_DICT, APNS_MODE, BASE_DIR, APNS_CONFIG, FCM_CONFIG, JPUSH_CONFIG, XMPUSH_CONFIG \
+    , VIVOPUSH_CONFIG, OPPOPUSH_CONFIG, MEIZUPUSH_CONFIG
+from Model.models import UidPushModel
+from Object.RedisObject import RedisObject
+from Service.CommonService import CommonService
+from Service.VivoPushService.push_admin.APIMessage import PushMessage
+from Service.VivoPushService.push_admin.APISender import APISender
+
+
+class PushObject:
+    # 推送对象
+    @staticmethod
+    def get_msg_title(nickname):
+        """
+        获取推送消息标题
+        @param nickname: 设备名
+        @return: msg_title
+        """
+        return nickname
+
+    @staticmethod
+    def get_gateway_msg_text(n_time, tz, lang, alarm):
+        """
+        获取网关推送消息内容
+        @param n_time: 当前时间
+        @param tz: 时区
+        @param lang: 语言
+        @param alarm: 警报
+        @return: msg_text
+        """
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
+        if lang == 'cn':
+            msg_text = '{} 日期:{}'.format(alarm, n_date)
+        else:
+            msg_text = '{} date:{}'.format(alarm, n_date)
+        return msg_text
+
+    @staticmethod
+    def get_ai_msg_text(channel, n_time, lang, tz, label):
+        """
+        获取AI推送内容
+        @param channel: 通道
+        @param n_time: 当前时间
+        @param lang: 语言
+        @param tz: 时区
+        @param label: 识别到的标签
+        @return: ai_msg_text
+        """
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
+        if lang == 'cn':
+            msg = '摄像头AI识别到了{}'.format(label)
+            ai_msg_text = '{msg} 通道:{channel} 日期:{date}'.format(msg=msg, channel=channel, date=n_date)
+        else:
+            msg = 'Camera AI recognizes {}'.format(label)
+            ai_msg_text = '{msg} channel:{channel} date:{date}'.format(msg=msg, channel=channel, date=n_date)
+        return ai_msg_text
+
+    @staticmethod
+    def get_low_power_msg_text(channel, n_time, lang, tz, electricity, is_sys=0):
+        """
+        获取低电量推送内容
+        @param channel: 通道
+        @param n_time: 当前时间
+        @param lang: 语言
+        @param tz: 时区
+        @param electricity: 电量
+        @param is_sys: 是否为系统消息
+        @return: low_power_msg_text
+        """
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
+        if lang == 'cn':
+            alarm = '剩余电量 ' + electricity
+            if is_sys:
+                low_power_msg_text = '{} 通道:{}'.format(alarm, channel)
+            else:
+                low_power_msg_text = '{} 通道:{} 日期:{}'.format(alarm, channel, n_date)
+        else:
+            alarm = 'Battery remaining ' + electricity
+            if is_sys:
+                low_power_msg_text = '{} channel:{}'.format(alarm, channel)
+            else:
+                low_power_msg_text = '{} channel:{} date:{}'.format(alarm, channel, n_date)
+        return low_power_msg_text
+
+    @staticmethod
+    def ios_apns_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
+                      uid='', channel='1', launch_image=None):
+        """
+        ios apns 推送
+        @param nickname: 设备昵称
+        @param app_bundle_id: app包id
+        @param token_val: 推送token
+        @param n_time: 当前时间
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param uid: uid
+        @param channel: 通道
+        @param launch_image: 推送图片链接
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            pem_path = os.path.join(BASE_DIR, APNS_CONFIG[app_bundle_id]['pem_path'])
+            logger.info('apns推送app_bundle_id:{}, pem_path:{}'.format(app_bundle_id, pem_path))
+            cli = apns2.APNSClient(mode=APNS_MODE, client_cert=pem_path)
+            alert = apns2.PayloadAlert(title=msg_title, body=msg_text, launch_image=launch_image)
+            push_data = {'alert': 'Motion', 'msg': '', 'sound': '', 'zpush': '1', 'uid': uid, 'channel': channel,
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                         'image_url': launch_image
+                         }
+            payload = apns2.Payload(alert=alert, custom=push_data, sound='default', category='myCategory',
+                                    mutable_content=True)
+            n = apns2.Notification(payload=payload, priority=apns2.PRIORITY_LOW)
+            res = cli.push(n=n, device_token=token_val, topic=app_bundle_id)
+            assert res.status_code == 200
+        except Exception as e:
+            logger.info('--->IOS推送异常{}'.format(repr(e)))
+            return repr(e)
+
+    @staticmethod
+    def android_fcm_push(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
+                         uid='', channel='1', image=''):
+        """
+        android fcm 推送
+        @param nickname: 设备昵称
+        @param app_bundle_id: app包id
+        @param token_val: 推送token
+        @param n_time: 当前时间
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param uid: uid
+        @param channel: 通道
+        @param image: 推送图片链接
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            serverKey = FCM_CONFIG[app_bundle_id]
+            push_service = FCMNotification(api_key=serverKey)
+            push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                         'uid': uid, 'channel': channel
+                         }
+            result = push_service.notify_single_device(registration_id=token_val, message_title=msg_title,
+                                                       message_body=msg_text, data_message=push_data,
+                                                       extra_kwargs={'default_sound': True,
+                                                                     'default_vibrate_timings': True,
+                                                                     'default_light_settings': True,
+                                                                     }
+                                                       )
+            logger.info('fcm推送结果:{}'.format(result))
+        except Exception as e:
+            return repr(e)
+
+    @staticmethod
+    def android_jpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text):
+        """
+        android 极光 推送
+        @param nickname: 设备昵称
+        @param app_bundle_id: app包id
+        @param token_val: 推送token
+        @param n_time: 当前时间
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @return: None
+        """
+        try:
+            app_key = JPUSH_CONFIG[app_bundle_id]['Key']
+            master_secret = JPUSH_CONFIG[app_bundle_id]['Secret']
+            # 换成各自的app_key和master_secret
+            _jpush = jpush.JPush(app_key, master_secret)
+            push = _jpush.create_push()
+            push.audience = jpush.registration_id(token_val)
+            push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname
+                         }
+            android = jpush.android(title=msg_title, big_text=msg_text, alert=msg_text, extras=push_data,
+                                    priority=1, style=1, alert_type=7
+                                    )
+            push.notification = jpush.notification(android=android)
+            push.platform = jpush.all_
+            res = push.send()
+            assert res.status_code == 200
+        except Exception as e:
+            return repr(e)
+
+    @staticmethod
+    def android_xmpush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
+                       uid='', channel='1', image=''):
+        """
+        android 小米 推送
+        @param nickname: 设备昵称
+        @param app_bundle_id: app包id
+        @param token_val: 推送token
+        @param n_time: 当前时间
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param uid: uid
+        @param channel: 通道
+        @param image: 推送图片链接
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            url = 'https://api.xmpush.xiaomi.com/v3/message/regid'
+            app_secret = XMPUSH_CONFIG[app_bundle_id]
+            payload = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                       'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                       'uid': uid, 'channel': channel
+                       }
+            data = {
+                'title': msg_title,
+                'description': msg_text,
+                'payload': 'payload',
+                'restricted_package_name': app_bundle_id,
+                'registration_id': token_val,
+            }
+            # if image:
+            #     data['extra.notification_style_type'] = 2
+            #     data['extra.notification_bigPic_uri'] = image
+            headers = {
+                'Authorization': 'key={}'.format(app_secret)
+            }
+            response = requests.post(url, data=data, headers=headers)
+            logger.info("小米推送返回值:{}".format(response.json()))
+            assert response.status_code == 200
+        except Exception as e:
+            return repr(e)
+
+    @staticmethod
+    def android_vivopush(token_val, n_time, event_type, msg_title, msg_text, app_bundle_id='', uid='', channel='1',
+                         image='', nickname='', appBundleId=''):
+        """
+        vivo 推送(不支持图片)
+        @param app_bundle_id: app包名
+        @param appBundleId: app包名
+        @param token_val: 推送token
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param n_time: 当前时间
+        @param nickname: 设备昵称
+        @param uid: uid
+        @param image: 推送图片链接
+        @param channel: 通道
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
+            # 获取redis里面的authToken
+            if msg_title == '':
+                msg_title = APP_BUNDLE_DICT[app_bundle_id]
+            app_id = VIVOPUSH_CONFIG[app_bundle_id]['ID']
+            app_key = VIVOPUSH_CONFIG[app_bundle_id]['Key']
+            app_secret = VIVOPUSH_CONFIG[app_bundle_id]['Secret']
+            sender = APISender(app_secret)
+            rec = sender.get_token(app_id, app_key)
+            # 鉴权接口调用获得authToken
+            sender_send = APISender(app_secret)
+            sender_send.set_token(rec['authToken'])
+            push_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1', 'image': image,
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                         'uid': uid, 'channel': channel
+                         }
+            #  获取唯一标识符
+            uid_push_qs = UidPushModel.objects.filter(token_val=token_val).values('m_code')
+            m_code = uid_push_qs[0]['m_code'] if uid_push_qs[0]['m_code'] else ''
+            # 推送 push_mode: 推送模式 (0:正式推送;1:测试推送,默认为0)
+            # 推送 event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
+            # 推送 skip_type: 跳转类型(1:打开 APP 首页 2:打开链接 3:自定义 4:打开 app 内指定页面)
+            message = PushMessage() \
+                .reg_id(token_val) \
+                .title(msg_title) \
+                .content(msg_text) \
+                .push_mode(1) \
+                .notify_type(3) \
+                .skip_type(1) \
+                .request_id(m_code) \
+                .classification(0) \
+                .client_custom_map(**push_data) \
+                .message_dict()
+            rec = sender_send.send(message)
+            logger.info('vivo推送结果:{}'.format(rec))
+            return rec
+        except Exception as e:
+            logger.info('vivo推送异常:{}'.format(e))
+
+    @staticmethod
+    def android_oppopush(nickname, app_bundle_id, token_val, n_time, event_type, msg_title, msg_text,
+                         uid='', channel='1', image=''):
+        """
+        android oppo 推送
+        @param nickname: 设备昵称
+        @param app_bundle_id: app包id
+        @param token_val: 推送token
+        @param n_time: 当前时间
+        @param event_type: 事件类型
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param uid: uid
+        @param channel: 通道
+        @param image: 推送图片链接
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            """
+            android 国内oppo APP消息提醒推送
+            """
+            app_key = OPPOPUSH_CONFIG[app_bundle_id]['Key']
+            master_secret = OPPOPUSH_CONFIG[app_bundle_id]['Secret']
+            url = 'https://api.push.oppomobile.com/'
+            now_time = str(round(time.time() * 1000))
+            # 1、实例化一个sha256对象
+            sha256 = hashlib.sha256()
+            # 2、调用update方法进行加密
+            sha256.update((app_key + now_time + master_secret).encode('utf-8'))
+            # 3、调用hexdigest方法,获取加密结果
+            sign = sha256.hexdigest()
+            # 获取auth_token
+            get_token_url = url + 'server/v1/auth'
+            post_data = {
+                'app_key': app_key,
+                'sign': sign,
+                'timestamp': now_time
+            }
+            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
+            response = requests.post(get_token_url, data=post_data, headers=headers)
+            result = response.json()
+            # 发送推送
+            push_url = url + 'server/v1/message/notification/unicast'
+            extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                         'uid': uid, 'channel': channel
+                         }
+            message = {
+                "target_type": 2,
+                "target_value": token_val,
+                "notification": {
+                    "title": msg_title,
+                    "content": msg_text,
+                }
+            }
+            push_data = {
+                'auth_token': result['data']['auth_token'],
+                'message': json.dumps(message)
+            }
+
+            response = requests.post(push_url, data=push_data, headers=headers)
+            logger.info("oppo推送返回值:{}".format(response.json()))
+            assert response.status_code == 200
+        except Exception as e:
+            return repr(e)
+
+    @staticmethod
+    def android_meizupush(token_val, n_time, event_type, msg_title, msg_text, uid='', channel='1',
+                          app_bundle_id='', appBundleId='', nickname='', image=''):
+        """
+        android 魅族推送(不支持图片)
+        @param app_bundle_id: app包名
+        @param appBundleId: app包名
+        @param token_val: 推送token
+        @param event_type: 消息类型 (0:运营类消息,1:系统类消息。默认为 0)
+        @param msg_title: 推送标题
+        @param msg_text: 推送内容
+        @param n_time: 当前时间
+        @param nickname: 设备昵称
+        @param uid: uid
+        @param image: 推送图片链接
+        @param channel: 通道
+        @return: None
+        """
+        logger = logging.getLogger('info')
+        try:
+            #  获取包和AppSecret
+            app_bundle_id = app_bundle_id if app_bundle_id != '' else appBundleId
+            appId = MEIZUPUSH_CONFIG[app_bundle_id]['ID']
+            appSecret = MEIZUPUSH_CONFIG[app_bundle_id]['AppSecret']
+            url = 'https://server-api-push.meizu.com/garcia/api/server/push/varnished/pushByPushId'
+            extra_data = {'alert': 'Motion', 'msg': '', 'sound': 'sound.aif', 'zpush': '1',
+                         'received_at': n_time, 'event_time': n_time, 'event_type': event_type, 'nickname': nickname,
+                         'uid': uid, 'channel': channel
+                         }
+            if msg_title == '':
+                msg_title = APP_BUNDLE_DICT[app_bundle_id]
+            # 拼接发送内容
+            messageJson = '{"clickTypeInfo":{"activity":"","clickType":0,"customAttribute":""},"extra":{},'
+            noticeBarInfo = ('"noticeBarInfo": {"title": "%s", "content":  "%s"},' % (msg_title, msg_text))
+            noticeExpandInfo = '"noticeExpandInfo":{"noticeExpandType":0},"pushTimeInfo":{"validTime":24}}'
+            messageJson += noticeBarInfo
+            messageJson += noticeExpandInfo
+            data_meizu = {
+                'appId': appId,
+                'pushIds': token_val,
+                'messageJson': messageJson
+            }
+            # 魅族MD5加密,生成密钥
+            sign = CommonService.getMD5Sign(data=data_meizu, key=appSecret)
+            data = {
+                'appId': appId,
+                'messageJson': messageJson,
+                'sign': sign,
+                'pushIds': token_val,
+            }
+            # 进行推送
+            response = requests.post(url, data=data)
+            logger.info("魅族推送结果:{}".format(response.json()))
+            return response.status_code
+        except Exception as e:
+            return repr(e)

+ 91 - 0
Service/VivoPushService/push_admin/APIConstants.py

@@ -0,0 +1,91 @@
+# coding=utf-8
+class Constants(object):
+    def __init__(self):
+        pass
+
+    def enum(**self):
+        return type('Enum', (), self)
+
+    __VERSION__ = '1.0'
+    __HTTP_GET__ = 0
+    __HTTP_POST__ = 1
+
+    _METHOD_MAP = {'GET': __HTTP_GET__, 'POST': __HTTP_POST__}
+
+    http_server = "https://api-push.vivo.com.cn"
+
+    '''
+        targetMessage parameter name
+    '''
+
+    http_param_reg_ids = "regIds"
+    http_param_aliases = "aliases"
+    http_param_task_id = "taskId"
+
+    '''
+        pushMessage parameter name
+    '''
+    http_param_tag_expression = "tagExpression"
+    http_param_or_tags = "orTags"
+    http_param_and_tags = "andTags"
+    http_param_not_tags = "notTags"
+    http_param_segment_name = "segmentName"
+    http_param_reg_id = "regId"
+    http_param_content = "content"
+    http_param_title = "title"
+    http_param_ali_as = "alias"
+    http_param_notify_type = "notifyType"
+    http_param_time_to_live = "timeToLive"
+    http_param_skip_type = "skipType"
+    http_param_skip_content = "skipContent"
+    http_param_network_type = "networkType"
+    http_param_client_custom_map = "clientCustomMap"
+    http_param_extra = "extra"
+    http_param_callback = "callback"
+    http_param_callback_param = "callback.param"
+
+    http_param_request_id = "requestId"
+
+    http_param_classification = "classification"
+
+    http_param_push_mode = "pushMode"
+
+
+    http_param_task_ids = "taskIds"
+
+    '''
+        tagMessage parameter name
+    '''
+    http_param_name = "name"
+    http_param_old_name = "oldName"
+    http_param_new_name = "newName"
+    http_param_desc = "desc"
+    http_param_group = "group"
+    http_param_type = "type"
+    http_param_ids = "ids"
+
+    http_param_tag_list = "tagList"
+
+    http_param_expression = "expression"
+
+    request_path = enum(
+        GET_TOKEN=["/message/auth"],
+        PUSH_TO_SINGLE=['/message/send'],
+        SAVE_LIST_PAYLOAD=['/message/saveListPayload '],
+        PUSH_TO_LIST=['/message/pushToList'],
+        PUSH_TO_ALL=['/message/all'],
+        PUSH_TO_TAG=['/message/tagPush'],
+        GET_STATISTICS=['/report/getStatistics'],
+
+        ADD_TAG=['/tag/add'],
+        UPDATE_TAG=['/tag/update'],
+        ADD_MEMBERS=['/tag/addMembers'],
+        REMOVE_MEMBERS=['/tag/removeMembers'],
+
+        ADD_TAG_GROUP=['/tagGroup/add'],
+        UPDATE_TAG_GROUP=['/tagGroup/update'],
+        ADD_TAG_TO_GROUP=['/tagGroup/addToGroup'],
+
+        ADD_TAG_SEGMENT=['/tagSegment/add'],
+        UPDATE_TAG_SEGMENT=['/tagSegment/update'],
+    )

+ 13 - 0
Service/VivoPushService/push_admin/APIError.py

@@ -0,0 +1,13 @@
+
+class APIError(Exception):
+    """
+    raise APIError if receiving json message indicating failure.
+    """
+    def __init__(self, error_code, error, request):
+        self.error_code = error_code
+        self.error = error
+        self.request = request
+        Exception.__init__(self, error)
+
+    def __str__(self):
+        return 'APIError: %s: %s, request: %s' % (self.error_code, self.error, self.request)

+ 103 - 0
Service/VivoPushService/push_admin/APIMessage.py

@@ -0,0 +1,103 @@
+# coding=utf-8
+from Service.VivoPushService.push_admin.APIConstants import Constants
+
+
+class MessageDict(dict):
+    def __getattr__(self, item):
+        try:
+            return self[item]
+        except KeyError:
+            raise AttributeError(r"'message' object has no attribute %s'" % item)
+
+    def __setattr__(self, key, value):
+        self[key] = value
+
+class PushMessage(object):
+    tag_expression={}
+
+    def __init__(self):
+        self.__message_dict = MessageDict()
+
+    def or_tags(self,or_tags):
+        PushMessage.tag_expression[Constants.http_param_or_tags]=or_tags
+        self.__message_dict[Constants.http_param_tag_expression] = PushMessage.tag_expression
+        return self
+
+    def and_tags(self,and_tags):
+        PushMessage.tag_expression[Constants.http_param_and_tags]=and_tags
+        self.__message_dict[Constants.http_param_tag_expression] = PushMessage.tag_expression
+        return self
+
+    def not_tags(self,not_tags):
+        PushMessage.tag_expression[Constants.http_param_not_tags]=not_tags
+        self.__message_dict[Constants.http_param_tag_expression] = PushMessage.tag_expression
+        return self
+
+    def segment_name(self, segment_name):
+        self.__message_dict[Constants.http_param_segment_name] = segment_name
+        self.__message_dict[Constants.http_param_tag_expression] = PushMessage.tag_expression
+        return self
+
+    def reg_id(self, reg_id):
+        self.__message_dict[Constants.http_param_reg_id] = reg_id
+        return self
+
+    def ali_as(self, ali_as):
+        self.__message_dict[Constants.http_param_ali_as] = ali_as
+        return self
+
+    def content(self, content):
+        self.__message_dict[Constants.http_param_content] = content
+        return self
+
+    def title(self, title):
+        self.__message_dict[Constants.http_param_title] = title
+        return self
+
+    def notify_type(self, notify_type):
+        self.__message_dict[Constants.http_param_notify_type] = notify_type
+        return self
+
+    def time_to_live(self, time_to_live):
+        self.__message_dict[Constants.http_param_time_to_live] = time_to_live
+        return self
+
+    def skip_type(self, skip_type):
+        self.__message_dict[Constants.http_param_skip_type] = skip_type
+        return self
+
+    def skip_content(self, skip_content):
+        self.__message_dict[Constants.http_param_skip_content] = skip_content
+        return self
+
+    def network_type(self, network_type=-1):
+        self.__message_dict[Constants.http_param_network_type] = network_type
+        return self
+
+    def request_id(self, request_id):
+        self.__message_dict[Constants.http_param_request_id] = request_id
+        return self
+
+    def classification(self, classification):
+        self.__message_dict[Constants.http_param_classification] = classification
+        return self
+
+    def push_mode(self, push_mode):
+        self.__message_dict[Constants.http_param_push_mode] = push_mode
+        return self
+
+    def client_custom_map(self, **client_custom_map):
+        self.__message_dict[Constants.http_param_client_custom_map] = client_custom_map
+        return self
+
+    def extra(self, value1, value2):
+        extra = {Constants.http_param_callback: value1, Constants.http_param_callback_param: value2}
+        self.__message_dict[Constants.http_param_extra] = extra
+        return self
+
+    '''
+        message params build method
+    '''
+
+    def message_dict(self):
+        return self.__message_dict

+ 73 - 0
Service/VivoPushService/push_admin/APISender.py

@@ -0,0 +1,73 @@
+# coding=utf-8
+import time
+
+from Service.VivoPushService.push_admin.APIConstants import Constants
+from Service.VivoPushService.push_admin.APISenderBase import Base
+from Service.VivoPushService.push_admin.APISignUtil import SignUtil
+
+_BROADCAST_TOPIC_MAX = 5
+_TOPIC_SPLITTER = ';$;'
+
+
+class APISender(Base):
+    """
+    发送消息API(send push message class)
+    构造方法接收两个参数:
+    @:param secret 必填 - APP_SECRET
+    @:param token 可选 - authToken,发送消息时需带该参数以进行鉴权操作,调用鉴权接口获得
+    """
+
+    def get_token(self, app_id, app_key):
+        timestamp = int(round(time.time() * 1000))
+        sign = SignUtil(app_id, app_key, self.secret).sign_util(timestamp)
+        get_token = {'appId': app_id, 'appKey': app_key, 'timestamp': timestamp, 'sign': sign}
+        return self._try_http_request(Constants.request_path.GET_TOKEN, retry_times=3, **get_token)
+
+    def send(self, push_message, retry_times=3):
+        """
+        发送单推消息
+        :param push_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.PUSH_TO_SINGLE, retry_times, **push_message)
+
+    def save_list_payload(self, push_message, retry_times=3):
+        """
+        保存群推消息
+        :param push_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.SAVE_LIST_PAYLOAD, retry_times, **push_message)
+
+    def send_to_list(self, target_push_message, retry_times=3):
+        """
+        推送群推消息
+        :param target_push_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.PUSH_TO_LIST, retry_times, **target_push_message)
+
+    def send_to_all(self, push_message, retry_times=3):
+        """
+        发送全推消息
+        :param push_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.PUSH_TO_ALL, retry_times, **push_message)
+
+    def send_to_tag(self, push_message, retry_times=3):
+        """
+        发送标签推消息
+        :param push_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.PUSH_TO_TAG,retry_times,**push_message)
+
+    def get_statistics(self, taskids_message, retry_times=3):
+        """
+        发送全推消息
+        :param taskids_message: 消息体(请求参数对象)
+        :param retry_times: 重试次数
+        """
+        return self._try_http_request(Constants.request_path.GET_STATISTICS, retry_times, Constants.__HTTP_GET__,
+                                      **taskids_message)

+ 131 - 0
Service/VivoPushService/push_admin/APISenderBase.py

@@ -0,0 +1,131 @@
+import logging
+import time
+import json
+import urllib.request, urllib.parse, urllib.error
+import urllib.request, urllib.error, urllib.parse
+
+from Service.VivoPushService.push_admin.APIConstants import Constants
+from Service.VivoPushService.push_admin.APIError import APIError
+
+_MAX_BACKOFF_DELAY = 1024000
+
+
+class JsonDict(dict):
+    def __getattr__(self, item):
+        try:
+            return self[item]
+        except KeyError:
+            raise AttributeError(r"'JsonDict' object has no attribute %s'" % item)
+
+    def __setattr__(self, key, value):
+        self[key] = value
+
+
+def _parse_json(body):
+    """
+    convert json object to python object
+    :param body: response data
+    """
+    def _obj_hook(pairs):
+        o = JsonDict()
+        for k, v in pairs.items():
+            o[str(k)] = v
+        return o
+
+    return json.loads(body, object_hook=_obj_hook)
+
+
+def _build_request_url(request_path):
+    return Constants.http_server + request_path[0]
+
+
+def _http_call(url, method, token, **message):
+    """
+    :param url: http request url
+    :param method: http request method
+    :param message: params
+    """
+    params = _encode_params(message) if method == Constants.__HTTP_GET__ else ''
+    http_url = '%s?%s' % (url, params) if method == Constants.__HTTP_GET__ else url
+    http_body = None if method == Constants.__HTTP_GET__ else message
+    req = urllib.request.Request(http_url, data=json.dumps(http_body).encode("utf-8"))
+    if token:
+        req.add_header('authToken', token)
+    req.add_header('Content-Type', 'application/json;charset=UTF-8')
+    try:
+        resp = urllib.request.urlopen(req, timeout=5)
+        r = _parse_json(resp.read().decode())
+        return r
+    except urllib.error.URLError as e:
+        raise APIError('-5', e.reason, 'http error ' + str(e.code))
+    except BaseException as e:
+        raise e
+
+def _encode_params(kw):
+    """
+    splic get request url
+    :param kw: params
+    """
+    args = ''
+    s = ''
+    for k, v in kw.items():
+        for t in v:
+            s = s+ str(t) + ','
+        args = '%s=%s' %(k, s[:-1])
+    return args
+
+class Base(object):
+    def __init__(self, secret, token=None):
+        self.secret = secret
+        self.token = token
+
+    def set_token(self, token):
+        self.token = token
+
+    def _http_request(self, request_path, method, **message):
+        """
+        :param request_path: http interface
+        :param method: GET|POST
+        :param message: params
+        """
+        request_url = _build_request_url(request_path)
+        try:
+            ret = _http_call(request_url, method, self.token, **message)
+            return ret
+        except APIError as ex:
+            logging.error("%s request: [%s] error [%s]" % (Constants.http_server, request_url, ex))
+            raise ex
+
+    def http_post(self, request_path, **message):
+        logging.info("POST %s" % request_path[0])
+        return self._http_request(request_path, Constants.__HTTP_POST__, **message)
+
+    def http_get(self, request_path, **message):
+        logging.info("GET %s" % request_path[0])
+        return self._http_request(request_path, Constants.__HTTP_GET__, **message)
+
+    def _try_http_request(self, request_path, retry_times, method=Constants.__HTTP_POST__, **message):
+        is_fail, try_time, result, sleep_time = True, 0, None, 1
+        while is_fail and try_time < retry_times:
+            try:
+                if method == Constants.__HTTP_POST__:
+                    result = self.http_post(request_path, **message)
+                elif method == Constants.__HTTP_GET__:
+                    result = self.http_get(request_path, **message)
+                else:
+                    raise APIError('-2', 'not support %s http request' % method, 'http error')
+                is_fail = False
+            except APIError as ex:
+                '''
+                    failure retry
+                '''
+                if ex.error_code == '-5':
+                    is_fail = True
+                try_time += 1
+                logging.error('code:[%s] - description:[%s] - reason:[%s] - try_time:[%s]' % (ex.error_code, ex.error, ex.request, try_time))
+                time.sleep(sleep_time)
+                if 2 * sleep_time < _MAX_BACKOFF_DELAY:
+                    sleep_time *= 2
+        if not result:
+            raise APIError('-3', 'retry %s time failure' % retry_times, 'request error')
+        return result

+ 17 - 0
Service/VivoPushService/push_admin/APISignUtil.py

@@ -0,0 +1,17 @@
+# coding=utf-8
+import  hashlib
+
+
+class SignUtil:
+    def __init__(self, app_id, app_key, app_secret):
+        self.app_id = app_id
+        self.app_key = app_key
+        self.app_secret = app_secret
+
+    def sign_util(self, timestamp):
+        m = hashlib.md5()
+        m.update(self.app_id.encode("UTF-8"))
+        m.update(self.app_key.encode("UTF-8"))
+        m.update(str(timestamp).encode("UTF-8"))
+        m.update(self.app_secret.encode("UTF-8"))
+        return m.hexdigest()

+ 0 - 0
Service/VivoPushService/push_admin/__init__.py


+ 1 - 1
cn_formal_manage.py

@@ -10,7 +10,7 @@ if __name__ == '__main__':
     if arg_m == 'migrate':
     if arg_m == 'migrate':
         print('do not migrate')
         print('do not migrate')
         exit()
         exit()
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.cn_formal_settings')
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.cn_config.cn_formal_settings')
     try:
     try:
         from django.core.management import execute_from_command_line
         from django.core.management import execute_from_command_line
     except ImportError as exc:
     except ImportError as exc:

+ 1 - 1
eur_formal_manage.py

@@ -10,7 +10,7 @@ if __name__ == '__main__':
     if arg_m == 'migrate':
     if arg_m == 'migrate':
         print('do not migrate')
         print('do not migrate')
         exit()
         exit()
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.eur_formal_settings')
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.eur_config.eur_formal_settings')
     try:
     try:
         from django.core.management import execute_from_command_line
         from django.core.management import execute_from_command_line
     except ImportError as exc:
     except ImportError as exc:

+ 1 - 1
formal_manage.py

@@ -10,7 +10,7 @@ if __name__ == '__main__':
     if arg_m == 'migrate':
     if arg_m == 'migrate':
         print('do not migrate')
         print('do not migrate')
         exit()
         exit()
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.formal_settings')
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.us_config.formal_settings')
     try:
     try:
         from django.core.management import execute_from_command_line
         from django.core.management import execute_from_command_line
     except ImportError as exc:
     except ImportError as exc:

+ 1 - 1
local_manage.py

@@ -10,7 +10,7 @@ if __name__ == '__main__':
     if arg_m == 'migrate':
     if arg_m == 'migrate':
         print('do not migrate')
         print('do not migrate')
         exit()
         exit()
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.local_settings')
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.dev_config.local_settings')
     try:
     try:
         from django.core.management import execute_from_command_line
         from django.core.management import execute_from_command_line
     except ImportError as exc:
     except ImportError as exc:

+ 68 - 216
requirements.txt

@@ -1,224 +1,76 @@
-alabaster==0.7.12
-anaconda-client==1.7.2
-anaconda-navigator==1.9.7
-anaconda-project==0.8.3
+aliyun-python-sdk-core==2.13.36
+aliyun-python-sdk-core-v3==2.13.33
+aliyun-python-sdk-kms==2.16.0
+apns2-client==0.5.4
 asgiref==3.2.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
+asn1crypto==0.24.0
+blinker==1.4
+boto3==1.11.16
+botocore==1.14.17
+certifi==2022.12.7
+cffi==1.15.1
+chardet==5.1.0
+charset-normalizer==2.0.12
+configobj==5.0.6
+crcmod==1.7
+cryptography==39.0.2
 Django==3.0.3
 Django==3.0.3
-django-appconf==1.0.3
+django-appconf==1.0.5
 django-cors-headers==3.2.1
 django-cors-headers==3.2.1
-django-imagekit==4.0.2
-django-tinymce==2.8.0
+django-imagekit==4.1.0
 docutils==0.15.2
 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
+grpcio==1.52.0
+h2==2.6.2
+hpack==3.0.0
+httplib2==0.9.2
+hyper==0.7.0
+hyperframe==3.2.0
+idna==3.4
+ipip-ipdb==1.3.2
+jmespath==0.9.4
+jpush==3.3.8
+jsonpatch==1.16
+jsonpointer==1.10
+keyrings.alt==3.0
 mysqlclient==1.4.6
 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
+numpy==1.21.6
+oauthlib==2.0.6
+oss2==2.9.1
+packaging==21.3
+paypalrestsdk==1.13.1
+pendulum==2.1.2
+pexpect==4.2.1
 pilkit==2.0
 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
+Pillow==9.4.0
+protobuf==3.19.6
+ptyprocess==0.7.0
+pyasn1==0.4.8
+pycparser==2.21
+pycryptodome==3.17
+pyfcm==1.4.7
+pyipip==0.1.1
+PyJWT==1.5.3
+pyOpenSSL==23.0.0
+pyparsing==3.0.9
+pyserial==3.4
+python-dateutil==2.8.1
+python-debian==0.1.32
+python-rapidjson==1.9
 pytz==2019.3
 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
+pytzdata==2020.1
+pyxdg==0.25
+redis==3.4.1
+requests==2.28.2
+requests-unixsocket==0.1.5
+s3transfer==0.3.7
+SecretStorage==2.3.1
+simplejson==3.17.0
+six==1.11.0
 sqlparse==0.3.0
 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
+ssh-import-id==5.7
+supervisor==4.1.0
+tritonclient==2.30.0
+typing_extensions==4.5.0
+ujson==5.7.0
+urllib3==1.25.11
+var-dump==1.2

+ 1 - 1
test_manage.py

@@ -10,7 +10,7 @@ if __name__ == '__main__':
     if arg_m == 'migrate':
     if arg_m == 'migrate':
         print('do not migrate')
         print('do not migrate')
         exit()
         exit()
-    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_settings')
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AnsjerPush.test_config.test_settings')
     try:
     try:
         from django.core.management import execute_from_command_line
         from django.core.management import execute_from_command_line
     except ImportError as exc:
     except ImportError as exc: