Эх сурвалжийг харах

Merge branch 'test' of http://192.168.136.99:3000/servers/ASJServer

locky 3 жил өмнө
parent
commit
548918967d

+ 34 - 4
AdminController/DeviceManagementController.py

@@ -16,7 +16,7 @@ from Service.EquipmentInfoService import EquipmentInfoService
 from Service.ModelService import ModelService
 from Service.ModelService import ModelService
 from Service.CommonService import CommonService
 from Service.CommonService import CommonService
 from Model.models import Device_Info, UidSetModel, LogModel, UID_Bucket, Unused_Uid_Meal, Order_Model, StsCrdModel, \
 from Model.models import Device_Info, UidSetModel, LogModel, UID_Bucket, Unused_Uid_Meal, Order_Model, StsCrdModel, \
-    VodHlsModel, ExperienceContextModel, DeviceTypeModel, Equipment_Info, UidUserModel
+    VodHlsModel, ExperienceContextModel, DeviceTypeModel, Equipment_Info, UidUserModel, ExperienceAiModel, AiService
 
 
 
 
 class DeviceManagement(View):
 class DeviceManagement(View):
@@ -51,8 +51,10 @@ class DeviceManagement(View):
                 return self.getDeviceInfoList(request_dict, response)
                 return self.getDeviceInfoList(request_dict, response)
             elif operation == 'deleteDevice':
             elif operation == 'deleteDevice':
                 return self.deleteDevice(request_dict, response)
                 return self.deleteDevice(request_dict, response)
-            elif operation == 'resetVod':
+            elif operation == 'resetVod':   # 重置云存
                 return self.resetVod(request, request_dict, response)
                 return self.resetVod(request, request_dict, response)
+            elif operation == 'resetAi':    # 重置AI
+                return self.reset_ai(request, request_dict, response)
             elif operation == 'resetPrimaryUser':
             elif operation == 'resetPrimaryUser':
                 return self.resetPrimaryUser(request, request_dict, response)
                 return self.resetPrimaryUser(request, request_dict, response)
             elif operation == 'getDeviceTypeList':
             elif operation == 'getDeviceTypeList':
@@ -182,14 +184,14 @@ class DeviceManagement(View):
                 'time': int(time.time()),
                 'time': int(time.time()),
                 'url': 'deviceManagement/resetVod',
                 'url': 'deviceManagement/resetVod',
                 'content': json.dumps(content),
                 'content': json.dumps(content),
-                'operation': '{}重置设备云存'.format(uid),
+                'operation': '{}重置云存'.format(uid),
             }
             }
             with transaction.atomic():
             with transaction.atomic():
                 LogModel.objects.create(**log)
                 LogModel.objects.create(**log)
                 # 删除和更新设备云存相关数据
                 # 删除和更新设备云存相关数据
                 UID_Bucket.objects.filter(uid=uid).delete()
                 UID_Bucket.objects.filter(uid=uid).delete()
                 Unused_Uid_Meal.objects.filter(uid=uid).delete()
                 Unused_Uid_Meal.objects.filter(uid=uid).delete()
-                Order_Model.objects.filter(UID=uid).delete()
+                Order_Model.objects.filter(UID=uid, order_type=0).delete()
                 StsCrdModel.objects.filter(uid=uid).delete()
                 StsCrdModel.objects.filter(uid=uid).delete()
                 VodHlsModel.objects.filter(uid=uid).delete()
                 VodHlsModel.objects.filter(uid=uid).delete()
                 ExperienceContextModel.objects.filter(uid=uid).delete()
                 ExperienceContextModel.objects.filter(uid=uid).delete()
@@ -199,6 +201,34 @@ class DeviceManagement(View):
             print(e)
             print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
+    @staticmethod
+    def reset_ai(request, request_dict, response):
+        uid = request_dict.get('uid', None)
+        if not uid:
+            return response.json(444)
+        try:
+            # 记录操作日志
+            ip = CommonService.get_ip_address(request)
+            content = json.loads(json.dumps(request_dict))
+            log = {
+                'ip': ip,
+                'user_id': 2,
+                'status': 200,
+                'time': int(time.time()),
+                'url': 'deviceManagement/resetAi',
+                'content': json.dumps(content),
+                'operation': '{}重置AI'.format(uid),
+            }
+            with transaction.atomic():
+                LogModel.objects.create(**log)
+                # 删除和更新设备AI相关数据
+                ExperienceAiModel.objects.filter(uid=uid).delete()
+                AiService.objects.filter(uid=uid).delete()
+            return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(500, repr(e))
+
     # 获取设备类型数据
     # 获取设备类型数据
     def getDeviceTypeList(self, request_dict, response):
     def getDeviceTypeList(self, request_dict, response):
         name = request_dict.get('name', None)
         name = request_dict.get('name', None)

+ 5 - 28
AdminController/LogManagementController.py

@@ -154,8 +154,9 @@ class LogManagementView(View):
             print(e)
             print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
-    def requestPublishMqtt(self, request_dict, response):
-        # 通用发布MQTT主题通知
+    # 通用发布MQTT通知
+    @staticmethod
+    def requestPublishMqtt(request_dict, response):
         msg = request_dict.get('msg', None)
         msg = request_dict.get('msg', None)
         thing_name = request_dict.get('thing_name', None)
         thing_name = request_dict.get('thing_name', None)
         topic_name = request_dict.get('topic_name', None)
         topic_name = request_dict.get('topic_name', None)
@@ -163,34 +164,10 @@ class LogManagementView(View):
             return response.json(444)
             return response.json(444)
 
 
         try:
         try:
-            # 获取数据组织将要请求的url
-            iot = iotdeviceInfoModel.objects.filter(
-                thing_name=thing_name).values(
-                'endpoint', 'token_iot_number')
-            if not iot.exists():
-                return response.json(10043)
-            endpoint = iot[0]['endpoint']
-            Token = iot[0]['token_iot_number']
-
-            # api doc: https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/http.html
-            # url: https://IoT_data_endpoint/topics/url_encoded_topic_name?qos=1
-            # post请求url发布MQTT消息
-            url = 'https://{}/topics/{}'.format(endpoint, topic_name)
-            authorizer_name = 'Ansjer_Iot_Auth'
-            signature = CommonService.rsa_sign(Token)  # Token签名
-            headers = {
-                'x-amz-customauthorizer-name': authorizer_name,
-                'Token': Token,
-                'x-amz-customauthorizer-signature': signature}
             msg = eval(msg)
             msg = eval(msg)
-            r = requests.post(url=url, headers=headers, json=msg, timeout=2)
-            if r.status_code == 200:
-                res = r.json()
-                if res['message'] == 'OK':
-                    return response.json(0)
-                return response.json(10044)
-            else:
+            if not CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg):
                 return response.json(10044)
                 return response.json(10044)
+            return response.json(0)
         except Exception as e:
         except Exception as e:
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 

+ 4 - 4
Ansjer/local_config/local_settings.py

@@ -97,16 +97,16 @@ WSGI_APPLICATION = 'Ansjer.local_config.local_wsgi.application'
 # DATABASES_PASS = 'UKv78ezQhiGMmSef5U5s'
 # DATABASES_PASS = 'UKv78ezQhiGMmSef5U5s'
 
 
 # 本地数据库
 # 本地数据库
-DATABASE_DATA = 'ansjer_test'
+DATABASE_DATA = 'ansjer'
 SERVER_HOST = '127.0.0.1'
 SERVER_HOST = '127.0.0.1'
 DATABASES_USER = 'root'
 DATABASES_USER = 'root'
-DATABASES_PASS = 'root'
+DATABASES_PASS = '123456'
 
 
 # 推送数据库
 # 推送数据库
-DATABASE_DATA2 = 'ansjerpush'
+DATABASE_DATA2 = 'push'
 SERVER_HOST2 = '127.0.0.1'
 SERVER_HOST2 = '127.0.0.1'
 DATABASES_USER2 = 'root'
 DATABASES_USER2 = 'root'
-DATABASES_PASS2 = 'root'
+DATABASES_PASS2 = '123456'
 
 
 # 序列号公共数据库
 # 序列号公共数据库
 # DATABASE_DATA3 = 'ansjer_test'
 # DATABASE_DATA3 = 'ansjer_test'

+ 20 - 0
Ansjer/server_urls/loocam_url.py

@@ -0,0 +1,20 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : loocam_url.py
+@Time    : 2022/5/20 11:44
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+from django.urls import re_path
+
+from SensorGateway import EquipmentFamilyController, GatewayFamilyRoomController, GatewayFamilyMemberController, \
+    SubDeviceController
+
+urlpatterns = [
+    re_path(r'^sensor/gateway/(?P<operation>.*)$', EquipmentFamilyController.EquipmentFamilyView.as_view()),
+    re_path(r'^gateway/family/room/(?P<operation>.*)$', GatewayFamilyRoomController.GatewayFamilyRoomView.as_view()),
+    re_path(r'^gateway/family/member/(?P<operation>.*)$',
+            GatewayFamilyMemberController.GatewayFamilyMemberView.as_view()),
+    re_path(r'^gateway/subdevice/(?P<operation>.*)$', SubDeviceController.GatewaySubDeviceView.as_view()),
+]

+ 14 - 6
Ansjer/urls.py

@@ -1,7 +1,10 @@
-from django.contrib import admin
 from django.conf.urls import url
 from django.conf.urls import url
+from django.contrib import admin
 from django.urls import path, re_path
 from django.urls import path, re_path
 
 
+from AdminController import UserManageController, RoleController, MenuController, TestServeController, \
+    ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController, \
+    AiServeController, SurveysManageController, SerialManageController
 from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppInfo, \
 from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppInfo, \
     AccessLog, DynamoDBLog, Test, MealManage, DeviceManage, EquipmentStatus, SysManage, DeviceLog, LogAccess, \
     AccessLog, DynamoDBLog, Test, MealManage, DeviceManage, EquipmentStatus, SysManage, DeviceLog, LogAccess, \
     AppColophon, DateController, \
     AppColophon, DateController, \
@@ -20,12 +23,11 @@ from Controller import FeedBack, EquipmentOTA, EquipmentInfo, AdminManage, AppIn
     RegionController, VPGController, LanguageController, TestController, DeviceConfirmRegion, S3GetStsController, \
     RegionController, VPGController, LanguageController, TestController, DeviceConfirmRegion, S3GetStsController, \
     DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, PaymentCycle, \
     DetectControllerV2, ShadowController, TestDetectController, PcInfo, PctestController, DeviceDebug, PaymentCycle, \
     DeviceLogController, CouponController, AiController
     DeviceLogController, CouponController, AiController
-from Controller.Surveys import CloudStorageController
 from Controller.Cron import CronTaskController
 from Controller.Cron import CronTaskController
-from AdminController import UserManageController, RoleController, MenuController, TestServeController, \
-    ServeManagementController, LogManagementController, DeviceManagementController, VersionManagementController, \
-    AiServeController, SurveysManageController, SerialManageController
-from SensorGateway import SensorGatewayController
+from Controller.MessagePush import EquipmentMessagePush
+from Controller.Surveys import CloudStorageController
+from SensorGateway import SensorGatewayController, EquipmentFamilyController
+from django.urls import include
 
 
 urlpatterns = [
 urlpatterns = [
     url(r'^testApi/(?P<operation>.*)$', TestApi.testView.as_view()),
     url(r'^testApi/(?P<operation>.*)$', TestApi.testView.as_view()),
@@ -242,6 +244,8 @@ urlpatterns = [
 
 
     # AI服务
     # AI服务
     url(r'^AiService/(?P<operation>.*)$', AiController.AiView.as_view()),
     url(r'^AiService/(?P<operation>.*)$', AiController.AiView.as_view()),
+    # 消息提醒
+    url(r'^app/setting/notification/(?P<operation>.*)$', EquipmentMessagePush.EquipmentMessagePushView.as_view()),
 
 
     # 新增解密的接口
     # 新增解密的接口
     url(r'^v3/account/changePwd$', UserController.v3ChangePwdView.as_view()),
     url(r'^v3/account/changePwd$', UserController.v3ChangePwdView.as_view()),
@@ -359,6 +363,10 @@ urlpatterns = [
     # 问卷调查
     # 问卷调查
     url(r'^api/surveys/(?P<operation>.*)$', CloudStorageController.CloudStorageView.as_view()),
     url(r'^api/surveys/(?P<operation>.*)$', CloudStorageController.CloudStorageView.as_view()),
 
 
+    # 网关家庭模块
+    url(r'^app/sensor/gateway/(?P<operation>.*)$', EquipmentFamilyController.EquipmentFamilyView.as_view()),
+    url(r'^loocam/', include("Ansjer.server_urls.loocam_url")),
+
     # 传感器网关
     # 传感器网关
     re_path('sensorGateway/(?P<operation>.*)', SensorGatewayController.SensorGateway.as_view()),
     re_path('sensorGateway/(?P<operation>.*)', SensorGatewayController.SensorGateway.as_view()),
 
 

+ 197 - 116
Controller/AiController.py

@@ -11,62 +11,37 @@
 @file: cloudstorage.py
 @file: cloudstorage.py
 @Contact: chanjunkai@163.com
 @Contact: chanjunkai@163.com
 """
 """
-import base64
-import json
+import logging
 import os
 import os
 import time
 import time
-import glob
-import urllib
 from urllib.parse import quote, parse_qs, unquote
 from urllib.parse import quote, parse_qs, unquote
 
 
 import apns2
 import apns2
 import boto3
 import boto3
+import botocore
 import jpush
 import jpush
-import oss2
 import paypalrestsdk
 import paypalrestsdk
-import threading
-import calendar
-import datetime
-import logging
-import sys
-import requests
-from aliyunsdkcore import client
-from aliyunsdksts.request.v20150401 import AssumeRoleRequest
 from boto3.session import Session
 from boto3.session import Session
-from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
+from botocore import client
 from django.db import transaction
 from django.db import transaction
+from django.db.models import Q, F, Sum
+from django.http import HttpResponseRedirect, HttpResponse
 from django.views.generic.base import View
 from django.views.generic.base import View
-import jwt
-from Object.ETkObject import ETkObject
 from pyfcm import FCMNotification
 from pyfcm import FCMNotification
-from Ansjer.config import OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET, OSS_ROLE_ARN, SERVER_DOMAIN, PAYPAL_CRD, \
-    SERVER_DOMAIN_SSL, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ARN, APNS_MODE, APNS_CONFIG, BASE_DIR, \
-    JPUSH_CONFIG, FCM_CONFIG, OAUTH_ACCESS_TOKEN_SECRET, DETECT_PUSH_DOMAINS
-from Controller.CheckUserData import DataValid
-from Model.models import Device_Info, Order_Model, Store_Meal, VodHlsModel, OssCrdModel, UID_Bucket, StsCrdModel, \
-    ExperienceContextModel, Pay_Type, CDKcontextModel, Device_User, SysMassModel, SysMsgModel, UidPushModel, \
-    Unused_Uid_Meal, UIDMainUser, UserModel, PromotionRuleModel, VideoPlaybackTimeModel, CloudLogModel, CouponModel, \
-    AiStoreMeal, AiService, UidSetModel, Ai_Push_Info, iotdeviceInfoModel, AiProcessTime, Equipment_Info
-from Object.AWS.S3Email import S3Email
+
+from Ansjer.config import PAYPAL_CRD, \
+    SERVER_DOMAIN_SSL, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, APNS_MODE, APNS_CONFIG, BASE_DIR, \
+    JPUSH_CONFIG, FCM_CONFIG, DETECT_PUSH_DOMAINS
+from Model.models import Device_Info, Order_Model, ExperienceAiModel, Pay_Type, CDKcontextModel, UidPushModel, \
+    AiStoreMeal, AiService, UidSetModel, Ai_Push_Info
 from Object.AliPayObject import AliPayObject
 from Object.AliPayObject import AliPayObject
-from Object.AliSmsObject import AliSmsObject
+from Object.ETkObject import ETkObject
 from Object.RedisObject import RedisObject
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
 from Object.TokenObject import TokenObject
-from Object.UidTokenObject import UidTokenObject
-from Service.CommonService import CommonService
-from Object.m3u8generate import PlaylistGenerator
 from Object.WechatPayObject import WechatPayObject
 from Object.WechatPayObject import WechatPayObject
-from django.db.models import Q, F, Count, Sum
-from Controller.PaymentCycle import Paypal
-from decimal import Decimal
-from Ansjer.config import SERVER_TYPE
+from Service.CommonService import CommonService
 from Service.ModelService import ModelService
 from Service.ModelService import ModelService
-from Object import MergePic
-import boto3
-import botocore
-from botocore import client
-
 
 
 
 
 # AI服务
 # AI服务
@@ -106,7 +81,7 @@ class AiView(View):
             elif operation == 'getAiStatus':  # 获取AI开关状态
             elif operation == 'getAiStatus':  # 获取AI开关状态
                 return self.getAiStatus(userID, request_dict, response)
                 return self.getAiStatus(userID, request_dict, response)
             elif operation == 'commoditylist':  # 获取AI套餐列表
             elif operation == 'commoditylist':  # 获取AI套餐列表
-                return self.do_commodity_list(userID, request_dict, response)
+                return self.do_commodity_list(request_dict, response)
             elif operation == 'queryInfo':  # 查询消息列表
             elif operation == 'queryInfo':  # 查询消息列表
                 return self.queryInfo(userID, request_dict, response)
                 return self.queryInfo(userID, request_dict, response)
             elif operation == 'readInfo':  # 消息已读
             elif operation == 'readInfo':  # 消息已读
@@ -117,6 +92,8 @@ class AiView(View):
                 return self.do_querylist(userID, request_dict, response)
                 return self.do_querylist(userID, request_dict, response)
             elif operation == 'getUsingPackage':  # 获取当前使用的ai套餐
             elif operation == 'getUsingPackage':  # 获取当前使用的ai套餐
                 return self.getUsingPackage(request_dict, userID, response)
                 return self.getUsingPackage(request_dict, userID, response)
+            elif operation == 'experienceOrder':  # 体验AI套餐
+                return self.experience_order(request_dict, userID, response)
             else:
             else:
                 return response.json(414)
                 return response.json(414)
 
 
@@ -163,21 +140,23 @@ class AiView(View):
             if nowTime > endTime:
             if nowTime > endTime:
                 return response.json(10054)
                 return response.json(10054)
 
 
-            dvqs = Device_Info.objects.filter(userID_id=userID, UID=uid)
+            # 查询设备是否属于该用户
+            device_info_qs = Device_Info.objects.filter(userID_id=userID, UID=uid)
+            if not device_info_qs.exists():
+                return response.json(14)
             status = int(status)
             status = int(status)
             nowTime = int(time.time())
             nowTime = int(time.time())
-            if not dvqs.exists():
-                return response.json(14)
+
             uid_set_qs = UidSetModel.objects.filter(uid=uid)
             uid_set_qs = UidSetModel.objects.filter(uid=uid)
             if uid_set_qs.exists():
             if uid_set_qs.exists():
                 uid_set_id = uid_set_qs[0].id
                 uid_set_id = uid_set_qs[0].id
+                interval = uid_set_qs[0].new_detect_interval if not interval else interval
                 qs_data = {
                 qs_data = {
                     'updTime': nowTime,
                     'updTime': nowTime,
                 }
                 }
                 if interval:
                 if interval:
                     qs_data['detect_interval'] = int(interval)
                     qs_data['detect_interval'] = int(interval)
-                if detect_group:
-                    qs_data['detect_group'] = detect_group
+                    qs_data['detect_group'] = detect_group if detect_group else ''
                 uid_set_qs.update(**qs_data)
                 uid_set_qs.update(**qs_data)
             else:
             else:
                 qs_data = {
                 qs_data = {
@@ -187,23 +166,23 @@ class AiView(View):
                 }
                 }
                 if interval:
                 if interval:
                     qs_data['detect_interval'] = int(interval)
                     qs_data['detect_interval'] = int(interval)
-                if detect_group:
-                    qs_data['detect_group'] = detect_group
+                qs_data['detect_group'] = detect_group if detect_group else ''
                 # 添加设备配置
                 # 添加设备配置
                 uid_set_qs = UidSetModel.objects.create(**qs_data)
                 uid_set_qs = UidSetModel.objects.create(**qs_data)
                 uid_set_id = uid_set_qs.id
                 uid_set_id = uid_set_qs.id
 
 
-            qs_data['detect_status'] = status       # ai开关状态
+            qs_data['detect_status'] = status  # ai开关状态
             ai_service_qs.update(**qs_data)
             ai_service_qs.update(**qs_data)
-            topic_name = 'ansjer/generic/{}'.format(uid)
-            if status == 0:     # 关闭
+            thing_name = CommonService.query_serial_with_uid(uid)   # 存在序列号则为使用序列号作为物品名
+            topic_name = 'ansjer/generic/{}'.format(thing_name)
+            if status == 0:  # 关闭
                 # mqtt通知设备关闭AI识别功能
                 # mqtt通知设备关闭AI识别功能
                 msg = {'commandType': 'AIDisable'}
                 msg = {'commandType': 'AIDisable'}
-                req_success = CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
+                req_success = CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg)
                 if not req_success:
                 if not req_success:
                     return response.json(10044)
                     return response.json(10044)
                 return response.json(0)
                 return response.json(0)
-            elif status == 1:       # 开启
+            elif status == 1:  # 开启
                 uid_push_qs = UidPushModel.objects.filter(userID_id=userID, m_code=m_code, uid_set__uid=uid)
                 uid_push_qs = UidPushModel.objects.filter(userID_id=userID, m_code=m_code, uid_set__uid=uid)
 
 
                 if uid_push_qs.exists():
                 if uid_push_qs.exists():
@@ -237,7 +216,8 @@ class AiView(View):
                 etkObj = ETkObject(etk='')
                 etkObj = ETkObject(etk='')
                 etk = etkObj.encrypt(uid)
                 etk = etkObj.encrypt(uid)
 
 
-                aiIdentificationUrl = "{DETECT_PUSH_DOMAIN}AiService/identification".format(DETECT_PUSH_DOMAIN=DETECT_PUSH_DOMAINS)
+                aiIdentificationUrl = "{DETECT_PUSH_DOMAIN}AiService/identification".format(
+                    DETECT_PUSH_DOMAIN=DETECT_PUSH_DOMAINS)
 
 
                 # mqtt通知设备开启AI识别功能
                 # mqtt通知设备开启AI识别功能
                 msg = {
                 msg = {
@@ -248,7 +228,7 @@ class AiView(View):
                               'aiIdentificationUrl': aiIdentificationUrl,
                               'aiIdentificationUrl': aiIdentificationUrl,
                           }
                           }
                       },
                       },
-                req_success = CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
+                req_success = CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg)
                 if not req_success:
                 if not req_success:
                     return response.json(10044)
                     return response.json(10044)
                 return response.json(0, {'aiIdentificationUrl': aiIdentificationUrl, 'endTime': endTime, 'etk': etk})
                 return response.json(0, {'aiIdentificationUrl': aiIdentificationUrl, 'endTime': endTime, 'etk': etk})
@@ -273,7 +253,9 @@ class AiView(View):
         except Exception as e:
         except Exception as e:
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
-    def do_commodity_list(self, userID, request_dict, response):
+    # 获取AI套餐列表
+    @staticmethod
+    def do_commodity_list(request_dict, response):
         uid = request_dict.get('uid', None)
         uid = request_dict.get('uid', None)
         lang = request_dict.get('lang', 'en')
         lang = request_dict.get('lang', 'en')
 
 
@@ -283,10 +265,18 @@ class AiView(View):
             if device_info_qs.exists():
             if device_info_qs.exists():
                 return response.json(0)
                 return response.json(0)
 
 
+            # 没免费体验过的设备只返回体验套餐数据,体验过的不再返回
+            exc_ai_qs = ExperienceAiModel.objects.filter(uid=uid, experience_type=0)
+            if exc_ai_qs.exists():
+                ai_meal_qs = AiStoreMeal.objects.filter(~Q(pay_type=10))
+            else:
+                ai_meal_qs = AiStoreMeal.objects.filter(pay_type=10)
+
             # 查询套餐数据
             # 查询套餐数据
-            ai_meal_qs = AiStoreMeal.objects.filter(is_show=1, lang__lang=lang).\
-                annotate(ai_meal_id=F('id'), title=F('lang__title'), content=F('lang__content')).\
-                values("ai_meal_id", "title", "content", "price", "effective_day", "currency", "virtual_price", "symbol")
+            ai_meal_qs = ai_meal_qs.filter(is_show=1, lang__lang=lang). \
+                annotate(ai_meal_id=F('id'), title=F('lang__title'), content=F('lang__content')). \
+                values("ai_meal_id", "title", "content", "price", "effective_day", "currency", "virtual_price",
+                       "symbol")
             if not ai_meal_qs.exists():
             if not ai_meal_qs.exists():
                 return response.json(0)
                 return response.json(0)
 
 
