修改token

This commit is contained in:
Ubuntu 2025-05-14 14:52:23 +08:00
parent 5f984935cb
commit e5dae177ef
5 changed files with 355 additions and 586 deletions

View File

@ -0,0 +1,88 @@
# Generated by Django 5.1.5 on 2025-05-14 06:49
import django.db.models.deletion
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='OperatorAccount',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')),
('username', models.CharField(max_length=100, unique=True, verbose_name='用户名')),
('password', models.CharField(max_length=255, verbose_name='密码')),
('real_name', models.CharField(max_length=50, verbose_name='真实姓名')),
('email', models.EmailField(max_length=254, verbose_name='邮箱')),
('phone', models.CharField(max_length=15, verbose_name='电话')),
('position', models.CharField(choices=[('editor', '编辑'), ('planner', '策划'), ('operator', '运营'), ('admin', '管理员')], max_length=20, verbose_name='工作定位')),
('department', models.CharField(max_length=50, verbose_name='部门')),
('is_active', models.BooleanField(default=True, verbose_name='是否在职')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '运营账号',
'verbose_name_plural': '运营账号',
},
),
migrations.CreateModel(
name='PlatformAccount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('platform_name', models.CharField(choices=[('youtube', 'YouTube'), ('tiktok', 'TikTok'), ('twitter', 'Twitter/X'), ('instagram', 'Instagram'), ('facebook', 'Facebook'), ('bilibili', 'Bilibili')], max_length=20, verbose_name='平台名称')),
('account_name', models.CharField(max_length=100, verbose_name='账号名称')),
('account_id', models.CharField(max_length=100, verbose_name='账号ID')),
('status', models.CharField(choices=[('active', '正常'), ('restricted', '限流'), ('suspended', '封禁'), ('inactive', '未激活')], default='active', max_length=20, verbose_name='账号状态')),
('followers_count', models.IntegerField(default=0, verbose_name='粉丝数')),
('account_url', models.URLField(blank=True, null=True, verbose_name='账号链接')),
('description', models.TextField(blank=True, null=True, verbose_name='账号描述')),
('tags', models.CharField(blank=True, help_text='用逗号分隔的标签列表', max_length=255, null=True, verbose_name='标签')),
('profile_image', models.URLField(blank=True, null=True, verbose_name='账号头像')),
('last_posting', models.DateTimeField(blank=True, null=True, verbose_name='最后发布时间')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间')),
('operator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='platform_accounts', to='operation.operatoraccount', verbose_name='关联运营')),
],
options={
'verbose_name': '平台账号',
'verbose_name_plural': '平台账号',
'unique_together': {('platform_name', 'account_id')},
},
),
migrations.CreateModel(
name='Video',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='视频标题')),
('description', models.TextField(blank=True, null=True, verbose_name='视频描述')),
('video_url', models.URLField(blank=True, null=True, verbose_name='视频地址')),
('local_path', models.CharField(blank=True, max_length=255, null=True, verbose_name='本地路径')),
('thumbnail_url', models.URLField(blank=True, null=True, verbose_name='缩略图地址')),
('status', models.CharField(choices=[('draft', '草稿'), ('scheduled', '已排期'), ('published', '已发布'), ('failed', '发布失败'), ('deleted', '已删除')], default='draft', max_length=20, verbose_name='发布状态')),
('views_count', models.IntegerField(default=0, verbose_name='播放次数')),
('likes_count', models.IntegerField(default=0, verbose_name='点赞数')),
('comments_count', models.IntegerField(default=0, verbose_name='评论数')),
('shares_count', models.IntegerField(default=0, verbose_name='分享数')),
('tags', models.CharField(blank=True, max_length=500, null=True, verbose_name='标签')),
('publish_time', models.DateTimeField(blank=True, null=True, verbose_name='发布时间')),
('video_id', models.CharField(blank=True, max_length=100, null=True, verbose_name='视频ID')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('platform_account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='videos', to='operation.platformaccount', verbose_name='发布账号')),
],
options={
'verbose_name': '视频',
'verbose_name_plural': '视频',
},
),
]

View File

