from django.db.models import F from django.shortcuts import render from rest_framework.exceptions import ValidationError from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers, status from django_redis import get_redis_connection import random # Create your views here. import uuid import re def phone_validator(value): if not re.match(r'^(1[3|4|5|6|7|8|9])\d{9}$', value): raise ValidationError('手机格式错误') class LoginSerializer(serializers.Serializer): phone = serializers.CharField(label='手机号', validators=[phone_validator, ]) code = serializers.CharField(label='短信验证码') def validate_code(self, value): if len(value) !=4: raise ValidationError('短信格式错误') if not value.isdecimal(): raise ValidationError("短信格式错误") phone=self.initial_data.get('phone') conn=get_redis_connection() code=conn.get(phone) if not code: raise ValidationError("验证码过期") if value!=code.decode('utf-8'): raise ValidationError("验证码错误") return value from rest_framework.views import APIView from rest_framework.response import Response from .models import UserInfo, UserBehavior import uuid from datetime import datetime, timedelta from rest_framework.views import APIView from rest_framework.response import Response from django.utils import timezone from datetime import timedelta import uuid class LoginView(APIView): def post(self, request, *args, **kwargs): # 验证登录数据 ser = LoginSerializer(data=request.data) if not ser.is_valid(): return Response({'status': False, 'message': '验证码错误'}, status=400) phone = ser.validated_data.get('phone') # 获取或创建用户 user_object, created = UserInfo.objects.get_or_create(phone=phone) user_object.token = str(uuid.uuid4()) user_object.save() # 如果是新用户,初始化三个核心功能的行为记录 if created: self.initialize_user_features(user_object) return Response({ 'status': True, 'data': { 'token': user_object.token, 'phone': phone, 'id': user_object.id, 'is_new_user': created } }) def initialize_user_features(self, user): """为新用户初始化功能映射""" CORE_FEATURES = [ { 'feature_name': 'ai-plan', 'page_path': '/pages/ai-plan/ai-plan', 'priority': 1.0 }, { 'feature_name': 'quiz-main', 'page_path': '/pages/quiz/quiz', 'priority': 1.0 }, { 'feature_name': 'search-all', 'page_path': '/pages/gongjiao/gongjiao', 'priority': 1.0 } ] # 批量创建更高效 FeatureMapping.objects.bulk_create([ FeatureMapping( user_info=user, feature_name=feature['feature_name'], page_path=feature['page_path'], priority=feature['priority'] ) for feature in CORE_FEATURES ]) class MessageSerializer(serializers.Serializer): phone = serializers.CharField(label='手机号', validators=[phone_validator, ]) class MessageView(APIView): def get(self, request, *args, **kwargs): ser=MessageSerializer(data=request.query_params) if not ser.is_valid(): return Response({'status':False,'message':'手机号错误'}) phone = ser.validated_data.get('phone') print(phone) random_code = random.randint(1000, 9999) print(random_code) import redis conn=get_redis_connection() conn.set(phone,random_code,ex=60) print(conn.keys()) return Response({'status': True,'message':'发送成功'}) from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from django.utils import timezone from django.db import models, transaction from .models import UserBehavior, FeatureMapping, UserInfo from .serializers import UserBehaviorSerializer, FeatureMappingSerializer import math from datetime import timedelta class RecommendLayoutView(APIView): """ 推荐个性化布局API 请求方式:GET 请求头:Authorization: Token 返回:布局类型和推荐功能排序 """ def update_feature_priorities(self, feature_weights,user_info): """更新功能优先级到数据库""" with transaction.atomic(): for feature_name, weight in feature_weights.items(): # 将权重转换为1-10的优先级(保留2位小数) priority = round(weight * 10, 2) FeatureMapping.objects.filter( feature_name=feature_name, user_info_id = user_info, ).update(priority=priority) def get_user_by_token(self, token): try: return UserInfo.objects.get(token=token) except UserInfo.DoesNotExist: return None def calculate_feature_weights(self, behaviors, user_info): """计算功能权重(现在考虑用户特定的优先级)""" feature_weights = {} total_usage = sum(b.usage_count for b in behaviors) or 1 for behavior in behaviors: # 1. 使用频率权重 (40%) freq_weight = behavior.usage_count / total_usage # 2. 最近使用权重 (30%) days_ago = (timezone.now() - behavior.last_used).days recency_weight = max(0, (30 - days_ago) / 30) # 3. 获取业务优先级 (30%) —— 现在查询用户特定的优先级 try: mapping = FeatureMapping.objects.get( feature_name=behavior.feature_name, user_info=user_info # 关键修改:关联用户 ) priority = mapping.priority / 10.0 except FeatureMapping.DoesNotExist: priority = 0.3 # 综合权重(保持不变) feature_weights[behavior.feature_name] = ( 0.4 * freq_weight + 0.3 * recency_weight + 0.3 * priority ) return feature_weights def determine_layout_type(self, behaviors, feature_weights): """确定布局类型""" if not behaviors: return 'default' sorted_features = sorted(feature_weights.items(), key=lambda x: x[1], reverse=True) top_feature = sorted_features[0][0] # 根据最高权重的功能决定布局 if top_feature in ['map', 'scenic_spot']: return 'map_focused' elif top_feature in ['ai-plan', 'gongjiao']: return 'feature_focused' elif top_feature in ['vr', 'panorama']: return 'vr_focused' else: return 'default' def get(self, request): # 通过Token获取用户 token = request.META.get('HTTP_AUTHORIZATION', '').split(' ')[-1] user_info = self.get_user_by_token(token) if not user_info: default_response = { 'layout_type': 'default', 'feature_weights': {}, 'features': FeatureMappingSerializer(FeatureMapping.objects.none(), many=True).data, 'recommended_order': [], 'is_default': True } return Response(default_response) # 获取用户行为数据 behaviors = UserBehavior.objects.filter(user_info=user_info) # 计算功能权重 feature_weights = self.calculate_feature_weights(behaviors, user_info) self.update_feature_priorities(feature_weights, user_info) # 确定布局类型 layout_type = self.determine_layout_type(behaviors, feature_weights) # 获取所有功能映射 features = FeatureMapping.objects.filter(user_info_id=user_info) # 构建响应数据 response_data = { 'layout_type': layout_type, 'feature_weights': feature_weights, 'features': FeatureMappingSerializer(features, many=True).data, 'recommended_order': sorted(feature_weights.keys(), key=lambda x: -feature_weights[x]), 'is_default': not behaviors.exists() } return Response(response_data) class RecordUsageView(APIView): """ 记录用户行为API 请求方式:POST 请求参数:feature_name (功能名称) 请求头:Authorization: Token """ def post(self, request): # 增强Token提取逻辑 print("接收到的请求数据:", request.data) # 调试日志 print("请求头:", request.headers) auth_header = request.META.get('HTTP_AUTHORIZATION', '') if not auth_header.startswith('Token '): return Response( {'error': '授权头格式错误,应为 Token '}, status=status.HTTP_401_UNAUTHORIZED ) token = auth_header.split(' ')[-1] if not token: return Response( {'error': 'Token不能为空'}, status=status.HTTP_401_UNAUTHORIZED ) try: user_info = UserInfo.objects.get(token=token) except UserInfo.DoesNotExist: return Response( { 'error': '用户不存在或未授权', 'debug': f'提供的Token: {token}', 'solution': '请检查用户是否完成登录流程' }, status=status.HTTP_401_UNAUTHORIZED ) # 获取功能名称 feature_name = request.data.get('feature_name') if not feature_name: return Response({'error': '缺少feature_name参数'}, status=status.HTTP_400_BAD_REQUEST) # 验证功能是否存在 if not FeatureMapping.objects.filter(feature_name=feature_name).exists(): return Response({'error': '无效的功能名称'}, status=status.HTTP_400_BAD_REQUEST) # 更新或创建行为记录 behavior, created = UserBehavior.objects.get_or_create( user_info=user_info, feature_name=feature_name, defaults={ 'usage_count': 0, # 新记录初始化为0 'last_used': timezone.now() } ) # 然后更新(此时无论新旧记录都能用F()) UserBehavior.objects.filter( id=behavior.id ).update( usage_count=F('usage_count') + 1 ) # 重新获取对象(此时usage_count是计算后的值) behavior.refresh_from_db() # 关键步骤 return Response({ 'status': 'success', 'feature': feature_name, 'usage_count': behavior.usage_count, 'last_used': behavior.last_used }, status=status.HTTP_200_OK)