from django.shortcuts import render import json import uuid import logging from django.db import transaction from django.shortcuts import get_object_or_404 from django.conf import settings from django.utils import timezone from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.response import Response from django.db.models import Q import os from .models import OperatorAccount, PlatformAccount, Video from .serializers import ( OperatorAccountSerializer, PlatformAccountSerializer, VideoSerializer, MultiPlatformAccountSerializer ) from .pagination import CustomPagination logger = logging.getLogger(__name__) class OperatorAccountViewSet(viewsets.ModelViewSet): """运营账号管理视图集""" queryset = OperatorAccount.objects.all() serializer_class = OperatorAccountSerializer pagination_class = CustomPagination def list(self, request, *args, **kwargs): """获取运营账号列表""" queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) # 使用自定义分页器的响应 return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response({ "code": 200, "message": "获取运营账号列表成功", "data": serializer.data }) def retrieve(self, request, *args, **kwargs): """获取运营账号详情""" instance = self.get_object() serializer = self.get_serializer(instance) return Response({ "code": 200, "message": "获取运营账号详情成功", "data": serializer.data }) def update(self, request, *args, **kwargs): """更新运营账号信息""" partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response({ "code": 200, "message": "更新运营账号信息成功", "data": serializer.data }) def partial_update(self, request, *args, **kwargs): """部分更新运营账号信息""" kwargs['partial'] = True return self.update(request, *args, **kwargs) def create(self, request, *args, **kwargs): """创建运营账号""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response({ "code": 200, "message": "运营账号创建成功", "data": serializer.data }, status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): """删除运营账号""" operator = self.get_object() operator.is_active = False # 软删除 operator.save() return Response({ "code": 200, "message": "运营账号已停用", "data": None }) class PlatformAccountViewSet(viewsets.ModelViewSet): """平台账号管理视图集""" queryset = PlatformAccount.objects.all() serializer_class = PlatformAccountSerializer pagination_class = CustomPagination def list(self, request, *args, **kwargs): """获取平台账号列表""" queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) # 处理数据结构 response_data = serializer.data restructured_data = [] for account_data in response_data: # 提取平台信息并放入platforms字段 platform_info = { "platform_name": account_data.pop("platform_name"), "account_url": account_data.pop("account_url"), "account_id": account_data.pop("account_id"), "account_name": account_data.pop("account_name") } # 添加platforms字段作为数组 account_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] restructured_data.append(account_data) # 使用自定义分页器的响应,但替换数据 return self.get_paginated_response(restructured_data) serializer = self.get_serializer(queryset, many=True) # 处理数据结构 response_data = serializer.data restructured_data = [] for account_data in response_data: # 提取平台信息并放入platforms字段 platform_info = { "platform_name": account_data.pop("platform_name"), "account_url": account_data.pop("account_url"), "account_id": account_data.pop("account_id"), "account_name": account_data.pop("account_name") } # 添加platforms字段作为数组 account_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] restructured_data.append(account_data) return Response({ "code": 200, "message": "获取平台账号列表成功", "data": restructured_data }) def retrieve(self, request, *args, **kwargs): """获取平台账号详情""" instance = self.get_object() serializer = self.get_serializer(instance) # 处理数据结构 account_data = serializer.data # 提取平台信息并放入platforms字段 platform_info = { "platform_name": account_data.pop("platform_name"), "account_url": account_data.pop("account_url"), "account_id": account_data.pop("account_id"), "account_name": account_data.pop("account_name") } # 添加platforms字段 account_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] return Response({ "code": 200, "message": "获取平台账号详情成功", "data": account_data }) def update(self, request, *args, **kwargs): """更新平台账号信息""" partial = kwargs.pop('partial', False) instance = self.get_object() # 单独处理platforms字段,多平台信息需要特殊处理 data = request.data.copy() platforms_data = None if 'platforms' in data and isinstance(data['platforms'], list): platforms_data = data.pop('platforms') # 如果有platforms数据,先将第一个平台的信息移至顶层,保证基本平台信息能正常更新 if platforms_data and len(platforms_data) > 0: first_platform = platforms_data[0] if 'platform_name' in first_platform: data['platform_name'] = first_platform['platform_name'] if 'account_url' in first_platform: data['account_url'] = first_platform['account_url'] if 'account_id' in first_platform: data['account_id'] = first_platform['account_id'] if 'account_name' in first_platform: data['account_name'] = first_platform['account_name'] serializer = self.get_serializer(instance, data=data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) # 处理多平台信息 # 这里我们需要实现新的逻辑来处理额外的平台 # 注意:这需要修改模型结构或添加关联模型来支持多平台 # 由于当前模型结构限制,我们暂时只能在此记录其他平台数据 # 用于未来扩展,目前会在日志中记录这些信息 if platforms_data and len(platforms_data) > 1: logger = logging.getLogger(__name__) logger.info(f"接收到多平台数据,但当前版本仅支持一个平台。额外平台数据: {platforms_data[1:]}") # 这里应该添加创建关联平台记录的代码 # 例如: # for platform_data in platforms_data[1:]: # RelatedPlatform.objects.create( # primary_account=instance, # platform_name=platform_data.get('platform_name', ''), # account_id=platform_data.get('account_id', ''), # account_name=platform_data.get('account_name', ''), # account_url=platform_data.get('account_url', '') # ) # 处理数据结构 account_data = serializer.data # 提取平台信息并放入platforms字段 platform_info = { "platform_name": account_data.pop("platform_name"), "account_url": account_data.pop("account_url"), "account_id": account_data.pop("account_id"), "account_name": account_data.pop("account_name") } # 添加platforms字段 # 如果有platforms_data,使用原始请求中的数据,否则使用当前的单平台数据 if platforms_data: account_data["platforms"] = platforms_data else: account_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] return Response({ "code": 200, "message": "更新平台账号信息成功", "data": account_data }) def partial_update(self, request, *args, **kwargs): """部分更新平台账号信息""" kwargs['partial'] = True return self.update(request, *args, **kwargs) def create(self, request, *args, **kwargs): """创建平台账号""" # 传统单平台账号创建流程 print(f"{request.data}") with transaction.atomic(): # 处理operator字段,可能是字符串类型的ID data = request.data.copy() if 'operator' in data and isinstance(data['operator'], str): try: # 尝试通过ID查找运营账号 operator_id = data['operator'] try: # 先尝试通过整数ID查找 operator_id_int = int(operator_id) operator = OperatorAccount.objects.get(id=operator_id_int) except (ValueError, OperatorAccount.DoesNotExist): # 如果无法转换为整数或找不到对应账号,尝试通过用户名或真实姓名查找 operator = OperatorAccount.objects.filter( Q(username=operator_id) | Q(real_name=operator_id) ).first() if not operator: return Response({ "code": 404, "message": f"未找到运营账号: {operator_id},请提供有效的ID、用户名或真实姓名", "data": None }, status=status.HTTP_404_NOT_FOUND) # 更新请求数据中的operator字段为找到的operator的ID data['operator'] = operator.id except Exception as e: return Response({ "code": 400, "message": f"处理运营账号ID时出错: {str(e)}", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 创建平台账号 serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) # 创建平台账号 self.perform_create(serializer) # 处理响应数据 account_data = serializer.data # 提取平台信息并放入platforms字段 platform_info = { "platform_name": account_data.pop("platform_name"), "account_url": account_data.pop("account_url"), "account_id": account_data.pop("account_id"), "account_name": account_data.pop("account_name") } # 添加platforms字段 account_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] return Response({ "code": 200, "message": "平台账号创建成功", "data": account_data }, status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): """删除平台账号""" platform_account = self.get_object() self.perform_destroy(platform_account) return Response({ "code": 200, "message": "平台账号已删除", "data": None }) @action(detail=True, methods=['post']) def update_followers(self, request, pk=None): """更新平台账号粉丝数""" platform_account = self.get_object() followers_count = request.data.get('followers_count') if not followers_count: return Response({ "code": 400, "message": "粉丝数不能为空", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 更新粉丝数 platform_account.followers_count = followers_count platform_account.save() # 准备响应数据,与其他方法保持一致 platform_data = self.get_serializer(platform_account).data # 提取平台信息并放入platforms字段 platform_info = { "platform_name": platform_data.pop("platform_name"), "account_url": platform_data.pop("account_url"), "account_id": platform_data.pop("account_id"), "account_name": platform_data.pop("account_name") } # 添加platforms字段 platform_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not platform_data.get("name"): platform_data["name"] = platform_info["platform_name"] return Response({ "code": 200, "message": "粉丝数更新成功", "data": platform_data }) @action(detail=True, methods=['post']) def update_profile(self, request, pk=None): """更新平台账号的头像、标签和最后发布时间""" platform_account = self.get_object() # 获取更新的资料数据 profile_data = {} # 处理标签 if 'tags' in request.data: # 处理tags,支持字符串或数组格式 tags = request.data['tags'] if isinstance(tags, list): profile_data['tags'] = ','.join(tags) else: profile_data['tags'] = tags # 处理头像 if 'profile_image' in request.data: profile_data['profile_image'] = request.data['profile_image'] # 处理最后发布时间 if 'last_posting' in request.data: try: # 尝试解析时间字符串 from dateutil import parser last_posting = parser.parse(request.data['last_posting']) profile_data['last_posting'] = last_posting except Exception as e: return Response({ "code": 400, "message": f"最后发布时间格式错误: {str(e)}", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 处理名称 if 'name' in request.data: profile_data['name'] = request.data['name'] if not profile_data: return Response({ "code": 400, "message": "没有提供任何需更新的资料数据", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 更新平台账号资料 for field, value in profile_data.items(): setattr(platform_account, field, value) platform_account.save() # 准备响应数据,与其他方法保持一致 platform_data = self.get_serializer(platform_account).data # 提取平台信息并放入platforms字段 platform_info = { "platform_name": platform_data.pop("platform_name"), "account_url": platform_data.pop("account_url"), "account_id": platform_data.pop("account_id"), "account_name": platform_data.pop("account_name") } # 添加platforms字段 platform_data["platforms"] = [platform_info] # 保留用户传入的name字段,如果没有则使用platform_name if not platform_data.get("name"): platform_data["name"] = platform_info["platform_name"] return Response({ "code": 200, "message": "平台账号资料更新成功", "data": platform_data }) class VideoViewSet(viewsets.ModelViewSet): """视频管理视图集""" queryset = Video.objects.all() serializer_class = VideoSerializer pagination_class = CustomPagination def list(self, request, *args, **kwargs): """获取视频列表""" queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) # 使用自定义分页器的响应 return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response({ "code": 200, "message": "获取视频列表成功", "data": serializer.data }) def retrieve(self, request, *args, **kwargs): """获取视频详情""" instance = self.get_object() serializer = self.get_serializer(instance) return Response({ "code": 200, "message": "获取视频详情成功", "data": serializer.data }) def update(self, request, *args, **kwargs): """更新视频信息""" partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response({ "code": 200, "message": "更新视频信息成功", "data": serializer.data }) def partial_update(self, request, *args, **kwargs): """部分更新视频信息""" kwargs['partial'] = True return self.update(request, *args, **kwargs) def create(self, request, *args, **kwargs): """创建视频""" with transaction.atomic(): # 处理platform_account字段,可能是字符串类型的ID data = request.data.copy() if 'platform_account' in data and isinstance(data['platform_account'], str): try: # 尝试通过ID查找平台账号 platform_id = data['platform_account'] try: # 先尝试通过整数ID查找 platform_id_int = int(platform_id) platform = PlatformAccount.objects.get(id=platform_id_int) except (ValueError, PlatformAccount.DoesNotExist): # 如果无法转换为整数或找不到对应账号,尝试通过账号名称或账号ID查找 platform = PlatformAccount.objects.filter( Q(account_name=platform_id) | Q(account_id=platform_id) ).first() if not platform: return Response({ "code": 404, "message": f"未找到平台账号: {platform_id},请提供有效的ID、账号名称或账号ID", "data": None }, status=status.HTTP_404_NOT_FOUND) # 更新请求数据中的platform_account字段为找到的platform的ID data['platform_account'] = platform.id except Exception as e: return Response({ "code": 400, "message": f"处理平台账号ID时出错: {str(e)}", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 创建视频 serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) # 创建视频 self.perform_create(serializer) return Response({ "code": 200, "message": "视频创建成功", "data": serializer.data }, status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): """删除视频记录""" video = self.get_object() self.perform_destroy(video) return Response({ "code": 200, "message": "视频记录已删除", "data": None }) @action(detail=True, methods=['post']) def update_stats(self, request, pk=None): """更新视频统计数据""" video = self.get_object() # 获取更新的统计数据 stats = {} for field in ['views_count', 'likes_count', 'comments_count', 'shares_count']: if field in request.data: stats[field] = request.data[field] if not stats: return Response({ "code": 400, "message": "没有提供任何统计数据", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 更新视频统计数据 for field, value in stats.items(): setattr(video, field, value) video.save() return Response({ "code": 200, "message": "视频统计数据更新成功", "data": { "id": video.id, "title": video.title, "views_count": video.views_count, "likes_count": video.likes_count, "comments_count": video.comments_count, "shares_count": video.shares_count } }) @action(detail=True, methods=['post']) def publish(self, request, pk=None): """发布视频并更新状态""" video = self.get_object() # 检查视频状态 if video.status not in ['draft', 'scheduled']: return Response({ "code": 400, "message": f"当前视频状态为 {video.get_status_display()},无法发布", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 获取视频URL video_url = request.data.get('video_url') if not video_url: return Response({ "code": 400, "message": "未提供视频URL", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 更新视频状态和URL video.video_url = video_url video.status = 'published' video.publish_time = timezone.now() video.save() return Response({ "code": 200, "message": "视频已成功发布", "data": { "id": video.id, "title": video.title, "status": video.status, "video_url": video.video_url, "publish_time": video.publish_time } }) @action(detail=False, methods=['post']) def upload_video(self, request): """上传视频文件并创建视频记录""" try: # 获取上传的视频文件 video_file = request.FILES.get('video_file') if not video_file: return Response({ "code": 400, "message": "未提供视频文件", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 获取平台账号ID platform_account_id = request.data.get('platform_account') if not platform_account_id: return Response({ "code": 400, "message": "未提供平台账号ID", "data": None }, status=status.HTTP_400_BAD_REQUEST) try: platform_account = PlatformAccount.objects.get(id=platform_account_id) except PlatformAccount.DoesNotExist: return Response({ "code": 404, "message": f"未找到ID为{platform_account_id}的平台账号", "data": None }, status=status.HTTP_404_NOT_FOUND) # 创建保存视频的目录 import os from django.conf import settings # 确保文件保存目录存在 media_root = getattr(settings, 'MEDIA_ROOT', os.path.join(settings.BASE_DIR, 'media')) videos_dir = os.path.join(media_root, 'videos') account_dir = os.path.join(videos_dir, f"{platform_account.platform_name}_{platform_account.account_name}") if not os.path.exists(videos_dir): os.makedirs(videos_dir) if not os.path.exists(account_dir): os.makedirs(account_dir) # 生成唯一的文件名 import time timestamp = int(time.time()) file_name = f"{timestamp}_{video_file.name}" file_path = os.path.join(account_dir, file_name) # 保存视频文件 with open(file_path, 'wb+') as destination: for chunk in video_file.chunks(): destination.write(chunk) # 创建视频记录 video_data = { 'platform_account': platform_account, 'title': request.data.get('title', os.path.splitext(video_file.name)[0]), 'description': request.data.get('description', ''), 'local_path': file_path, 'status': 'draft', 'tags': request.data.get('tags', '') } # 创建视频记录 video = Video.objects.create(**video_data) return Response({ "code": 200, "message": "视频上传成功", "data": { "id": video.id, "title": video.title, "status": video.get_status_display(), } }, status=status.HTTP_201_CREATED) except Exception as e: logger.error(f"视频上传失败: {str(e)}") return Response({ "code": 500, "message": f"视频上传失败: {str(e)}", "data": None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) @action(detail=True, methods=['post']) def manual_publish(self, request, pk=None): """手动发布视频""" video = self.get_object() # 检查视频状态是否允许发布 if video.status not in ['draft', 'scheduled']: return Response({ "code": 400, "message": f"当前视频状态为 {video.get_status_display()},无法发布", "data": None }, status=status.HTTP_400_BAD_REQUEST) # 检查视频文件是否存在 if video.local_path and not os.path.exists(video.local_path): return Response({ "code": 400, "message": "视频文件不存在,无法发布", "data": None }, status=status.HTTP_400_BAD_REQUEST) try: # 获取视频URL,如果没有提供,则创建一个模拟的URL video_url = request.data.get('video_url') if not video_url: # 创建模拟的视频URL和ID platform_account = video.platform_account platform_name = platform_account.platform_name video_url = f"https://example.com/{platform_name}/{video.id}" # 更新视频状态 video.status = 'published' video.publish_time = timezone.now() video.video_url = video_url video.video_id = f"VID_{video.id}" video.save() logger.info(f"视频 {video.id} 已手动发布") return Response({ "code": 200, "message": "视频发布成功", "data": { "id": video.id, "title": video.title, "status": "published", "video_url": video_url, "publish_time": video.publish_time.strftime("%Y-%m-%d %H:%M:%S") } }) except Exception as e: logger.error(f"手动发布视频失败: {str(e)}") return Response({ "code": 500, "message": f"发布失败: {str(e)}", "data": None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)