|
@@ -0,0 +1,184 @@
|
|
|
+# -*- encoding: utf-8 -*-
|
|
|
+"""
|
|
|
+@File : DeviceReportController.py
|
|
|
+@Time : 2025/4/3 16:20
|
|
|
+@Author : stephen
|
|
|
+@Email : zhangdongming@asj6.wecom.work
|
|
|
+@Software: PyCharm
|
|
|
+"""
|
|
|
+import time
|
|
|
+from datetime import timedelta, datetime
|
|
|
+
|
|
|
+import pytz
|
|
|
+from django.db.models import Sum, Value
|
|
|
+from django.db.models.functions import Coalesce
|
|
|
+from django.http import QueryDict
|
|
|
+from django.views import View
|
|
|
+
|
|
|
+from Ansjer.config import LOGGER
|
|
|
+from Model.models import DeviceDailyReport
|
|
|
+from Object.ResponseObject import ResponseObject
|
|
|
+from Object.TokenObject import TokenObject
|
|
|
+
|
|
|
+
|
|
|
+class DeviceReportView(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 delete(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ delete = QueryDict(request.body)
|
|
|
+ if not delete:
|
|
|
+ delete = request.GET
|
|
|
+ return self.validation(delete, request, operation)
|
|
|
+
|
|
|
+ def put(self, request, *args, **kwargs):
|
|
|
+ request.encoding = 'utf-8'
|
|
|
+ operation = kwargs.get('operation')
|
|
|
+ put = QueryDict(request.body)
|
|
|
+ return self.validation(put, request, operation)
|
|
|
+
|
|
|
+ def validation(self, request_dict, request, operation):
|
|
|
+ response = ResponseObject('cn')
|
|
|
+ tko = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
|
|
|
+ if tko.code != 0:
|
|
|
+ return response.json(tko.code)
|
|
|
+ response.lang = tko.lang
|
|
|
+ userID = tko.userID
|
|
|
+ if operation == 'getBatteryCapacityList': # 获取电池电量列表
|
|
|
+ return self.get_battery_capacity_list(userID, request, request_dict, response)
|
|
|
+ elif operation == 'getWakeSleepData':
|
|
|
+ return self.get_wake_sleep_data(userID, request_dict, response)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_wake_sleep_data(cls, userID, request_dict, response):
|
|
|
+ """
|
|
|
+ 获取设备睡眠唤醒统计数据
|
|
|
+ :param userID: 响应对象
|
|
|
+ :param request_dict: 参数对象
|
|
|
+ :param response: 响应对象
|
|
|
+ :return: 包含7天汇总统计和30天明细数据的JSON响应
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # region 时间参数计算
|
|
|
+ uid = request_dict.get('device_id', None)
|
|
|
+ if not uid:
|
|
|
+ return response.json(444)
|
|
|
+ current_time = int(time.time())
|
|
|
+ SECONDS_PER_DAY = 24 * 60 * 60 # 单日秒数常量
|
|
|
+
|
|
|
+ # 计算时间范围(秒级时间戳)
|
|
|
+ seven_days_ago = current_time - 7 * SECONDS_PER_DAY # 7天前
|
|
|
+ thirty_days_ago = current_time - 30 * SECONDS_PER_DAY # 30天前
|
|
|
+ # endregion
|
|
|
+
|
|
|
+ # region 7天汇总统计 (使用Coalesce避免空值)
|
|
|
+
|
|
|
+ seven_days_stats = DeviceDailyReport.objects.filter(
|
|
|
+ device_id=uid,
|
|
|
+ report_time__range=(seven_days_ago, current_time) # 时间范围查询
|
|
|
+ ).aggregate(
|
|
|
+ total_human_detection=Coalesce(Sum('human_detection'), Value(0)),
|
|
|
+ total_working_hours=Coalesce(Sum('working_hours'), Value(0)),
|
|
|
+ total_wake_sleep=Coalesce(Sum('wake_sleep'), Value(0))
|
|
|
+ )
|
|
|
+ # endregion
|
|
|
+
|
|
|
+ # region 30天明细数据(按时间倒序)
|
|
|
+ thirty_days_details = DeviceDailyReport.objects.filter(
|
|
|
+ device_id=uid,
|
|
|
+ report_time__gte=thirty_days_ago,
|
|
|
+ report_time__lte=current_time
|
|
|
+ ).order_by('-report_time').values_list(
|
|
|
+ 'human_detection',
|
|
|
+ 'working_hours',
|
|
|
+ 'wake_sleep',
|
|
|
+ 'battery_level',
|
|
|
+ 'report_time',
|
|
|
+ named=True # 使用命名元组方便访问
|
|
|
+ )
|
|
|
+ # endregion
|
|
|
+
|
|
|
+ # 组合返回数据结构
|
|
|
+ result_data = {
|
|
|
+ **seven_days_stats,
|
|
|
+ 'detail_report': list(thirty_days_details) # 转换查询集为列表
|
|
|
+ }
|
|
|
+
|
|
|
+ return response.json(0, result_data)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ error_msg = f"查询异常: {str(e)},userID: {userID}"
|
|
|
+ error_line = e.__traceback__.tb_lineno
|
|
|
+ LOGGER.error(f"{error_msg} 行号: {error_line}")
|
|
|
+
|
|
|
+ # 返回安全空数据
|
|
|
+ return response.json(500, {
|
|
|
+ 'total_human_detection': 0,
|
|
|
+ 'total_working_hours': 0,
|
|
|
+ 'total_wake_sleep': 0,
|
|
|
+ 'detail_report': []})
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_battery_capacity_list(cls, userID, request, request_dict, response):
|
|
|
+ try:
|
|
|
+ # 参数校验
|
|
|
+ device_id = request_dict.get('device_id')
|
|
|
+ if not device_id:
|
|
|
+ return response.json(444, "设备ID不能为空")
|
|
|
+ tz_offset = float(request_dict.get('tz', 0)) # 允许小数时区如5.5
|
|
|
+ if not (-12 <= tz_offset <= 14):
|
|
|
+ raise ValueError
|
|
|
+ tz = pytz.FixedOffset(int(tz_offset * 60)) # 转换为分钟偏移
|
|
|
+
|
|
|
+ # 计算日期范围(关键修改:end_time取昨日23:59:59)
|
|
|
+ query_days = int(request_dict.get('days', 7))
|
|
|
+ client_now = datetime.now(tz)
|
|
|
+ end_time = client_now.replace(
|
|
|
+ hour=23, minute=59, second=59
|
|
|
+ ) - timedelta(days=1) # 截止到客户端昨日
|
|
|
+
|
|
|
+ start_time = end_time - timedelta(days=query_days - 1)
|
|
|
+ start_time = start_time.replace(hour=0, minute=0, second=0)
|
|
|
+
|
|
|
+ # 转换时间范围到UTC时间戳
|
|
|
+ start_utc_timestamp = int(start_time.astimezone(pytz.utc).timestamp())
|
|
|
+ end_utc_timestamp = int(end_time.astimezone(pytz.utc).timestamp())
|
|
|
+
|
|
|
+ # 查询数据库
|
|
|
+ records = DeviceDailyReport.objects.filter(
|
|
|
+ device_id=device_id,
|
|
|
+ type=1,
|
|
|
+ report_time__gte=start_utc_timestamp,
|
|
|
+ report_time__lte=end_utc_timestamp
|
|
|
+ ).order_by('report_time')
|
|
|
+
|
|
|
+ # 构建日期-电量映射(按客户端时区)
|
|
|
+ date_battery_map = {}
|
|
|
+ for record in records:
|
|
|
+ local_date = datetime.fromtimestamp(record.report_time, tz).date()
|
|
|
+ date_str = local_date.strftime("%Y-%m-%d")
|
|
|
+ date_battery_map[date_str] = record.battery_level
|
|
|
+
|
|
|
+ # 生成完整日期序列
|
|
|
+ report_data = []
|
|
|
+ for day_offset in range(query_days):
|
|
|
+ current_date = (end_time.date() - timedelta(days=query_days - 1 - day_offset))
|
|
|
+ report_data.append({
|
|
|
+ "index": current_date.day - 1, # 日期下标从0开始
|
|
|
+ "battery": date_battery_map.get(current_date.strftime("%Y-%m-%d"), 0),
|
|
|
+ "time": current_date.strftime("%Y-%m-%d")
|
|
|
+ })
|
|
|
+
|
|
|
+ return response.json(0, report_data)
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error('查询设备电量上报列表异常error_line:{}, error_msg:{}'.format(e.__traceback__.tb_lineno, repr(e)))
|
|
|
+ return response.json(0, {})
|