|
@@ -0,0 +1,219 @@
|
|
|
+# -*- encoding: utf-8 -*-
|
|
|
+"""
|
|
|
+@File : ActivityCenterController.py
|
|
|
+@Time : 2025/3/26 14:23
|
|
|
+@Author : stephen
|
|
|
+@Email : zhangdongming@asj6.wecom.work
|
|
|
+@Software: PyCharm
|
|
|
+"""
|
|
|
+import json
|
|
|
+import time
|
|
|
+
|
|
|
+from django.views import View
|
|
|
+
|
|
|
+from Model.models import FreeEvaluationActivity, ActivityTime
|
|
|
+from Object.Enums.RedisKeyConstant import RedisKeyConstant
|
|
|
+from Object.RedisObject import RedisObject
|
|
|
+from Object.ResponseObject import ResponseObject
|
|
|
+from Object.TokenObject import TokenObject
|
|
|
+from Ansjer.config import LOGGER
|
|
|
+
|
|
|
+
|
|
|
+class ActivityCenterView(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):
|
|
|
+ response = ResponseObject('en')
|
|
|
+ tko = TokenObject(request.META.get('HTTP_AUTHORIZATION'))
|
|
|
+ LOGGER.info(f'APNConfigView -- tko: {tko.code}, request_dict: {request_dict}')
|
|
|
+ if tko.code != 0:
|
|
|
+ return response.json(tko.code)
|
|
|
+ response.lang = tko.lang
|
|
|
+ userID = tko.userID
|
|
|
+ if operation == 'updateActivityVisited':
|
|
|
+ return self.update_activity_visited(request_dict, userID, response)
|
|
|
+ else:
|
|
|
+ return response.json(414)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def update_activity_visited(request_data: dict, user_id: str, response) -> bool:
|
|
|
+ """
|
|
|
+ 标记用户访问特定活动,并更新访问次数
|
|
|
+
|
|
|
+ Args:
|
|
|
+ request_data: 请求参数字典,需包含activityId
|
|
|
+ user_id: 用户唯一标识字符串
|
|
|
+ response: 响应对象,用于返回标准HTTP状态
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ bool: 操作是否成功,或通过response返回错误状态
|
|
|
+
|
|
|
+ 逻辑说明:
|
|
|
+ 1. 校验请求中是否包含有效活动ID
|
|
|
+ 2. 获取当前有效活动列表
|
|
|
+ 3. 仅当活动未过期时更新访问次数
|
|
|
+ 4. 访问次数存储在Redis并设置活动过期时间
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 参数有效性校验
|
|
|
+ activity_id = request_data.get('activityId')
|
|
|
+ if not activity_id:
|
|
|
+ return response.json(10, "Missing activityId") # 更规范的HTTP状态
|
|
|
+
|
|
|
+ # 获取有效活动列表
|
|
|
+ redis_client = RedisObject(4)
|
|
|
+ activity_info = ActivityCenterView.query_product_activity()
|
|
|
+ if not activity_info:
|
|
|
+ return response.json(10, "No available activities")
|
|
|
+
|
|
|
+ # 查找目标活动并验证有效期
|
|
|
+ current_time = int(time.time())
|
|
|
+ target_activity = None
|
|
|
+
|
|
|
+ # 遍历查找匹配活动
|
|
|
+ for activity in activity_info['activity_list']:
|
|
|
+ if str(activity.get('id')) == str(activity_id): # 兼容字符串和数字ID
|
|
|
+ if int(activity.get('end_time', 0)) > current_time:
|
|
|
+ target_activity = activity
|
|
|
+ break # 找到即终止循环
|
|
|
+
|
|
|
+ if not target_activity:
|
|
|
+ return response.json(10, "Invalid or expired activity")
|
|
|
+
|
|
|
+ # 构造Redis键并更新访问次数
|
|
|
+ base_key = RedisKeyConstant.ACTIVITY_INFO.value
|
|
|
+ redis_key = f"{base_key}:{user_id}:{activity_id}"
|
|
|
+
|
|
|
+ # 原子性增加计数并设置过期时间
|
|
|
+ expire_seconds = target_activity['end_time'] - current_time
|
|
|
+ if expire_seconds <= 0:
|
|
|
+ return response.json(10, "Activity already expired")
|
|
|
+
|
|
|
+ # 使用incr命令保证原子操作
|
|
|
+ redis_client.incr(redis_key, 1, expire_seconds)
|
|
|
+
|
|
|
+ return response.json(0)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(
|
|
|
+ f"活动访问标记异常,用户:{user_id},"
|
|
|
+ f"错误行号:{e.__traceback__.tb_lineno},"
|
|
|
+ f"详情:{repr(e)}"
|
|
|
+ )
|
|
|
+ return response.json(500)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_activity_badge_status(user_id: str) -> bool:
|
|
|
+ """
|
|
|
+ 检查用户是否有未读活动红点(Redis无记录时视为未读)
|
|
|
+
|
|
|
+ Args:
|
|
|
+ user_id: 用户唯一标识字符串
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ bool: True-存在未读活动,False-无未读或异常降级
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ activity_info = ActivityCenterView.query_product_activity()
|
|
|
+ if not activity_info:
|
|
|
+ return False # 无活动不显示红点
|
|
|
+
|
|
|
+ redis_client = RedisObject(4)
|
|
|
+ base_key = RedisKeyConstant.ACTIVITY_INFO.value
|
|
|
+ current_time = int(time.time())
|
|
|
+ for activity in activity_info['activity_list']:
|
|
|
+ activity_id = activity.get('id')
|
|
|
+ if not activity_id: # 防御空ID
|
|
|
+ continue
|
|
|
+ if int(activity['end_time']) < current_time:
|
|
|
+ continue
|
|
|
+ redis_key = f"{base_key}:{user_id}:{activity_id}"
|
|
|
+ raw_value = redis_client.get_data(redis_key)
|
|
|
+
|
|
|
+ # 关键判断逻辑:空值或值为0时触发红点
|
|
|
+ if raw_value is None: # Redis无记录
|
|
|
+ return True
|
|
|
+ try:
|
|
|
+ if int(raw_value) == 0: # 明确标记为未读
|
|
|
+ return True
|
|
|
+ except (TypeError, ValueError):
|
|
|
+ LOGGER.warning(
|
|
|
+ f"活动状态值非法,用户:{user_id},"
|
|
|
+ f"活动ID:{activity_id},值:{raw_value}"
|
|
|
+ )
|
|
|
+ return True # 异常值保守返回红点
|
|
|
+
|
|
|
+ return False
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ LOGGER.error(
|
|
|
+ f"红点查询异常,用户:{user_id},"
|
|
|
+ f"错误行号:{e.__traceback__.tb_lineno},"
|
|
|
+ f"详情:{repr(e)}"
|
|
|
+ )
|
|
|
+ return False
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def query_product_activity():
|
|
|
+ """
|
|
|
+ 查询当前有效的产品活动信息,优先从Redis读取,不存在时从数据库加载并缓存
|
|
|
+ Returns:
|
|
|
+ dict: 包含活动数量及详细信息的字典,异常时返回None
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 初始化Redis客户端,使用4号数据库
|
|
|
+ redis_client = RedisObject(4)
|
|
|
+ redis_key = RedisKeyConstant.ACTIVITY_INFO.value
|
|
|
+
|
|
|
+ # 尝试从Redis获取缓存数据
|
|
|
+ cached_data = redis_client.get_data(redis_key)
|
|
|
+ if cached_data:
|
|
|
+ return json.loads(cached_data)
|
|
|
+
|
|
|
+ # 数据库查询:获取所有可见的活动
|
|
|
+ activity_queryset = FreeEvaluationActivity.objects.filter(is_show=1).values('id')
|
|
|
+ if not activity_queryset.exists(): # 无数据时提前返回
|
|
|
+ return None
|
|
|
+
|
|
|
+ activity_list = []
|
|
|
+ # 遍历每个活动,获取其时间范围
|
|
|
+ for activity in activity_queryset:
|
|
|
+ activity_id = activity['id']
|
|
|
+ time_queryset = ActivityTime.objects.filter(activity_id=activity_id).order_by('start_time')
|
|
|
+
|
|
|
+ # 处理存在的时间记录
|
|
|
+ if time_queryset.exists():
|
|
|
+ first_time = time_queryset.first()
|
|
|
+ activity_dict = {
|
|
|
+ 'id': activity_id,
|
|
|
+ 'start_time': first_time.start_time,
|
|
|
+ 'end_time': time_queryset.last().end_time if time_queryset.count() > 1 else first_time.end_time
|
|
|
+ }
|
|
|
+ activity_list.append(activity_dict)
|
|
|
+
|
|
|
+ # 构建返回数据结构
|
|
|
+ result = {
|
|
|
+ 'count': len(activity_list),
|
|
|
+ 'activity_list': activity_list
|
|
|
+ }
|
|
|
+
|
|
|
+ # 写入Redis缓存(24小时有效期)
|
|
|
+ redis_client.set_data(
|
|
|
+ redis_key,
|
|
|
+ json.dumps(result),
|
|
|
+ RedisKeyConstant.EXPIRE_TIME_24_HOURS.value
|
|
|
+ )
|
|
|
+ return result
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ # 异常处理(包含错误行号打印)
|
|
|
+ LOGGER.error(f'活动查询异常,行号:{e.__traceback__.tb_lineno},错误信息:{repr(e)}')
|
|
|
+ return None
|