@ -1,6 +1,118 @@
from django.db import models from django.db import models
from user_management.models import OperatorAccount, PlatformAccount, Video, KnowledgeBase, KnowledgeBaseDocument import uuid
from django.utils import timezone
# Create your models here. # Create your models here.
# 我们可以在这里添加额外的模型或关系但现在使用user_management中的现有模型 # 我们可以在这里添加额外的模型或关系但现在使用user_management中的现有模型
class OperatorAccount(models.Model):
"""运营账号信息表"""
id = models.AutoField(primary_key=True)
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID')
POSITION_CHOICES = [
('editor', '编辑'),
('planner', '策划'),
('operator', '运营'),
('admin', '管理员'),
]
username = models.CharField(max_length=100, unique=True, verbose_name='用户名')
password = models.CharField(max_length=255, verbose_name='密码')
real_name = models.CharField(max_length=50, verbose_name='真实姓名')
email = models.EmailField(verbose_name='邮箱')
phone = models.CharField(max_length=15, verbose_name='电话')
position = models.CharField(max_length=20, choices=POSITION_CHOICES, verbose_name='工作定位')
department = models.CharField(max_length=50, verbose_name='部门')
is_active = models.BooleanField(default=True, verbose_name='是否在职')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
verbose_name = '运营账号'
verbose_name_plural = '运营账号'
def __str__(self):
return f"{self.real_name} ({self.username})"
class PlatformAccount(models.Model):
"""平台账号信息表"""
STATUS_CHOICES = [
('active', '正常'),
('restricted', '限流'),
('suspended', '封禁'),
('inactive', '未激活'),
]
PLATFORM_CHOICES = [
('youtube', 'YouTube'),
('tiktok', 'TikTok'),
('twitter', 'Twitter/X'),
('instagram', 'Instagram'),
('facebook', 'Facebook'),
('bilibili', 'Bilibili'),
]
operator = models.ForeignKey(OperatorAccount, on_delete=models.CASCADE, related_name='platform_accounts', verbose_name='关联运营')
platform_name = models.CharField(max_length=20, choices=PLATFORM_CHOICES, verbose_name='平台名称')
account_name = models.CharField(max_length=100, verbose_name='账号名称')
account_id = models.CharField(max_length=100, verbose_name='账号ID')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active', verbose_name='账号状态')
followers_count = models.IntegerField(default=0, verbose_name='粉丝数')
account_url = models.URLField(blank=True, null=True, verbose_name='账号链接')
description = models.TextField(blank=True, null=True, verbose_name='账号描述')
# 新增字段
tags = models.CharField(max_length=255, blank=True, null=True, verbose_name='标签', help_text='用逗号分隔的标签列表')
profile_image = models.URLField(blank=True, null=True, verbose_name='账号头像')
last_posting = models.DateTimeField(blank=True, null=True, verbose_name='最后发布时间')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
last_login = models.DateTimeField(blank=True, null=True, verbose_name='最后登录时间')
class Meta:
verbose_name = '平台账号'
verbose_name_plural = '平台账号'
unique_together = ('platform_name', 'account_id')
def __str__(self):
return f"{self.account_name} ({self.get_platform_name_display()})"
class Video(models.Model):
"""视频信息表"""
STATUS_CHOICES = [
('draft', '草稿'),
('scheduled', '已排期'),
('published', '已发布'),
('failed', '发布失败'),
('deleted', '已删除'),
]
platform_account = models.ForeignKey(PlatformAccount, on_delete=models.CASCADE, related_name='videos', verbose_name='发布账号')
title = models.CharField(max_length=200, verbose_name='视频标题')
description = models.TextField(blank=True, null=True, verbose_name='视频描述')
video_url = models.URLField(blank=True, null=True, verbose_name='视频地址')
local_path = models.CharField(max_length=255, blank=True, null=True, verbose_name='本地路径')
thumbnail_url = models.URLField(blank=True, null=True, verbose_name='缩略图地址')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft', verbose_name='发布状态')
views_count = models.IntegerField(default=0, verbose_name='播放次数')
likes_count = models.IntegerField(default=0, verbose_name='点赞数')
comments_count = models.IntegerField(default=0, verbose_name='评论数')
shares_count = models.IntegerField(default=0, verbose_name='分享数')
tags = models.CharField(max_length=500, blank=True, null=True, verbose_name='标签')
publish_time = models.DateTimeField(blank=True, null=True, verbose_name='发布时间')
video_id = models.CharField(max_length=100, blank=True, null=True, verbose_name='视频ID')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
verbose_name = '视频'
verbose_name_plural = '视频'
def __str__(self):
return f"{self.title} ({self.platform_account.account_name})"

View File

