修改token
This commit is contained in:
parent
5f984935cb
commit
e5dae177ef
88
operation/migrations/0001_initial.py
Normal file
88
operation/migrations/0001_initial.py
Normal 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': '视频',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -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})"
|
||||||
|
@ -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']
|
|
||||||
|
|
@ -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": "视频发布成功",
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user