Browse Source

定制化推送

locky 1 year ago
parent
commit
2d7573e4b5
5 changed files with 262 additions and 31 deletions
  1. 176 15
      AdminController/UserManageController.py
  2. 12 5
      Controller/SysMsg.py
  3. 27 2
      Model/models.py
  4. 22 7
      Object/ApschedulerObject.py
  5. 25 2
      Service/CommonService.py

+ 176 - 15
AdminController/UserManageController.py

@@ -1,19 +1,7 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-@Copyright (C) ansjer cop Video Technology Co.,Ltd.All rights reserved.
-@AUTHOR: ASJRD018
-@NAME: AnsjerFormal
-@software: PyCharm
-@DATE: 2018/9/11 15:08
-@Version: python3.6
-@MODIFY DECORD:ansjer dev
-@file: UserController.py
-@Contact: chanjunkai@163.com
-"""
 import datetime
 import time
 import oss2
+import requests
 from django.contrib.auth.hashers import make_password, check_password  # 对密码加密模块
 from django.db import transaction
 from django.db.models import Q
@@ -22,10 +10,13 @@ from django.utils.timezone import utc
 from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import TemplateView
 
-from Ansjer.config import SERVER_DOMAIN, OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET
+from Ansjer.config import SERVER_DOMAIN, OSS_STS_ACCESS_KEY, OSS_STS_ACCESS_SECRET, AWS_ACCESS_KEY_ID, \
+    AWS_SECRET_ACCESS_KEY, AWS_SES_ACCESS_REGION, DETECT_PUSH_DOMAINS
 from Controller.CheckUserData import DataValid, RandomStr
 from Model.models import Device_User, Role, UserExModel, CountryModel, MenuModel, FeedBackModel, StatResModel, \
-    SysMassModel, App_Info, SysMsgModel, DeviceSuperPassword
+    SysMassModel, App_Info, SysMsgModel, DeviceSuperPassword, CustomizedPush, DeviceTypeModel
+from Object.AWS.AmazonS3Util import AmazonS3Util
+from Object.ApschedulerObject import ApschedulerObject
 from Object.RedisObject import RedisObject
 from Object.ResponseObject import ResponseObject
 from Object.TokenObject import TokenObject
@@ -883,3 +874,173 @@ class UserManagement(View):
             return response.json(173)
         device_super_password_qs.delete()
         return response.json(0)
+
+    @staticmethod
+    def getCustomizedPushList(request_dict, response):
+        title = request_dict.get('title', None)
+        country = request_dict.get('country', None)
+        device_type = request_dict.get('device_type', None)
+        push_satus = request_dict.get('push_satus', None)
+        page = request_dict.get('pageNo', None)
+        line = request_dict.get('pageSize', None)
+
+        if not all([page, line]):
+            return response.json(444)
+
+        page = int(page)
+        line = int(line)
+
+        try:
+            customized_push_qs = CustomizedPush.objects.all()
+            if title:
+                customized_push_qs = customized_push_qs.filter(title=title)
+            if country:
+                customized_push_qs = customized_push_qs.filter(country=country)
+            if device_type:
+                customized_push_qs = customized_push_qs.filter(device_type__contains=device_type)
+            if push_satus:
+                customized_push_qs = customized_push_qs.filter(push_satus=push_satus)
+            if not customized_push_qs.exists():
+                return response.json(0, [])
+            total = customized_push_qs.count()
+            customized_push_qs = customized_push_qs.\
+                values('id', 'title', 'msg', 'link', 'icon_link', 'country', 'device_type', 'register_period',
+                       'time_zone', 'push_time', 'push_app', 'push_satus')
+            customized_push_qs = customized_push_qs[(page - 1) * line:page * line]
+            customized_push_list = []
+            for customized_push in customized_push_qs:
+                # 格式化数据
+                register_period = customized_push['register_period'] + '年'
+                time_zone = 'UTC ' + customized_push['time_zone']
+
+                customized_push_list.append({
+                    'customized_push_id': customized_push['id'],
+                    'title': customized_push['title'],
+                    'msg': customized_push['msg'],
+                    'link': customized_push['link'],
+                    'icon_link': customized_push['icon_link'],
+                    'country': customized_push['country'],
+                    'device_type': customized_push['device_type'],
+                    'register_period': register_period,
+                    'time_zone': time_zone,
+                    'push_time': customized_push['push_time'],
+                    'push_app': customized_push['push_app'],
+                    'push_satus': customized_push['push_satus']
+                })
+            return response.json(0, {'list': customized_push_list, 'total': total})
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @classmethod
+    def addOrEditCustomizedPush(cls, request, request_dict, response):
+        title = request_dict.get('title', None)
+        msg = request_dict.get('msg', None)
+        link = request_dict.get('link', None)
+        icon = request.FILES.get('icon', None)
+        country = request_dict.get('country', None)
+        device_type = request_dict.get('deviceType', None)
+        register_period = request_dict.get('registerPeriod', None)
+        time_zone = request_dict.get('timeZone', None)
+        push_time = request_dict.get('pushTime', None)
+        push_app = request_dict.get('pushApp', None)
+        is_edit = request_dict.get('isEdit', None)
+
+        if not all([title, msg, link, country, device_type, register_period, time_zone, push_time, push_app]):
+            return response.json(444)
+
+        # 截掉UTC和空格
+        time_zone = time_zone[4:]
+        try:
+            push_timestamp = CommonService.convert_to_timestamp(float(time_zone), push_time)
+            if push_timestamp <= int(time.time()):
+                return response.json(806)
+            customized_push_data = {
+                'title': title,
+                'msg': msg,
+                'link': link,
+                'country': country,
+                'device_type': device_type,
+                'register_period': register_period,
+                'time_zone': time_zone,
+                'push_time': push_time,
+                'push_timestamp': push_timestamp,
+                'push_app': push_app
+            }
+            icon_link = ''
+            if icon is not None:
+                icon_name = icon.name
+                icon_link = 'https://ansjerfilemanager.s3.amazonaws.com/customized-push/' + icon_name
+                customized_push_data['icon_link'] = icon_link
+
+                if icon_link:
+                    # 上传没有上传过的图片到S3
+                    customized_push_qs = CustomizedPush.objects.filter(icon_link=icon_link)
+                    if not customized_push_qs.exists():
+                        bucket = 'ansjerfilemanager'
+                        file_key = 'customized-push/' + icon_name
+                        s3 = AmazonS3Util(AWS_ACCESS_KEY_ID[1], AWS_SECRET_ACCESS_KEY[1], AWS_SES_ACCESS_REGION)
+                        s3.upload_file_obj(
+                            bucket,
+                            file_key,
+                            icon,
+                            {'ContentType': icon.content_type, 'ACL': 'public-read'})
+
+            apscheduler_obj = ApschedulerObject()
+            if is_edit:     # 编辑
+                customized_push_id = request_dict.get('customizedPushId', None)
+                if not customized_push_id:
+                    return response.json(444)
+                customized_push_data['push_satus'] = False
+                CustomizedPush.objects.filter(id=customized_push_id).update(**customized_push_data)
+                apscheduler_obj.del_job('customizedPushId_{}'.format(customized_push_id))  # 删除旧定时任务
+            else:           # 新增
+                customized_push = CustomizedPush.objects.create(**customized_push_data)
+                customized_push_id = customized_push.id
+
+            # 创建定时任务
+            task_id = 'customized_push_id_{}'.format(customized_push_id)
+            apscheduler_obj.create_date_job(func=cls.req_customized_push, task_id=task_id, time_stamp=push_timestamp,
+                                            args=(customized_push_id,))
+            return response.json(0)
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def req_customized_push(customized_push_id):
+        """
+        请求定制化推送
+        @param customized_push_id:
+        @return:
+        """
+        data = {'customized_push_id': customized_push_id}
+        print(data)
+        url = DETECT_PUSH_DOMAINS + 'customized_push/start'
+        req = requests.post(url=url, data=data, timeout=8)
+
+    @staticmethod
+    def getCountryList(response):
+        try:
+            country_qs = CountryModel.objects.filter().values('country_name')
+            if not country_qs.exists():
+                return response.json(173)
+
+            country_list = []
+            for country in country_qs:
+                country_list.append(country['country_name'])
+            return response.json(0, {'list': country_list})
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
+
+    @staticmethod
+    def getDeviceTypeList(response):
+        try:
+            device_type_qs = DeviceTypeModel.objects.filter().values('name')
+            if not device_type_qs.exists():
+                return response.json(173)
+
+            device_type_list = []
+            for device_type in device_type_qs:
+                device_type_list.append(device_type['name'])
+            return response.json(0, {'list': device_type_list})
+        except Exception as e:
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))

+ 12 - 5
Controller/SysMsg.py

@@ -12,6 +12,7 @@
 @Contact: chanjunkai@163.com
 """
 import time