@ -1,31 +1,19 @@
from rest_framework import serializers from rest_framework import serializers
from user_management.models import OperatorAccount, PlatformAccount, Video, KnowledgeBase, KnowledgeBaseDocument from .models import OperatorAccount, PlatformAccount, Video
import uuid import uuid
from django.db.models import Q from django.db.models import Q
class OperatorAccountSerializer(serializers.ModelSerializer): class OperatorAccountSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(read_only=False, required=False) # 允许前端不提供ID但如果提供则必须是有效的UUID id = serializers.IntegerField(read_only=True) # ID是自动递增的整数字段
class Meta: class Meta:
model = OperatorAccount model = OperatorAccount
fields = ['id', 'username', 'password', 'real_name', 'email', 'phone', 'position', 'department', 'is_active', 'created_at', 'updated_at'] fields = ['id', 'uuid', 'username', 'password', 'real_name', 'email', 'phone', 'position', 'department', 'is_active', 'created_at', 'updated_at']
read_only_fields = ['created_at', 'updated_at'] read_only_fields = ['created_at', 'updated_at', 'uuid']
extra_kwargs = { extra_kwargs = {
'password': {'write_only': True} 'password': {'write_only': True}
} }
def create(self, validated_data):
# 如果没有提供ID则生成一个UUID
if 'id' not in validated_data:
validated_data['id'] = uuid.uuid4()
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password:
instance.password = password # 在实际应用中应该加密存储密码
instance.save()
return instance
class PlatformAccountSerializer(serializers.ModelSerializer): class PlatformAccountSerializer(serializers.ModelSerializer):
@ -40,7 +28,7 @@ class PlatformAccountSerializer(serializers.ModelSerializer):
read_only_fields = ['id', 'created_at', 'updated_at'] read_only_fields = ['id', 'created_at', 'updated_at']
def to_internal_value(self, data): def to_internal_value(self, data):
# 处理operator字段可能是字符串格式的UUID # 处理operator字段可能是字符串格式的ID
if 'operator' in data and isinstance(data['operator'], str): if 'operator' in data and isinstance(data['operator'], str):
try: try:
# 尝试获取对应的运营账号对象 # 尝试获取对应的运营账号对象
@ -129,7 +117,7 @@ class VideoSerializer(serializers.ModelSerializer):
fields = ['id', 'platform_account', 'platform_account_name', 'platform_name', 'title', fields = ['id', 'platform_account', 'platform_account_name', 'platform_name', 'title',
'description', 'video_url', 'local_path', 'thumbnail_url', 'status', 'description', 'video_url', 'local_path', 'thumbnail_url', 'status',
'views_count', 'likes_count', 'comments_count', 'shares_count', 'tags', 'views_count', 'likes_count', 'comments_count', 'shares_count', 'tags',
'publish_time', 'scheduled_time', 'created_at', 'updated_at'] 'publish_time', 'video_id', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at', 'views_count', 'likes_count', read_only_fields = ['id', 'created_at', 'updated_at', 'views_count', 'likes_count',
'comments_count', 'shares_count'] 'comments_count', 'shares_count']
@ -139,7 +127,7 @@ class VideoSerializer(serializers.ModelSerializer):
data = data.copy() data = data.copy()
data['tags'] = ','.join(data['tags']) data['tags'] = ','.join(data['tags'])
# 处理platform_account字段可能是字符串格式的UUID # 处理platform_account字段可能是字符串格式的ID
if 'platform_account' in data and isinstance(data['platform_account'], str): if 'platform_account' in data and isinstance(data['platform_account'], str):
try: try:
# 尝试获取对应的平台账号对象 # 尝试获取对应的平台账号对象
@ -159,21 +147,5 @@ class VideoSerializer(serializers.ModelSerializer):
representation = super().to_representation(instance) representation = super().to_representation(instance)
if representation.get('tags'): if representation.get('tags'):
representation['tags'] = representation['tags'].split(',') representation['tags'] = representation['tags'].split(',')
return representation return representation
class KnowledgeBaseSerializer(serializers.ModelSerializer):
class Meta:
model = KnowledgeBase
fields = ['id', 'user_id', 'name', 'desc', 'type', 'department', 'group',
'external_id', 'create_time', 'update_time']
read_only_fields = ['id', 'create_time', 'update_time']
class KnowledgeBaseDocumentSerializer(serializers.ModelSerializer):
class Meta:
model = KnowledgeBaseDocument
fields = ['id', 'knowledge_base', 'document_id', 'document_name',
'external_id', 'uploader_name', 'status', 'create_time', 'update_time']
read_only_fields = ['id', 'create_time', 'update_time']

View File