@@ -323,8 +313,10 @@ class AiView(View):
             count = omqs.count()
             count = omqs.count()
             omqs = omqs.annotate(rank__title=F('ai_rank__lang__title'), rank__content=F('ai_rank__lang__content'),
             omqs = omqs.annotate(rank__title=F('ai_rank__lang__title'), rank__content=F('ai_rank__lang__content'),
                                  rank__day=F('ai_rank__effective_day'), rank__price=F('ai_rank__price'),
                                  rank__day=F('ai_rank__effective_day'), rank__price=F('ai_rank__price'),
-                                 rank__expire=F('ai_rank__effective_day'), rank__id=F('ai_rank_id'), rank__currency=F('ai_rank__currency'))
-            order_ql = omqs[(page - 1) * line:page * line].values("orderID", "UID", "channel", "desc", "price", "currency",
+                                 rank__expire=F('ai_rank__effective_day'), rank__id=F('ai_rank_id'),
+                                 rank__currency=F('ai_rank__currency'))
+            order_ql = omqs[(page - 1) * line:page * line].values("orderID", "UID", "channel", "desc", "price",
+                                                                  "currency",
                                                                   "addTime",
                                                                   "addTime",
                                                                   "updTime", "paypal", "rank__day", "payType",
                                                                   "updTime", "paypal", "rank__day", "payType",
                                                                   "rank__price", "status",
                                                                   "rank__price", "status",
@@ -366,8 +358,10 @@ class AiView(View):
                 return response.json(0, [])
                 return response.json(0, [])
 
 
             # 计算套餐过期时间
             # 计算套餐过期时间
-            sum_end_time = AiService.objects.filter(Q(uid=uid), ~Q(use_status=2)).aggregate(Sum('endTime'))['endTime__sum']
-            ai_service_qs = ai_service_qs.order_by('addTime').annotate(bucket__content=F('orders__ai_rank__lang__title')).\
+            sum_end_time = AiService.objects.filter(Q(uid=uid), ~Q(use_status=2)).aggregate(Sum('endTime'))[
+                'endTime__sum']
+            ai_service_qs = ai_service_qs.order_by('addTime').annotate(
+                bucket__content=F('orders__ai_rank__lang__title')). \
                 values('uid', 'use_status', 'bucket__content')
                 values('uid', 'use_status', 'bucket__content')
             ai_service_data = ai_service_qs[0]
             ai_service_data = ai_service_qs[0]
             ai_service_data['endTime'] = sum_end_time
             ai_service_data['endTime'] = sum_end_time
@@ -376,13 +370,100 @@ class AiView(View):
             print(e)
             print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
+    # 体验AI套餐
+    @staticmethod
+    def experience_order(request_dict, userID, response):
+        uid = request_dict.get('uid', None)
+        channel = request_dict.get('channel', None)
+        pay_type = int(request_dict.get('pay_type', None))
+        rank = request_dict.get('rank', None)
+        cdk = request_dict.get('cdk', None)
+        lang = request_dict.get('lang', 'en')
+
+        # 使用redis设置唯一key加锁
+        redisObj = RedisObject()
+        redis_key = uid + 'do_experience_ai_order'
+        isLock = redisObj.CONN.setnx(redis_key, 1)
+        redisObj.CONN.expire(redis_key, 60)
+        if not isLock:
+            return response.json(5)
+        try:
+            if pay_type == 10:  # 判断是否已体验过套餐
+                exc_ai_qs = ExperienceAiModel.objects.filter(uid=uid, experience_type=0)
+                if exc_ai_qs.exists():
+                    return response.json(5)
+
+            if cdk is not None and pay_type == 11:
+                cdk_qs = CDKcontextModel.objects.filter(cdk=cdk).values('is_activate', 'rank__id', 'rank__commodity_code')
+                if not cdk_qs.exists():
+                    return response.json(10040)
+                if cdk_qs[0]['is_activate'] == 1:
+                    return response.json(10039)
+                rank = cdk_qs[0]['rank__id']
+
+            if uid is None or channel is None or pay_type is None or rank is None:
+                redisObj.del_data(key=redis_key)
+                return response.json(444)
+
+            # 判断是否为主用户操作
+            device_info_qs = Device_Info.objects.filter(Q(UID=uid) & ~Q(vodPrimaryUserID='')).values('vodPrimaryUserID')
+            if device_info_qs.exists():
+                if device_info_qs[0]['vodPrimaryUserID'] != userID:
+                    if pay_type == 10:
+                        return response.json(10035)
+                    if pay_type == 11:
+                        return response.json(10036)
+
+            dv_qs = Device_Info.objects.filter(userID_id=userID, UID=uid, isShare=False, isExist=1)
+            if not dv_qs.exists():
+                return response.json(12)
+
+            orderID = CommonService.createOrderID()
+            nowTime = int(time.time())
+            ai_store_meal_qs = AiStoreMeal.objects.filter(id=rank, lang__lang=lang, is_show=1). \
+                values('lang__content', 'price', 'currency', 'effective_day')
+            if not ai_store_meal_qs.exists():
+                return response.json(173)
+
+            effective_day = ai_store_meal_qs[0]['effective_day']
+            endTime = nowTime + effective_day * 24 * 60 * 60  # 套餐结束时间
+
+            with transaction.atomic():
+                # 订单表创建数据
+                Order_Model.objects.create(orderID=orderID, UID=uid, channel=channel, userID_id=userID,
+                                           desc=ai_store_meal_qs[0]['lang__content'], payType=pay_type, payTime=nowTime,
+                                           price=ai_store_meal_qs[0]['price'], currency=ai_store_meal_qs[0]['currency'],
+                                           addTime=nowTime, updTime=nowTime, pay_url='AI体验',
+                                           rank_id=1, ai_rank_id=rank, status=1)
+                # ai服务表创建数据
+                AiService.objects.create(uid=uid, channel=channel, orders_id=orderID, detect_status=1, endTime=endTime,
+                                         addTime=nowTime, updTime=nowTime, use_status=1)
+
+                if pay_type == 10:
+                    ExperienceAiModel.objects.create(
+                        experience_type=0,
+                        uid=uid,
+                        do_time=nowTime
+                    )
+
+                elif pay_type == 11:
+                    CDKcontextModel.objects.filter(cdk=cdk).update(is_activate=1, order=orderID)
+
+                redisObj.del_data(key=redis_key)
+                pay_ok_url = "{}cloudstorage/payOK?paytype={}&lang={}".format(SERVER_DOMAIN_SSL, pay_type, lang)
+                return response.json(0, pay_ok_url)
+        except Exception as e:
+            print(e)
+            redisObj.del_data(key=redis_key)
+            return response.json(474)
+
     def do_create_pay_order(self, request_dict, request, userID, response):
     def do_create_pay_order(self, request_dict, request, userID, response):
         uid = request_dict.get('uid', None)
         uid = request_dict.get('uid', None)
         channel = request_dict.get('channel', None)
         channel = request_dict.get('channel', None)
         pay_type = int(request_dict.get('pay_type', 1))
         pay_type = int(request_dict.get('pay_type', 1))
-        ai_meal_id = request_dict.get('ai_meal_id', None)
+        rank = request_dict.get('rank', None)
         lang = request_dict.get('lang', 'en')
         lang = request_dict.get('lang', 'en')
-        if not uid or not channel or not pay_type or not ai_meal_id:
+        if not uid or not channel or not pay_type or not rank:
             return response.json(444)
             return response.json(444)
 
 
         try:
         try:
@@ -400,7 +481,7 @@ class AiView(View):
             #         return response.json(10033)
             #         return response.json(10033)
 
 
             # 获取ai套餐数据
             # 获取ai套餐数据
-            ai_sm_qs = AiStoreMeal.objects.filter(id=ai_meal_id, pay_type=pay_type, is_show=1, lang__lang=lang). \
+            ai_sm_qs = AiStoreMeal.objects.filter(id=rank, pay_type=pay_type, is_show=1, lang__lang=lang). \
                 values('lang__title', 'lang__content', 'currency', 'price')
                 values('lang__title', 'lang__content', 'currency', 'price')
             if not ai_sm_qs.exists():
             if not ai_sm_qs.exists():
                 return response.json(173)
                 return response.json(173)
@@ -425,20 +506,22 @@ class AiView(View):
                 'currency': currency,
                 'currency': currency,
                 'addTime': nowTime,
                 'addTime': nowTime,
                 'updTime': nowTime,
                 'updTime': nowTime,
-                'ai_rank_id': ai_meal_id,
+                'ai_rank_id': rank,
                 'rank_id': 1,
                 'rank_id': 1,
                 'order_type': 1,
                 'order_type': 1,
             }
             }
 
 
-            if pay_type == 1:       # PayPal支付
-                order_dict['paymentID'], order_dict['pay_url'] = self.create_paypal_payment(lang, orderID, price, currency, content, response)
+            if pay_type == 1:  # PayPal支付
+                order_dict['paymentID'], order_dict['pay_url'] = self.create_paypal_payment(lang, orderID, price,
+                                                                                            currency, content, response)
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID}
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID}
-            elif pay_type == 2:     # 支付宝
+            elif pay_type == 2:  # 支付宝
                 order_dict['pay_url'] = self.create_alipay_payment(lang, orderID, price, title, content, response)
                 order_dict['pay_url'] = self.create_alipay_payment(lang, orderID, price, title, content, response)
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID}
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID}
-            elif pay_type == 3:     # 微信支付
+            elif pay_type == 3:  # 微信支付
                 ip = CommonService.get_ip_address(request)
                 ip = CommonService.get_ip_address(request)
-                order_dict['pay_url'], sign_params = self.create_wechat_payment(lang, orderID, price, ip, content, response)
+                order_dict['pay_url'], sign_params = self.create_wechat_payment(lang, orderID, price, ip, content,
+                                                                                response)
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID, 'result': sign_params}
                 res_data = {'redirectUrl': order_dict['pay_url'], 'orderID': orderID, 'result': sign_params}
             else:
             else:
                 return response.json(444, {'param': 'pay_type'})
                 return response.json(444, {'param': 'pay_type'})
@@ -591,8 +674,8 @@ class AiView(View):
             attach = data["attach"]
             attach = data["attach"]
             parmap = dict([(k, v[0]) for k, v in parse_qs(unquote(attach)).items()])
             parmap = dict([(k, v[0]) for k, v in parse_qs(unquote(attach)).items()])
             lang = parmap['lang']
             lang = parmap['lang']
-            trade_status = data['result_code']      # 业务结果  SUCCESS/FAIL
-            orderID = data['out_trade_no']          # 商户订单号
+            trade_status = data['result_code']  # 业务结果  SUCCESS/FAIL
+            orderID = data['out_trade_no']  # 商户订单号
             order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
             order_qs = Order_Model.objects.filter(orderID=orderID, status=0)
             if not order_qs.exists():
             if not order_qs.exists():
                 return response.json(173)
                 return response.json(173)
@@ -634,7 +717,6 @@ class AiView(View):
                            'detect_status': 1,
                            'detect_status': 1,
                            'addTime': nowTime,
                            'addTime': nowTime,
                            'updTime': nowTime,
                            'updTime': nowTime,
-                           'detect_group': '1'
                            }
                            }
         if ai_service_qs.exists():  # 有正在使用的套餐,套餐结束时间保存为套餐有效期
         if ai_service_qs.exists():  # 有正在使用的套餐,套餐结束时间保存为套餐有效期
             ai_service_dict['endTime'] = effective_day * 24 * 60 * 60
             ai_service_dict['endTime'] = effective_day * 24 * 60 * 60
@@ -672,13 +754,13 @@ class AiView(View):
             '2': ['Dog', 'Pet', 'Canine', 'Animal', 'Puppy'],  # 动物
             '2': ['Dog', 'Pet', 'Canine', 'Animal', 'Puppy'],  # 动物
             '3': ['Car', 'Vehicle', 'Transportation', 'Automobile']  # 车
             '3': ['Car', 'Vehicle', 'Transportation', 'Automobile']  # 车
         }
         }
-        #找出识别的所有标签
+        # 找出识别的所有标签
         for label in labels:
         for label in labels:
             label_name.append(label['Name'])
             label_name.append(label['Name'])
             for Parents in label['Parents']:
             for Parents in label['Parents']:
                 label_name.append(Parents['Name'])
                 label_name.append(Parents['Name'])
 
 
-        #删除用户没有选择的ai识别类型, 并且得出最终识别结果
+        # 删除用户没有选择的ai识别类型, 并且得出最终识别结果
         user_detect_list = user_detect_group.split(',')
         user_detect_list = user_detect_group.split(',')
         user_detect_list = [i.strip() for i in user_detect_list]
         user_detect_list = [i.strip() for i in user_detect_list]
         label_result_list = []
         label_result_list = []
@@ -690,56 +772,56 @@ class AiView(View):
                     if label in label_name:
                     if label in label_name:
                         label_result_list.append(label)
                         label_result_list.append(label)
 
 
-        #找出标签边框线位置信息
+        # 找出标签边框线位置信息
         boundingBoxList = []
         boundingBoxList = []
         for label in labels:
         for label in labels:
             if label['Name'] in label_result_list:
             if label['Name'] in label_result_list:
                 for boundingBox in label['Instances']:
                 for boundingBox in label['Instances']:
                     boundingBoxList.append(boundingBox['BoundingBox'])
                     boundingBoxList.append(boundingBox['BoundingBox'])
 
 
-        #找出边框位置信息对应的单图位置并重新计算位置比
+        # 找出边框位置信息对应的单图位置并重新计算位置比
         merge_image_height = image_size['height']
         merge_image_height = image_size['height']
         merge_image_width = image_size['width']
         merge_image_width = image_size['width']
-        single_height = merge_image_height//image_size['num']
+        single_height = merge_image_height // image_size['num']
         new_bounding_box_dict = {}
         new_bounding_box_dict = {}
-        new_bounding_box_dict[n_time+'_0'] = []
-        new_bounding_box_dict[n_time+'_1'] = []
-        new_bounding_box_dict[n_time+'_2'] = []
-        new_bounding_box_dict[n_time+'_3'] = []
+        new_bounding_box_dict[n_time + '_0'] = []
+        new_bounding_box_dict[n_time + '_1'] = []
+        new_bounding_box_dict[n_time + '_2'] = []
+        new_bounding_box_dict[n_time + '_3'] = []
         for k, val in enumerate(boundingBoxList):
         for k, val in enumerate(boundingBoxList):
             boundingBoxTop = merge_image_height * val['Top']
             boundingBoxTop = merge_image_height * val['Top']
-            #找出当前边框属于哪张图片范围
+            # 找出当前边框属于哪张图片范围
             boxDict = {}
             boxDict = {}
             for i in range(image_size['num']):
             for i in range(image_size['num']):
-                min = i*single_height       #第n张图
-                max = (i+1)*single_height
+                min = i * single_height  # 第n张图
+                max = (i + 1) * single_height
                 if boundingBoxTop >= min and boundingBoxTop <= max:
                 if boundingBoxTop >= min and boundingBoxTop <= max:
                     # print("属于第{i}张图".format(i=i+1))
                     # print("属于第{i}张图".format(i=i+1))
                     boxDict['Width'] = val['Width']
                     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['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']
                     boxDict['Left'] = val['Left']
-                    boxDict['picName'] = "{n_time}_{i}".format(n_time=n_time,i=i)
+                    boxDict['picName'] = "{n_time}_{i}".format(n_time=n_time, i=i)
                     # new_bounding_box_list.append(boxDict)
                     # new_bounding_box_list.append(boxDict)
                     # new_bounding_box_list.append(boxDict)
                     # new_bounding_box_list.append(boxDict)
-                    new_bounding_box_dict["{n_time}_{i}".format(n_time=n_time,i=i)].append(boxDict)
+                    new_bounding_box_dict["{n_time}_{i}".format(n_time=n_time, i=i)].append(boxDict)
         # exit(new_bounding_box_list)
         # exit(new_bounding_box_list)
         user_labels_list = list(new_labels_type.keys())
         user_labels_list = list(new_labels_type.keys())
         user_labels_list.sort()
         user_labels_list.sort()
         return {'label_type': user_labels_list, 'label_list': label_result_list,
         return {'label_type': user_labels_list, 'label_list': label_result_list,
-                'new_bounding_box_dict':new_bounding_box_dict}
-
+                'new_bounding_box_dict': new_bounding_box_dict}
 
 
     def upload_s3(self, file_path, upload_path):
     def upload_s3(self, file_path, upload_path):
         try:
         try:
-            aws_key = "AKIA2E67UIMD45Y3HL53" #【你的 aws_access_key】
-            aws_secret = "ckYLg4Lo9ZXJIcJEAKkzf2rWvs8Xth1FCjqiAqUw" # 【你的 aws_secret_key】
+            aws_key = "AKIA2E67UIMD45Y3HL53"  # 【你的 aws_access_key】
+            aws_secret = "ckYLg4Lo9ZXJIcJEAKkzf2rWvs8Xth1FCjqiAqUw"  # 【你的 aws_secret_key】
             session = Session(aws_access_key_id=aws_key,
             session = Session(aws_access_key_id=aws_key,
                               aws_secret_access_key=aws_secret,
                               aws_secret_access_key=aws_secret,
                               region_name="us-east-1")
                               region_name="us-east-1")
             s3 = session.resource("s3")
             s3 = session.resource("s3")
             # client = session.client("s3")
             # client = session.client("s3")
-            bucket = "foreignpush" # 【你 bucket 的名字】 # 首先需要保.证 s3 上已经存在该存储桶,否则报错
+            bucket = "foreignpush"  # 【你 bucket 的名字】 # 首先需要保.证 s3 上已经存在该存储桶,否则报错
             upload_data = open(file_path, "rb")
             upload_data = open(file_path, "rb")
             # upload_key = "test"
             # upload_key = "test"
             s3.Bucket(bucket).put_object(Key=upload_path, Body=upload_data)
             s3.Bucket(bucket).put_object(Key=upload_path, Body=upload_data)
@@ -768,7 +850,7 @@ class AiView(View):
             return nickname
             return nickname
 
 
     def get_msg_text(self, channel, n_time, lang, tz, label_list):
     def get_msg_text(self, channel, n_time, lang, tz, label_list):
-        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz,lang=lang)
+        n_date = CommonService.get_now_time_str(n_time=n_time, tz=tz, lang=lang)
         if lang == 'cn':
         if lang == 'cn':
             msg = '摄像头AI识别到了{}'.format(label_list)
             msg = '摄像头AI识别到了{}'.format(label_list)
             send_text = '{msg} 通道:{channel} 日期:{date}'.format(msg=msg, channel=channel, date=n_date)
             send_text = '{msg} 通道:{channel} 日期:{date}'.format(msg=msg, channel=channel, date=n_date)
@@ -815,7 +897,6 @@ class AiView(View):
         except Exception as e:
         except Exception as e:
             return 'serverKey abnormal'
             return 'serverKey abnormal'
 
 