+import re
 
 from django.views.generic.base import View
 
@@ -133,7 +134,7 @@ class SysMsgView(View):
                         SysMsgModel.objects.create(**create_data)
                         fb_qs.update(status=1)
                     except Exception as e:
-                        return response.json(500, repr(e))
+                        return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
                     else:
                         return response.json(0, {'status': 1, 'updTime': nowTime})
                 else:
@@ -154,7 +155,8 @@ class SysMsgView(View):
         try:
             # 如果没有系统消息,周视ios用户暂时生成消息并返回
             if not sm_qs.exists():
-                user_ex_qs = UserExModel.objects.filter(userID_id=userID, appBundleId='com.ansjer.zccloud').values('region')
+                user_ex_qs = UserExModel.objects.filter(userID_id=userID, appBundleId='com.ansjer.zccloud').values(
+                    'region')
                 if not user_ex_qs.exists():
                     return response.json(0, [])
                 now_time = int(time.time())
@@ -182,14 +184,19 @@ class SysMsgView(View):
 
             count = sm_qs.count()
             sm_qs = sm_qs[(page - 1) * line:page * line]. \
-                values('status', 'id', 'msg', 'addTime', 'updTime', 'uid', 'eventType')
+                values('status', 'id', 'msg', 'addTime', 'updTime', 'uid', 'eventType', 'title', 'jumpLink')
             data_res = []
             uid_list = []
 
             for sm_q in sm_qs:
