123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- 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 <user_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 <user_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 <your_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)
|