-
     def do_apns(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
     def do_apns(self, uid, channel, appBundleId, token_val, event_type, n_time, msg_title, msg_text):
         logger = logging.getLogger('info')
         logger = logging.getLogger('info')
         logger.info("进来do_apns函数了")
         logger.info("进来do_apns函数了")
@@ -823,7 +904,8 @@ class AiView(View):
         logger.info(APNS_MODE)
         logger.info(APNS_MODE)
         logger.info(os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
         logger.info(os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
         try:
         try:
-            cli = apns2.APNSClient(mode=APNS_MODE, client_cert=os.path.join(BASE_DIR, APNS_CONFIG[appBundleId]['pem_path']))
+            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": "",
             push_data = {"alert": "Motion ", "event_time": n_time, "event_type": event_type, "msg": "",
                          "received_at": n_time, "sound": "", "uid": uid, "zpush": "1", "channel": channel}
                          "received_at": n_time, "sound": "", "uid": uid, "zpush": "1", "channel": channel}
             alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
             alert = apns2.PayloadAlert(body=msg_text, title=msg_title)
@@ -854,7 +936,7 @@ class AiView(View):
         eventType = request_dict.get('eventType', None)
         eventType = request_dict.get('eventType', None)
 
 
         now_time = int(time.time())
         now_time = int(time.time())
-        seven_days_ago = now_time - 7 * 24 * 3600   # 查询7天内的数据
+        seven_days_ago = now_time - 7 * 24 * 3600  # 查询7天内的数据
         qs = Ai_Push_Info.objects.filter(userID_id=userID, eventTime__gt=seven_days_ago).order_by('-eventTime')
         qs = Ai_Push_Info.objects.filter(userID_id=userID, eventTime__gt=seven_days_ago).order_by('-eventTime')
 
 
         if startTime and endTime:
         if startTime and endTime:
@@ -903,17 +985,17 @@ class AiView(View):
                 s3_img_cover = '{uid}/{channel}/cover{time}.jpg'.format(uid=devUid, channel=channel, time=eventTime)
                 s3_img_cover = '{uid}/{channel}/cover{time}.jpg'.format(uid=devUid, channel=channel, time=eventTime)
                 s3_img_desc = '{uid}/{channel}/desc{time}.jpg'.format(uid=devUid, channel=channel, time=eventTime)
                 s3_img_desc = '{uid}/{channel}/desc{time}.jpg'.format(uid=devUid, channel=channel, time=eventTime)
                 response_url_cover = aws_s3_client.generate_presigned_url('get_object',
                 response_url_cover = aws_s3_client.generate_presigned_url('get_object',
-                                                                    ExpiresIn=300,
-                                                                    Params={
-                                                                        'Bucket': 'aipush', 'Key': s3_img_cover
-                                                                    },
-                                                                    )
-                response_url_desc = aws_s3_client.generate_presigned_url('get_object',
                                                                           ExpiresIn=300,
                                                                           ExpiresIn=300,
                                                                           Params={
                                                                           Params={
-                                                                              'Bucket': 'aipush', 'Key': s3_img_desc
+                                                                              'Bucket': 'aipush', 'Key': s3_img_cover
                                                                           },
                                                                           },
                                                                           )
                                                                           )
+                response_url_desc = aws_s3_client.generate_presigned_url('get_object',
+                                                                         ExpiresIn=300,
+                                                                         Params={
+                                                                             'Bucket': 'aipush', 'Key': s3_img_desc
+                                                                         },
+                                                                         )
                 p['img'] = response_url_cover
                 p['img'] = response_url_cover
                 p['img_list'] = [response_url_desc]
                 p['img_list'] = [response_url_desc]
 
 
@@ -923,13 +1005,14 @@ class AiView(View):
                 # 列表装载回放时间戳标记
                 # 列表装载回放时间戳标记
                 p['img_list'] = []
                 p['img_list'] = []
                 for i in range(4):
                 for i in range(4):
-                    thumbspng = '{uid}/{channel}/{time}_{st}.jpg'.format(uid=devUid, channel=p['Channel'], time=eventTime, st=i)
+                    thumbspng = '{uid}/{channel}/{time}_{st}.jpg'.format(uid=devUid, channel=p['Channel'],
+                                                                         time=eventTime, st=i)
                     response_url = aws_s3_client.generate_presigned_url('get_object',
                     response_url = aws_s3_client.generate_presigned_url('get_object',
-                                                                             ExpiresIn=300,
-                                                                             Params={
-                                                                                 'Bucket': 'aipush', 'Key': thumbspng
-                                                                             },
-                                                                             )
+                                                                        ExpiresIn=300,
+                                                                        Params={
+                                                                            'Bucket': 'aipush', 'Key': thumbspng
+                                                                        },
+                                                                        )
                     p['img_list'].append(response_url)
                     p['img_list'].append(response_url)
 
 
             if devUid in uid_type_dict.keys():
             if devUid in uid_type_dict.keys():
@@ -944,18 +1027,18 @@ class AiView(View):
         is_update_all = request_dict.get('is_update_all', 0)
         is_update_all = request_dict.get('is_update_all', 0)
 
 
         try:
         try:
-            if int(is_update_all) == 1:     # 全部已读
+            if int(is_update_all) == 1:  # 全部已读
                 is_update = Ai_Push_Info.objects.filter(userID_id=userID).update(status=1)
                 is_update = Ai_Push_Info.objects.filter(userID_id=userID).update(status=1)
                 return response.json(0, {'update_count': is_update})
                 return response.json(0, {'update_count': is_update})
             else:
             else:
                 id_list = request_dict.get('id_list', None)
                 id_list = request_dict.get('id_list', None)
                 if not id_list:
                 if not id_list:
-                    request_dict.getlist('id_list[]', None)     # 获取IOS数组传参
+                    request_dict.getlist('id_list[]', None)  # 获取IOS数组传参
                 logger = logging.getLogger('info')
                 logger = logging.getLogger('info')
                 logger.info('已读ai消息id_list:{}'.format(id_list))
                 logger.info('已读ai消息id_list:{}'.format(id_list))
                 if not id_list:
                 if not id_list:
                     return response.json(444)
                     return response.json(444)
-                id_list = eval(id_list)     # 字符串转列表
+                id_list = eval(id_list)  # 字符串转列表
                 param_flag = CommonService.get_param_flag(data=id_list)
                 param_flag = CommonService.get_param_flag(data=id_list)
                 if not param_flag:
                 if not param_flag:
                     return response.json(444)
                     return response.json(444)
@@ -995,5 +1078,3 @@ class AiView(View):
         except Exception as e:
         except Exception as e:
             print(e)
             print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
-
-

+ 62 - 10
Controller/AppSetController.py

@@ -12,13 +12,15 @@
 @Contact: chanjunkai@163.com
 @Contact: chanjunkai@163.com
 """
 """
 from Ansjer.config import SERVER_TYPE
 from Ansjer.config import SERVER_TYPE
-from Model.models import AppSetModel,PromotionRuleModel
+from Model.models import AppSetModel, PromotionRuleModel, PopupsConfig, RedDotsConfig
 from django.views.generic.base import View
 from django.views.generic.base import View
 from Object.RedisObject import RedisObject
 from Object.RedisObject import RedisObject
 from Object.TokenObject import TokenObject
 from Object.TokenObject import TokenObject
 from Service.ModelService import ModelService
 from Service.ModelService import ModelService
-import time,json
+import time, json
 from Object.ResponseObject import ResponseObject
 from Object.ResponseObject import ResponseObject
+
+
 class AppSetView(View):
 class AppSetView(View):
     def get(self, request, *args, **kwargs):
     def get(self, request, *args, **kwargs):
         request.encoding = 'utf-8'
         request.encoding = 'utf-8'
@@ -51,7 +53,14 @@ class AppSetView(View):
             else:
             else:
                 return response.json(tko.code)
                 return response.json(tko.code)
         else:
         else:
-            return response.json(414)
+            token = request_dict.get('token', None)
+            tko = TokenObject(token)
+            if tko.code == 0:
+                userID = tko.userID
+                if operation == 'page_set':  # app弹窗标记红点设置
+                    return self.do_page_set(userID, request_dict, response)
+            else:
+                return response.json(tko.code)
 
 
     # 查询
     # 查询
     def do_query(self, request_dict, response):
     def do_query(self, request_dict, response):
@@ -67,12 +76,13 @@ class AppSetView(View):
             if not app_set_qs[0]['content']:
             if not app_set_qs[0]['content']:
                 return response.json(0)
                 return response.json(0)
             dict_json = json.loads(app_set_qs[0]['content'])
             dict_json = json.loads(app_set_qs[0]['content'])
-            #加入促销弹窗
-            promotion = PromotionRuleModel.objects.filter(status=1).values('startTime','endTime','popups')
+
+            # 加入促销弹窗
+            promotion = PromotionRuleModel.objects.filter(status=1).values('startTime', 'endTime', 'popups')
             if promotion.exists():
             if promotion.exists():
                 dict_json['popupsStartTime'] = promotion[0]['startTime']
                 dict_json['popupsStartTime'] = promotion[0]['startTime']
                 dict_json['popupsEndTime'] = promotion[0]['endTime']
                 dict_json['popupsEndTime'] = promotion[0]['endTime']
-                dict_json['popupsContent'] = json.loads(promotion[0]['popups']).get(lang,'')
+                dict_json['popupsContent'] = json.loads(promotion[0]['popups']).get(lang, '')
                 dict_json['nowTime'] = int(time.time())
                 dict_json['nowTime'] = int(time.time())
             if 'editionUpgrading' in dict_json:
             if 'editionUpgrading' in dict_json:
                 if dict_json['editionUpgrading'] == 1:
                 if dict_json['editionUpgrading'] == 1:
@@ -82,9 +92,11 @@ class AppSetView(View):
                         dict_json['editionUpgrading'] = 'Upgrading, please sign in later'
                         dict_json['editionUpgrading'] = 'Upgrading, please sign in later'
                 else:
                 else:
                     dict_json['editionUpgrading'] = ''
                     dict_json['editionUpgrading'] = ''
+
             return response.json(0, dict_json)
             return response.json(0, dict_json)
         except Exception as e:
         except Exception as e:
-            return response.json(500, repr(e))
+            return response.json(500, "错误行数:{errLine}, 错误信息: {errmsg}".format(errLine=e.__traceback__.tb_lineno,
+                                                                              errmsg=repr(e)))
 
 
         # res = {}
         # res = {}
         # res['grade'] = 1
         # res['grade'] = 1
@@ -115,7 +127,7 @@ class AppSetView(View):
         sm_qs = AppSetModel.objects.filter(appBundleId=appBundleId)
         sm_qs = AppSetModel.objects.filter(appBundleId=appBundleId)
         count = sm_qs.count()
         count = sm_qs.count()
         nowTime = int(time.time())
         nowTime = int(time.time())
-        if count>0:
+        if count > 0:
             sm_qs = sm_qs.values('id', 'appBundleId', 'content', 'addTime', 'updTime')
             sm_qs = sm_qs.values('id', 'appBundleId', 'content', 'addTime', 'updTime')
             return response.json(0, {'data': list(sm_qs), 'count': count})
             return response.json(0, {'data': list(sm_qs), 'count': count})
         else:
         else:
@@ -137,7 +149,7 @@ class AppSetView(View):
         sm_qs = AppSetModel.objects.filter(appBundleId=appBundleId)
         sm_qs = AppSetModel.objects.filter(appBundleId=appBundleId)
         redis = RedisObject()
         redis = RedisObject()
         if SERVER_TYPE != "Ansjer.formal_settings":
         if SERVER_TYPE != "Ansjer.formal_settings":
-            key_id= "www"+appBundleId
+            key_id = "www" + appBundleId
         else:
         else:
             key_id = "test" + appBundleId
             key_id = "test" + appBundleId
         redis.del_data(key=key_id)
         redis.del_data(key=key_id)
@@ -145,4 +157,44 @@ class AppSetView(View):
             sm_qs.update(content=content, updTime=nowTime)
             sm_qs.update(content=content, updTime=nowTime)
             return response.json(0)
             return response.json(0)
         else:
         else:
-            return response.json(173)
+            return response.json(173)
+
+    def do_page_set(self, userID, request_dict, response):
+        lang = request_dict.get('lang', 'en')
+        dict_json = {}
+        now_time = int(time.time())
+        dict_json['popups'] = {
+            'title': '',
+            'content': '',
+            'status': 0,
+            'tag': 1,
+        }
+        #弹窗
+        popups_obj = PopupsConfig.objects.filter(lang=lang).values('title','content','start_time','end_time','tag')
+        if popups_obj.exists:
+            popups_status = 0
+            if now_time >= popups_obj[0]['start_time'] and now_time <= popups_obj[0]['end_time']:
+                popups_status = 1
+            dict_json['popups'] = {
+                'title': popups_obj[0]['title'],
+                'content': popups_obj[0]['content'],
+                'status': popups_status,
+                'tag': popups_obj[0]['tag'],
+            }
+
+        #红点标记
+        dict_json['red_dots'] = []
+        red_dots_obj = RedDotsConfig.objects.values('module','start_time','end_time')
+        for red_dots in red_dots_obj:
+            red_dots_status = 0
+            if now_time >= red_dots['start_time'] and now_time <= red_dots['end_time']:
+                red_dots_status = 1
+            dict_json['red_dots'].append({
+                'module': red_dots['module'],
+                'status': red_dots_status,
+            })
+
+
+
+        dict_json['red_dots'] = list(dict_json['red_dots'])
+        return response.json(0, dict_json)

+ 13 - 181
Controller/CloudStorage.py

@@ -60,6 +60,7 @@ from Ansjer.config import SERVER_TYPE
 from Service.ModelService import ModelService
 from Service.ModelService import ModelService
 
 
 # SERVER_DOMAIN = 'http://test.dvema.com/'
 # SERVER_DOMAIN = 'http://test.dvema.com/'
+from Service.PayService import PaymentService
 
 
 '''
 '''
 生成订单
 生成订单
@@ -625,184 +626,15 @@ class CloudStorageView(View):
 
 
     def do_pay_error(self):
     def do_pay_error(self):
         response = HttpResponse()
         response = HttpResponse()
-        response.content = '''
-<!DOCTYPE html>
-<html>
-<head>
-	<!--浏览器不缓存-->
-	<meta http-equiv="Pragma" content="no-cache">
-	<meta http-equiv="Cache-Control" content="no-cache">
-	<meta http-equiv="Expires" content="0">
-	<!--utf-8-->
-    <meta http-equiv="content-type" content="text/html;charset=utf-8">
-    <!-- viewport的<meta>标签,这个标签可以修改在大部分的移动设备上面的显示,为了确保适当的绘制和触屏缩放。-->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="shortcut icon" href="https://test.zositechc.cn/web/images/favicon.ico" type="image/x-icon"  charset="utf-8"/>  
-    <title>Trading particulars</title>
-    <style>
-    	.title_head{
-    		height: 50px;
-    		border-radius: 5px;
-    		background-color: #c3c6c7; 
-    		text-align: center;
-    		line-height: 50px;
-    	}
-    	.content{
-    		text-align: center;
-    		margin-top: 50px;
-    		font-size: 20px;
-    		color : #ec7648
-    	}
-    	.content_img{
-    		width: 60px; 
-    		height: 60px;
-    	}
-    	.bottom{
-    		 margin-bottom: 10px; 
-    		 margin-top: 250px; 
-    		 color : #ec7648
-    	}
-    	.bottom_div{
-    		border: 1px solid #ec7648; 
-    		line-height: 38px; 
-    		text-align: center; 
-    		width: 100px; 
-    		height: 38px;
-    		border-radius: 5px;
-    	}
-    	
-    	.bottom_div:hover{
-    		background-color: #dde4e2;
-    	}
-    </style>
-</head>
-<body>
-	<div class="title_head">Trading particulars</div>
-    <div class="content">
-    	<p >
-    		<img src="https://test.zositechc.cn/web/images/failed.jpg" class="content_img">
-    		<br />
-    		Payment failure
-    	</p>
-    </div>
-    <center class="bottom">
-    	<div class="bottom_div" onclick="payOKButton()"> 
-    	 Finish
-    	</div>
-    </center>
-    <script> 	    // 点击付款成功按钮
-    function payOKButton() {
-        // 复杂数据
-        console.log('success')
-        window.location.href="https://www.baidu.com?page=closePage";
-    }
-	</script>
-</body> 
-</html>
-                '''
+        response.content = PaymentService.get_pay_error_content()
         return response
         return response
 
 
     def do_pay_ok(self, request_dict):  # 支付成功
     def do_pay_ok(self, request_dict):  # 支付成功
         response = HttpResponse()
         response = HttpResponse()
-        paytype = request_dict.get('paytype', None)
         lang = request_dict.get('lang', 'en')
         lang = request_dict.get('lang', 'en')
+        pay_type = request_dict.get('paytype', None)
 
 
-        showtitle = "支付成功"
-        if paytype == "10" :
-            showtitle = "成功体验云存"
-
-        if paytype == "11":
-            showtitle = "兑换成功"
-
-        wancheng = '完成'
-        if lang != 'cn':
-            showtitle = "Payment successful"
-            if paytype == "10":
-                showtitle = "Successful experience of cloud storage"
-
-            if paytype == "11":
-                showtitle = "Successful exchange"
-
-            wancheng = 'complete'
-        response.content = '''
-        
-<html>
-<head>
-        <!--浏览器不缓存-->
-        <meta http-equiv="Pragma" content="no-cache">
-        <meta http-equiv="Cache-Control" content="no-cache">
-        <meta http-equiv="Expires" content="0">
-        <!--utf-8-->
-    <meta http-equiv="content-type" content="text/html;charset=utf-8">
-    <!-- viewport的<meta>标签,这个标签可以修改在大部分的移动设备上面的显示,为了确保适当的绘制和触屏缩放。-->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="shortcut icon" href="https://test.zositechc.cn/web/images/favicon.ico" type="image/x-icon" charset="utf-8">  
-    <title>''' + showtitle + '''</title>
-    <style>
-            .title_head{
-                    height: 50px;
-                    border-radius: 5px;
-                    background-color: #c3c6c7; 
-                    text-align: center;
-                    line-height: 50px;
-            }
-            .content{
-                    text-align: center;
-                    margin-top: 50px;
-                    font-size: 15px;
-                    color:#0000008A;
-					
-            }
-            .content_img{
-					margin-bottom:15px;
-                    width: 60px; 
-                    height: 60px;
-            }
-            .bottom{
-                     margin-bottom: 10px; 
-                     margin-top: 250px; 
-                     color : white;
-            }
-            .bottom_div{
-                    border: 1px solid #68c9c5; 
-                    line-height: 38px; 
-                    text-align: center; 
-                    width: 100px; 
-                    height: 38px;
-                    border-radius: 30px;
-					background-color:#68c9c5; 
-            }
-            
-            .bottom_div:hover{
-                    background-color: #dde4e2;
-            }
-    </style>
-</head>
-<body style="" rlt="1" inmaintabuse="true">
-        
-    <div class="content">
-            <p>
-					<img src="https://test.zositechc.cn/web/images/success.png" class="content_img">
-                    <br>
-                    ''' + showtitle + '''
-            </p>
-    </div>
-    <center class="bottom">
-            <div class="bottom_div" onclick="payOKButton()"> 
-             '''+wancheng+'''
-            </div>
-    </center>
-    <script src="//hm.baidu.com/hm.js?eaa57ca47dacb4ad4f5a257001a3457c"></script><script>             // 点击付款成功按钮
-    function payOKButton() {
-        // 复杂数据
-        console.log('success')
-        window.location.href="https://www.baidu.com?page=closePage"  
-    }
-        </script>
- 
-
-        <div id="qds" style="display:none;"></div></body></html>
-        '''
+        response.content = PaymentService.get_pay_ok_content(lang, pay_type)
         return response
         return response
 
 
     def do_pay_by_ali_callback(self, request):  # 阿里支付回调
     def do_pay_by_ali_callback(self, request):  # 阿里支付回调
@@ -902,7 +734,7 @@ class CloudStorageView(View):
 
 
                     # 核销coupon
                     # 核销coupon
                     if order_list[0]['coupon_id']:
                     if order_list[0]['coupon_id']:
-                        CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2)
+                        CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime)
 
 
                     order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                     order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                     datetime = time.strftime("%Y-%m-%d", time.localtime())
                     datetime = time.strftime("%Y-%m-%d", time.localtime())
@@ -1052,7 +884,7 @@ class CloudStorageView(View):
 
 
                 # 核销coupon
                 # 核销coupon
                 if order_list[0]['coupon_id']:
                 if order_list[0]['coupon_id']:
-                    CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2)
+                    CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime)
 
 
                 order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                 order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                 datetime = time.strftime("%Y-%m-%d", time.localtime())
                 datetime = time.strftime("%Y-%m-%d", time.localtime())
@@ -1182,7 +1014,7 @@ class CloudStorageView(View):
 
 
                     # 核销coupon
                     # 核销coupon
                     if order_list[0]['coupon_id']:
                     if order_list[0]['coupon_id']:
-                        CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2)
+                        CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime)
 
 
                     order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                     order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id, promotion_rule_id=promotion_rule_id)
                     datetime = time.strftime("%Y-%m-%d", time.localtime())
                     datetime = time.strftime("%Y-%m-%d", time.localtime())
@@ -1271,16 +1103,16 @@ class CloudStorageView(View):
         orderID = CommonService.createOrderID()
         orderID = CommonService.createOrderID()
         #优惠券
         #优惠券
         if coupon_id:
         if coupon_id:
-            couponObj = CouponModel.objects.filter(id=coupon_id, use_status=0, distributeTime__lte=nowTime,
+            couponObj = CouponModel.objects.filter(id=coupon_id, use_status=0, distribute_time__lte=nowTime,
                                                    valid_time__gt=nowTime)
                                                    valid_time__gt=nowTime)
-            couponQuery = couponObj.values("id", "type", "coupon_discount")
+            couponQuery = couponObj.values("id", "coupon_config__type", "coupon_config__coupon_discount")
             if not couponQuery.exists():
             if not couponQuery.exists():
                 return response.json(10049)
                 return response.json(10049)
             price = Decimal(price)
             price = Decimal(price)
-            coupon_discount = Decimal(couponQuery[0]['coupon_discount'])
-            if couponQuery[0]['type'] == 1:  #打折
+            coupon_discount = Decimal(couponQuery[0]['coupon_config__coupon_discount'])
+            if couponQuery[0]['coupon_config__type'] == 1:  #打折
                 price = coupon_discount/10 * price
                 price = coupon_discount/10 * price
-            elif couponQuery[0]['type'] == 2:  #抵扣
+            elif couponQuery[0]['coupon_config__type'] == 2:  #抵扣
                 price = price - coupon_discount
                 price = price - coupon_discount
         price = float(price)
         price = float(price)
         if price < 0 or price == 0 or price < 0.01:
         if price < 0 or price == 0 or price < 0.01:
@@ -1305,7 +1137,7 @@ class CloudStorageView(View):
                                            rank_id=rank, plan_id=subInfo['plan_id'],coupon_id=coupon_id,ai_rank_id=1)
                                            rank_id=rank, plan_id=subInfo['plan_id'],coupon_id=coupon_id,ai_rank_id=1)
                 # if coupon_id:
                 # if coupon_id:
                 #     #冻结优惠券
                 #     #冻结优惠券
-                #     CouponModel.objects.filter(id=coupon_id, use_status=0, distributeTime__lte=nowTime,
+                #     CouponModel.objects.filter(id=coupon_id, use_status=0, distribute_time__lte=nowTime,
                 #                                            valid_time__gt=nowTime).update(use_status=1)
                 #                                            valid_time__gt=nowTime).update(use_status=1)
 
 
                 return response.json(0, {"redirectUrl": subInfo['url'], "orderID": orderID})
                 return response.json(0, {"redirectUrl": subInfo['url'], "orderID": orderID})

+ 58 - 38
Controller/CouponController.py

@@ -6,7 +6,7 @@ import os
 import time
 import time
 import math
 import math
 from django.views.generic.base import View
 from django.views.generic.base import View
-from Model.models import CouponModel,Device_User
+from Model.models import CouponModel, Device_User, CouponConfigModel, CouponLang
 from Object.ResponseObject import ResponseObject
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
 from Object.TokenObject import TokenObject
 from django.db.models import Q, F, Count
 from django.db.models import Q, F, Count
@@ -41,59 +41,79 @@ class CouponView(View):
             if tko.code != 0:
             if tko.code != 0:
                 return response.json(tko.code)
                 return response.json(tko.code)
             userID = tko.userID
             userID = tko.userID
-            if operation == 'UserCoupon':  #用户优惠券
+            if operation == 'UserCoupon':  # 用户优惠券
                 return self.query_user_coupon(request_dict, userID, response)
                 return self.query_user_coupon(request_dict, userID, response)
             else:
             else:
                 return response.json(414)
                 return response.json(414)
 
 
-    def generate_coupon(self,request_dict,response):
+    def generate_coupon(self, request_dict, response):
         username = request_dict.get('username', None)
         username = request_dict.get('username', None)
         num = request_dict.get('num', None)
         num = request_dict.get('num', None)
         userID = Device_User.objects.filter(username=username).values('userID')[0]['userID']
         userID = Device_User.objects.filter(username=username).values('userID')[0]['userID']
 
 
         try:
         try:
             data = []
             data = []
-            for i in range(int(num)):
-                if i % 2 == 0:
-                    data.append(CouponModel(
-                        userID_id=userID,
-                        use_status=0,
-                        type=1,
-                        coupon_discount=7,
-                        distributeTime=int(time.time()),
-                        valid_time=int(time.time()) + 8640000,
-                        addTime=int(time.time())
-                    ))
-                else:
-                    data.append(CouponModel(
-                        userID_id=userID,
-                        use_status=0,
-                        type=2,
-                        coupon_discount=0.01,
-                        distributeTime=int(time.time()),
-                        valid_time=int(time.time()) + 8640000,
-                        addTime=int(time.time())
-                    ))
-            CouponModel.objects.bulk_create(data)
+            # CouponConfigModel.objects.create(type=1, use_range=1, coupon_discount=8)
+            # CouponLang.objects.create(
+            #     lang='en',
+            #     instruction='for the first month of the Auto-renewal plan',
+            #     quota='20.0%',
+            #     unit='off',
+            #     remark='This coupon can be used on each device once only.'
+            # )
+            # CouponConfigModel.objects.get(id=1).lang.add(1)
+            now_time = int(time.time())
+            CouponModel.objects.create(
+                use_status=0,
+                distribute_time=now_time,
+                valid_time=now_time+10000000,
+                userID=userID,
+                coupon_config_id=1,
+                update_time=now_time,
+                create_time=now_time
+            )
             return HttpResponse('success')
             return HttpResponse('success')
         except Exception as e:
         except Exception as e:
-            return HttpResponse(repr(e))
+            return HttpResponse(
+                "错误行数:{errLine}, 错误信息: {errmsg}".format(errLine=e.__traceback__.tb_lineno, errmsg=repr(e)))
 
 
+    def query_user_coupon(self, request_dict, userID, response):  # 用户优惠券列表
+        now_time = int(time.time())
+        lang = request_dict.get('lang', 'en')
+        # couponObj = CouponModel.objects.filter(userID_id=userID, use_status=0, distributeTime__lte=now_time,
+        #                                        valid_time__gt=now_time).annotate(coupon_id=F('id')).values(
+        #     "coupon_id", "type", "coupon_discount", "valid_time")
 
 
+        coupon_obj = CouponModel.objects.filter(
+            userID=userID,
+            use_status=0,
+            distribute_time__lte=now_time,
+            valid_time__gt=now_time,
+            coupon_config__lang__lang=lang,
+        ).annotate(
+            coupon_id=F('id'),
+            type=F('coupon_config__type'),
+            coupon_discount=F('coupon_config__coupon_discount'),
+            instruction=F('coupon_config__lang__instruction'),
+            remark=F('coupon_config__lang__remark'),
+            quota=F('coupon_config__lang__quota'),
+            unit=F('coupon_config__lang__unit'),
+        ).values(
+            "coupon_id",
+            "type",
+            "coupon_discount",
+            "valid_time",
+            "instruction",
+            "remark",
+            "quota",
+            "unit",
+        )
 
 
+        for couponList in coupon_obj:
+            couponList['valid_time'] = CommonService.timestamp_to_str(couponList['valid_time'])
 
 
-
-
-
-    def query_user_coupon(self, request_dict, userID, response):  #用户优惠券列表
-        nowTime = int(time.time())
-        couponObj = CouponModel.objects.filter(userID_id=userID,use_status=0,distributeTime__lte=nowTime,
-                                               valid_time__gt=nowTime).annotate(coupon_id=F('id')).values(
-                                               "coupon_id","type","coupon_discount","valid_time")
-        for couponList in couponObj:
-            couponList['valid_time'] =  CommonService.timestamp_to_str(couponList['valid_time'])
         result = {
         result = {
-            'count':couponObj.count(),
-            'couponList':list(couponObj),
+            'count': coupon_obj.count(),
+            'couponList': list(coupon_obj),
         }
         }
         return response.json(0, result)
         return response.json(0, result)

+ 21 - 6
Controller/DetectControllerV2.py

@@ -55,10 +55,14 @@ class DetectControllerViewV2(View):
         return self.validation(request.POST, operation)
         return self.validation(request.POST, operation)
 
 
     def validation(self, request_dict, operation):
     def validation(self, request_dict, operation):
+
         response = ResponseObject()
         response = ResponseObject()
         if operation is None:
         if operation is None:
             return response.json(444, 'error path')
             return response.json(444, 'error path')
         token = request_dict.get('token', None)
         token = request_dict.get('token', None)
+        lang = request_dict.get('lang', None)
+        if lang:
+            response = ResponseObject(lang)
         tko = TokenObject(token)
         tko = TokenObject(token)
         if tko.code == 0:
         if tko.code == 0:
             userID = tko.userID
             userID = tko.userID
@@ -132,7 +136,9 @@ class DetectControllerViewV2(View):
             # 判断用户是否拥有设备
             # 判断用户是否拥有设备
             device_info_qs = Device_Info.objects.filter(userID_id=userID, UID=uid)
             device_info_qs = Device_Info.objects.filter(userID_id=userID, UID=uid)
             if not device_info_qs.exists():
             if not device_info_qs.exists():
-                return response.json(14)
+                device_info_qs = Device_Info.objects.filter(userID_id=userID, serial_number=uid)
+                if not device_info_qs.exists():
+                    return response.json(14)
 
 
             # 更新或创建uid_set数据
             # 更新或创建uid_set数据
             nowTime = int(time.time())
             nowTime = int(time.time())
@@ -147,7 +153,9 @@ class DetectControllerViewV2(View):
             # 检测类型
             # 检测类型
             if detect_group:
             if detect_group:
                 uid_set_data['detect_group'] = detect_group
                 uid_set_data['detect_group'] = detect_group
-
+            uid_set = UidSetModel.objects.filter(uid=uid).order_by('-updTime')
+            if uid_set.exists():
+                interval = uid_set.first().new_detect_interval if not interval else interval
             # 设置消息推送间隔
             # 设置消息推送间隔
             if interval:
             if interval:
                 interval = int(interval)
                 interval = int(interval)
@@ -162,9 +170,10 @@ class DetectControllerViewV2(View):
                             'IntervalTime': interval
                             'IntervalTime': interval
                         }
                         }
                     }
                     }
-                    req_success = CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
-                    if not req_success:
-                        return response.json(10044)
+                    CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
+                    # req_success = CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
+                    # if not req_success:
+                    #     return response.json(10044)
 
 
             uid_set_qs = UidSetModel.objects.filter(uid=uid)
             uid_set_qs = UidSetModel.objects.filter(uid=uid)
             if uid_set_qs.exists():
             if uid_set_qs.exists():
@@ -351,12 +360,13 @@ class DetectControllerViewV2(View):
             region_name='us-east-1'
             region_name='us-east-1'
         )
         )
         # vod_time_list = []
         # vod_time_list = []
+        # ai消息标识所有组合标签
+        ai_all_event_type = EquipmentInfoService.get_all_comb_event_type()
         for p in qr:
         for p in qr:
             devUid = p['devUid']
             devUid = p['devUid']
             eventTime = p['eventTime']
             eventTime = p['eventTime']
             channel = p['Channel']
             channel = p['Channel']
             storage_location = p['storage_location']
             storage_location = p['storage_location']
-            p['borderCoords'] = '' if p['borderCoords'] == '' else json.loads(p['borderCoords'])
             if p['is_st'] == 1:
             if p['is_st'] == 1:
                 thumbspng = '{uid}/{channel}/{time}.jpeg'.format(uid=devUid, channel=p['Channel'], time=eventTime)
                 thumbspng = '{uid}/{channel}/{time}.jpeg'.format(uid=devUid, channel=p['Channel'], time=eventTime)
                 if storage_location == 1:  # oss
                 if storage_location == 1:  # oss
@@ -433,6 +443,11 @@ class DetectControllerViewV2(View):
                 p['devNickName'] = uid_type_dict[devUid]['NickName']
                 p['devNickName'] = uid_type_dict[devUid]['NickName']
             else:
             else:
                 p['uid_type'] = ''
                 p['uid_type'] = ''
+
+            p['borderCoords'] = '' if p['borderCoords'] == '' else json.loads(p['borderCoords'])  # ai消息坐标信息
+            p['ai_event_type_list'] = []
+            if p['eventType'] in ai_all_event_type:  # 如果是ai消息类型,则分解eventType, 如:123 -> [1,2,3]
+                p['ai_event_type_list'] = list(map(int, str(p['eventType'])))
             res.append(p)
             res.append(p)
         return response.json(0, {'datas': res, 'count': count})
         return response.json(0, {'datas': res, 'count': count})
 
 

+ 19 - 46
Controller/IotCoreController.py

@@ -4,6 +4,7 @@ import hashlib
 import logging
 import logging
 import time
 import time
 import uuid
 import uuid
+from collections import OrderedDict
 
 
 import requests
 import requests
 from django.views import View
 from django.views import View
@@ -41,7 +42,7 @@ class IotCoreView(View):
         if operation == 'createKeysAndCertificate':
         if operation == 'createKeysAndCertificate':
             return self.create_keys_and_certificate(request_dict, response, request)
             return self.create_keys_and_certificate(request_dict, response, request)
         elif operation == 'requestPublishMessage':
         elif operation == 'requestPublishMessage':