@ -9,14 +9,13 @@ from django.utils import timezone
from rest_framework import viewsets, status from rest_framework import viewsets, status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db.models import Q from django.db.models import Q
import os import os
from user_management.models import OperatorAccount, PlatformAccount, Video, KnowledgeBase, KnowledgeBaseDocument, User from .models import OperatorAccount, PlatformAccount, Video
from .serializers import ( from .serializers import (
OperatorAccountSerializer, PlatformAccountSerializer, VideoSerializer, OperatorAccountSerializer, PlatformAccountSerializer, VideoSerializer,
KnowledgeBaseSerializer, KnowledgeBaseDocumentSerializer, MultiPlatformAccountSerializer MultiPlatformAccountSerializer
) )
from .pagination import CustomPagination from .pagination import CustomPagination
@ -26,7 +25,6 @@ class OperatorAccountViewSet(viewsets.ModelViewSet):
"""运营账号管理视图集""" """运营账号管理视图集"""
queryset = OperatorAccount.objects.all() queryset = OperatorAccount.objects.all()
serializer_class = OperatorAccountSerializer serializer_class = OperatorAccountSerializer
permission_classes = [IsAuthenticated]
pagination_class = CustomPagination pagination_class = CustomPagination
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@ -76,125 +74,34 @@ class OperatorAccountViewSet(viewsets.ModelViewSet):
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""创建运营账号并自动创建对应的私有知识库""" """创建运营账号"""
with transaction.atomic(): serializer = self.get_serializer(data=request.data)
# 1. 创建运营账号 serializer.is_valid(raise_exception=True)
serializer = self.get_serializer(data=request.data) self.perform_create(serializer)
serializer.is_valid(raise_exception=True)
return Response({
# 2. 手动保存数据而不是使用serializer.save()确保不传入UUID "code": 200,
operator_data = serializer.validated_data "message": "运营账号创建成功",
operator = OperatorAccount.objects.create(**operator_data) "data": serializer.data
}, status=status.HTTP_201_CREATED)
# 3. 为每个运营账号创建一个私有知识库
knowledge_base = KnowledgeBase.objects.create(
user_id=request.user.id, # 使用当前用户作为创建者
name=f"{operator.real_name}的运营知识库",
desc=f"用于存储{operator.real_name}({operator.username})相关的运营数据",
type='private',
department=operator.department
)
# 4. 创建知识库文档记录 - 运营信息文档
document_data = {
"name": f"{operator.real_name}_运营信息",
"paragraphs": [
{
"title": "运营账号基本信息",
"content": f"""
用户名: {operator.username}
真实姓名: {operator.real_name}
邮箱: {operator.email}
电话: {operator.phone}
职位: {operator.get_position_display()}
部门: {operator.department}
创建时间: {operator.created_at.strftime('%Y-%m-%d %H:%M:%S')}
uuid: {operator.uuid}
""",
"is_active": True
}
]
}
# 调用外部API创建文档
document_id = self._create_document(knowledge_base.external_id, document_data)
if document_id:
# 创建知识库文档记录
KnowledgeBaseDocument.objects.create(
knowledge_base=knowledge_base,
document_id=document_id,
document_name=document_data["name"],
external_id=document_id,
uploader_name=request.user.username
)
return Response({
"code": 200,
"message": "运营账号创建成功,并已创建对应知识库",
"data": {
"operator": self.get_serializer(operator).data,
"knowledge_base": {
"id": knowledge_base.id,
"name": knowledge_base.name,
"external_id": knowledge_base.external_id
}
}
}, status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""删除运营账号并更新相关知识库状态""" """删除运营账号"""
operator = self.get_object() operator = self.get_object()
# 更新知识库状态或删除关联文档
knowledge_bases = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
)
for kb in knowledge_bases:
# 可以选择删除知识库,或者更新知识库状态
# 这里我们更新对应的文档状态
documents = KnowledgeBaseDocument.objects.filter(
knowledge_base=kb,
document_name__contains=operator.real_name
)
for doc in documents:
doc.status = 'deleted'
doc.save()
operator.is_active = False # 软删除 operator.is_active = False # 软删除
operator.save() operator.save()
return Response({ return Response({
"code": 200, "code": 200,
"message": "运营账号已停用,相关知识库文档已标记为删除", "message": "运营账号已停用",
"data": None "data": None
}) })
def _create_document(self, external_id, doc_data):
"""调用外部API创建文档"""
try:
if not external_id:
logger.error("创建文档失败知识库external_id为空")
return None
# 在实际应用中这里需要调用外部API创建文档
# 模拟创建文档并返回document_id
document_id = str(uuid.uuid4())
logger.info(f"模拟创建文档成功document_id: {document_id}")
return document_id
except Exception as e:
logger.error(f"创建文档失败: {str(e)}")
return None
class PlatformAccountViewSet(viewsets.ModelViewSet): class PlatformAccountViewSet(viewsets.ModelViewSet):
"""平台账号管理视图集""" """平台账号管理视图集"""
queryset = PlatformAccount.objects.all() queryset = PlatformAccount.objects.all()
serializer_class = PlatformAccountSerializer serializer_class = PlatformAccountSerializer
permission_classes = [IsAuthenticated]
pagination_class = CustomPagination pagination_class = CustomPagination
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@ -204,24 +111,60 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
if page is not None: if page is not None:
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True)
# 使用自定义分页器的响应 # 处理数据结构
return self.get_paginated_response(serializer.data) 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")
}
# 添加platforms字段
account_data["platforms"] = platform_info
restructured_data.append(account_data)
# 使用自定义分页器的响应,但替换数据
return self.get_paginated_response(restructured_data)
serializer = self.get_serializer(queryset, many=True) 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")
}
# 添加platforms字段
account_data["platforms"] = platform_info
restructured_data.append(account_data)
return Response({ return Response({
"code": 200, "code": 200,
"message": "获取平台账号列表成功", "message": "获取平台账号列表成功",
"data": serializer.data "data": restructured_data
}) })
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
"""获取平台账号详情""" """获取平台账号详情"""
instance = self.get_object() instance = self.get_object()
serializer = self.get_serializer(instance) 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")
}
# 添加platforms字段
account_data["platforms"] = platform_info
return Response({ return Response({
"code": 200, "code": 200,
"message": "获取平台账号详情成功", "message": "获取平台账号详情成功",
"data": serializer.data "data": account_data
}) })
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
@ -232,10 +175,20 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
self.perform_update(serializer) self.perform_update(serializer)
# 处理数据结构
account_data = serializer.data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url")
}
# 添加platforms字段
account_data["platforms"] = platform_info
return Response({ return Response({
"code": 200, "code": 200,
"message": "更新平台账号信息成功", "message": "更新平台账号信息成功",
"data": serializer.data "data": account_data
}) })
def partial_update(self, request, *args, **kwargs): def partial_update(self, request, *args, **kwargs):
@ -244,7 +197,7 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""创建平台账号并记录到知识库""" """创建平台账号"""
# 检查请求数据中是否包含platforms字段判断是否为多平台账号创建 # 检查请求数据中是否包含platforms字段判断是否为多平台账号创建
if 'platforms' in request.data and isinstance(request.data['platforms'], list): if 'platforms' in request.data and isinstance(request.data['platforms'], list):
# 使用多平台账号序列化器 # 使用多平台账号序列化器
@ -269,16 +222,29 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
# 创建平台账号 # 创建平台账号
platform_account = PlatformAccount.objects.create(**platform_account_data) platform_account = PlatformAccount.objects.create(**platform_account_data)
created_accounts.append(platform_account) created_accounts.append(platform_account)
# 记录到知识库
self._add_to_knowledge_base(platform_account, request.user)
# 将创建的账号序列化返回 # 将创建的账号序列化返回
result_serializer = self.get_serializer(created_accounts, many=True) result_serializer = self.get_serializer(created_accounts, many=True)
# 修改响应数据结构
response_data = result_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")
}
# 添加platforms字段
account_data["platforms"] = platform_info
restructured_data.append(account_data)
return Response({ return Response({
"code": 200, "code": 200,
"message": "多平台账号创建成功,并已添加到知识库", "message": "多平台账号创建成功",
"data": result_serializer.data "data": restructured_data
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
else: else:
# 传统单平台账号创建流程 # 传统单平台账号创建流程
@ -320,123 +286,39 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(data=data) serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
# 手动创建平台账号不使用serializer.save()避免ID问题 # 创建平台账号
platform_data = serializer.validated_data self.perform_create(serializer)
platform_account = PlatformAccount.objects.create(**platform_data)
# 记录到知识库 # 处理响应数据
self._add_to_knowledge_base(platform_account, request.user) account_data = serializer.data
# 提取平台信息并放入platforms字段
platform_info = {
"platform_name": account_data.pop("platform_name"),
"account_url": account_data.pop("account_url")
}
# 添加platforms字段
account_data["platforms"] = platform_info
return Response({ return Response({
"code": 200, "code": 200,
"message": "平台账号创建成功,并已添加到知识库", "message": "平台账号创建成功",
"data": self.get_serializer(platform_account).data "data": account_data
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
def _add_to_knowledge_base(self, platform_account, user):
"""将平台账号添加到知识库"""
# 获取关联的运营账号
operator = platform_account.operator
# 查找对应的知识库
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base and knowledge_base.external_id:
# 创建平台账号文档
document_data = {
"name": f"{platform_account.account_name}_{platform_account.platform_name}_账号信息",
"paragraphs": [
{
"title": "平台账号基本信息",
"content": f"""
平台: {platform_account.get_platform_name_display()}
账号名称: {platform_account.account_name}
账号ID: {platform_account.account_id}
账号状态: {platform_account.get_status_display()}
粉丝数: {platform_account.followers_count}
账号链接: {platform_account.account_url}
账号描述: {platform_account.description or ''}
标签: {platform_account.tags or ''}
头像链接: {platform_account.profile_image or ''}
最后发布时间: {platform_account.last_posting.strftime('%Y-%m-%d %H:%M:%S') if platform_account.last_posting else '未发布'}
创建时间: {platform_account.created_at.strftime('%Y-%m-%d %H:%M:%S')}
最后登录: {platform_account.last_login.strftime('%Y-%m-%d %H:%M:%S') if platform_account.last_login else '从未登录'}
""",
"is_active": True
}
]
}
# 调用外部API创建文档
document_id = self._create_document(knowledge_base.external_id, document_data)
if document_id:
# 创建知识库文档记录
KnowledgeBaseDocument.objects.create(
knowledge_base=knowledge_base,
document_id=document_id,
document_name=document_data["name"],
external_id=document_id,
uploader_name=user.username
)
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""删除平台账号并更新相关知识库文档""" """删除平台账号"""
platform_account = self.get_object() platform_account = self.get_object()
# 获取关联的运营账号
operator = platform_account.operator
# 查找对应的知识库
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base:
# 查找相关文档并标记为删除
documents = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base
).filter(
Q(document_name__contains=platform_account.account_name) |
Q(document_name__contains=platform_account.platform_name)
)
for doc in documents:
doc.status = 'deleted'
doc.save()
# 删除平台账号
self.perform_destroy(platform_account) self.perform_destroy(platform_account)
return Response({ return Response({
"code": 200, "code": 200,
"message": "平台账号已删除,相关知识库文档已标记为删除", "message": "平台账号已删除",
"data": None "data": None
}) })
def _create_document(self, external_id, doc_data):
"""调用外部API创建文档"""
try:
if not external_id:
logger.error("创建文档失败知识库external_id为空")
return None
# 在实际应用中这里需要调用外部API创建文档
# 模拟创建文档并返回document_id
document_id = str(uuid.uuid4())
logger.info(f"模拟创建文档成功document_id: {document_id}")
return document_id
except Exception as e:
logger.error(f"创建文档失败: {str(e)}")
return None
@action(detail=True, methods=['post']) @action(detail=True, methods=['post'])
def update_followers(self, request, pk=None): def update_followers(self, request, pk=None):
"""更新平台账号粉丝数并同步到知识库""" """更新平台账号粉丝数"""
platform_account = self.get_object() platform_account = self.get_object()
followers_count = request.data.get('followers_count') followers_count = request.data.get('followers_count')
@ -451,36 +333,20 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
platform_account.followers_count = followers_count platform_account.followers_count = followers_count
platform_account.save() platform_account.save()
# 同步到知识库 # 准备响应数据,与其他方法保持一致
operator = platform_account.operator platform_data = self.get_serializer(platform_account).data
knowledge_base = KnowledgeBase.objects.filter( # 提取平台信息并放入platforms字段
name__contains=operator.real_name, platform_info = {
type='private' "platform_name": platform_data.pop("platform_name"),
).first() "account_url": platform_data.pop("account_url")
}
if knowledge_base: # 添加platforms字段
# 查找相关文档 platform_data["platforms"] = platform_info
document = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
status='active'
).filter(
Q(document_name__contains=platform_account.account_name) |
Q(document_name__contains=platform_account.platform_name)
).first()
if document:
# 这里应该调用外部API更新文档内容
# 但由于我们没有实际的API只做记录
logger.info(f"应当更新文档 {document.document_id} 的粉丝数为 {followers_count}")
return Response({ return Response({
"code": 200, "code": 200,
"message": "粉丝数更新成功", "message": "粉丝数更新成功",
"data": { "data": platform_data
"id": platform_account.id,
"account_name": platform_account.account_name,
"followers_count": platform_account.followers_count
}
}) })
@action(detail=True, methods=['post']) @action(detail=True, methods=['post'])
@ -530,30 +396,20 @@ class PlatformAccountViewSet(viewsets.ModelViewSet):
setattr(platform_account, field, value) setattr(platform_account, field, value)
platform_account.save() platform_account.save()
# 同步到知识库 # 准备响应数据,与其他方法保持一致
# 在实际应用中应该调用外部API更新文档内容 platform_data = self.get_serializer(platform_account).data
operator = platform_account.operator # 提取平台信息并放入platforms字段
knowledge_base = KnowledgeBase.objects.filter( platform_info = {
name__contains=operator.real_name, "platform_name": platform_data.pop("platform_name"),
type='private' "account_url": platform_data.pop("account_url")
).first() }
# 添加platforms字段
if knowledge_base: platform_data["platforms"] = platform_info
document = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
status='active'
).filter(
Q(document_name__contains=platform_account.account_name) |
Q(document_name__contains=platform_account.platform_name)
).first()
if document:
logger.info(f"应当更新文档 {document.document_id} 的平台账号资料数据")
return Response({ return Response({
"code": 200, "code": 200,
"message": "平台账号资料更新成功", "message": "平台账号资料更新成功",
"data": self.get_serializer(platform_account).data "data": platform_data
}) })
@ -561,7 +417,6 @@ class VideoViewSet(viewsets.ModelViewSet):
"""视频管理视图集""" """视频管理视图集"""
queryset = Video.objects.all() queryset = Video.objects.all()
serializer_class = VideoSerializer serializer_class = VideoSerializer
permission_classes = [IsAuthenticated]
pagination_class = CustomPagination pagination_class = CustomPagination
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@ -611,7 +466,7 @@ class VideoViewSet(viewsets.ModelViewSet):
return self.update(request, *args, **kwargs) return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""创建视频并记录到知识库""" """创建视频"""
with transaction.atomic(): with transaction.atomic():
# 处理platform_account字段可能是字符串类型的ID # 处理platform_account字段可能是字符串类型的ID
data = request.data.copy() data = request.data.copy()
@ -650,117 +505,29 @@ class VideoViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(data=data) serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
# 手动创建视频不使用serializer.save()避免ID问题 # 创建视频
video_data = serializer.validated_data self.perform_create(serializer)
video = Video.objects.create(**video_data)
# 获取关联的平台账号和运营账号
platform_account = video.platform_account
operator = platform_account.operator
# 查找对应的知识库
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base and knowledge_base.external_id:
# 创建视频文档
document_data = {
"name": f"{video.title}_{platform_account.account_name}_视频信息",
"paragraphs": [
{
"title": "视频基本信息",
"content": f"""
标题: {video.title}
平台: {platform_account.get_platform_name_display()}
账号: {platform_account.account_name}
视频ID: {video.video_id}
发布时间: {video.publish_time.strftime('%Y-%m-%d %H:%M:%S') if video.publish_time else '未发布'}
视频链接: {video.video_url}
点赞数: {video.likes_count}
评论数: {video.comments_count}
分享数: {video.shares_count}
观看数: {video.views_count}
视频描述: {video.description or ''}
""",
"is_active": True
}
]
}
# 调用外部API创建文档
document_id = self._create_document(knowledge_base.external_id, document_data)
if document_id:
# 创建知识库文档记录
KnowledgeBaseDocument.objects.create(
knowledge_base=knowledge_base,
document_id=document_id,
document_name=document_data["name"],
external_id=document_id,
uploader_name=request.user.username
)
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频创建成功,并已添加到知识库", "message": "视频创建成功",
"data": self.get_serializer(video).data "data": serializer.data
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
"""删除视频记录并更新相关知识库文档""" """删除视频记录"""
video = self.get_object() video = self.get_object()
# 获取关联的平台账号和运营账号
platform_account = video.platform_account
operator = platform_account.operator
# 查找对应的知识库
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base:
# 查找相关文档并标记为删除
documents = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
document_name__contains=video.title
)
for doc in documents:
doc.status = 'deleted'
doc.save()
# 删除视频记录
self.perform_destroy(video) self.perform_destroy(video)
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频记录已删除,相关知识库文档已标记为删除", "message": "视频记录已删除",
"data": None "data": None
}) })
def _create_document(self, external_id, doc_data):
"""调用外部API创建文档"""
try:
if not external_id:
logger.error("创建文档失败知识库external_id为空")
return None
# 在实际应用中这里需要调用外部API创建文档
# 模拟创建文档并返回document_id
document_id = str(uuid.uuid4())
logger.info(f"模拟创建文档成功document_id: {document_id}")
return document_id
except Exception as e:
logger.error(f"创建文档失败: {str(e)}")
return None
@action(detail=True, methods=['post']) @action(detail=True, methods=['post'])
def update_stats(self, request, pk=None): def update_stats(self, request, pk=None):
"""更新视频统计数据并同步到知识库""" """更新视频统计数据"""
video = self.get_object() video = self.get_object()
# 获取更新的统计数据 # 获取更新的统计数据
@ -781,25 +548,6 @@ class VideoViewSet(viewsets.ModelViewSet):
setattr(video, field, value) setattr(video, field, value)
video.save() video.save()
# 同步到知识库
# 在实际应用中应该调用外部API更新文档内容
platform_account = video.platform_account
operator = platform_account.operator
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base:
document = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
document_name__contains=video.title,
status='active'
).first()
if document:
logger.info(f"应当更新文档 {document.document_id} 的视频统计数据")
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频统计数据更新成功", "message": "视频统计数据更新成功",
@ -841,25 +589,6 @@ class VideoViewSet(viewsets.ModelViewSet):
video.publish_time = timezone.now() video.publish_time = timezone.now()
video.save() video.save()
# 同步到知识库
# 在实际应用中应该调用外部API更新文档内容
platform_account = video.platform_account
operator = platform_account.operator
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base:
document = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
document_name__contains=video.title,
status='active'
).first()
if document:
logger.info(f"应当更新文档 {document.document_id} 的视频发布状态")
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频已成功发布", "message": "视频已成功发布",
@ -938,31 +667,9 @@ class VideoViewSet(viewsets.ModelViewSet):
'tags': request.data.get('tags', '') 'tags': request.data.get('tags', '')
} }
# 如果提供了计划发布时间,则设置状态为已排期
scheduled_time = request.data.get('scheduled_time')
if scheduled_time:
from dateutil import parser
try:
parsed_time = parser.parse(scheduled_time)
video_data['scheduled_time'] = parsed_time
video_data['status'] = 'scheduled'
except Exception as e:
return Response({
"code": 400,
"message": f"计划发布时间格式错误: {str(e)}",
"data": None
}, status=status.HTTP_400_BAD_REQUEST)
# 创建视频记录 # 创建视频记录
video = Video.objects.create(**video_data) video = Video.objects.create(**video_data)
# 添加到知识库
self._add_to_knowledge_base(video, platform_account)
# 如果是已排期状态,创建定时任务
if video.status == 'scheduled':
self._create_publish_task(video)
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频上传成功", "message": "视频上传成功",
@ -970,7 +677,6 @@ class VideoViewSet(viewsets.ModelViewSet):
"id": video.id, "id": video.id,
"title": video.title, "title": video.title,
"status": video.get_status_display(), "status": video.get_status_display(),
"scheduled_time": video.scheduled_time
} }
}, status=status.HTTP_201_CREATED) }, status=status.HTTP_201_CREATED)
@ -982,87 +688,6 @@ class VideoViewSet(viewsets.ModelViewSet):
"data": None "data": None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def _add_to_knowledge_base(self, video, platform_account):
"""将视频添加到知识库"""
# 获取关联的运营账号
operator = platform_account.operator
# 查找对应的知识库
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base and knowledge_base.external_id:
# 创建视频文档
document_data = {
"name": f"{video.title}_{platform_account.account_name}_视频信息",
"paragraphs": [
{
"title": "视频基本信息",
"content": f"""
标题: {video.title}
平台: {platform_account.get_platform_name_display()}
账号: {platform_account.account_name}
状态: {video.get_status_display()}
本地路径: {video.local_path}
计划发布时间: {video.scheduled_time.strftime('%Y-%m-%d %H:%M:%S') if video.scheduled_time else '未设置'}
视频描述: {video.description or ''}
标签: {video.tags or ''}
创建时间: {video.created_at.strftime('%Y-%m-%d %H:%M:%S')}
""",
"is_active": True
}
]
}
# 调用外部API创建文档
document_id = self._create_document(knowledge_base.external_id, document_data)
if document_id:
# 创建知识库文档记录
KnowledgeBaseDocument.objects.create(
knowledge_base=knowledge_base,
document_id=document_id,
document_name=document_data["name"],
external_id=document_id,
uploader_name="系统"
)
def _create_publish_task(self, video):
"""创建定时发布任务"""
try:
from django_celery_beat.models import PeriodicTask, CrontabSchedule
import json
from datetime import datetime
scheduled_time = video.scheduled_time
# 创建定时任务
schedule, _ = CrontabSchedule.objects.get_or_create(
minute=scheduled_time.minute,
hour=scheduled_time.hour,
day_of_month=scheduled_time.day,
month_of_year=scheduled_time.month,
)
# 创建周期性任务
task_name = f"Publish_Video_{video.id}_{datetime.now().timestamp()}"
PeriodicTask.objects.create(
name=task_name,
task='user_management.tasks.publish_scheduled_video',
crontab=schedule,
args=json.dumps([video.id]),
one_off=True, # 只执行一次
start_time=scheduled_time
)
logger.info(f"已创建视频 {video.id} 的定时发布任务,计划发布时间: {scheduled_time}")
except Exception as e:
logger.error(f"创建定时发布任务失败: {str(e)}")
# 记录错误但不中断流程
@action(detail=True, methods=['post']) @action(detail=True, methods=['post'])
def manual_publish(self, request, pk=None): def manual_publish(self, request, pk=None):
"""手动发布视频""" """手动发布视频"""
@ -1077,51 +702,32 @@ class VideoViewSet(viewsets.ModelViewSet):
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
# 检查视频文件是否存在 # 检查视频文件是否存在
if not video.local_path or not os.path.exists(video.local_path): if video.local_path and not os.path.exists(video.local_path):
return Response({ return Response({
"code": 400, "code": 400,
"message": "视频文件不存在,无法发布", "message": "视频文件不存在,无法发布",
"data": None "data": None
}, status=status.HTTP_400_BAD_REQUEST) }, status=status.HTTP_400_BAD_REQUEST)
# 自动发布 - 不依赖Celery任务
try: try:
# 模拟上传到平台 # 获取视频URL如果没有提供则创建一个模拟的URL
platform_account = video.platform_account video_url = request.data.get('video_url')
platform_name = platform_account.platform_name
# 创建模拟的视频URL和ID if not video_url:
video_url = f"https://example.com/{platform_name}/{video.id}" # 创建模拟的视频URL和ID
video_id = f"VID_{video.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.status = 'published'
video.publish_time = timezone.now() video.publish_time = timezone.now()
video.video_url = video_url video.video_url = video_url
video.video_id = video_id video.video_id = f"VID_{video.id}"
video.save() video.save()
logger.info(f"视频 {video.id} 已手动发布") logger.info(f"视频 {video.id} 已手动发布")
# 更新知识库文档
platform_account = video.platform_account
operator = platform_account.operator
knowledge_base = KnowledgeBase.objects.filter(
name__contains=operator.real_name,
type='private'
).first()
if knowledge_base:
document = KnowledgeBaseDocument.objects.filter(
knowledge_base=knowledge_base,
document_name__contains=video.title,
status='active'
).first()
if document:
logger.info(f"应当更新文档 {document.document_id} 的视频发布状态")
return Response({ return Response({
"code": 200, "code": 200,
"message": "视频发布成功", "message": "视频发布成功",

View File

@ -159,8 +159,13 @@ REST_FRAMEWORK = {
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
], ],
'DEFAULT_PERMISSION_CLASSES': [ 'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.AllowAny',
] ],
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
} }
# Channels 配置 # Channels 配置
@ -280,21 +285,7 @@ SESSION_COOKIE_SECURE = False # 开发环境设置为 False
SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax' SESSION_COOKIE_SAMESITE = 'Lax'
# REST Framework 配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication', # WebSocket 需要
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
}
# Gmail API配置 # Gmail API配置
GOOGLE_CLOUD_PROJECT = 'knowledge-454905' # 更新为当前使用的项目ID GOOGLE_CLOUD_PROJECT = 'knowledge-454905' # 更新为当前使用的项目ID