diff --git a/apps/daren_detail/views.py b/apps/daren_detail/views.py index 6a41284..e7b96f3 100644 --- a/apps/daren_detail/views.py +++ b/apps/daren_detail/views.py @@ -1,5 +1,4 @@ from django.http import JsonResponse -# from .models import TiktokUserVideos import logging import os from django.views.decorators.http import require_http_methods @@ -13,6 +12,15 @@ import dotenv import random import uuid from django.db.models import Q +from rest_framework.decorators import api_view, authentication_classes +from apps.user.authentication import CustomTokenAuthentication +from rest_framework.response import Response +from .models import ( + CreatorProfile, BrandCampaign, CreatorCampaign, + CollaborationMetrics, VideoMetrics, LiveMetrics, + FollowerMetrics, TrendMetrics, CreatorVideo +) +from apps.brands.models import Campaign, Brand dotenv.load_dotenv() @@ -26,12 +34,13 @@ monitor_thread = None is_monitoring = False +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def filter_creators(request): """根据过滤条件筛选达人信息(POST版)""" try: - from .models import CreatorProfile import json # 解析POST请求体 @@ -205,12 +214,13 @@ def filter_creators(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def add_creator(request): """添加或更新达人信息""" try: - from .models import CreatorProfile import json data = json.loads(request.body) @@ -281,7 +291,7 @@ def add_creator(request): creator.collab_count = data.get('collab_count', creator.collab_count) creator.latest_collab = data.get('latest_collab', creator.latest_collab) creator.e_commerce_platforms = data.get('e_commerce_platforms', creator.e_commerce_platforms) - creator.is_active = data.get('is_active', creator.is_active) + # creator.is_active = data.get('is_active', creator.is_active) creator.mcn = data.get('mcn', creator.mcn) # 保存更新 @@ -307,6 +317,8 @@ def add_creator(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_campaigns(request): @@ -349,6 +361,8 @@ def get_campaigns(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def add_to_campaign(request): @@ -446,11 +460,13 @@ def add_to_campaign(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) +@csrf_exempt @require_http_methods(["GET"]) def get_creator_detail(request, creator_id): """获取达人详情信息""" try: - from .models import CreatorProfile import json # 查询指定ID的达人信息 @@ -555,12 +571,13 @@ def get_creator_detail(request, creator_id): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def update_creator_detail(request): """更新达人详细信息""" try: - from .models import CreatorProfile import json data = json.loads(request.body) @@ -624,12 +641,14 @@ def update_creator_detail(request): # 获取特定达人与各品牌的合作详情 +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_creator_brand_campaigns(request, creator_id=None): """获取特定达人与各品牌的合作详情""" try: - from .models import CreatorProfile, BrandCampaign, CreatorCampaign, Campaign + import json # 检查creator_id是否提供 if not creator_id: @@ -674,7 +693,8 @@ def get_creator_brand_campaigns(request, creator_id=None): if not brand_campaigns_with_campaign.exists(): # 获取所有相关Campaign的品牌首字母 campaigns = Campaign.objects.filter(id__in=campaign_ids) - brands_needed = [campaign.brand[:1].upper() for campaign in campaigns if campaign.brand] + brands_needed = [campaign.brand.name[:1].upper() if isinstance(campaign.brand, Brand) else campaign.brand[:1].upper() + for campaign in campaigns if campaign.brand] # 合并品牌活动数据 campaign_list = [] @@ -747,7 +767,8 @@ def get_creator_brand_campaigns(request, creator_id=None): # 为每个CreatorCampaign创建一个默认的活动数据 for cc in creator_campaigns: campaign = cc.campaign - brand_id = campaign.brand[:1].upper() if campaign.brand else 'U' + # 修改获取brand_id的逻辑 + brand_id = campaign.brand.name[:1].upper() if isinstance(campaign.brand, Brand) else (campaign.brand[:1].upper() if campaign.brand else 'U') # 为不同品牌设置不同颜色 colors = { @@ -762,7 +783,7 @@ def get_creator_brand_campaigns(request, creator_id=None): campaign_data = { "brand": { "id": brand_id, - "name": campaign.brand or "brand", + "name": campaign.brand.name if isinstance(campaign.brand, Brand) else (campaign.brand or "brand"), "color": colors.get(brand_id, '#000000') }, "pricing_detail": "$80", # 默认价格 @@ -828,12 +849,13 @@ def get_creator_brand_campaigns(request, creator_id=None): # 获取创作者的协作指标、视频和直播指标数据 +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_creator_metrics(request, creator_id): """获取创作者的协作指标、视频和直播指标数据""" try: - from .models import CreatorProfile, CollaborationMetrics, VideoMetrics, LiveMetrics import json from datetime import datetime @@ -999,12 +1021,13 @@ def get_creator_metrics(request, creator_id): # 更新创作者的指标数据 +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def update_creator_metrics(request): """更新创作者的指标数据""" try: - from .models import CreatorProfile, CollaborationMetrics, VideoMetrics, LiveMetrics import json from datetime import datetime @@ -1209,12 +1232,13 @@ def update_creator_metrics(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_creator_followers_metrics(request, creator_id=None): """获取创作者的粉丝统计指标数据""" try: - from .models import CreatorProfile, FollowerMetrics import json from datetime import datetime @@ -1307,12 +1331,13 @@ def get_creator_followers_metrics(request, creator_id=None): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_creator_trends(request, creator_id=None): """获取创作者的趋势指标数据""" try: - from .models import CreatorProfile, TrendMetrics import json from datetime import datetime, timedelta @@ -1458,12 +1483,13 @@ def get_creator_trends(request, creator_id=None): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def update_creator_followers(request): """更新创作者的粉丝统计数据""" try: - from .models import CreatorProfile, FollowerMetrics import json from datetime import datetime @@ -1565,12 +1591,13 @@ def update_creator_followers(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def update_creator_trend(request): """更新创作者的趋势数据""" try: - from .models import CreatorProfile, TrendMetrics import json from datetime import datetime @@ -1649,12 +1676,13 @@ def update_creator_trend(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_creator_videos(request, creator_id=None): """获取创作者的视频列表,分为普通视频和带产品视频""" try: - from .models import CreatorProfile, CreatorVideo import json from datetime import datetime @@ -1850,12 +1878,13 @@ def get_creator_videos(request, creator_id=None): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def add_creator_video(request): """添加创作者视频""" try: - from .models import CreatorProfile, CreatorVideo import json from datetime import datetime @@ -1956,6 +1985,8 @@ def add_creator_video(request): ######################################## 公有达人和私有达人 ######################################## +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_public_creators(request): @@ -2065,6 +2096,8 @@ def get_public_creators(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def add_to_public_pool(request): @@ -2144,6 +2177,8 @@ def add_to_public_pool(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_private_pools(request): @@ -2197,6 +2232,8 @@ def get_private_pools(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def create_private_pool(request): @@ -2306,6 +2343,8 @@ def create_private_pool(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["GET"]) def get_private_pool_creators(request, pool_id=None): @@ -2452,6 +2491,8 @@ def get_private_pool_creators(request, pool_id=None): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def add_creator_to_private_pool(request): @@ -2574,6 +2615,8 @@ def add_creator_to_private_pool(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def update_creator_in_private_pool(request): @@ -2638,6 +2681,8 @@ def update_creator_in_private_pool(request): }, json_dumps_params={'ensure_ascii': False}) +@api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @csrf_exempt @require_http_methods(["POST"]) def remove_creator_from_private_pool(request): diff --git a/apps/user/authentication.py b/apps/user/authentication.py index e690a3e..9c2bf2c 100644 --- a/apps/user/authentication.py +++ b/apps/user/authentication.py @@ -1,42 +1,47 @@ -from rest_framework import authentication -from rest_framework import exceptions -from django.contrib.auth.models import AnonymousUser -from .models import User, UserToken -from django.utils import timezone - -class CustomTokenAuthentication(authentication.BaseAuthentication): - keyword = 'Token' # 设置认证头关键字 - - def authenticate(self, request): - # 从请求头获取token - auth_header = request.META.get('HTTP_AUTHORIZATION') - - if not auth_header: - return None - - try: - # 提取token - parts = auth_header.split() - - if len(parts) != 2 or parts[0] != self.keyword: - raise exceptions.AuthenticationFailed('无效的认证头格式,应为: Token ') - - token = parts[1] - - # 查找token记录并确保token存在且有效 - try: - token_obj = UserToken.objects.select_related('user').get( - token=token, - expired_at__gt=timezone.now() # 确保token未过期 - ) - except UserToken.DoesNotExist: - raise exceptions.AuthenticationFailed('无效的token') - - # 检查用户是否激活 - if not token_obj.user.is_active: - raise exceptions.AuthenticationFailed('用户已被禁用') - - return (token_obj.user, None) - - except Exception as e: - raise exceptions.AuthenticationFailed(f'认证失败: {str(e)}') \ No newline at end of file +from rest_framework import authentication +from rest_framework import exceptions +from django.contrib.auth.models import AnonymousUser +from .models import User, UserToken +from django.utils import timezone + +class CustomTokenAuthentication(authentication.BaseAuthentication): + keyword = 'Token' # 设置认证头关键字 + + def authenticate(self, request): + # 从请求头获取token + auth_header = request.META.get('HTTP_AUTHORIZATION') + + if not auth_header: + raise exceptions.AuthenticationFailed('未提供认证头') + + try: + # 提取token + parts = auth_header.split() + + if len(parts) != 2 or parts[0] != self.keyword: + raise exceptions.AuthenticationFailed('认证头格式不正确') + + token = parts[1] + + # 查找token记录并确保token存在且有效 + try: + token_obj = UserToken.objects.select_related('user').get( + token=token, + expired_at__gt=timezone.now() # 确保token未过期 + ) + except UserToken.DoesNotExist: + raise exceptions.AuthenticationFailed('无效或过期的token') + + # 检查用户是否激活 + if not token_obj.user.is_active: + raise exceptions.AuthenticationFailed('用户未激活') + + return (token_obj.user, None) + + except Exception as e: + if isinstance(e, exceptions.AuthenticationFailed): + raise e + raise exceptions.AuthenticationFailed('认证失败') + + def authenticate_header(self, request): + return self.keyword \ No newline at end of file diff --git a/apps/user/views.py b/apps/user/views.py index 616fd0a..51c2e5d 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -11,8 +11,9 @@ import concurrent.futures import shutil import dotenv import random -from rest_framework.decorators import api_view, permission_classes +from rest_framework.decorators import api_view, permission_classes, authentication_classes from rest_framework.permissions import IsAuthenticated, AllowAny +from .authentication import CustomTokenAuthentication import hashlib import time from django.contrib.auth.hashers import check_password @@ -140,6 +141,7 @@ def user_login(request): @csrf_exempt @api_view(['POST']) +@authentication_classes([CustomTokenAuthentication]) @permission_classes([IsAuthenticated]) def update_user_info(request): """更新用户信息,需要认证""" diff --git a/daren/settings.py b/daren/settings.py index ed52ca0..4890e08 100644 --- a/daren/settings.py +++ b/daren/settings.py @@ -205,6 +205,9 @@ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'apps.user.authentication.CustomTokenAuthentication', ), + 'UNAUTHENTICATED_USER': None, + 'DEFAULT_PERMISSION_CLASSES': [], # 默认不需要任何权限 + 'DEFAULT_AUTHENTICATION_CLASSES': [] # 默认不需要任何认证 } # JWT 设置