-            return self.request_publish_message(request_dict, response, request)
+            return self.request_publish_message(request_dict, response)
         elif operation == 'getS3PullKey':
         elif operation == 'getS3PullKey':
             return self.get_s3_pull_key(request_dict, response, request)
             return self.get_s3_pull_key(request_dict, response, request)
         elif operation == 'thingRegroup':
         elif operation == 'thingRegroup':
@@ -230,7 +231,7 @@ class IotCoreView(View):
             print(e)
             print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
-    def clear_Iot_Cerm(self, userID, request_dict, response):
+    def clear_Iot_Cerm(self, request_dict, response):
         serial_number = request_dict.get('serial_number', None)
         serial_number = request_dict.get('serial_number', None)
 
 
         if serial_number:
         if serial_number:
@@ -243,56 +244,28 @@ class IotCoreView(View):
         else:
         else:
             return response.json(444)
             return response.json(444)
 
 
-    def request_publish_message(self, request_dict, response, request):
-        # Alexa请求IoT Core下发MQTT消息通知设备开始或停止推流,或唤醒设备
+    # Alexa请求IoT Core下发MQTT消息通知设备开始或停止推流,或唤醒设备
+    @staticmethod
+    def request_publish_message(request_dict, response):
         UID = request_dict.get('UID', None)
         UID = request_dict.get('UID', None)
-        MSG = request_dict.get('MSG', None)
-
-        if not all([UID, MSG]):
+        rtsp = request_dict.get('rtsp', None)
+        enable = request_dict.get('enable', '1')
+        if not all([UID, rtsp]):
             return response.json(444)
             return response.json(444)
 
 
         try:
         try:
-            # 获取设备的物品名后缀
-            device_info_qs = Device_Info.objects.filter(UID=UID).values('UID', 'serial_number')
-            if not device_info_qs.exists():
-                return response.json(10043)
-            uid = device_info_qs[0]['UID']
-            serial_number = device_info_qs[0]['serial_number']
-            # 如果device_info表的serial_number不为空,物品名为'Ansjer_Device_序列号'
-            thing_name_suffix = serial_number if serial_number != '' else uid
-            # 获取数据组织将要请求的url
-            iot = iotdeviceInfoModel.objects.filter(thing_name__contains=thing_name_suffix).values('thing_name',
-                                                                                                   'endpoint',
-                                                                                                   'token_iot_number')
-            if not iot.exists():
-                return response.json(10043)
-            thing_name = iot[0]['thing_name'][14:]  # IoT core上的物品名: Ansjer_Device_ + 序列号+企业编码/uid
-            endpoint = iot[0]['endpoint']
-            Token = iot[0]['token_iot_number']
-            # Token = '297a601b3925e04daab5a60280650e09'
-            topic_suffix = '_power_topic' if 'Turn' in MSG else '_rtsp_topic'
-            topic_name = thing_name + topic_suffix  # MQTT主题
-
-            # api doc: https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/http.html
-            # url: https://IoT_data_endpoint/topics/url_encoded_topic_name?qos=1
-            # post请求url来发布MQTT消息
-            url = 'https://{}/topics/{}'.format(endpoint, topic_name)
-            authorizer_name = 'Ansjer_Iot_Auth'
-            signature = CommonService.rsa_sign(Token)  # Token签名
-            headers = {'x-amz-customauthorizer-name': authorizer_name, 'Token': Token,
-                       'x-amz-customauthorizer-signature': signature}
-            params = {'command': MSG}
-            r = requests.post(url=url, headers=headers, json=params, timeout=2)
-            if r.status_code == 200:
-                res = r.json()
-                if res['message'] == 'OK':
-                    return response.json(0)
-                return response.json(10044)
-            else:
-                # print('发布失败')
+            thing_name = CommonService.query_serial_with_uid(UID)   # 存在序列号则为使用序列号作为物品名
+            topic_name = 'ansjer/generic/{}'.format(thing_name)
+            msg = OrderedDict(
+                [
+                    ('alexaRtspCommand', rtsp),
+                    ('enable', int(enable)),
+                ]
+            )
+            if not CommonService.req_publish_mqtt_msg(thing_name, topic_name, msg):
                 return response.json(10044)
                 return response.json(10044)
+            return response.json(0)
         except Exception as e:
         except Exception as e:
-            # print(e)
             return response.json(500, repr(e))
             return response.json(500, repr(e))
 
 
     def get_s3_pull_key(self, request_dict, response, request):
     def get_s3_pull_key(self, request_dict, response, request):

+ 119 - 0
Controller/MessagePush/EquipmentMessagePush.py

@@ -0,0 +1,119 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : EquipmentMessagePush.py
+@Time    : 2022/5/16 11:46
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+
+import logging
+import time
+
+from django.db import transaction
+from django.views.generic.base import View
+
+from Model.models import AiService, UidSetModel
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from Service.CommonService import CommonService
+
+
+# 设备消息推送视图
+class EquipmentMessagePushView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
+        lang = request_dict.get('lang', None)
+        logger = logging.getLogger('info')
+        logger.info("传参语言{}".format(lang))
+        lang = lang if lang else token.lang
+        response = ResponseObject(lang)
+        if token.code != 0:
+            return response.json(token.code)
+        if operation is None:
+            return response.json(444, 'error path')
+        elif operation == 'update':
+            return self.do_change_status_all(request_dict, response)
+        elif operation == 'query':
+            return self.do_uid_set_query(request_dict, response)
+
+    @classmethod
+    def do_uid_set_query(cls, request_dict, response):
+
+        logger = logging.getLogger('info')
+        uid = request_dict.get('uid', None)
+        logger.info("----查询状态打印uid{}".format(uid))
+        if not uid:
+            return response.json(444, 'uid')
+        uid_set_qs = UidSetModel.objects.filter(uid=uid).order_by('-updTime')
+        if uid_set_qs.exists():
+            uid_set = uid_set_qs.first()
+            data = {
+                'status': uid_set.is_notification,
+                'interval': uid_set.new_detect_interval
+            }
+            return response.json(0, res=data)
+        return response.json(0)
+
+    @classmethod
+    def do_change_status_all(cls, request_dict, response):
+        logger = logging.getLogger('info')
+        status = request_dict.get('status', None)
+        uid = request_dict.get('uid', None)
+        interval = request_dict.get('interval', None)
+        if not status:
+            return response.json(444, 'status')
+        if not uid:
+            return response.json(444, 'uid')
+        try:
+            with transaction.atomic():
+                n_time = int(time.time())
+                status = int(status)
+                uid_set_qs = UidSetModel.objects.filter(uid=uid)
+                if not uid_set_qs.exists():
+                    qs_data = {
+                        'uid': uid,
+                        'addTime': n_time,
+                        'updTime': n_time,
+                    }
+                    if interval:
+                        qs_data['new_detect_interval'] = int(interval)
+                        qs_data['is_notification'] = status
+                    UidSetModel.objects.create(**qs_data)
+                if status == 1:
+                    if uid_set_qs.exists():
+                        qs_data = {
+                            'updTime': n_time,
+                        }
+                        if interval:
+                            qs_data['new_detect_interval'] = int(interval)
+                        qs_data['is_notification'] = status
+                        uid_set_qs.update(**qs_data)
+                if status == 0:
+                    ai_service_qs = AiService.objects.filter(uid=uid, use_status=1)
+                    if ai_service_qs.exists():
+                        qs_data = {'uid': uid, 'updTime': n_time, 'detect_status': status}
+                        ai_service_qs.update(**qs_data)
+                        topic_name = 'ansjer/generic/{}'.format(uid)
+                        # mqtt通知设备关闭AI识别功能
+                        msg = {'commandType': 'AIDisable'}
+                        req_success = CommonService.req_publish_mqtt_msg(uid, topic_name, msg)
+                        logger.info("推送>>>> mqtt回调:{}".format(req_success))
+                    if uid_set_qs.exists():
+                        uid_set_data = {'updTime': n_time, 'uid': uid, 'new_detect_interval': int(interval),
+                                        'detect_status': int(interval), 'is_notification': status}
+                        uid_set_qs.update(**uid_set_data)
+            return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(500, repr(e))

+ 1 - 1
Controller/PaymentCycle.py

@@ -269,7 +269,7 @@ class PaypalCycleNotify(View):
 
 
                 # 核销coupon
                 # 核销coupon
                 if order_list[0]['coupon_id']:
                 if order_list[0]['coupon_id']:
-                    CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2)
+                    CouponModel.objects.filter(id=order_list[0]['coupon_id']).update(use_status=2, update_time=nowTime)
 
 
                 order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
                 order_qs.update(status=1, updTime=nowTime, uid_bucket_id=uid_bucket_id,
                                 promotion_rule_id=promotion_rule_id, agreement_id=agreement_id)
                                 promotion_rule_id=promotion_rule_id, agreement_id=agreement_id)

+ 294 - 67
Model/models.py

@@ -1,13 +1,14 @@
 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, timezone
+from django.utils import six
+from django.utils import timezone
 from django.utils.encoding import python_2_unicode_compatible
 from django.utils.encoding import python_2_unicode_compatible
 from imagekit.models import ProcessedImageField
 from imagekit.models import ProcessedImageField
 from imagekit.processors import ResizeToFill
 from imagekit.processors import ResizeToFill
 
 
 from Ansjer.config import SERVER_DOMAIN
 from Ansjer.config import SERVER_DOMAIN
-from django.utils import timezone
 
 
 
 
 class PermissionsManager(models.Manager):
 class PermissionsManager(models.Manager):
@@ -78,17 +79,19 @@ class Permissions(models.Model):
     def natural_key(self):
     def natural_key(self):
         return (self.permName)
         return (self.permName)
 
 
+
 class MenuModel(models.Model):
 class MenuModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     parentId = models.IntegerField(default=0, verbose_name='父节点ID')
     parentId = models.IntegerField(default=0, verbose_name='父节点ID')
-    name = models.CharField(max_length=50,  null=True, default='', verbose_name='名称')   #首字母大写,一定要与vue文件的name对应起来,用于noKeepAlive缓存控制(该项特别重要)
-    path = models.CharField(max_length=100, null=True, default='',verbose_name='路径')
-    component = models.CharField(max_length=100, null=True, default='', verbose_name='vue文件路径') #所谓的vue 组件
+    name = models.CharField(max_length=50, null=True, default='',
+                            verbose_name='名称')  # 首字母大写,一定要与vue文件的name对应起来,用于noKeepAlive缓存控制(该项特别重要)
+    path = models.CharField(max_length=100, null=True, default='', verbose_name='路径')
+    component = models.CharField(max_length=100, null=True, default='', verbose_name='vue文件路径')  # 所谓的vue 组件
     hidden = models.BooleanField(blank=True, default=False, verbose_name=u'是否隐藏')
     hidden = models.BooleanField(blank=True, default=False, verbose_name=u'是否隐藏')
     alwaysShow = models.BooleanField(blank=True, default=False, verbose_name=u'始终显示当前节点')
     alwaysShow = models.BooleanField(blank=True, default=False, verbose_name=u'始终显示当前节点')
     levelHidden = models.BooleanField(blank=True, default=False, verbose_name=u'是否隐藏一级路由')
     levelHidden = models.BooleanField(blank=True, default=False, verbose_name=u'是否隐藏一级路由')
-    title = models.CharField(max_length=50, default='',verbose_name='标题')
-    icon = models.CharField(max_length=50, default='',verbose_name='图标名')
+    title = models.CharField(max_length=50, default='', verbose_name='标题')
+    icon = models.CharField(max_length=50, default='', verbose_name='图标名')
     isCustomSvg = models.BooleanField(blank=True, default=False, verbose_name=u'是否是自定义svg图标')
     isCustomSvg = models.BooleanField(blank=True, default=False, verbose_name=u'是否是自定义svg图标')
     noKeepAlive = models.BooleanField(blank=True, default=False, verbose_name=u'当前路由是否不缓存')
     noKeepAlive = models.BooleanField(blank=True, default=False, verbose_name=u'当前路由是否不缓存')
     noClosable = models.BooleanField(blank=True, default=False, verbose_name=u'当前路由是否可关闭多标签页')
     noClosable = models.BooleanField(blank=True, default=False, verbose_name=u'当前路由是否可关闭多标签页')
@@ -99,7 +102,7 @@ class MenuModel(models.Model):
     dynamicNewTab = models.BooleanField(blank=True, default=False, verbose_name=u'动态传参路由是否新开标签页')
     dynamicNewTab = models.BooleanField(blank=True, default=False, verbose_name=u'动态传参路由是否新开标签页')
     redirect = models.CharField(max_length=50, default='', verbose_name='重定向')
     redirect = models.CharField(max_length=50, default='', verbose_name='重定向')
     menu_code = models.CharField(max_length=100, default='', verbose_name='菜单编码')
     menu_code = models.CharField(max_length=100, default='', verbose_name='菜单编码')
-    menutype  = models.SmallIntegerField(default=1, verbose_name=u'类型') #类型: 1-菜单 2-按钮
+    menutype = models.SmallIntegerField(default=1, verbose_name=u'类型')  # 类型: 1-菜单 2-按钮
     sort = models.IntegerField(default=0, verbose_name='排序')
     sort = models.IntegerField(default=0, verbose_name='排序')
 
 
     class Meta:
     class Meta:
@@ -107,6 +110,7 @@ class MenuModel(models.Model):
         verbose_name = u'菜单表'
         verbose_name = u'菜单表'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
 
 
+
 class Role(models.Model):
 class Role(models.Model):
     rid = models.SmallIntegerField(primary_key=True, unique=True, verbose_name=u'用户角色组ID')
     rid = models.SmallIntegerField(primary_key=True, unique=True, verbose_name=u'用户角色组ID')
     roleName = models.CharField(max_length=32, unique=True,
     roleName = models.CharField(max_length=32, unique=True,
@@ -148,6 +152,7 @@ class Role(models.Model):
             permslist.sort()
             permslist.sort()
             return permslist
             return permslist
 
 
+
 class Device_User(AbstractBaseUser):
 class Device_User(AbstractBaseUser):
     userID = models.CharField(blank=True, max_length=32, primary_key=True,
     userID = models.CharField(blank=True, max_length=32, primary_key=True,
                               verbose_name=u'用户ID', unique=True)
                               verbose_name=u'用户ID', unique=True)
@@ -175,10 +180,10 @@ class Device_User(AbstractBaseUser):
     language = models.CharField(blank=True, max_length=16, default='en', verbose_name=u'语言地区')
     language = models.CharField(blank=True, max_length=16, default='en', verbose_name=u'语言地区')
     # 手机注册增加字段
     # 手机注册增加字段
     phone = models.CharField(max_length=16, db_index=True, verbose_name=u'手机号', default='', blank=True)
     phone = models.CharField(max_length=16, db_index=True, verbose_name=u'手机号', default='', blank=True)
-    fingerprint_enable = models.SmallIntegerField(default=0, verbose_name=u'是否开启了指纹登录') # 0:未开启,1:已开启
+    fingerprint_enable = models.SmallIntegerField(default=0, verbose_name=u'是否开启了指纹登录')  # 0:未开启,1:已开启
     fingerprint_key = models.CharField(max_length=128, default='', verbose_name=u'客户端用于解码的密钥等信息')
     fingerprint_key = models.CharField(max_length=128, default='', verbose_name=u'客户端用于解码的密钥等信息')
-    is_local = models.BooleanField(blank=True, default=False, verbose_name=u'是否是本地登录用户') # False:账号登录,1:本地登录
-    subscribe_email = models.SmallIntegerField(default=0, verbose_name=u'是否订阅营销邮件') # 0:未订阅,1:订阅,2:不订阅
+    is_local = models.BooleanField(blank=True, default=False, verbose_name=u'是否是本地登录用户')  # False:账号登录,1:本地登录
+    subscribe_email = models.SmallIntegerField(default=0, verbose_name=u'是否订阅营销邮件')  # 0:未订阅,1:订阅,2:不订阅
     region_country = models.IntegerField(blank=True, default=0, verbose_name='地区表唯一标识')
     region_country = models.IntegerField(blank=True, default=0, verbose_name='地区表唯一标识')
     objects = UserManager()
     objects = UserManager()
 
 
@@ -270,7 +275,7 @@ class Device_Info(models.Model):
     iSNotification = models.BooleanField(blank=True, verbose_name=u'报警通知 0:关闭,1:开启)', default=False)
     iSNotification = models.BooleanField(blank=True, verbose_name=u'报警通知 0:关闭,1:开启)', default=False)
     isVod = models.SmallIntegerField(blank=True, default=0, verbose_name='是否支持云存')  # 是否支持云存设备
     isVod = models.SmallIntegerField(blank=True, default=0, verbose_name='是否支持云存')  # 是否支持云存设备
     isExist = models.SmallIntegerField(blank=True, default=1, verbose_name='是否被删除')  # 是否被删除了(需主用户交互) 1存在,0不存在,2设备被重置
     isExist = models.SmallIntegerField(blank=True, default=1, verbose_name='是否被删除')  # 是否被删除了(需主用户交互) 1存在,0不存在,2设备被重置
-    isCameraOpenCloud =  models.SmallIntegerField(blank=True, default=1, verbose_name='是否开启云存')  # 0:不开启  1:开启
+    isCameraOpenCloud = models.SmallIntegerField(blank=True, default=1, verbose_name='是否开启云存')  # 0:不开启  1:开启
     serial_number = models.CharField(blank=True, max_length=9, default='', verbose_name='关联序列号')
     serial_number = models.CharField(blank=True, max_length=9, default='', verbose_name='关联序列号')
     ###
     ###
     REQUIRED_FIELDS = []
     REQUIRED_FIELDS = []
@@ -324,7 +329,7 @@ class Equipment_Info(models.Model):
     alarm = models.CharField(blank=True, max_length=256, verbose_name=u'报警信息')
     alarm = models.CharField(blank=True, max_length=256, verbose_name=u'报警信息')
     eventTime = models.CharField(blank=True, db_index=True, default='', max_length=16, verbose_name=u'设备报警时间')
     eventTime = models.CharField(blank=True, db_index=True, default='', max_length=16, verbose_name=u'设备报警时间')
     receiveTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'接收到报警时间')
     receiveTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'接收到报警时间')
-    userID_id = models.CharField(default='',  db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
+    userID_id = models.CharField(default='', db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
     is_st = models.SmallIntegerField(default=0, verbose_name='是否截图')  # 0 否,1 是图,2,视频
     is_st = models.SmallIntegerField(default=0, verbose_name='是否截图')  # 0 否,1 是图,2,视频
     storage_location = models.SmallIntegerField(default=1, verbose_name='数据信息存储位置。1:阿里云oss,2:aws')
     storage_location = models.SmallIntegerField(default=1, verbose_name='数据信息存储位置。1:阿里云oss,2:aws')
     borderCoords = models.TextField(default='', blank=True, verbose_name=u'ai类型图片边框位置信息')
     borderCoords = models.TextField(default='', blank=True, verbose_name=u'ai类型图片边框位置信息')
@@ -568,7 +573,7 @@ class Ai_Push_Info(models.Model):
     alarm = models.CharField(blank=True, max_length=256, verbose_name=u'报警信息')
     alarm = models.CharField(blank=True, max_length=256, verbose_name=u'报警信息')
     eventTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'设备报警时间')
     eventTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'设备报警时间')
     receiveTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'接收到报警时间')
     receiveTime = models.CharField(blank=True, default='', max_length=16, verbose_name=u'接收到报警时间')
-    userID_id = models.CharField(default='',  db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
+    userID_id = models.CharField(default='', db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
     is_st = models.SmallIntegerField(default=0, verbose_name='是否截图')  # 0 否,1 是图,2,视频
     is_st = models.SmallIntegerField(default=0, verbose_name='是否截图')  # 0 否,1 是图,2,视频
     storage_location = models.SmallIntegerField(default=1, db_index=True, verbose_name='数据信息存储位置。1:阿里云oss,2:aws')
     storage_location = models.SmallIntegerField(default=1, db_index=True, verbose_name='数据信息存储位置。1:阿里云oss,2:aws')
     # message_id = models.CharField(blank=True, max_length=32, default='', verbose_name='第三方推送服务器返回的id')
     # message_id = models.CharField(blank=True, max_length=32, default='', verbose_name='第三方推送服务器返回的id')
@@ -700,10 +705,10 @@ class Store_Meal(models.Model):
     symbol = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'符号')
     symbol = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'符号')
     price = models.CharField(blank=True, max_length=32, verbose_name=u'价格')
     price = models.CharField(blank=True, max_length=32, verbose_name=u'价格')
     virtual_price = models.CharField(blank=True, max_length=32, verbose_name=u'虚拟价格')
     virtual_price = models.CharField(blank=True, max_length=32, verbose_name=u'虚拟价格')
-    is_discounts = models.SmallIntegerField(default=0, verbose_name=u'该套餐是否有优惠 [0=否,1是]')   # 0没有;1有
+    is_discounts = models.SmallIntegerField(default=0, verbose_name=u'该套餐是否有优惠 [0=否,1是]')  # 0没有;1有
     discount_price = models.CharField(blank=True, max_length=32, verbose_name=u'第二年优惠价格')
     discount_price = models.CharField(blank=True, max_length=32, verbose_name=u'第二年优惠价格')
-    day = models.IntegerField(default=0, blank=True, verbose_name=u'云存录像保存天数(循环)')   # 7,30,180,360
-    expire = models.IntegerField(default=0, blank=True, verbose_name=u'有效期') #单位月
+    day = models.IntegerField(default=0, blank=True, verbose_name=u'云存录像保存天数(循环)')  # 7,30,180,360
+    expire = models.IntegerField(default=0, blank=True, verbose_name=u'有效期')  # 单位月
     # content = models.TextField(blank=True, null=True, verbose_name=u'描述')
     # content = models.TextField(blank=True, null=True, verbose_name=u'描述')
     add_time = models.DateTimeField(blank=True, null=True, verbose_name=u'加入时间', auto_now_add=True)
     add_time = models.DateTimeField(blank=True, null=True, verbose_name=u'加入时间', auto_now_add=True)
     # type = models.SmallIntegerField(default=0, verbose_name='付款类型')  # 0是paypal,1为支付宝
     # type = models.SmallIntegerField(default=0, verbose_name='付款类型')  # 0是paypal,1为支付宝
@@ -717,7 +722,7 @@ class Store_Meal(models.Model):
     # lang = models.CharField(default='', max_length=20, verbose_name='语言/国家')
     # lang = models.CharField(default='', max_length=20, verbose_name='语言/国家')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='store_meal_lang')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='store_meal_lang')
     cycle_config_id = models.IntegerField(null=True, verbose_name='周期付款配置表id')
     cycle_config_id = models.IntegerField(null=True, verbose_name='周期付款配置表id')
-    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前') #单位月
+    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前')  # 单位月
     # 备用字段
     # 备用字段
     spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_3 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段3')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
     spare_4 = models.CharField(default='', blank=True, max_length=64, db_index=True, verbose_name=u'备用字段4')
@@ -731,6 +736,7 @@ class Store_Meal(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('id',)
         ordering = ('id',)
 
 
+
 class AiStoreMeal(models.Model):
 class AiStoreMeal(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增ID')
     price = models.CharField(blank=True, max_length=32, verbose_name=u'价格')
     price = models.CharField(blank=True, max_length=32, verbose_name=u'价格')
@@ -738,9 +744,9 @@ class AiStoreMeal(models.Model):
     symbol = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'符号')
     symbol = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'符号')
     currency = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'货币符号')
     currency = models.CharField(blank=True, default='$', max_length=32, verbose_name=u'货币符号')
     is_show = models.SmallIntegerField(default=0, verbose_name=u'是否显示')  # 0: 否, 1: 是
     is_show = models.SmallIntegerField(default=0, verbose_name=u'是否显示')  # 0: 否, 1: 是
-    is_discounts = models.SmallIntegerField(default=0, verbose_name=u'是否有优惠')   # 0: 没有, 1: 有
+    is_discounts = models.SmallIntegerField(default=0, verbose_name=u'是否有优惠')  # 0: 没有, 1: 有
     discount_price = models.CharField(blank=True, max_length=32, verbose_name=u'第二年优惠价格')
     discount_price = models.CharField(blank=True, max_length=32, verbose_name=u'第二年优惠价格')
-    effective_day = models.IntegerField(default=0, blank=True, verbose_name=u'有效天数')   # 7, 30
+    effective_day = models.IntegerField(default=0, blank=True, verbose_name=u'有效天数')  # 7, 30
     pay_type = models.ManyToManyField(to='Pay_Type', verbose_name='付款类型', db_table='ai_store_meal_pay')
     pay_type = models.ManyToManyField(to='Pay_Type', verbose_name='付款类型', db_table='ai_store_meal_pay')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='ai_store_meal_lang')
     lang = models.ManyToManyField(to='Lang', verbose_name='套餐语言', db_table='ai_store_meal_lang')
     add_time = models.DateTimeField(blank=True, verbose_name=u'添加时间', auto_now_add=True)
     add_time = models.DateTimeField(blank=True, verbose_name=u'添加时间', auto_now_add=True)
@@ -868,7 +874,7 @@ class Order_Model(models.Model):
     orderID = models.CharField(blank=True, max_length=20, primary_key=True, verbose_name=u'订单id')
     orderID = models.CharField(blank=True, max_length=20, primary_key=True, verbose_name=u'订单id')
     paymentID = models.CharField(blank=True, max_length=64, default='', verbose_name='付款id')
     paymentID = models.CharField(blank=True, max_length=64, default='', verbose_name='付款id')
     trade_no = models.CharField(blank=True, max_length=32, default='', verbose_name='第三方订单号')
     trade_no = models.CharField(blank=True, max_length=32, default='', verbose_name='第三方订单号')
-    userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)    # 订单关联用户
+    userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)  # 订单关联用户
     UID = models.CharField(max_length=20, verbose_name='设备UID')
     UID = models.CharField(max_length=20, verbose_name='设备UID')
     channel = models.SmallIntegerField(default=0, verbose_name=u'通道数')
     channel = models.SmallIntegerField(default=0, verbose_name=u'通道数')
     desc = models.CharField(max_length=50, default='', verbose_name='商品描述')
     desc = models.CharField(max_length=50, default='', verbose_name='商品描述')