-                sm_q['jumpLink'] = ''
                 if sm_q['eventType'] > 0:
                     uid_list.append(sm_q['uid'])
+                if sm_q['eventType'] == 2:
+                    msg = sm_q['msg']
+                    number_list = re.findall('\d+', msg)
+                    for number in number_list:
+                        if len(number) == 6:
+                            sm_q['code'] = number
                 data_res.append(sm_q)
             if uid_list:
                 uid_set_qs = UidSetModel.objects.filter(uid__in=uid_list).values('uid', 'nickname')
@@ -201,7 +208,7 @@ class SysMsgView(View):
                     data_res.append(sm_q)
             return response.json(0, {'data': data_res, 'count': count})
         except Exception as e:
-            return response.json(500, repr(e))
+            return response.json(500, 'error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
 
     def do_query_by_admin(self, userID, request_dict, response):
         own_perm = ModelService.check_perm(userID, 30)

+ 27 - 2
Model/models.py

@@ -1905,11 +1905,14 @@ class UID_Preview(models.Model):
 class SysMsgModel(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='自增id')
     userID_id = models.CharField(default='', db_index=True, blank=True, max_length=32, verbose_name=u'用户ID')
-    msg = models.TextField(blank=True, default='', verbose_name=u'发送内容')
+    title = models.CharField(default='', max_length=64, verbose_name='标题')
+    msg = models.TextField(blank=True, default='', verbose_name='发送内容')
     status = models.SmallIntegerField(verbose_name='是否已读', default=0)  # 0:否,1:是
     addTime = models.IntegerField(verbose_name='添加时间', default=0)
     updTime = models.IntegerField(verbose_name='更新时间', default=0)
-    eventType = models.IntegerField(verbose_name='消息类型', default=0)  # 默认系统消息类型,0系统消息,1 ipcamera消息,2 超级密码
+    # 消息类型, 0:系统通知, 702:休眠, 704:低电量
+    eventType = models.IntegerField(verbose_name='消息类型', default=0)
+    jumpLink = models.TextField(default='', verbose_name='跳转链接')
     uid = models.CharField(default='', max_length=20, db_index=True, verbose_name='设备UID')
 
     class Meta:
@@ -4296,6 +4299,28 @@ class SerialNumberPackage(models.Model):
         verbose_name_plural = verbose_name
 
 
+class CustomizedPush(models.Model):
+    id = models.AutoField(primary_key=True, verbose_name='主键')
+    title = models.CharField(default='', max_length=64, verbose_name='标题')
+    msg = models.TextField(default='', verbose_name='内容')
+    link = models.TextField(default='', verbose_name='链接')
+    icon_link = models.TextField(default='', verbose_name='预览图链接')
+    country = models.CharField(default='', max_length=32, verbose_name='国家')
+    # 多选型号用,分开
+    device_type = models.TextField(default='', verbose_name='设备类型')
+    register_period = models.CharField(default='', max_length=32, verbose_name='注册年限')
+    time_zone = models.CharField(default='', max_length=8, verbose_name='时区')
+    push_time = models.CharField(default='', max_length=32, verbose_name='推送时间')
+    push_timestamp = models.IntegerField(default=0, verbose_name='推送时间戳')
+    push_app = models.CharField(default='', max_length=64, verbose_name='推送APP')
+    # False:待推送, True:已推送
+    push_satus = models.BooleanField(default=False, verbose_name='推送状态')
+
+    class Meta:
+        db_table = 'customized_push'
+        verbose_name = '定制化推送'
+
+
 class AbnormalOrder(models.Model):
     id = models.AutoField(primary_key=True, verbose_name='主键')
     order_id = models.CharField(max_length=30, db_index=True, verbose_name='订单ID', blank=True, default='')

+ 22 - 7
Object/ApschedulerObject.py

@@ -1,7 +1,6 @@
 import time
 from apscheduler.schedulers.background import BackgroundScheduler
 from django_apscheduler.jobstores import DjangoJobStore
-from Ansjer.config import LOGGER
 from django_apscheduler.models import DjangoJob
 import datetime
 
@@ -17,13 +16,29 @@ class ApschedulerObject:
         now_time = time.time()
         print('hello world:[{}]'.format(now_time))
 
-    def cron_job(self, task_id, day_of_week, hour, minute):  # 周期任务
-        self.scheduler.add_job(self.auto_hello, 'cron', day_of_week=day_of_week, hour=hour, minute=minute,
-                               replace_existing=True, id=task_id, max_instances=1, coalesce=True)
+    def create_cron_job(self, func, task_id, day_of_week, hour, minute, args):  # 周期任务
+        job = self.scheduler.add_job(func=func, trigger='cron', day_of_week=day_of_week, hour=hour, minute=minute,
+                                     replace_existing=True, id=task_id, max_instances=1, coalesce=False, args=args,
+                                     misfire_grace_time=300)
+        print(job)
 
-    def date_job(self, task_id, time_stamp):  # 时间点任务
-        self.scheduler.add_job(self.auto_hello, 'date', run_date=datetime.datetime.fromtimestamp(time_stamp),
-                               replace_existing=True, id=task_id, max_instances=1, coalesce=True)
+    def create_interval_job(self, func, task_id, minutes, start_time, end_time, args):  # 间隔任务
+        self.scheduler.add_job(func=func, trigger='interval', minutes=minutes,
+                               start_date=datetime.datetime.fromtimestamp(start_time),
+                               end_date=datetime.datetime.fromtimestamp(end_time),
+                               replace_existing=True, id=task_id, max_instances=1, coalesce=False, args=args)
+
+    def create_date_job(self, func, task_id, time_stamp, args):
+        """
+        创建时间点任务
+        @param func:
+        @param task_id:
+        @param time_stamp:
+        @param args:
+        @return:
+        """
+        self.scheduler.add_job(func=func, trigger='date', run_date=datetime.datetime.fromtimestamp(time_stamp),
+                               replace_existing=True, id=task_id, max_instances=1, coalesce=False, args=args)
 
     @staticmethod
     def del_job(task_id):  # 删除任务

+ 25 - 2
Service/CommonService.py

@@ -1,13 +1,14 @@
 import base64
+import calendar
 import datetime
-import ipdb
 import time
 from base64 import encodebytes
+from distutils.version import LooseVersion
 from pathlib import Path
 from random import Random
-from distutils.version import LooseVersion
 
 import OpenSSL.crypto as ct
+import ipdb
 import requests
 import simplejson as json
 from dateutil.relativedelta import relativedelta
@@ -786,6 +787,28 @@ GCqvlyw5dfxNA+EtxNE2wCW/LW7ENJlACgcfgPlBZtpLheWoZB/maw4=
                 continue
         return version_list
 
+    @staticmethod
+    def convert_to_timestamp(timezone_offset, time_string):
+        """
+        时间字符串转为时间戳
+        @param timezone_offset: 时区
+        @param time_string: 时间字符串
+        @return: timestamp
+        """
+        datetime_obj = datetime.datetime.strptime(time_string, '%Y-%m-%d %H:%M:%S')
+
+        # 创建一个表示指定时区的timedelta对象
+        utc_offset = datetime.timedelta(hours=timezone_offset)
+
+        # 调整时区
+        datetime_obj = datetime_obj - utc_offset
+
+        # datetime.datetime对象 -> str
+        time_str_utc = datetime_obj.strftime("%Y-%m-%d %H:%M:%S")
+        timestamp = calendar.timegm(time.strptime(time_str_utc, '%Y-%m-%d %H:%M:%S'))
+
+        return timestamp
+
     @staticmethod
     def get_uid_by_serial_number(serial_number):
         """