@@ -884,7 +890,8 @@ class Order_Model(models.Model):
     payType = models.SmallIntegerField(default=0, verbose_name='支付方式')
     payType = models.SmallIntegerField(default=0, verbose_name='支付方式')
     payTime = models.IntegerField(verbose_name='支付成功时间', default=0)
     payTime = models.IntegerField(verbose_name='支付成功时间', default=0)
     rank = models.ForeignKey(Store_Meal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='关联云存套餐表')
     rank = models.ForeignKey(Store_Meal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='关联云存套餐表')
-    ai_rank = models.ForeignKey(AiStoreMeal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='关联ai套餐表')
+    ai_rank = models.ForeignKey(AiStoreMeal, to_field='id', default='', on_delete=models.CASCADE,
+                                verbose_name='关联ai套餐表')
     order_type = models.SmallIntegerField(default=0, verbose_name='订单类型:0:云存:1:ai')
     order_type = models.SmallIntegerField(default=0, verbose_name='订单类型:0:云存:1:ai')
     nickname = models.CharField(default='', max_length=64, verbose_name='设备昵称')
     nickname = models.CharField(default='', max_length=64, verbose_name='设备昵称')
     uid_bucket_id = models.IntegerField(default=0, verbose_name='关联uid_bucket的字段')
     uid_bucket_id = models.IntegerField(default=0, verbose_name='关联uid_bucket的字段')
@@ -909,16 +916,53 @@ class Order_Model(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('-orderID',)
         ordering = ('-orderID',)
 
 
-class CouponModel(models.Model):
+
+# 优惠券文案语言表
+class CouponLang(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='自增ID')
+    lang = models.CharField(default='', max_length=20, db_index=True, verbose_name='语言/国家')
+    instruction = models.CharField(blank=True, default='', max_length=200, verbose_name=u'用法')
+    quota = models.CharField(blank=True, default='', max_length=200, verbose_name=u'优惠额度, 例如20.0%')
+    unit = models.CharField(blank=True, default='', max_length=200, verbose_name=u'优惠单位, 例如off, 折')
+    remark = models.CharField(blank=True, default='', max_length=200, verbose_name=u'备注')
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'coupon_lang'
+        verbose_name = '套餐语言'
+        verbose_name_plural = verbose_name
+        ordering = ('id',)
+
+
+class CouponConfigModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     id = models.AutoField(primary_key=True, verbose_name='主键')
-    userID_id = models.CharField(default='', db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
-    use_status = models.SmallIntegerField(default=0, verbose_name='使用状态') #0未使用;1冻结;2已使用
-    type = models.SmallIntegerField(default=0, verbose_name='优惠类型') #1打折; 2抵扣
+    type = models.SmallIntegerField(default=0, verbose_name='优惠类型')  # 1打折; 2抵扣
+    use_range = models.CharField(default=0, max_length=100, verbose_name='使用限制')  # 0:所有服务都可使用; 1:云存;2:ai;多种1,2,3
     coupon_discount = models.CharField(blank=True, max_length=32, verbose_name=u'折扣力度')
     coupon_discount = models.CharField(blank=True, max_length=32, verbose_name=u'折扣力度')
-    distributeTime = models.IntegerField(verbose_name='发放到用户账户时间', default=0)
-    valid_time = models.PositiveIntegerField(default=9999999999, verbose_name='有效期间')  #超过有效期不可在使用;0永久
-    # use_limit = models.CharField(default=0, max_length=100, verbose_name='使用限制')  #0:所有服务都可使用; 1:云存;2:ai;
-    addTime = models.IntegerField(verbose_name='添加时间', default=0)
+    lang = models.ManyToManyField(to='CouponLang', verbose_name='套餐语言', db_table='coupon_lang_associated')
+
+    def __str__(self):
+        return self.id
+
+    class Meta:
+        db_table = 'coupon_config'
+        verbose_name = u'优惠券配置表'
+        verbose_name_plural = verbose_name
+
+
+class CouponModel(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    use_status = models.SmallIntegerField(default=0, verbose_name='使用状态')  # 0未使用;1冻结;2已使用
+    distribute_time = models.IntegerField(verbose_name='发放到用户账户时间', default=0)
+    valid_time = models.PositiveIntegerField(default=9999999999, verbose_name='有效期间')  # 超过有效期不可在使用;0永久
+    # userID = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE)  # 关联用户
+    userID = models.CharField(blank=True, max_length=32, db_index=True, verbose_name=u'用户ID')
+    coupon_config = models.ForeignKey(CouponConfigModel, null=True, blank=True, to_field='id', on_delete=models.CASCADE,
+                                      verbose_name='关联优惠券配置表的id')
+    update_time = models.IntegerField(verbose_name='更新时间', default=0)
+    create_time = models.IntegerField(verbose_name='添加时间', default=0)
 
 
     def __str__(self):
     def __str__(self):
         return self.id
         return self.id
@@ -928,12 +972,13 @@ class CouponModel(models.Model):
         verbose_name = u'优惠券'
         verbose_name = u'优惠券'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
 
 
+
 class PayCycleConfigModel(models.Model):
 class PayCycleConfigModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     id = models.AutoField(primary_key=True, verbose_name='主键')
     # name = models.CharField(default='',max_length=200, verbose_name='计划名字')
     # name = models.CharField(default='',max_length=200, verbose_name='计划名字')
     # tax = models.CharField(max_length=2000, default='', verbose_name='税金')
     # tax = models.CharField(max_length=2000, default='', verbose_name='税金')
     cycles = models.IntegerField(verbose_name='周期:0代表无限期', default=0)
     cycles = models.IntegerField(verbose_name='周期:0代表无限期', default=0)
-    frequency = models.CharField(max_length=50,verbose_name='频率,MONTH,WEEK,YEAR', default=0)
+    frequency = models.CharField(max_length=50, verbose_name='频率,MONTH,WEEK,YEAR', default=0)
     frequencyInterval = models.IntegerField(default=0, verbose_name='频率周期')
     frequencyInterval = models.IntegerField(default=0, verbose_name='频率周期')
     remark = models.CharField(max_length=1000, default='', verbose_name='备注')
     remark = models.CharField(max_length=1000, default='', verbose_name='备注')
 
 
@@ -945,16 +990,19 @@ class PayCycleConfigModel(models.Model):
         verbose_name = u'周期付款计划'
         verbose_name = u'周期付款计划'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
 
 
+
 class PromotionRuleModel(models.Model):
 class PromotionRuleModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     id = models.AutoField(primary_key=True, verbose_name='主键')
-    ruleName = models.TextField(default='', verbose_name='规则名字')                     #json格式, 例: {"cn":"黑色星期五","en":"Black Friday"}
-    ruleDesc = models.TextField(default='', verbose_name='规则描述')                     #json格式,   例:  {"cn":"买一送一","en":"buy one get one free"}
-    ruleConfig = models.CharField(max_length=2000, default='', verbose_name='规则配置')  #json格式, 例: {"buy": 1, "get": 1}
+    ruleName = models.TextField(default='', verbose_name='规则名字')  # json格式, 例: {"cn":"黑色星期五","en":"Black Friday"}
+    ruleDesc = models.TextField(default='',
+                                verbose_name='规则描述')  # json格式,   例:  {"cn":"买一送一","en":"buy one get one free"}
+    ruleConfig = models.CharField(max_length=2000, default='', verbose_name='规则配置')  # json格式, 例: {"buy": 1, "get": 1}
     startTime = models.IntegerField(verbose_name='促销活动开始时间', default=0)
     startTime = models.IntegerField(verbose_name='促销活动开始时间', default=0)
     endTime = models.IntegerField(verbose_name='促销活动结束时间', default=0)
     endTime = models.IntegerField(verbose_name='促销活动结束时间', default=0)
     status = models.SmallIntegerField(default=0, verbose_name='活动状态:0未进行;1进行中')
     status = models.SmallIntegerField(default=0, verbose_name='活动状态:0未进行;1进行中')
     remark = models.CharField(max_length=50, default='', verbose_name='备注')
     remark = models.CharField(max_length=50, default='', verbose_name='备注')
-    popups = models.CharField(max_length=2000, default='', verbose_name='app弹窗消息')   #json格式 ,例: {"cn":"买一送一","en":"buy one get one free"}
+    popups = models.CharField(max_length=2000, default='',
+                              verbose_name='app弹窗消息')  # json格式 ,例: {"cn":"买一送一","en":"buy one get one free"}
 
 
     def __str__(self):
     def __str__(self):
         return self.id
         return self.id
@@ -966,7 +1014,6 @@ class PromotionRuleModel(models.Model):
         ordering = ('-id',)
         ordering = ('-id',)
 
 
 
 
-
 class VodHlsModel(models.Model):
 class VodHlsModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='回放列表主键')
     id = models.AutoField(primary_key=True, verbose_name='回放列表主键')
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
@@ -976,8 +1023,7 @@ class VodHlsModel(models.Model):
     sec = models.IntegerField(verbose_name='秒数', default=0)
     sec = models.IntegerField(verbose_name='秒数', default=0)
     bucket = models.ForeignKey(VodBucketModel, blank=True, to_field='id', on_delete=models.CASCADE, default=1,
     bucket = models.ForeignKey(VodBucketModel, blank=True, to_field='id', on_delete=models.CASCADE, default=1,
                                verbose_name='存储空间')
                                verbose_name='存储空间')
-    fg = models.CharField(max_length=20,verbose_name='ts个数,时间描述片段数') # 阿里为时间片段数,亚马逊为一个32bit整型,前28bit代表ts文件的时长
-
+    fg = models.CharField(max_length=20, verbose_name='ts个数,时间描述片段数')  # 阿里为时间片段数,亚马逊为一个32bit整型,前28bit代表ts文件的时长
 
 
     def __str__(self):
     def __str__(self):
         return self.id
         return self.id
@@ -1016,7 +1062,7 @@ class StsCrdModel(models.Model):
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     bucket = models.ForeignKey(VodBucketModel, blank=True, to_field='id', on_delete=models.CASCADE, default=1,
     bucket = models.ForeignKey(VodBucketModel, blank=True, to_field='id', on_delete=models.CASCADE, default=1,
                                verbose_name='存储空间')
                                verbose_name='存储空间')
-    type = models.SmallIntegerField(default=0,verbose_name='sts类型') # 0:阿里云,1:s3
+    type = models.SmallIntegerField(default=0, verbose_name='sts类型')  # 0:阿里云,1:s3
 
 
     def __str__(self):
     def __str__(self):
         return self.id
         return self.id
@@ -1046,6 +1092,7 @@ class UID_Bucket(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('id',)
         ordering = ('id',)
 
 
+
 class Unused_Uid_Meal(models.Model):
 class Unused_Uid_Meal(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
@@ -1054,6 +1101,7 @@ class Unused_Uid_Meal(models.Model):
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     expire = models.IntegerField(verbose_name='存储桶存储时长[月份]', default=0)
     expire = models.IntegerField(verbose_name='存储桶存储时长[月份]', default=0)
     num = models.IntegerField(verbose_name='个数', default=0)
     num = models.IntegerField(verbose_name='个数', default=0)
+
     # effect_time = models.BigIntegerField(verbose_name='生效时间', db_index=True, default=0)
     # effect_time = models.BigIntegerField(verbose_name='生效时间', db_index=True, default=0)
     # uid_bucket = models.ForeignKey(UID_Bucket, blank=True, to_field='id', on_delete=models.CASCADE, default=0,
     # uid_bucket = models.ForeignKey(UID_Bucket, blank=True, to_field='id', on_delete=models.CASCADE, default=0,
     #                            verbose_name='uid_bucket关联')
     #                            verbose_name='uid_bucket关联')
@@ -1111,14 +1159,14 @@ class UidSetModel(models.Model):
     version = models.CharField(max_length=32, verbose_name='设备版本', default='')
     version = models.CharField(max_length=32, verbose_name='设备版本', default='')
     p2p_region = models.CharField(max_length=16, verbose_name='设备p2p区域', default='ALL')  # ALL CN EU US
     p2p_region = models.CharField(max_length=16, verbose_name='设备p2p区域', default='ALL')  # ALL CN EU US
     cloud_vod = models.SmallIntegerField(default=2, verbose_name='云存开关')  # 0,关闭,1开启,2,不支持
     cloud_vod = models.SmallIntegerField(default=2, verbose_name='云存开关')  # 0,关闭,1开启,2,不支持
-    tz = models.CharField(default='', max_length=16, verbose_name ='设备时区')  # +8
+    tz = models.CharField(default='', max_length=16, verbose_name='设备时区')  # +8
     video_code = models.SmallIntegerField(default=0, verbose_name='编码类型')  # 0:264,1:265
     video_code = models.SmallIntegerField(default=0, verbose_name='编码类型')  # 0:264,1:265
     nickname = models.CharField(default='', max_length=64, verbose_name='设备昵称')
     nickname = models.CharField(default='', max_length=64, verbose_name='设备昵称')
     ip = models.CharField(max_length=20, default='', verbose_name=u'设备ip')
     ip = models.CharField(max_length=20, default='', verbose_name=u'设备ip')
     # 设备重置后第一次启动时间
     # 设备重置后第一次启动时间
     is_alexa = models.IntegerField(default=0, verbose_name='是否支持alexa')  # 0为不支持,1为支持,2为开启alexa发现
     is_alexa = models.IntegerField(default=0, verbose_name='是否支持alexa')  # 0为不支持,1为支持,2为开启alexa发现
     detect_group = models.CharField(default='', max_length=32, verbose_name=u'检测类型')
     detect_group = models.CharField(default='', max_length=32, verbose_name=u'检测类型')
-    pwd = models.CharField(max_length=32, default='', verbose_name=u'设备密码') # 暂时是预留字段
+    pwd = models.CharField(max_length=32, default='', verbose_name=u'设备密码')  # 暂时是预留字段
     resetTime = models.IntegerField(default=0, verbose_name='设备重置时间')
     resetTime = models.IntegerField(default=0, verbose_name='设备重置时间')
     region_alexa = models.CharField(max_length=8, verbose_name='设备alexa区域', default='ALL')  # ALL CN EU US
     region_alexa = models.CharField(max_length=8, verbose_name='设备alexa区域', default='ALL')  # ALL CN EU US
     deviceModel = models.CharField(blank=True, max_length=64, default='', verbose_name=u'设备型号')
     deviceModel = models.CharField(blank=True, max_length=64, default='', verbose_name=u'设备型号')
@@ -1137,6 +1185,9 @@ class UidSetModel(models.Model):
     double_wifi = models.IntegerField(default=0, verbose_name='是否支持双频wifi。0:不支持,1:支持')
     double_wifi = models.IntegerField(default=0, verbose_name='是否支持双频wifi。0:不支持,1:支持')
     is_ptz = models.IntegerField(default=0, verbose_name='是否支持云台。0:不支持,1:支持')
     is_ptz = models.IntegerField(default=0, verbose_name='是否支持云台。0:不支持,1:支持')
     is_ai = models.IntegerField(default=2, verbose_name='是否支持ai')  # 0,关闭,1开启,2,不支持
     is_ai = models.IntegerField(default=2, verbose_name='是否支持ai')  # 0,关闭,1开启,2,不支持
+    is_notification = models.IntegerField(blank=True, default=1, verbose_name='新加-消息提醒开关')  # 0:关闭,1:开启
+    new_detect_interval = models.IntegerField(blank=True, verbose_name='新加-消息提醒间隔', default=60)  # 秒
+
     class Meta:
     class Meta:
         db_table = 'uid_set'
         db_table = 'uid_set'
         verbose_name = u'设备配置表'
         verbose_name = u'设备配置表'
@@ -1380,7 +1431,7 @@ class GrantCodeModel(models.Model):
 class UserAppFrequencyModel(models.Model):
 class UserAppFrequencyModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     user = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE, verbose_name='关联设备用户表')
     user = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE, verbose_name='关联设备用户表')
-    type = models.SmallIntegerField(default=0, verbose_name='使用频率类型') # 1:每天,2:三天,3:一周,4:两周,5:一个月,6:一个月以上
+    type = models.SmallIntegerField(default=0, verbose_name='使用频率类型')  # 1:每天,2:三天,3:一周,4:两周,5:一个月,6:一个月以上
     data_time = models.IntegerField(default=0, verbose_name='数据时间')
     data_time = models.IntegerField(default=0, verbose_name='数据时间')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
@@ -1415,6 +1466,7 @@ class AppFrequencyYearStatisticsModel(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         db_table = 'app_frequency_year_statistics'
         db_table = 'app_frequency_year_statistics'
 
 
+
 # alexa连接数统计表
 # alexa连接数统计表
 class AlexaConnectStatisticsModel(models.Model):
 class AlexaConnectStatisticsModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
@@ -1445,7 +1497,7 @@ class AppLogModel(models.Model):
     user = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE, verbose_name='关联设备用户表')
     user = models.ForeignKey(Device_User, to_field='userID', on_delete=models.CASCADE, verbose_name='关联设备用户表')
     uid = models.CharField(max_length=20, default='', verbose_name='设备uid')
     uid = models.CharField(max_length=20, default='', verbose_name='设备uid')
     average_delay = models.CharField(max_length=32, default='', verbose_name='最高平均延时')
     average_delay = models.CharField(max_length=32, default='', verbose_name='最高平均延时')
-    status = models.SmallIntegerField(default=0, verbose_name='失败状态')   # 0: 成功,1: 失败
+    status = models.SmallIntegerField(default=0, verbose_name='失败状态')  # 0: 成功,1: 失败
     filename = models.CharField(max_length=120, default='', verbose_name='文件名')
     filename = models.CharField(max_length=120, default='', verbose_name='文件名')
     add_time = models.IntegerField(default=0, verbose_name='日期')
     add_time = models.IntegerField(default=0, verbose_name='日期')
 
 
@@ -1460,9 +1512,9 @@ class DeviceLogModel(models.Model):
     ip = models.CharField(default='', max_length=32, verbose_name='ip')
     ip = models.CharField(default='', max_length=32, verbose_name='ip')
     uid = models.CharField(max_length=32, default='', verbose_name='设备uid')
     uid = models.CharField(max_length=32, default='', verbose_name='设备uid')
     serial_number = models.CharField(max_length=9, default='', verbose_name='序列号')
     serial_number = models.CharField(max_length=9, default='', verbose_name='序列号')
-    status = models.SmallIntegerField(default=0, verbose_name='上传状态')   # 0: 成功,1: 失败, 3非文件形式,与error_info相关
+    status = models.SmallIntegerField(default=0, verbose_name='上传状态')  # 0: 成功,1: 失败, 3非文件形式,与error_info相关
     filename = models.CharField(max_length=120, default='', verbose_name='文件名')
     filename = models.CharField(max_length=120, default='', verbose_name='文件名')
-    error_info = models.TextField(blank = True, default = '', verbose_name='设备异常信息')
+    error_info = models.TextField(blank=True, default='', verbose_name='设备异常信息')
     add_time = models.DateTimeField(blank=True, auto_now_add=True, verbose_name=u'添加时间')
     add_time = models.DateTimeField(blank=True, auto_now_add=True, verbose_name=u'添加时间')
 
 
     class Meta:
     class Meta:
@@ -1504,8 +1556,8 @@ class CountryIPModel(models.Model):
 class ZositechHelpModel(models.Model):
 class ZositechHelpModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     locale = models.CharField(default='', db_index=True, max_length=50, verbose_name='语言')
     locale = models.CharField(default='', db_index=True, max_length=50, verbose_name='语言')
-    label_names =  models.CharField(default='', db_index=True, max_length=50, verbose_name='标签')
-    origin  =  models.CharField(default='', db_index=True ,max_length=50, verbose_name='来源')
+    label_names = models.CharField(default='', db_index=True, max_length=50, verbose_name='标签')
+    origin = models.CharField(default='', db_index=True, max_length=50, verbose_name='来源')
     # author_id = models.CharField(default='', max_length=50, verbose_name='管理员id')
     # author_id = models.CharField(default='', max_length=50, verbose_name='管理员id')
     # body = models.TextField(default='', blank=True, verbose_name='内容')
     # body = models.TextField(default='', blank=True, verbose_name='内容')
     # comments_disabled = models.CharField(default='', max_length=10, verbose_name='comments')
     # comments_disabled = models.CharField(default='', max_length=10, verbose_name='comments')
@@ -1532,7 +1584,8 @@ class EquipmentVersionLimitModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     type = models.SmallIntegerField(default=0, verbose_name='限制类型')
     type = models.SmallIntegerField(default=0, verbose_name='限制类型')
     content = models.TextField(default='', verbose_name='限制内容')
     content = models.TextField(default='', verbose_name='限制内容')
-    equipment_version = models.ForeignKey(Equipment_Version, to_field='eid', on_delete=models.CASCADE, verbose_name='关联设备版本信息id')
+    equipment_version = models.ForeignKey(Equipment_Version, to_field='eid', on_delete=models.CASCADE,
+                                          verbose_name='关联设备版本信息id')
     status = models.SmallIntegerField(default=0, verbose_name='是否启用。0:不启用,1:启用')
     status = models.SmallIntegerField(default=0, verbose_name='是否启用。0:不启用,1:启用')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
@@ -1545,9 +1598,10 @@ class EquipmentVersionLimitModel(models.Model):
 
 
 class ExperienceContextModel(models.Model):
 class ExperienceContextModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
-    experience_type = models.SmallIntegerField(default=0,verbose_name='体验类型') # 0: 免费体验套餐, 1: 激活码
+    experience_type = models.SmallIntegerField(default=0, verbose_name='体验类型')  # 0: 免费体验套餐, 1: 激活码
     uid = models.CharField(max_length=20, default='', verbose_name='设备uid')
     uid = models.CharField(max_length=20, default='', verbose_name='设备uid')
     do_time = models.IntegerField(default=0, verbose_name='激活时间')
     do_time = models.IntegerField(default=0, verbose_name='激活时间')
+
     # is_experience = models.SmallIntegerField(default=0, verbose_name=u'是否云存体验用户')  # 0:不是体验用户,1:是体验用户
     # is_experience = models.SmallIntegerField(default=0, verbose_name=u'是否云存体验用户')  # 0:不是体验用户,1:是体验用户
 
 
     class Meta:
     class Meta:
@@ -1555,13 +1609,26 @@ class ExperienceContextModel(models.Model):
         verbose_name = '设备体验表'
         verbose_name = '设备体验表'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
 
 
+
+class ExperienceAiModel(models.Model):
+    id = models.AutoField(primary_key=True)
+    experience_type = models.SmallIntegerField(default=0, verbose_name='体验类型')  # 0: 免费体验, 1: 激活码
+    uid = models.CharField(max_length=20, default='', verbose_name='设备uid')
+    do_time = models.IntegerField(default=0, verbose_name='激活时间')
+
+    class Meta:
+        db_table = 'experience_ai'
+        verbose_name = 'ai体验表'
+        verbose_name_plural = verbose_name
+
+
 class CDKcontextModel(models.Model):
 class CDKcontextModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
-    cdk = models.CharField(max_length=50,unique=True,verbose_name='激活码')
+    cdk = models.CharField(max_length=50, unique=True, verbose_name='激活码')
     create_time = models.IntegerField(default=0, verbose_name='创建时间')
     create_time = models.IntegerField(default=0, verbose_name='创建时间')
-    valid_time = models.IntegerField(default=0, verbose_name='有效期间')  #超过有效期激活码不可在激活 ,0:永久
-    is_activate = models.SmallIntegerField(default=0, verbose_name='是否已激活') #0 未激活  1 已激活
-    is_down = models.SmallIntegerField(default=0, verbose_name='是否已下载') #0 未下载 1 已下载
+    valid_time = models.IntegerField(default=0, verbose_name='有效期间')  # 超过有效期激活码不可在激活 ,0:永久
+    is_activate = models.SmallIntegerField(default=0, verbose_name='是否已激活')  # 0 未激活  1 已激活
+    is_down = models.SmallIntegerField(default=0, verbose_name='是否已下载')  # 0 未下载 1 已下载
     rank = models.ForeignKey(Store_Meal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='套餐类型')
     rank = models.ForeignKey(Store_Meal, to_field='id', default='', on_delete=models.CASCADE, verbose_name='套餐类型')
     # order = models.ForeignKey(Order_Model, blank=True, max_length=20, to_field='orderID', on_delete=models.CASCADE, verbose_name='订单id', unique=True)
     # order = models.ForeignKey(Order_Model, blank=True, max_length=20, to_field='orderID', on_delete=models.CASCADE, verbose_name='订单id', unique=True)
     order = models.CharField(max_length=20, blank=True, unique=True, verbose_name='订单id')
     order = models.CharField(max_length=20, blank=True, unique=True, verbose_name='订单id')
@@ -1640,6 +1707,7 @@ class ProcessInfoLogsModel(models.Model):
         verbose_name = '过程信息表'
         verbose_name = '过程信息表'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
 
 
+
 class EquipmentLogModel(models.Model):
 class EquipmentLogModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     user = models.CharField(blank=False, max_length=32, db_index=True, verbose_name=u'操作用户')
     user = models.CharField(blank=False, max_length=32, db_index=True, verbose_name=u'操作用户')
@@ -1650,6 +1718,7 @@ class EquipmentLogModel(models.Model):
     time = models.DateTimeField(null=True, blank=True, db_index=True, verbose_name=u'访问时间')
     time = models.DateTimeField(null=True, blank=True, db_index=True, verbose_name=u'访问时间')
     operatingcontent = models.TextField(blank=True, default='', verbose_name=u'操作内容')
     operatingcontent = models.TextField(blank=True, default='', verbose_name=u'操作内容')
     url = models.CharField(max_length=150, default='', blank=True, verbose_name=u'访问路径')
     url = models.CharField(max_length=150, default='', blank=True, verbose_name=u'访问路径')
+
     class Meta:
     class Meta:
         db_table = 'equipment_log'
         db_table = 'equipment_log'
         verbose_name = '设备日志表'
         verbose_name = '设备日志表'
@@ -1693,7 +1762,6 @@ class RegionModel(models.Model):
     continent_code = models.CharField(max_length=3, default='', verbose_name='洲代码')
     continent_code = models.CharField(max_length=3, default='', verbose_name='洲代码')
     api = models.CharField(max_length=50, default='', verbose_name='请求地址')
     api = models.CharField(max_length=50, default='', verbose_name='请求地址')
 
 
-
     class Meta:
     class Meta:
         db_table = 'tb_region'
         db_table = 'tb_region'
         verbose_name = '区域表'
         verbose_name = '区域表'
@@ -1720,10 +1788,10 @@ class UIDModel(models.Model):
     uid = models.CharField(max_length=20, null=False, db_index=True, unique=True, verbose_name='设备id')
     uid = models.CharField(max_length=20, null=False, db_index=True, unique=True, verbose_name='设备id')
     mac = models.CharField(max_length=17, null=True, default='', verbose_name='设备id对应的mac地址')
     mac = models.CharField(max_length=17, null=True, default='', verbose_name='设备id对应的mac地址')
     uid_extra = models.TextField(default='', verbose_name='uid的额外描述')
     uid_extra = models.TextField(default='', verbose_name='uid的额外描述')
-    status = models.SmallIntegerField(default=0, verbose_name='使用状态')   # 0:未使用, 2:已使用
+    status = models.SmallIntegerField(default=0, verbose_name='使用状态')  # 0:未使用, 2:已使用
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
-    area = models.SmallIntegerField(default=0, verbose_name='区域')   # 0:国内, 1:国外
+    area = models.SmallIntegerField(default=0, verbose_name='区域')  # 0:国内, 1:国外
     vpg = models.ForeignKey(VPGModel, to_field='id', default=1, on_delete=models.DO_NOTHING, verbose_name='关联VPG表的id')
     vpg = models.ForeignKey(VPGModel, to_field='id', default=1, on_delete=models.DO_NOTHING, verbose_name='关联VPG表的id')
     p2p_type = models.IntegerField(default=1, verbose_name='p2p类型。1:宸云,2:tutk')
     p2p_type = models.IntegerField(default=1, verbose_name='p2p类型。1:宸云,2:tutk')
     full_uid_code = models.CharField(max_length=256, default='', verbose_name='宸云完整uid')
     full_uid_code = models.CharField(max_length=256, default='', verbose_name='宸云完整uid')
@@ -1895,7 +1963,8 @@ class CompanySerialModel(models.Model):
 class UIDCompanySerialModel(models.Model):
 class UIDCompanySerialModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     uid = models.ForeignKey(UIDModel, to_field='id', on_delete=models.CASCADE, verbose_name='关联UID表')
     uid = models.ForeignKey(UIDModel, to_field='id', on_delete=models.CASCADE, verbose_name='关联UID表')
-    company_serial = models.ForeignKey(CompanySerialModel, to_field='id', on_delete=models.CASCADE, verbose_name='6位数序列号')
+    company_serial = models.ForeignKey(CompanySerialModel, to_field='id', on_delete=models.CASCADE,
+                                       verbose_name='6位数序列号')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     add_time = models.IntegerField(default=0, verbose_name='添加时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
     update_time = models.IntegerField(default=0, verbose_name='更新时间')
 
 
@@ -1907,7 +1976,8 @@ class UIDCompanySerialModel(models.Model):
 
 
 class iotdeviceInfoModel(models.Model):
 class iotdeviceInfoModel(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
-    serial_number = models.CharField(max_length=11, blank=True, default='', db_index=True, verbose_name=u'关联Device_Info表的序列号')
+    serial_number = models.CharField(max_length=11, blank=True, default='', db_index=True,
+                                     verbose_name=u'关联Device_Info表的序列号')
     uid = models.CharField(blank=True, max_length=32, default='', db_index=True, verbose_name=u'设备UID')
     uid = models.CharField(blank=True, max_length=32, default='', db_index=True, verbose_name=u'设备UID')
     certificate_id = models.CharField(blank=True, max_length=256, default='', verbose_name=u'证书id')
     certificate_id = models.CharField(blank=True, max_length=256, default='', verbose_name=u'证书id')
     certificate_pem = models.TextField(blank=True, default='', verbose_name=u'证书项目')
     certificate_pem = models.TextField(blank=True, default='', verbose_name=u'证书项目')
@@ -1916,9 +1986,10 @@ class iotdeviceInfoModel(models.Model):
     thing_name = models.CharField(blank=True, max_length=256, default='', verbose_name=u'IoT Thing Name')
     thing_name = models.CharField(blank=True, max_length=256, default='', verbose_name=u'IoT Thing Name')
     thing_groups = models.CharField(blank=True, max_length=256, default='', verbose_name=u'IoT Thing Groups')
     thing_groups = models.CharField(blank=True, max_length=256, default='', verbose_name=u'IoT Thing Groups')
     endpoint = models.CharField(blank=True, max_length=256, db_index=True, default='', verbose_name=u'iot端点')
     endpoint = models.CharField(blank=True, max_length=256, db_index=True, default='', verbose_name=u'iot端点')
-    token_iot_number = models.CharField(blank=True,  db_index=True, default='', max_length=50, verbose_name='连接iot令牌')
+    token_iot_number = models.CharField(blank=True, db_index=True, default='', max_length=50, verbose_name='连接iot令牌')
     add_time = models.DateTimeField(blank=True, auto_now_add=True, verbose_name=u'添加时间')
     add_time = models.DateTimeField(blank=True, auto_now_add=True, verbose_name=u'添加时间')
     update_time = models.DateTimeField(blank=True, auto_now=True, verbose_name=u'更新时间')
     update_time = models.DateTimeField(blank=True, auto_now=True, verbose_name=u'更新时间')
+
     class Meta:
     class Meta:
         db_table = 'iot_deviceInfo'
         db_table = 'iot_deviceInfo'
         verbose_name = 'iot设备信息表'
         verbose_name = 'iot设备信息表'
@@ -1930,6 +2001,7 @@ class UIDMainUser(models.Model):
     id = models.AutoField(primary_key=True)
     id = models.AutoField(primary_key=True)
     UID = models.CharField(blank=True, max_length=32, verbose_name=u'设备UID', default='')
     UID = models.CharField(blank=True, max_length=32, verbose_name=u'设备UID', default='')
     user_id = models.CharField(blank=True, max_length=32, verbose_name=u'用户ID', default='')
     user_id = models.CharField(blank=True, max_length=32, verbose_name=u'用户ID', default='')
+
     class Meta:
     class Meta:
         db_table = 'uid_mainuser'
         db_table = 'uid_mainuser'
         verbose_name = '设备主用户表'
         verbose_name = '设备主用户表'
@@ -1954,7 +2026,6 @@ class Pc_Info(models.Model):
     content = models.TextField(blank=True, default='', verbose_name=u'内容信息')
     content = models.TextField(blank=True, default='', verbose_name=u'内容信息')
     authority = models.SmallIntegerField(blank=True, default=0, verbose_name='权限')
     authority = models.SmallIntegerField(blank=True, default=0, verbose_name='权限')
 
 
-
     class Meta:
     class Meta:
         db_table = 'pc_info'
         db_table = 'pc_info'
         verbose_name = u'pc信息表'
         verbose_name = u'pc信息表'
@@ -1971,6 +2042,7 @@ class CloudLogModel(models.Model):
     url = models.CharField(max_length=150, default='', blank=True, verbose_name=u'访问路径')
     url = models.CharField(max_length=150, default='', blank=True, verbose_name=u'访问路径')
     time = models.IntegerField(null=True, blank=True, db_index=True, verbose_name=u'访问时间')
     time = models.IntegerField(null=True, blank=True, db_index=True, verbose_name=u'访问时间')
     content = models.TextField(blank=True, default='', verbose_name=u'参数内容')
     content = models.TextField(blank=True, default='', verbose_name=u'参数内容')
+
     class Meta:
     class Meta:
         db_table = 'cloud_log'
         db_table = 'cloud_log'
         verbose_name = '云存api记录表'
         verbose_name = '云存api记录表'
@@ -1995,6 +2067,7 @@ class PctestjobModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     jobname = models.CharField(blank=True, max_length=32, verbose_name=u'岗位名字')
     jobname = models.CharField(blank=True, max_length=32, verbose_name=u'岗位名字')
     jobcode = models.SmallIntegerField(default=3, verbose_name='岗位code 。1:管理员,3:普通人员')
     jobcode = models.SmallIntegerField(default=3, verbose_name='岗位code 。1:管理员,3:普通人员')
+
     class Meta:
     class Meta:
         db_table = 'pctest_job'
         db_table = 'pctest_job'
         verbose_name = u'pc岗位表'
         verbose_name = u'pc岗位表'
@@ -2030,6 +2103,7 @@ class PctestfunctionModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     functionname = models.CharField(blank=True, max_length=32, verbose_name=u'职能名字')
     functionname = models.CharField(blank=True, max_length=32, verbose_name=u'职能名字')
     functioncode = models.SmallIntegerField(default=1, verbose_name='职能code 。PC端自己逻辑判断')
     functioncode = models.SmallIntegerField(default=1, verbose_name='职能code 。PC端自己逻辑判断')
+
     class Meta:
     class Meta:
         db_table = 'pctest_function'
         db_table = 'pctest_function'
         verbose_name = u'pc设备职能表'
         verbose_name = u'pc设备职能表'
@@ -2078,7 +2152,8 @@ class PctestjobdeviceModel(models.Model):
 class PctestModel(models.Model):
 class PctestModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     device = models.ForeignKey(PctestdeviceModel, to_field='id', on_delete=models.DO_NOTHING, verbose_name='关联pc测试设备表')
     device = models.ForeignKey(PctestdeviceModel, to_field='id', on_delete=models.DO_NOTHING, verbose_name='关联pc测试设备表')
-    function = models.ForeignKey(PctestfunctionModel, to_field='id', on_delete=models.DO_NOTHING, verbose_name='关联pc设备职能表')
+    function = models.ForeignKey(PctestfunctionModel, to_field='id', on_delete=models.DO_NOTHING,
+                                 verbose_name='关联pc设备职能表')
 
 
     class Meta:
     class Meta:
         db_table = 'pctest_device_function'
         db_table = 'pctest_device_function'
@@ -2144,16 +2219,17 @@ class AiService(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
     uid = models.CharField(max_length=20, verbose_name='设备UID', db_index=True)
     channel = models.SmallIntegerField(default=0, verbose_name='通道')
     channel = models.SmallIntegerField(default=0, verbose_name='通道')
-    orders = models.ForeignKey(Order_Model, to_field='orderID', default='', on_delete=models.CASCADE, verbose_name='关联订单表')
-    detect_status = models.SmallIntegerField(default=0, verbose_name='状态[0:关闭,1:开启]')
+    orders = models.ForeignKey(Order_Model, to_field='orderID', default='', on_delete=models.CASCADE,
+                               verbose_name='关联订单表')
+    detect_status = models.SmallIntegerField(default=0, verbose_name='状态')  # 0:关闭, 1:开启
     endTime = models.BigIntegerField(verbose_name='套餐结束时间', db_index=True, default=0)
     endTime = models.BigIntegerField(verbose_name='套餐结束时间', db_index=True, default=0)
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     updTime = models.BigIntegerField(verbose_name='更新时间', default=0)
     updTime = models.BigIntegerField(verbose_name='更新时间', default=0)
-    use_status = models.IntegerField(verbose_name='使用状态[0:未使用,1:使用中,2已过期]', default=0)
-    detect_group = models.CharField(blank=True, default='1', max_length=100, verbose_name='侦测类型[1=人;2=动物;3=车]')
+    use_status = models.IntegerField(verbose_name='使用状态', default=0)  # 0:未使用, 1:使用中, 2:已过期
+    # 1:人, 2:动物, 3:车, 4:包裹
+    detect_group = models.CharField(blank=True, default='1', max_length=100, verbose_name='侦测类型')
     detect_interval = models.IntegerField(verbose_name='推送间隔', default=60)  # 秒
     detect_interval = models.IntegerField(verbose_name='推送间隔', default=60)  # 秒
 
 
-
     class Meta:
     class Meta:
         db_table = 'ai_service'
         db_table = 'ai_service'
         verbose_name = 'ai设备服务表'
         verbose_name = 'ai设备服务表'
@@ -2263,6 +2339,7 @@ class CloudVodSurveysOperateLog(models.Model):
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('id',)
         ordering = ('id',)
 
 
+
 class DeviceOTAUpgradeRecord(models.Model):
 class DeviceOTAUpgradeRecord(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     created_time = models.IntegerField(default=0, verbose_name='创建时间')
     created_time = models.IntegerField(default=0, verbose_name='创建时间')
@@ -2272,12 +2349,14 @@ class DeviceOTAUpgradeRecord(models.Model):
     serial_number = models.CharField(max_length=11, blank=True, verbose_name='序列号', default='')
     serial_number = models.CharField(max_length=11, blank=True, verbose_name='序列号', default='')
     uid = models.CharField(max_length=22, blank=True, verbose_name='设备UID', default='')
     uid = models.CharField(max_length=22, blank=True, verbose_name='设备UID', default='')
     mci = models.CharField(max_length=10, blank=True, verbose_name='设备大类:IPC/NVR/DVR', default='')
     mci = models.CharField(max_length=10, blank=True, verbose_name='设备大类:IPC/NVR/DVR', default='')
+
     class Meta:
     class Meta:
         db_table = 'device_OTA_upgrade_record'
         db_table = 'device_OTA_upgrade_record'
         verbose_name = '设备OTA升级记录'
         verbose_name = '设备OTA升级记录'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
         ordering = ('id',)
         ordering = ('id',)
 
 
+
 class PaypalWebHookEvent(models.Model):
 class PaypalWebHookEvent(models.Model):
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
     webhook_event_id = models.CharField(max_length=200, blank=True, verbose_name='webhook事件ID', default='')
     webhook_event_id = models.CharField(max_length=200, blank=True, verbose_name='webhook事件ID', default='')
@@ -2298,4 +2377,152 @@ class PaypalWebHookEvent(models.Model):
         db_table = 'paypal_webhook_event'
         db_table = 'paypal_webhook_event'
         verbose_name = 'paypal钩子事件记录表'
         verbose_name = 'paypal钩子事件记录表'
         verbose_name_plural = verbose_name
         verbose_name_plural = verbose_name
-        ordering = ('id',)
+        ordering = ('id',)
+
+
+class PopupsConfig(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+
+    title = models.CharField(max_length=1000, verbose_name='弹窗标题', blank=True, default='')
+    content = models.CharField(max_length=1000, verbose_name='弹窗内容', blank=True, default='')
+    tag = models.SmallIntegerField(default=0, verbose_name='app跳转页面标签{1:云存储购 2:AI购买 3:优惠券}')
+    start_time = models.IntegerField(default=0, verbose_name='更新时间')
+    end_time = models.IntegerField(default=0, verbose_name='创建时间')
+    lang = models.CharField(blank=True, max_length=16, default='en', verbose_name=u'语言地区')
+
+    class Meta:
+        db_table = 'popups_config'
+        verbose_name = 'app弹窗设置'
+        verbose_name_plural = verbose_name
+        ordering = ('id',)
+
+
+class RedDotsConfig(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    module = models.CharField(max_length=1000, verbose_name='app模块', blank=True, default='')
+    start_time = models.IntegerField(default=0, verbose_name='更新时间')
+    end_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'red_dots_config'
+        verbose_name = '红点标记配置'
+        verbose_name_plural = verbose_name
+        ordering = ('id',)
+
+
+class UserFamily(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    user = models.ForeignKey(Device_User, to_field='userID', default='', on_delete=models.CASCADE,
+                             verbose_name='关联用户表ID')
+    name = models.CharField(max_length=50, db_index=True, verbose_name=u'家庭名称', default='', blank=True)
+    location = models.CharField(max_length=100, verbose_name='位置', blank=True, default='')
+    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'user_family'
+        verbose_name = '用户家庭'
+        verbose_name_plural = verbose_name
+
+
+class FamilyMemberPermission(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    no = models.CharField(max_length=10, unique=True, verbose_name='编号', default=0)
+    name = models.CharField(max_length=50, verbose_name=u'权限名称', default='')
+    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前')
+    describe = models.CharField(max_length=128, blank=True, verbose_name='权限描述', default=0)
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'family_member_permission'
+        verbose_name = '家庭成员权限'
+        verbose_name_plural = verbose_name
+
+
+class FamilyMember(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    user = models.ForeignKey(Device_User, to_field='userID', default='', on_delete=models.CASCADE,
+                             verbose_name='关联用户表id')
+    user_name = models.CharField(max_length=64, db_index=True, verbose_name=u'用户名', default='', blank=True)
+    family = models.ForeignKey(UserFamily, to_field='id', default='', on_delete=models.CASCADE,
+                               verbose_name='关联用户家庭id')
+    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前')
+    identity = models.SmallIntegerField(default=0, verbose_name='状态{0:普通成员,1:屋主}')
+    permission = models.ForeignKey(FamilyMemberPermission, to_field='id', default='', on_delete=models.CASCADE,
+                                   verbose_name='关联用户家庭id')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'family_member'
+        verbose_name = '家庭成员'
+        verbose_name_plural = verbose_name
+
+
+class FamilyRoom(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    name = models.CharField(max_length=128, verbose_name=u'房间名称', default='', blank=True)
+    family = models.ForeignKey(UserFamily, to_field='id', default='', on_delete=models.CASCADE,
+                               verbose_name='关联用户家庭id')
+    sort = models.IntegerField(default=99, blank=True, verbose_name=u'排序,越小越靠前')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'family_room'
+        verbose_name = '家庭房间'
+        verbose_name_plural = verbose_name
+
+
+class GatewaySubDevice(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    device = models.ForeignKey(Device_Info, to_field='id', default='', on_delete=models.CASCADE,
+                               verbose_name='关联设备信息id')
+    device_type = models.SmallIntegerField(default=0, verbose_name=u'设备类型')
+    nickname = models.CharField(default='', max_length=32, verbose_name=u'设备名称')
+    src_addr = models.CharField(default='', max_length=16, verbose_name=u'短地址')
+    status = models.SmallIntegerField(default=0, verbose_name='状态')  # 0:关闭, 1:开启
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+
+    class Meta:
+        db_table = 'gateway_sub_device'
+        verbose_name = '网关子设备'
+        verbose_name_plural = verbose_name
+
+
+class FamilyRoomDevice(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    room_id = models.IntegerField(default=0, blank=True, verbose_name=u'房间id')
+    family_id = models.IntegerField(default=0, blank=True, verbose_name=u'家庭id')
+    device = models.ForeignKey(Device_Info, to_field='id', default='', on_delete=models.CASCADE,
+                               verbose_name='关联设备信息id')
+    sub_device = models.ForeignKey(GatewaySubDevice, to_field='id', default='', on_delete=models.CASCADE,
+                                   verbose_name='关联子设备信息id')
+    sort = models.IntegerField(default=0, blank=True, verbose_name=u'排序,越小越靠前')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'family_room_device'
+        verbose_name = '家庭房间关联设备'
+        verbose_name_plural = verbose_name
+
+
+class FamilyMemberJoin(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name=u'自增标记ID')
+    user = models.ForeignKey(Device_User, to_field='userID', default='', on_delete=models.CASCADE,
+                             verbose_name='关联用户表id')
+    family = models.ForeignKey(UserFamily, to_field='id', default='', on_delete=models.CASCADE,
+                               verbose_name='关联用户家庭id')
+    status = models.SmallIntegerField(default=0, verbose_name='状态{0:未确认,1:拒绝,2:同意}')
+    updated_time = models.IntegerField(default=0, verbose_name='更新时间')
+    created_time = models.IntegerField(default=0, verbose_name='创建时间')
+
+    class Meta:
+        db_table = 'family_member_join'
+        verbose_name = '家庭成员加入'
+        verbose_name_plural = verbose_name
+

+ 850 - 0
SensorGateway/EquipmentFamilyController.py

@@ -0,0 +1,850 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : EquipmentFamilyController.py
+@Time    : 2022/5/13 15:50
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+
+import time
+
+import oss2
+from django.db import connection
+from django.db import transaction
+from django.db.models import Q, Count
+from django.views.generic.base import View
+
+from Ansjer.config import OSS_STS_ACCESS_SECRET, OSS_STS_ACCESS_KEY
+from Controller.DeviceConfirmRegion import Device_Region
+from Model.models import Device_Info, UID_Bucket, UID_Preview, UidSetModel, UidChannelSetModel, \
+    iotdeviceInfoModel, UIDModel, Device_User, UserFamily, FamilyMember, FamilyMemberPermission, \
+    FamilyRoomDevice, FamilyRoom
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from Service.CommonService import CommonService
+
+
+# 家庭设备管理
+class EquipmentFamilyView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+
+        token = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
+        lang = request_dict.get('lang', None)
+        if lang:
+            response = ResponseObject(lang)
+        else:
+            response = ResponseObject(token.lang) if token.lang else ResponseObject()
+
+        if token.code != 0:
+            return response.json(token.code)
+        user_id = token.userID
+
+        # 手机端添加设备,查询,修改
+        if operation == 'add':
+            return self.do_save(user_id, request_dict, response, request)
+        # 分页获取未添加房间设备
+        elif operation == 'family-device-query':
+            return self.get_device_not_in_room(user_id, request_dict, response)
+        # 条件查询设备列表
+        elif operation == 'query':
+            return self.do_device_query(user_id, request_dict, response)
+        # 获取家庭列表
+        elif operation == 'family-list':
+            return self.get_family_list(user_id, request_dict, response)
+        # 家庭保存
+        elif operation == 'family-save':
+            return self.family_save(user_id, request_dict, response)
+        # 家庭设置
+        elif operation == 'family-setting':
+            return self.get_family_setting(user_id, request_dict, response)
+        # 家庭成员删除
+        elif operation == 'member-del':
+            return self.family_member_del(user_id, request_dict, response)
+        # 获取房间列表
+        elif operation == 'room-list':
+            return self.get_family_room_list(request_dict, response)
+        # 房间保存
+        elif operation == 'room-save':
+            return self.room_save(request_dict, response)
+        # 权限列表
+        elif operation == 'permission-list':
+            return self.get_member_permission_list(user_id, request_dict, response)
+        # 成员权限修改
+        elif operation == 'permission-update':
+            return self.changes_member_permission(user_id, request_dict, response)
+        else:
+            return response.json(414)
+
+    @classmethod
+    def do_save(cls, user_id, request_dict, response, request):
+        """
+        添加网关设备
+        @param request:
+        @param user_id:
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        nick_name = request_dict.get('nickName', None)
+        serial_number = request_dict.get('serialNumber', None)
+        device_type = request_dict.get('deviceType', None)
+        family_id = request_dict.get('familyId', None)
+        room_id = request_dict.get('roomId', None)
+        # type 可能为0
+        if not all([nick_name, serial_number, device_type]):
+            return response.json(444, {'param': 'nick_name, serial_number, device_type'})
+        device_info_qs = Device_Info.objects.filter(serial_number=serial_number, userID_id=user_id)
+        if device_info_qs:
+            # 判断设备是否已存在
+            if device_info_qs[0].isExist == 1:
+                return response.json(174)
+            else:
+                device_info_qs.delete()
+        try:
+            with transaction.atomic():
+                # 格式化后的日期时间
+                now_time = CommonService.timestamp_to_str(int(time.time()))
+                device_id = CommonService.getUserID(getUser=False)
+                Device_Info.objects.create(id=device_id, userID_id=user_id, NickName=nick_name,
+                                           Type=device_type,
+                                           UID=serial_number,
+                                           serial_number=serial_number, data_joined=now_time,
+                                           update_time=now_time)
+                boole = cls.family_room_device_save(family_id, room_id, device_id)
+                if not boole:
+                    return response.json(15)
+                # 判断是否有用户绑定
+                us_qs = UidSetModel.objects.filter(uid=serial_number)
+                if not us_qs:
+                    n_time = int(time.time())
+                    ip = CommonService.get_ip_address(request)
+                    region_id = Device_Region().get_device_region(ip)
+                    region_alexa = 'CN' if region_id == 1 else 'ALL'
+                    uid_set_create_dict = {
+                        'uid': serial_number,
+                        'addTime': n_time,
+                        'updTime': n_time,
+                        'ip': CommonService.get_ip_address(request_dict),
+                        'nickname': nick_name,
+                        'region_alexa': region_alexa,
+                    }
+                    UidSetModel.objects.create(**uid_set_create_dict)
+                return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(177, repr(e))
+
+    @classmethod
+    def get_device_not_in_room(cls, user_id, request_dict, response):
+        """
+        获取不在房间的设备
+        @param user_id: 用户id
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        page_no = request_dict.get('pageNo', None)
+        page_size = request_dict.get('pageSize', None)
+        family_id = request_dict.get('familyId', None)
+        if not all([page_no, page_size, family_id]):
+            return response.json(444)
+        page_no = int(page_no)
+        page_size = int(page_size)
+        device_list = cls.get_family_device_list(user_id, page_no, page_size, family_id, 0)
+        return response.json(0, device_list)
+
+    @classmethod
+    def do_device_query(cls, user_id, request_dict, response):
+        """
+        查询用户设备信息
+        @param user_id: 用户id
+        @param request_dict: 请求参数
+        @param response: 响应对象
+        @return: response
+        """
+        page = request_dict.get('page', None)
+        line = request_dict.get('line', None)
+        nick_name = request_dict.get('NickName', None)
+        family_id = request_dict.get('familyId', None)
+        room_id = request_dict.get('roomId', None)
+
+        page = int(page)
+        line = int(line)
+        uid = request_dict.get('uid', None)
+        if family_id:
+            permission = cls.get_member_permission_details(user_id, int(family_id))
+            if not permission or permission == '003':
+                return response.json(404)
+        # 获取设备信息列表
+        device_info_list = cls.get_device_info_list(user_id, nick_name, uid,
+                                                    page, line, family_id, room_id)
+        uid_list = []
+        # 判断是否是主用户 isPrimaryUser=0:否,1:是
+        for dvl in device_info_list:
+            if dvl['primaryUserID'] and dvl['id'] == dvl['primaryUserID']:
+                dvl['isPrimaryUser'] = 1
+            else:
+                dvl['isPrimaryUser'] = 0
+            uid_list.append(dvl['UID'])
+        # 设备关联套餐,设备预览图
+        uid_bucket_qs, uid_preview_qs = cls.get_bucket_and_preview_by_uid(uid_list)
+        # 设备配置信息
+        uid_set_dict = cls.get_uid_set_dict(uid_list)
+        # 设备详情信息
+        result = cls.get_device_details(device_info_list, uid_bucket_qs, uid_preview_qs, uid_set_dict)
+        items = []
+        for index, item in enumerate(result):
+            # 加密
+            if item['View_Password']:
+                item['View_Password'] = CommonService.encode_data(item['View_Password'], 1, 4)
+            items.append(item)
+        return response.json(0, items)
+
+    @classmethod
+    def get_device_info_list(cls, user_id, nick_name, uid, page, line, family_id, room_id):
+        """
+        根据用户id获取设备信息
+        @param room_id: 家庭id
+        @param family_id: 房间id
+        @param uid: uid
+        @param nick_name: 设备名称
+        @param line: 条数
+        @param page: 页数
+        @param user_id: 用户id
+        @return: device_info_list 设备信息列表
+        """
+        # 获取用户设备信息
+        device_info_qs = Device_Info.objects.filter(userID_id=user_id)
+        # 过滤已重置的设备
+        device_info_qs = device_info_qs.filter(~Q(isExist=2))
+        if nick_name:
+            device_info_qs = device_info_qs.filter(NickName__icontains=nick_name)
+        if uid:
+            device_info_qs.filter(UID=uid)
+        if family_id or room_id:
+            # 根据家庭id获取房间id关联查询设备
+            return cls.get_family_device_list(user_id, page, line, family_id, room_id)
+        device_info_values = device_info_qs.values('id', 'userID', 'NickName', 'UID', 'View_Account', 'View_Password',
+                                                   'ChannelIndex',
+                                                   'Type', 'isShare', 'primaryUserID', 'primaryMaster', 'data_joined',
+                                                   'vodPrimaryUserID',
+                                                   'vodPrimaryMaster', 'userID__userEmail', 'version', 'isVod',
+                                                   'isExist', 'NotificationMode',
+                                                   'isCameraOpenCloud', 'serial_number')
+
+        device_info_values = device_info_values[(page - 1) * line:page * line]
+        device_info_list = CommonService.qs_to_list(device_info_values)
+        return device_info_list
+
+    @classmethod
+    def get_family_device_list(cls, user_id, page_no, page_size, family_id, room_id, is_room_other=False):
+        """
+        获取关联家庭设备列表
+        @param is_room_other: 是否显示其他房间设备
+        @param user_id: 用户id
+        @param page_no: 页数
+        @param page_size: 分页大小
+        @param family_id: 家庭id
+        @param room_id: 房间id
+        @return: result_list
+        """
+        cursor = connection.cursor()
+        sql = 'SELECT d.id,d.userID_id as userID,d.NickName,d.UID,d.View_Account,d.View_Password,d.ChannelIndex,' \
+              'd.Type,d.isShare,d.primaryUserID,d.primaryMaster,d.data_joined,d.vodPrimaryUserID,d.vodPrimaryMaster, ' \
+              'd.version,d.isVod,d.isExist,d.NotificationMode,d.isCameraOpenCloud,d.serial_number '
+        sql += 'FROM device_info d INNER JOIN family_room_device l ON d.id = l.device_id '
+        sql += 'WHERE d.userID_id = %s AND d.isExist != %s '
+        if family_id:
+            user_id = UserFamily.objects.filter(id=family_id).first().user_id
+            sql += ' AND l.family_id = %s '
+        if room_id and is_room_other:
+            sql += ' AND l.room_id != %s '
+        elif room_id:
+            sql += ' AND l.room_id = %s '
+        sql += ' GROUP BY d.id order by d.data_joined DESC,d.id DESC LIMIT %s,%s '
+        if family_id and room_id:
+            cursor.execute(sql, [user_id, 2, int(family_id), int(room_id), ((page_no - 1) * page_size),
+                                 page_size, ])
+            data_obj = cursor.fetchall()
+        else:
+            cursor.execute(sql, [user_id, 2, int(family_id) if family_id else int(room_id), ((page_no - 1) * page_size),
+                                 page_size, ])
+            data_obj = cursor.fetchall()
+        cursor.close()  # 执行完,关闭
+        connection.close()
+        result_list = []
+        col_names = [desc[0] for desc in cursor.description]
+        for item in data_obj:
+            val = dict(zip(col_names, item))
+            user_id = val['userID']
+            device_user_qs = Device_User.objects.filter(userID=user_id)
+            val['userID__userEmail'] = device_user_qs.first().userEmail
+            val['isShare'] = False if val['isShare'] == 0 else True
+            if 'data_joined' in val:
+                if val['data_joined']:
+                    val['data_joined'] = val['data_joined'].strftime("%Y-%m-%d %H:%M:%S")
+                else:
+                    val['data_joined'] = ''
+            result_list.append(val)
+        return result_list
+
+    @classmethod
+    def get_bucket_and_preview_by_uid(cls, uid_list):
+        """
+        根据uid列表查询套餐
+        @param uid_list: uid列表
+        @return: uid_bucket_qs
+        """
+        uid_bucket_qs = UID_Bucket.objects.filter(uid__in=uid_list).values('bucket__content', 'status', 'channel',
+                                                                           'endTime', 'uid')
+        uid_preview_qs = UID_Preview.objects.filter(uid__in=uid_list).order_by('channel').values('id', 'uid', 'channel')
+        return uid_bucket_qs, uid_preview_qs
+
+    @classmethod
+    def get_uid_set_dict(cls, uid_list):
+        """
+        获取uid配置信息
+        @param uid_list: uid列表
+        @return: uid_set_dict uid配置信息
+        """
+        uid_set_qs = UidSetModel.objects.filter(uid__in=uid_list) \
+            .values('id', 'uid', 'version', 'nickname', 'ucode',
+                    'detect_status', 'detect_group',
+                    'detect_interval',
+                    'region_alexa', 'is_alexa', 'deviceModel',
+                    'TimeZone', 'TimeStatus', 'SpaceUsable',
+                    'SpaceSum', 'MirrorType', 'RecordType',
+                    'OutdoorModel', 'WIFIName', 'isDetector',
+                    'DetectorRank', 'is_human', 'is_custom_voice',
+                    'is_ptz', 'double_wifi', 'is_ai')
+        uid_set_dict = {}
+        for us in uid_set_qs:
+            uid_set_dict[us['uid']] = {
+                'version': us['version'],
+                'nickname': us['nickname'],
+                'ucode': us['ucode'],
+                'detect_interval': us['detect_interval'],
+                'detect_group': us['detect_group'],
+                'detect_status': us['detect_status'],
+                'region_alexa': us['region_alexa'],
+                'is_alexa': us['is_alexa'],
+                'deviceModel': us['deviceModel'],
+                'TimeZone': us['TimeZone'],
+                'TimeStatus': us['TimeStatus'],
+                'SpaceUsable': us['SpaceUsable'],
+                'SpaceSum': us['SpaceSum'],
+                'MirrorType': us['MirrorType'],
+                'RecordType': us['RecordType'],
+                'OutdoorModel': us['OutdoorModel'],
+                'WIFIName': us['WIFIName'],
+                'isDetector': us['isDetector'],
+                'DetectorRank': us['DetectorRank'],
+                'is_human': us['is_human'],
+                'is_custom_voice': us['is_custom_voice'],
+                'is_ptz': us['is_ptz'],
+                'double_wifi': us['double_wifi'],
+                'is_ai': us['is_ai']
+            }
+            # 从uid_channel里面取出通道配置信息
+            uid_channel_set_qs = UidChannelSetModel.objects.filter(uid__id=us['id']) \
+                .values('channel', 'channel_name',
+                        'pir_audio', 'mic_audio',
+                        'battery_status',
+                        'battery_level',
+                        'sleep_status',
+                        'sleep_time',
+                        'light_night_model',
+                        'light_alarm_type',
+                        'light_alarm_level',
+                        'light_alarm_man_en',
+                        'light_alarm_vol',
+                        'light_long_light'
+                        )
+            channels_list = []
+            for ucs in uid_channel_set_qs:
+                channels_dict = {
+                    'channel': ucs['channel'],
+                    'channel_name': ucs['channel_name'],
+                    'pir_audio': ucs['pir_audio'],
+                    'mic_audio': ucs['mic_audio'],
+                    'battery_status': ucs['battery_status'],
+                    'battery_level': ucs['battery_level'],
+                    'sleep_status': ucs['sleep_status'],
+                    'sleep_time': ucs['sleep_time'],
+                    'light_night_model': ucs['light_night_model'],
+                    'light_alarm_type': ucs['light_alarm_type'],
+                    'light_alarm_level': ucs['light_alarm_level'],
+                    'light_alarm_man_en': ucs['light_alarm_man_en'],
+                    'light_alarm_vol': ucs['light_alarm_vol'],
+                    'light_long_light': ucs['light_long_light']
+                }
+                channels_list.append(channels_dict)
+            uid_set_dict[us['uid']]['channels'] = channels_list
+        return uid_set_dict
+
+    @classmethod
+    def get_device_details(cls, device_info_list, uid_bucket_qs, uid_preview_qs, uid_set_dict):
+        """
+        设备详情
+        @param device_info_list: 设备信息列表
+        @param uid_bucket_qs: 套餐对象
+        @param uid_preview_qs:
+        @param uid_set_dict:
+        @return:
+        """
+        now_time = int(time.time())
+        data = []
+        for p in device_info_list:
+            # 获取iotDeviceInfo表的endpoint和tokenIotNumber
+            p['iot'] = []
+            if p['serial_number']:  # 存在序列号根据序列号查询
+                iot_device_info_qs = iotdeviceInfoModel.objects.filter(serial_number=p['serial_number'][0:6])
+            else:  # 根据uid查询
+                iot_device_info_qs = iotdeviceInfoModel.objects.filter(uid=p['UID'])
+            if iot_device_info_qs.exists():
+                iot_device_Info = iot_device_info_qs.values('endpoint', 'token_iot_number')
+                p['iot'].append({
+                    'endpoint': iot_device_Info[0]['endpoint'],
+                    'token_iot_number': iot_device_Info[0]['token_iot_number']
+                })
+
+            p['vod'] = []
+            for dm in uid_bucket_qs:
+                if p['UID'] == dm['uid']:
+                    if dm['endTime'] > now_time:
+                        p['vod'].append(dm)
+            p['preview'] = []
+
+            auth = oss2.Auth(OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET)
+            bucket = oss2.Bucket(auth, 'oss-cn-hongkong.aliyuncs.com', 'statres')
+
+            for up in uid_preview_qs:
+                if p['UID'] == up['uid']:
+                    obj = 'uid_preview/{uid}/channel_{channel}.png'.format(uid=up['uid'], channel=up['channel'])
+                    img_sign = bucket.sign_url('GET', obj, 300)
+                    p['preview'].append(img_sign)
+
+            p_uid = p['UID']
+
+            # 返回设备初始化字符
+            uid_qs = UIDModel.objects.filter(uid=p_uid).values('platform', 'init_string', 'init_string_app')
+            if uid_qs.exists():
+                p['platform'] = uid_qs[0]['platform']
+                p['initString'] = uid_qs[0]['init_string']
+                p['initStringApp'] = uid_qs[0]['init_string_app']
+
+            if p_uid in uid_set_dict:
+                # 设备版本号
+                uidversion = uid_set_dict[p_uid]['version']
+                if len(uidversion) > 6:
+                    uidversion = uidversion[0: uidversion.rfind('.')]
+                p['uid_version'] = uidversion
+                p['ucode'] = uid_set_dict[p_uid]['ucode']
+                p['detect_interval'] = uid_set_dict[p_uid]['detect_interval']
+                p['detect_status'] = uid_set_dict[p_uid]['detect_status']
+                p['detect_group'] = uid_set_dict[p_uid]['detect_group']
+                p['region_alexa'] = uid_set_dict[p_uid]['region_alexa']
+                p['is_alexa'] = uid_set_dict[p_uid]['is_alexa']
+                p['deviceModel'] = uid_set_dict[p_uid]['deviceModel']
+                p['TimeZone'] = uid_set_dict[p_uid]['TimeZone']
+                p['TimeStatus'] = uid_set_dict[p_uid]['TimeStatus']
+                p['SpaceUsable'] = uid_set_dict[p_uid]['SpaceUsable']
+                p['SpaceSum'] = uid_set_dict[p_uid]['SpaceSum']
+                p['MirrorType'] = uid_set_dict[p_uid]['MirrorType']
+                p['RecordType'] = uid_set_dict[p_uid]['RecordType']
+                p['OutdoorModel'] = uid_set_dict[p_uid]['OutdoorModel']
+                p['WIFIName'] = uid_set_dict[p_uid]['WIFIName']
+                p['isDetector'] = uid_set_dict[p_uid]['isDetector']
+                p['DetectorRank'] = uid_set_dict[p_uid]['DetectorRank']
+                p['is_human'] = uid_set_dict[p_uid]['is_human']
+                p['is_custom_voice'] = uid_set_dict[p_uid]['is_custom_voice']
+                p['is_ptz'] = uid_set_dict[p_uid]['is_ptz']
+                p['channels'] = uid_set_dict[p_uid]['channels']
+                p['double_wifi'] = uid_set_dict[p_uid]['double_wifi']
+                p['is_ai'] = uid_set_dict[p_uid]['is_ai']
+                # 设备昵称 调用影子信息昵称,先阶段不可
+                if uid_set_dict[p_uid]['nickname']:
+                    p['NickName'] = uid_set_dict[p_uid]['nickname']
+            else:
+                # 设备版本号
+                p['uid_version'] = ''
+                p['ucode'] = ''
+            data.append(p)
+        return data
+
+    @classmethod
+    def get_family_list(cls, user_id, request_dict, response):
+        """
+        查询我的家庭列表
+        @param user_id: 用户id
+        @param request_dict: 请求
+        @param response: 响应
+        @return: 家庭列表items
+        """
+        lang = request_dict.get('lang', 'cn')
+        if user_id:
+            with transaction.atomic():
+                user_family_qs = UserFamily.objects.filter(user_id=user_id)
+                if not user_family_qs.exists():
+                    n_time = int(time.time())
+                    device_user = Device_User.objects.get(userID=user_id)
+                    # 创建默认家庭使用用户名或者邮箱作为名称
+                    family_name = device_user.username if device_user.username else device_user.userEmail
+                    family_name = family_name + "的家" if lang == 'cn' else family_name + " home"
+                    user_family = UserFamily.objects.create(user_id=user_id, name=family_name,
+                                                            updated_time=n_time,
+                                                            created_time=n_time)
+                    if user_family.id:
+                        member_permission_qs = FamilyMemberPermission.objects.filter(no='001').values('id')
+                        permission_id = member_permission_qs.first()['id']
+                        FamilyMember.objects.create(family_id=user_family.id, user_id=user_id,
+                                                    user_name=device_user.username, identity=1,
+                                                    permission_id=int(permission_id), sort=1, updated_time=n_time,
+                                                    created_time=n_time)
+                        cls.family_device_binding(user_id, family_id=user_family.id)
+
+                family_member_qs = FamilyMember.objects.filter(user_id=user_id) \
+                    .order_by('sort').values('identity', 'family_id', 'family__name', 'permission_id', 'permission__no',
+                                             'family__location', 'user__username', 'user__userIconUrl')
+                items = []
+                data = {}
+                for item in family_member_qs:
+                    data['familyId'] = item['family_id']
+                    data['identity'] = item['identity']
+                    data['familyName'] = item['family__name']
+                    data['permissionId'] = item['permission_id']
+                    data['permissionNo'] = item['permission__no']
+                    data['familyLocation'] = item['family__location']
+                    data['userName'] = item['user__username']
+                    data['userIconUrl'] = item['user__userIconUrl']
+                    room_qs = FamilyRoom.objects.filter(family_id=data['familyId']).order_by('sort') \
+                        .values('id', 'name')
+                    data['rooms'] = list(room_qs)
+                    items.append(data)
+                    data = {}
+                return response.json(0, items)
+        return response.json(309)
+
+    @classmethod
+    def get_family_setting(cls, user_id, request_dict, response):
+        """
+        家庭设置
+        @param user_id: 用户id
+        @param request_dict: 请求
+        @param response: 响应
+        @return: 家庭列表items
+        """
+        family_id = request_dict.get('familyId', None)
+        if not family_id:
+            return response.json(444)
+        user_family_qs = UserFamily.objects.filter(id=family_id, user_id=user_id).values('id', 'name', 'location')
+        if not user_family_qs.exists():
+            return response.json(404)
+        family_dict = user_family_qs.first()
+        family_dict['roomCount'] = FamilyRoom.objects.filter(family_id=family_id).count()
+
+        family_member_qs = FamilyMember.objects.filter(family_id=family_id)
+        family_member_qs = family_member_qs.values('identity', 'family_id',
+                                                   'permission_id',
+                                                   'permission__no',
+                                                   'user__username',
+                                                   'user_id',
+                                                   'user__userIconUrl',
+                                                   'user__NickName',
+                                                   'user__phone',
+                                                   'user__userEmail')
+        family_member_qs = family_member_qs.order_by('-identity').order_by('sort')
+        items = []
+        data = {}
+        for item in family_member_qs:
+            data['userName'] = item['user__username']
+            data['userIconUrl'] = item['user__userIconUrl']
+            data['userId'] = item['user_id']
+            data['identity'] = item['identity']
+            data['permissionId'] = item['permission_id']
+            data['permissionNo'] = item['permission__no']
+            data['nickName'] = item['user__NickName']
+            data['phone'] = item['user__phone']
+            data['userEmail'] = item['user__userEmail']
+            items.append(data)
+            data = {}
+        family_dict['members'] = items
+        return response.json(0, family_dict)
+
+    @classmethod
+    def family_save(cls, user_id, request_dict, response):
+        """
+        家庭保存
+        @param user_id: 用户id
+        @param request_dict: 参数
+        @param response: 响应
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        family_name = request_dict.get('familyName', None)
+        location = request_dict.get('location', None)
+        with transaction.atomic():
+            now_time = int(time.time())
+            if family_id:
+                is_owner = cls.get_family_owner(user_id, family_id)
+                if not is_owner:
+                    return response.json(404)
+                family_member = FamilyMember.objects.filter(family_id=family_id, user_id=user_id)
+                if family_member.exists():
+                    family_member = family_member.first()
+                    if family_member.identity == 0:
+                        return response.json(404)
+                    family_qs = UserFamily.objects.filter(id=family_id)
+                    if family_qs.exists():
+                        data = {
+                            'updated_time': now_time
+                        }
+                        if family_name:
+                            data['name'] = family_name
+                        if location:
+                            data['location'] = location
+                        family_qs.update(**data)
+                        return response.json(0)
+            data = {'user_id': user_id, 'updated_time': now_time, 'created_time': now_time}
+            if family_name:
+                data['name'] = family_name
+                if location:
+                    data['location'] = location
+                UserFamily.objects.create(**data)
+                return response.json(0)
+            return response.json(444)
+
+    @classmethod
+    def family_room_device_save(cls, family_id, room_id, device_id):
+        """
+        设备与家庭房间保存
+        @param family_id: 家庭id
+        @param room_id: 房间id
+        @param device_id: 设备id
+        @return: Boole
+        """
+        now_time = int(time.time())
+        family_room_device = FamilyRoomDevice.objects.filter(device_id=device_id)
+        if family_room_device.exists():
+            return False
+        data = {
+            'family_id': int(family_id),
+            'device_id': device_id,
+            'updated_time': now_time,
+            'created_time': now_time
+        }
+        if room_id:
+            room_id = int(room_id)
+            if FamilyRoom.objects.filter(id=room_id).exists():
+                data['room_id'] = room_id
+        FamilyRoomDevice.objects.create(**data)
+        return True
+
+    @classmethod
+    def family_device_binding(cls, user_id, family_id):
+        """
+        用户旧设备与默认家庭进行绑定
+        @param user_id:
+        @param family_id:
+        @return: True
+        """
+        device_info_qs = Device_Info.objects.filter(userID=user_id)
+        device_info_qs = device_info_qs.filter(~Q(isExist=0)).values('id')
+        if device_info_qs.exists():
+            with transaction.atomic():
+                not_time = time.time()
+                device_list = []
+                for item in device_info_qs:
+                    device_id = item['id']
+                    family_device_qs = FamilyRoomDevice.objects.filter(device_id=device_id)
+                    if not family_device_qs.exists():
+                        # 设备绑定家庭
+                        device_list.append(FamilyRoomDevice(family_id=family_id, device_id=device_id,
+                                                            created_time=not_time,
+                                                            updated_time=not_time))
+                if device_list:
+                    FamilyRoomDevice.objects.bulk_create(device_list)
+        return True
+
+    @classmethod
+    def get_family_room_list(cls, request_dict, response):
+        """
+        获取房间列表并统计该房间下有几台设备
+        @param request_dict: 请求参数
+        @param response: 响应参数
+        @return: total;data
+        """
+        family_id = request_dict.get('familyId', None)
+        if not family_id:
+            return response.json(444)
+        count = FamilyRoomDevice.objects.filter(family_id=family_id).values('device_id').annotate(
+            count=Count('device_id')).count()
+
+        room_qs = FamilyRoom.objects.filter(family_id=family_id).order_by('sort')
+        total = room_qs.count()
+        room_qs = room_qs.values('id', 'name', 'sort')
+        room_list = []
+        if not room_qs.exists():
+            return response.json(0, {'total': 0, 'data': room_list, 'deviceTotal': 0})
+        for item in room_qs:
+            item['deviceCount'] = FamilyRoomDevice.objects.filter(family_id=family_id, room_id=item['id']).count()
+            room_list.append(item)
+        return response.json(0, {'total': total, 'data': room_list, 'deviceTotal': count})
+
+    @classmethod
+    def room_save(cls, request_dict, response):
+        """
+        房间保存
+        @param request_dict: 请求参数
+        @param response: 响应参数
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        room_name = request_dict.get('roomName', None)
+        room_id = request_dict.get('roomId', None)
+        if not all([family_id, room_name]):
+            return response.json(444)
+        with transaction.atomic():
+            now_time = int(time.time())
+            if room_id:
+                room_qs = FamilyRoom.objects.filter(id=int(room_id))
+                if room_qs.exists():
+                    room_qs.update(name=room_name, updated_time=now_time)
+                    return response.json(0)
+            room_qs = FamilyRoom.objects.filter(family_id=family_id, name=room_name)
+
+            if room_qs.exists():
+                room_dict = {'updated_time': now_time, 'name': room_name}
+                room_qs.update(**room_dict)
+            FamilyRoom.objects.create(family_id=family_id, name=room_name, updated_time=now_time,
+                                      created_time=now_time)
+        return response.json(0)
+
+    @classmethod
+    def changes_member_permission(cls, app_user_id, request_dict, response):
+        """
+        更新家庭成员权限
+        @param app_user_id: 当前app登录用户
+        @param request_dict: 请求参数
+        @param response: 响应实体
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        user_id = request_dict.get('userId', None)
+        no = request_dict.get('no', None)
+        if not all([family_id, user_id, no]):
+            return response.json(444)
+        owner = cls.get_family_owner(app_user_id, family_id)
+        if not owner:
+            return response.json(404)
+        permission_qs = FamilyMemberPermission.objects.filter(no=no).values()
+        if permission_qs.exists():
+            permission_qs = permission_qs.first()
+            p_id = permission_qs['id']
+            FamilyMember.objects.filter(family_id=family_id, user_id=user_id).update(permission_id=p_id)
+        return response.json(0)
+
+    @classmethod
+    def family_member_del(cls, app_user_id, request_dict, response):
+        """
+        家庭成员删除
+        @param app_user_id: 当前app登录用户
+        @param request_dict: 请求参数
+        @param response: 响应实体
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        user_id = request_dict.get('userId', None)
+        if not all([family_id, user_id]):
+            return response.json(444)
+        owner = cls.get_family_owner(app_user_id, family_id)
+        if not owner:
+            return response.json(404)
+        family_member_qs = FamilyMember.objects.filter(family_id=family_id, user_id=user_id)
+        if family_member_qs.exists():
+            family_member_qs.delete()
+        return response.json(0)
+
+    @classmethod
+    def get_member_permission_list(cls, app_user_id, request_dict, response):
+        """
+        获取用户权限列表
+        @param app_user_id: 当前app登录用户
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        user_id = request_dict.get('userId', app_user_id)
+        if not family_id:
+            return response.json(404)
+        result = cls.get_member_permission_by_family_id(user_id, family_id)
+        return response.json(0, result)
+
+    @classmethod
+    def get_member_permission_by_family_id(cls, user_id, family_id):
+        """
+        获取权限列表并返回当前user_id所在家庭中权限
+        @param user_id:
+        @param family_id:
+        @return:
+        """
+        member_qs = FamilyMember.objects.filter(family_id=family_id)
+        if user_id:
+            member_qs = member_qs.filter(user_id=user_id).values()
+            if member_qs.exists():
+                member_qs = member_qs.first()
+        permission = FamilyMemberPermission.objects.all().values('id', 'no')
+        data_list = []
+        this_permission = {}
+        result = {}
+        for item in permission:
+            if item['id'] == member_qs['permission_id']:
+                this_permission['id'] = item['id']
+                this_permission['no'] = item['no']
+            data_list.append(item)
+        result['memberPermission'] = this_permission
+        result['permissionList'] = data_list
+        return result
+
+    @classmethod
+    def get_member_permission_details(cls, user_id, family_id):
+        """
+        根据用户id获取家庭设备权限
+        @param user_id:
+        @param family_id:
+        @return: 权限编号 001:所有权限,002:查看设备,003:暂无权限
+        """
+        member_qs = FamilyMember.objects.filter(family_id=family_id, user_id=user_id).values()
+        if member_qs.exists():
+            member_qs = member_qs.first()
+            permission_id = member_qs['permission_id']
+            permission_qs = FamilyMemberPermission.objects.filter(id=permission_id).values('no')
+            return permission_qs.first()['no']
+        return ''
+
+    @classmethod
+    def get_family_owner(cls, user_id, family_id):
+        """
+        判断是否是家庭主用户
+        @param user_id:
+        @param family_id:
+        @return:
+        """
+        user_family_qs = UserFamily.objects.filter(id=family_id, user_id=user_id)
+        if user_family_qs.exists():
+            return True
+        return False

+ 105 - 0
SensorGateway/GatewayFamilyMemberController.py

@@ -0,0 +1,105 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : GatewayFamilyMemberController.py
+@Time    : 2022/5/25 10:21
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+
+from django.views.generic.base import View
+
+from Model.models import FamilyMemberJoin, UserFamily
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from SensorGateway.EquipmentFamilyController import EquipmentFamilyView
+
+
+# 家庭房间管理
+class GatewayFamilyMemberView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token = request.META.get('HTTP_AUTHORIZATION')
+        token = TokenObject(token)
+        lang = request_dict.get('lang', None)
+        response = ResponseObject(lang) if lang else ResponseObject(token.lang)
+        if token.code != 0:
+            return response.json(token.code)
+        app_user_id = token.userID
+        # 添加设备关联房间
+        if operation == 'join':
+            return self.member_join(app_user_id, request_dict, response)
+        elif operation == 'join/page':
+            return self.member_join_page(request_dict, response)
+
+    @classmethod
+    def member_join(cls, app_user_id, request_dict, response):
+        pass
+
+    @classmethod
+    def member_join_page(cls, request_dict, response):
+        """
+        家庭成员邀请记录
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        page_no = request_dict.get('pageNo', None)
+        page_size = request_dict.get('pageSize', None)
+        family_id = request_dict.get('familyId', None)
+        if not all([page_no, page_size, family_id]):
+            return response.json(444)
+        member_join = FamilyMemberJoin.objects.filter(family_id=family_id).values('status', 'user__username',
+                                                                                  'user__userIconUrl', 'user__phone',
+                                                                                  'user__userEmail', 'updated_time',
+                                                                                  'created_time')
+
+        page_no = int(page_no)
+        page_size = int(page_size)
+        member_join = member_join.order_by('-created_time')[(page_no - 1) * page_size: page_no * page_size]
+        if not member_join.exists():
+            return response.json(0, [])
+        result = []
+        for item in member_join:
+            result.append({
+                'status': item['status'],
+                'userName': item['user__username'],
+                'userIconUrl': item['user__userIconUrl'],
+                'phone': item['user__phone'],
+                'userEmail': item['user__userEmail'],
+                'updatedTime': item['updated_time'],
+                'createdTime': item['created_time']
+            })
+        return response.json(0, result)
+
+    @classmethod
+    def family_qrcode(cls, user_id, request_dict, response):
+        """
+        获取邀请码信息
+        @param user_id:
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        if not family_id:
+            return response.json(444)
+        is_owner = EquipmentFamilyView.get_family_owner(user_id, family_id)
+        if not is_owner:
+            return response.json(404)
+        try:
+            UserFamily.objects.get(id=family_id)
+        except Exception as e:
+            print(e)
+            return response.json(173, repr(e))
+        pass

+ 193 - 0
SensorGateway/GatewayFamilyRoomController.py

@@ -0,0 +1,193 @@
+# -*- encoding: utf-8 -*-
+"""
+@File    : GatewayFamilyRoomController.py
+@Time    : 2022/5/24 19:43
+@Author  : stephen
+@Email   : zhangdongming@asj6.wecom.work
+@Software: PyCharm
+"""
+
+from django.db import transaction
+from django.db.models import Q, Count
+from django.views.generic.base import View
+
+from Model.models import FamilyRoomDevice, FamilyRoom
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+from SensorGateway.EquipmentFamilyController import EquipmentFamilyView
+
+
+# 家庭房间管理
+class GatewayFamilyRoomView(View):
+
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token = request.META.get('HTTP_AUTHORIZATION')
+        token = TokenObject(token)
+        lang = request_dict.get('lang', token.lang)
+        response = ResponseObject(lang)
+        if token.code != 0:
+            return response.json(token.code)
+        app_user_id = token.userID
+        # 添加设备关联房间
+        if operation == 'device-changes':
+            return self.room_device_save(app_user_id, request_dict, response)
+        # 房间排序
+        elif operation == 'sort':
+            return self.room_sort_save(request_dict, response)
+        # 房间删除
+        elif operation == 'del':
+            return self.room_del(app_user_id, request_dict, response)
+        # 房间详情
+        elif operation == 'details':
+            return self.get_room_details(app_user_id, request_dict, response)
+
+    @classmethod
+    def room_device_save(cls, app_user_id, request_dict, response):
+        """
+        房间加入设备or移除设备
+        @param app_user_id:
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        device_ids = request_dict.getlist('deviceIds', None)
+        room_id = request_dict.get('roomId', None)
+        operate = request_dict.get('operate', None)
+        if not all([family_id, device_ids, operate, room_id]):
+            return response.json(444)
+        operate = int(operate)
+        is_owner = EquipmentFamilyView.get_family_owner(app_user_id, family_id)
+        if not is_owner:
+            return response.json(404)
+        with transaction.atomic():
+            room_qs = FamilyRoom.objects.filter(family_id=family_id, id=room_id)
+            if not room_qs.exists():
+                return response.json(173)
+            for item in device_ids:
+                qs = FamilyRoomDevice.objects.filter(family_id=family_id, device_id=item)
+                if qs.exists():
+                    qs.update(room_id=0) if operate == 1 else qs.update(room_id=int(room_id))
+            return response.json(0)
+
+    @classmethod
+    def room_del(cls, user_id, request_dict, response):
+        """
+        房间多选删除
+        @param user_id: 当前登录用户id
+        @param request_dict: 请求参数
+        @param response: 响应参数
+        @return:
+        """
+        ids = request_dict.getlist('roomIds', None)
+        if not ids:
+            return response.json(444)
+        room_id = ids[0]
+        room_info = FamilyRoom.objects.filter(id=room_id)
+        if not room_info.exists():
+            return response.json(173)
+        is_owner = EquipmentFamilyView.get_family_owner(user_id, room_info.first().family_id)
+        if not is_owner:
+            return response.json(404)
+        try:
+            with transaction.atomic():
+                for item in ids:
+                    room_id = int(item)
+                    room_device = FamilyRoomDevice.objects.filter(room_id=room_id)
+                    if room_device.exists():
+                        room_device.update(room_id=0)
+                    FamilyRoom.objects.filter(id=room_id).delete()
+                return response.json(0)
+        except Exception as e:
+            print(e)
+            return response.json(177, repr(e))
+
+    @classmethod
+    def room_sort_save(cls, request_dict, response):
+        """
+        房间排序
+        @param request_dict: 请求参数
+        @param response: 响应参数
+        @return:
+        """
+        ids = request_dict.getlist('ids', None)
+        if not ids:
+            return response.json(444)
+        for item in ids:
+            items = item.split(',')
+            room_id = items[0]
+            sort = items[1]
+            family_room = FamilyRoom.objects.filter(id=int(room_id))
+            if family_room.exists():
+                family_room.update(sort=int(sort))
+        return response.json(0)
+
+    @classmethod
+    def get_room_details(cls, app_user_id, request_dict, response):
+        """
+        房间设备详情
+        @param app_user_id:
+        @param request_dict:
+        @param response:
+        @return:
+        """
+        family_id = request_dict.get('familyId', None)
+        room_id = request_dict.get('roomId', None)
+        page_no = request_dict.get('pageNo', None)
+        page_size = request_dict.get('pageSize', None)
+        if not all([family_id, room_id, page_no, page_size]):
+            return response.json(444)
+        room_count = FamilyRoomDevice.objects.filter(family_id=int(family_id), room_id=int(room_id)).values(
+            'device_id').annotate(count=Count('device_id')).count()
+        device_room = []
+        # 房间设备列表
+        if room_count > 0:
+            device_room_list = EquipmentFamilyView.get_family_device_list(user_id=app_user_id, page_no=1,
+                                                                          page_size=room_count,
+                                                                          family_id=int(family_id),
+                                                                          room_id=int(room_id))
+
+            if device_room_list:
+                room_name = FamilyRoom.objects.get(id=room_id).name
+
+                for item in device_room_list:
+                    device_room.append({
+                        'deviceId': item['id'],
+                        'deviceType': item['Type'],
+                        'nickName': item['NickName'],
+                        'roomName': room_name,
+                    })
+        device_not_room = []
+        device_not_room_count = FamilyRoomDevice.objects.filter(family_id=int(family_id), room_id=0).values(
+            'device_id').annotate(count=Count('device_id')).count()
+        if device_not_room_count > 0:
+            not_room_device_list = EquipmentFamilyView.get_family_device_list(user_id=app_user_id, page_no=int(page_no),
+                                                                              page_size=int(page_size),
+                                                                              family_id=int(family_id), room_id=0,
+                                                                              is_room_other=True)
+            if not_room_device_list:
+                for item in not_room_device_list:
+                    room_device_qs = FamilyRoomDevice.objects.filter(family_id=int(family_id))
+                    room_device_qs = room_device_qs.filter(~Q(room_id=0))
+                    name = ''
+                    if room_device_qs.exists():
+                        family_room_qs = FamilyRoom.objects.filter(id=room_device_qs.first().room_id)
+                        if family_room_qs.exists():
+                            name = family_room_qs.first().name
+                    device_not_room.append({
+                        'deviceId': item['id'],
+                        'deviceType': item['Type'],
+                        'nickName': item['NickName'],
+                        'roomName': name
+                    })
+        return response.json(0, {'deviceRooms': device_room, 'deviceNotRooms': device_not_room})

+ 175 - 0
SensorGateway/SubDeviceController.py

@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+"""
+@Time : 2022/5/25 15:17
+@Auth : Locky
+@File :SubDeviceController.py
+@IDE :PyCharm
+"""
+import time
+
+from django.db import transaction
+from django.views import View
+
+from Model.models import Device_Info, GatewaySubDevice, FamilyRoomDevice
+from Object.ResponseObject import ResponseObject
+from Object.TokenObject import TokenObject
+
+
+class GatewaySubDeviceView(View):
+    def get(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.GET, request, operation)
+
+    def post(self, request, *args, **kwargs):
+        request.encoding = 'utf-8'
+        operation = kwargs.get('operation')
+        return self.validation(request.POST, request, operation)
+
+    def validation(self, request_dict, request, operation):
+        token = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
+        lang = request_dict.get('lang', None)
+        response = ResponseObject(lang) if lang else ResponseObject(token.lang)
+
+        # if token.code != 0:
+        #     return response.json(token.code)
+        # user_id = token.userID
+        user_id = '154700384179113800138000'
+        if operation == 'add':  # 添加子设备
+            return self.add(request_dict, user_id, response)
+        elif operation == 'query':  # 查询子设备
+            return self.query(request_dict, user_id, response)
+        elif operation == 'update':  # 更新子设备信息
+            return self.update(request_dict, user_id, response)
+        elif operation == 'delete':  # 删除子设备
+            return self.delete(request_dict, user_id, response)
+        else:
+            return response.json(414)
+
+    @staticmethod
+    def add(request_dict, user_id, response):
+        """
+        添加子设备
+        @param request_dict: 请求参数
+        @request_dict serial_number: 序列号
+        @request_dict device_type: 设备类型
+        @request_dict nickname: 设备名
+        @request_dict src_addr: 短地址
+        @request_dict family_id: 家庭id
+        @request_dict room_id: 房间id
+        @param user_id: 用户id
+        @param response: 响应对象
+        @return: response 响应对象
+        """
+        serial_number = request_dict.get('serial_number', None)
+        device_type = int(request_dict.get('device_type', None))
+        nickname = request_dict.get('nickname', None)
+        src_addr = request_dict.get('src_addr', None)
+        family_id = request_dict.get('family_id', None)
+        room_id = request_dict.get('room_id', None)
+
+        if not all([serial_number, device_type, nickname, src_addr, family_id, room_id]):
+            return response.json(444)
+        now_time = int(time.time())
+        try:
+            device_info_qs = Device_Info.objects.filter(userID_id=user_id, serial_number=serial_number).values('id')
+            if not device_info_qs.exists():
+                return response.json(14)
+            device_id = device_info_qs[0]['id']
+            with transaction.atomic():
+                sub_device = GatewaySubDevice.objects.create(device_id=device_id, device_type=device_type,
+                                                             nickname=nickname, src_addr=src_addr, status=1,
+                                                             created_time=now_time, updated_time=now_time)
+                FamilyRoomDevice.objects.create(family_id=family_id, room_id=room_id, device_id=device_id,
+                                                sub_device=sub_device, created_time=now_time, updated_time=now_time)
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, repr(e))
+
+    @staticmethod
+    def query(request_dict, user_id, response):
+        """
+        查询子设备
+        @param request_dict: 请求参数
+        @request_dict serial_number: 序列号
+        @param user_id: 用户id
+        @param response: 响应对象
+        @return: response
+        """
+        serial_number = request_dict.get('serial_number', None)
+
+        if not all([serial_number]):
+            return response.json(444)
+        try:
+            device_info_qs = Device_Info.objects.filter(userID_id=user_id, serial_number=serial_number).values('id')
+            if not device_info_qs.exists():
+                return response.json(14)
+            device_id = device_info_qs[0]['id']
+            count = GatewaySubDevice.objects.filter(device_id=device_id).count()
+            gateway_sub_device_qs = GatewaySubDevice.objects.filter(device_id=device_id).values('device_type',
+                                                                                                'nickname', 'src_addr',
+                                                                                                'status')
+            gateway_sub_device_list = [gateway_sub_device for gateway_sub_device in gateway_sub_device_qs]
+            res = {
+                'count': count,
+                'gateway_sub_device_list': gateway_sub_device_list
+            }
+            return response.json(0, res)
+        except Exception as e:
+            return response.json(500, repr(e))
+
+    @staticmethod
+    def update(request_dict, user_id, response):
+        """
+        更新子设备信息
+        @param request_dict: 请求参数
+        @request_dict serial_number: 序列号
+        @request_dict sub_device_id: 子设备id
+        @param user_id: 用户id
+        @param response: 响应对象
+        @return: response
+        """
+        serial_number = request_dict.get('serial_number', None)
+        sub_device_id = request_dict.get('sub_device_id', None)
+        nickname = request_dict.get('nickname', None)
+        src_addr = request_dict.get('src_addr', None)
+        status = int(request_dict.get('status', None))
+        family_id = request_dict.get('family_id', None)
+        room_id = request_dict.get('room_id', None)
+
+        if not all([serial_number]):
+            return response.json(444)
+        try:
+            device_info_qs = Device_Info.objects.filter(userID_id=user_id, serial_number=serial_number).values('id')
+            if not device_info_qs.exists():
+                return response.json(14)
+            update_data = {
+                'nickname': nickname,
+                'src_addr': src_addr,
+                'status': status,
+            }
+            GatewaySubDevice.objects.filter(id=sub_device_id).update(**update_data)
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, repr(e))
+
+    @staticmethod
+    def delete(request_dict, user_id, response):
+        """
+        更新子设备信息
+        @param request_dict: 请求参数
+       @request_dict sub_device_id: 子设备id
+        @param user_id: 用户id
+        @param response: 响应对象
+        @return: response
+        """
+        sub_device_id = request_dict.get('sub_device_id', None)
+
+        if not all([sub_device_id]):
+            return response.json(444)
+        try:
+            GatewaySubDevice.objects.filter(id=sub_device_id).delete()
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, repr(e))
+

+ 7 - 1
Service/CommonService.py

@@ -476,7 +476,13 @@ class CommonService:
 
 
     @staticmethod
     @staticmethod
     def req_publish_mqtt_msg(thing_name, topic_name, msg):
     def req_publish_mqtt_msg(thing_name, topic_name, msg):
-        # 通用发布MQTT消息函数
+        """
+        通用发布MQTT消息函数
+        @param thing_name: 物品名
+        @param topic_name: 主题名
+        @param msg: 消息内容
+        @return: boolean
+        """
         if not all([thing_name, topic_name, msg]):
         if not all([thing_name, topic_name, msg]):
             return False
             return False
 
 

+ 59 - 4
Service/EquipmentInfoService.py

@@ -8,7 +8,7 @@
 """
 """
 import datetime
 import datetime
 import time
 import time
-
+import itertools
 from django.db.models import Value, CharField
 from django.db.models import Value, CharField
 
 
 from Model.models import EquipmentInfoMonday, EquipmentInfoTuesday, EquipmentInfoWednesday, EquipmentInfoThursday, \
 from Model.models import EquipmentInfoMonday, EquipmentInfoTuesday, EquipmentInfoWednesday, EquipmentInfoThursday, \
@@ -165,10 +165,9 @@ class EquipmentInfoService:
         if user_id:
         if user_id:
             qs = qs.filter(device_user_id=user_id)
             qs = qs.filter(device_user_id=user_id)
         if event_type:
         if event_type:
-            # 兼容AI查询
+            # 多类型查询
             if ',' in event_type:
             if ',' in event_type:
-                eventTypeList = event_type.split(',')
-                eventTypeList = [int(i.strip()) for i in eventTypeList]
+                eventTypeList = cls.get_comb_event_type(event_type)
                 qs = qs.filter(event_type__in=eventTypeList)
                 qs = qs.filter(event_type__in=eventTypeList)
             else:
             else:
                 qs = qs.filter(event_type=event_type)
                 qs = qs.filter(event_type=event_type)
@@ -220,3 +219,59 @@ class EquipmentInfoService:
             item.pop('border_coords')
             item.pop('border_coords')
             item.pop('tab_val')
             item.pop('tab_val')
         return qs_page
         return qs_page
+
+    @classmethod
+    def get_comb_event_type(cls, event_type):
+        """
+        重新组合ai消息类型查询,使其支持ai多标签查询
+        @param event_type: 消息类型
+        @return: event_type_list 消息类型数组
+        """
+        event_type_list = event_type.split(',')
+        event_type_list = [int(i.strip()) for i in event_type_list]
+        ai_event_type_list = []
+        for key, val in enumerate(event_type_list):
+            if val <= 4:  # 分离出ai类型,以便后续组合ai标签,目前只存在4个ai类型1,2,3,4
+                ai_event_type_list.append(val)
+                del (event_type_list[key])
+        if len(ai_event_type_list) < 1:
+            return event_type_list
+        ai_event_type_list.sort()
+        type = [1, 2, 3, 4]  # AI目前所有的标签,1人,2车,3宠物,4包裹,后续有新类型需要这里加, 后续会优化,存在表里,包裹存对应的aws标签
+        comb_ai_event_type = []
+        seen = set()
+        for i in range(1, len(type) + 1):  # 计算所有组合,如[1, 2, 3, 4], 4取1,4取2,4取3,4取4
+            for s in itertools.combinations(type, i):
+                if s not in seen:  # 去除重复项, 如a=[1,2,3,4,4],会有两个[1,2,3,4,4],[1,2,3,4,4]的组合
+                    seen.add(s)
+                    s_list = list(s)
+                    for ai_event_type in ai_event_type_list:
+                        if ai_event_type in s_list:  # 排除没有选择的标签组合
+                            if s_list not in comb_ai_event_type:
+                                s_list = [str(v) for v in s_list]
+                                comb_ai_event_type.append(s_list)
+        regroup_list = []
+        for val in comb_ai_event_type:  # 组合ai类型组合,如[[2,3],[1,3]] -> [23, 13]
+            val = ''.join(val)
+            regroup_list.append(int(val))
+        event_type_list = regroup_list + event_type_list  # 加上普通移动消息类型
+        return event_type_list
+
+    @classmethod
+    def get_all_comb_event_type(cls):
+        """
+        计算ai消息类型全组合
+        @return: event_type_list ai所有消息类型数组
+        """
+        type = [1, 2, 3, 4]  # AI目前所有的标签,1人,2车,3宠物,4包裹,后续有新类型需要这里加, 后续会优化,存在表里,包裹存对应的aws标签
+        comb_ai_event_type = []
+        for i in range(1, len(type) + 1):  # 计算所有组合,如[1, 2, 3, 4], 4取1,4取2,4取3,4取4
+            for s in itertools.combinations(type, i):
+                    s_list = list(s)
+                    s_list = [str(v) for v in s_list]
+                    comb_ai_event_type.append(s_list)
+        regroup_list = []
+        for val in comb_ai_event_type:  # 组合ai类型组合,如[[2,3],[1,3]] -> [23, 13]
+            val = ''.join(val)
+            regroup_list.append(int(val))
+        return regroup_list

+ 181 - 0
Service/PayService.py

@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+# 支付类
+
+
+class PaymentService:
+
+    # 返回支付失败html内容
+    @staticmethod
+    def get_pay_error_content():
+        return '''
+<!DOCTYPE html>
+<html>
+<head>
+	<!--浏览器不缓存-->
+	<meta http-equiv="Pragma" content="no-cache">
+	<meta http-equiv="Cache-Control" content="no-cache">
+	<meta http-equiv="Expires" content="0">
+	<!--utf-8-->
+    <meta http-equiv="content-type" content="text/html;charset=utf-8">
+    <!-- viewport的<meta>标签,这个标签可以修改在大部分的移动设备上面的显示,为了确保适当的绘制和触屏缩放。-->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="shortcut icon" href="https://test.zositechc.cn/web/images/favicon.ico" type="image/x-icon"  charset="utf-8"/>
+    <title>Trading particulars</title>
+    <style>
+    	.title_head{
+    		height: 50px;
+    		border-radius: 5px;
+    		background-color: #c3c6c7;
+    		text-align: center;
+    		line-height: 50px;
+    	}
+    	.content{
+    		text-align: center;
+    		margin-top: 50px;
+    		font-size: 20px;
+    		color : #ec7648
+    	}
+    	.content_img{
+    		width: 60px;
+    		height: 60px;
+    	}
+    	.bottom{
+    		 margin-bottom: 10px;
+    		 margin-top: 250px;
+    		 color : #ec7648
+    	}
+    	.bottom_div{
+    		border: 1px solid #ec7648;
+    		line-height: 38px;
+    		text-align: center;
+    		width: 100px;
+    		height: 38px;
+    		border-radius: 5px;
+    	}
+
+    	.bottom_div:hover{
+    		background-color: #dde4e2;
+    	}
+    </style>
+</head>
+<body>
+	<div class="title_head">Trading particulars</div>
+    <div class="content">
+    	<p >
+    		<img src="https://test.zositechc.cn/web/images/failed.jpg" class="content_img">
+    		<br />
+    		Payment failure
+    	</p>
+    </div>
+    <center class="bottom">
+    	<div class="bottom_div" onclick="payOKButton()">
+    	 Finish
+    	</div>
+    </center>
+    <script> 	    // 点击付款成功按钮
+    function payOKButton() {
+        // 复杂数据
+        console.log('success')
+        window.location.href="https://www.baidu.com?page=closePage";
+    }
+	</script>
+</body>
+</html>
+                '''
+
+    # 返回支付成功html内容
+    @staticmethod
+    def get_pay_ok_content(lang, pay_type):
+        title = "支付成功"
+        complete = '完成'
+        if lang == 'cn':
+            if pay_type == "10":
+                title = "体验成功"
+            if pay_type == "11":
+                title = "兑换成功"
+        else:
+            title = "Payment successful"
+            complete = 'complete'
+            if pay_type == "10":
+                title = "Successful experience"
+            if pay_type == "11":
+                title = "Successful exchange"
+
+        return '''
+        <html>
+        <head>
+                <!--浏览器不缓存-->
+                <meta http-equiv="Pragma" content="no-cache">
+                <meta http-equiv="Cache-Control" content="no-cache">
+                <meta http-equiv="Expires" content="0">
+                <!--utf-8-->
+            <meta http-equiv="content-type" content="text/html;charset=utf-8">
+            <!-- viewport的<meta>标签,这个标签可以修改在大部分的移动设备上面的显示,为了确保适当的绘制和触屏缩放。-->
+            <meta name="viewport" content="width=device-width, initial-scale=1.0">
+            <link rel="shortcut icon" href="https://test.zositechc.cn/web/images/favicon.ico" type="image/x-icon" charset="utf-8">
+            <title>''' + title + '''</title>
+            <style>
+                    .title_head{
+                            height: 50px;
+                            border-radius: 5px;
+                            background-color: #c3c6c7;
+                            text-align: center;
+                            line-height: 50px;
+                    }
+                    .content{
+                            text-align: center;
+                            margin-top: 50px;
+                            font-size: 15px;
+                            color:#0000008A;
+
+                    }
+                    .content_img{
+        					margin-bottom:15px;
+                            width: 60px;
+                            height: 60px;
+                    }
+                    .bottom{
+                             margin-bottom: 10px;
+                             margin-top: 250px;
+                             color : white;
+                    }
+                    .bottom_div{
+                            border: 1px solid #68c9c5;
+                            line-height: 38px;
+                            text-align: center;
+                            width: 100px;
+                            height: 38px;
+                            border-radius: 30px;
+        					background-color:#68c9c5;
+                    }
+
+                    .bottom_div:hover{
+                            background-color: #dde4e2;
+                    }
+            </style>
+        </head>
+        <body style="" rlt="1" inmaintabuse="true">
+
+            <div class="content">
+                    <p>
+        					<img src="https://test.zositechc.cn/web/images/success.png" class="content_img">
+                            <br>
+                            ''' + title + '''
+                    </p>
+            </div>
+            <center class="bottom">
+                    <div class="bottom_div" onclick="payOKButton()">
+                     ''' + complete + '''
+                    </div>
+            </center>
+            <script src="//hm.baidu.com/hm.js?eaa57ca47dacb4ad4f5a257001a3457c"></script><script>             // 点击付款成功按钮
+            function payOKButton() {
+                // 复杂数据
+                console.log('success')
+                window.location.href="https://www.baidu.com?page=closePage"
+            }
+                </script>
+
+
+                <div id="qds" style="display:none;"></div></body></html>
+                '''