diff --git a/operation/README.md b/operation/README.md new file mode 100644 index 0000000..5442129 --- /dev/null +++ b/operation/README.md @@ -0,0 +1,527 @@ +# Operation 模块接口文档 + +## 基础 URL + +所有接口的基础路径: `/api/operation/` + +## 通用响应格式 + +所有接口都返回以下格式的响应: + +```json +{ + "code": 200, // 状态码,200表示成功 + "message": "操作成功", // 操作结果描述 + "data": {} // 数据内容,具体格式根据接口不同而变化 +} +``` + +分页接口的响应格式: + +```json +{ + "code": 200, + "message": "获取数据成功", + "data": { + "count": 100, // 总记录数 + "next": "http://example.com/api/operation/xxx/?page=2", // 下一页链接,没有下一页则为null + "previous": null, // 上一页链接,没有上一页则为null + "results": [], // 当前页的数据列表 + "page": 1, // 当前页码 + "pages": 10, // 总页数 + "page_size": 10 // 每页记录数 + } +} +``` + +## 接口列表 + +### 1. 运营账号管理 + +#### 1.1 获取运营账号列表 + +- **URL**: `/operators/` +- **方法**: GET +- **参数**: + - `page`: 页码,默认1 + - `page_size`: 每页记录数,默认10 + +- **响应示例**: +```json +{ + "code": 200, + "message": "获取运营账号列表成功", + "data": { + "count": 10, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "username": "operator1", + "real_name": "张三", + "email": "zhangsan@example.com", + "phone": "13800138000", + "position": "editor", + "department": "内容部", + "is_active": true, + "created_at": "2023-09-01T10:00:00Z", + "updated_at": "2023-09-01T10:00:00Z" + } + ], + "page": 1, + "pages": 1, + "page_size": 10 + } +} +``` + +#### 1.2 获取运营账号详情 + +- **URL**: `/operators/{id}/` +- **方法**: GET +- **响应示例**: +```json +{ + "code": 200, + "message": "获取运营账号详情成功", + "data": { + "id": 1, + "uuid": "550e8400-e29b-41d4-a716-446655440000", + "username": "operator1", + "real_name": "张三", + "email": "zhangsan@example.com", + "phone": "13800138000", + "position": "editor", + "department": "内容部", + "is_active": true, + "created_at": "2023-09-01T10:00:00Z", + "updated_at": "2023-09-01T10:00:00Z" + } +} +``` + +#### 1.3 创建运营账号 + +- **URL**: `/operators/` +- **方法**: POST +- **请求参数**: +```json +{ + "username": "operator1", + "password": "password123", + "real_name": "张三", + "email": "zhangsan@example.com", + "phone": "13800138000", + "position": "editor", + "department": "内容部" +} +``` +- **响应示例**: 同详情接口 + +#### 1.4 更新运营账号 + +- **URL**: `/operators/{id}/` +- **方法**: PUT/PATCH +- **请求参数**: 同创建接口,PATCH 可部分更新 +- **响应示例**: 同详情接口 + +#### 1.5 删除运营账号 + +- **URL**: `/operators/{id}/` +- **方法**: DELETE +- **响应示例**: +```json +{ + "code": 200, + "message": "运营账号已停用", + "data": null +} +``` + +### 2. 平台账号管理 + +#### 2.1 获取平台账号列表 + +- **URL**: `/platforms/` +- **方法**: GET +- **参数**: + - `page`: 页码,默认1 + - `page_size`: 每页记录数,默认10 + +- **响应示例**: +```json +{ + "code": 200, + "message": "获取平台账号列表成功", + "data": { + "count": 10, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "operator": 1, + "operator_name": "张三", + "status": "active", + "followers_count": 1000, + "description": "测试账号", + "tags": ["科技", "数码"], + "profile_image": "https://example.com/image.jpg", + "last_posting": "2023-09-01T10:00:00Z", + "created_at": "2023-09-01T10:00:00Z", + "updated_at": "2023-09-01T10:00:00Z", + "last_login": "2023-09-01T10:00:00Z", + "platforms": [ + { + "platform_name": "youtube", + "account_url": "https://youtube.com/channel/123", + "account_id": "channel123", + "account_name": "测试频道" + } + ], + "name": "youtube" + } + ], + "page": 1, + "pages": 1, + "page_size": 10 + } +} +``` + +#### 2.2 获取平台账号详情 + +- **URL**: `/platforms/{id}/` +- **方法**: GET +- **响应示例**: 类似列表但无分页 + +#### 2.3 创建平台账号 + +- **URL**: `/platforms/` +- **方法**: POST +- **请求参数**: +```json +{ + "operator": 1, + "name": "Shizuku", + "status": "active", + "followers_count": 1000, + "description": "测试频道", + "tags": ["Vlogs", "Foodie"], + "profile_image": "https://example.com/image.jpg", + "platforms": [ + { + "platform_name": "youtube", + "account_name": "测试频道", + "account_id": "channel123", + "account_url": "https://youtube.com/channel/123" + } + ] +} +``` +- **响应示例**: 同详情接口 + +#### 2.4 更新平台账号 + +- **URL**: `/platforms/{id}/` +- **方法**: PUT/PATCH +- **请求参数**: 同创建接口,PATCH 可部分更新 +- **响应示例**: 同详情接口 + +#### 2.5 删除平台账号 + +- **URL**: `/platforms/{id}/` +- **方法**: DELETE +- **响应示例**: +```json +{ + "code": 200, + "message": "平台账号已删除", + "data": null +} +``` + +#### 2.6 更新粉丝数 + +- **URL**: `/platforms/{id}/update_followers/` +- **方法**: POST +- **请求参数**: +```json +{ + "followers_count": 2000 +} +``` +- **响应示例**: 同详情接口 + +#### 2.7 更新账号资料 + +- **URL**: `/platforms/{id}/update_profile/` +- **方法**: POST +- **请求参数**: +```json +{ + "tags": ["科技", "数码"], + "profile_image": "https://example.com/new_image.jpg", + "last_posting": "2023-09-02T10:00:00Z" +} +``` +- **响应示例**: 同详情接口 + +### 3. 视频管理 + +#### 3.1 获取视频列表 + +- **URL**: `/videos/` +- **方法**: GET +- **参数**: + - `page`: 页码,默认1 + - `page_size`: 每页记录数,默认10 + +- **响应示例**: +```json +{ + "code": 200, + "message": "获取视频列表成功", + "data": { + "count": 10, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "platform_account": 1, + "platform_account_name": "测试频道", + "platform_name": "youtube", + "title": "测试视频", + "description": "这是一个测试视频", + "video_url": "https://youtube.com/watch?v=123", + "local_path": "/path/to/video.mp4", + "thumbnail_url": "https://example.com/thumb.jpg", + "status": "published", + "views_count": 1000, + "likes_count": 100, + "comments_count": 50, + "shares_count": 20, + "tags": ["测试", "视频"], + "publish_time": "2023-09-01T10:00:00Z", + "video_id": "v123", + "created_at": "2023-09-01T10:00:00Z", + "updated_at": "2023-09-01T10:00:00Z" + } + ], + "page": 1, + "pages": 1, + "page_size": 10 + } +} +``` + +#### 3.2 获取视频详情 + +- **URL**: `/videos/{id}/` +- **方法**: GET +- **响应示例**: 类似列表但无分页 + +#### 3.3 创建视频 + +- **URL**: `/videos/` +- **方法**: POST +- **请求参数**: +```json +{ + "platform_account": 1, + "title": "测试视频", + "description": "这是一个测试视频", + "status": "draft", + "tags": ["测试", "视频"] +} +``` +- **响应示例**: 同详情接口 + +#### 3.4 更新视频信息 + +- **URL**: `/videos/{id}/` +- **方法**: PUT/PATCH +- **请求参数**: 同创建接口,PATCH 可部分更新 +- **响应示例**: 同详情接口 + +#### 3.5 删除视频 + +- **URL**: `/videos/{id}/` +- **方法**: DELETE +- **响应示例**: +```json +{ + "code": 200, + "message": "视频记录已删除", + "data": null +} +``` + +#### 3.6 更新视频统计数据 + +- **URL**: `/videos/{id}/update_stats/` +- **方法**: POST +- **请求参数**: +```json +{ + "views_count": 2000, + "likes_count": 200, + "comments_count": 100, + "shares_count": 50 +} +``` +- **响应示例**: +```json +{ + "code": 200, + "message": "视频统计数据更新成功", + "data": { + "id": 1, + "title": "测试视频", + "views_count": 2000, + "likes_count": 200, + "comments_count": 100, + "shares_count": 50 + } +} +``` + +#### 3.7 发布视频 + +- **URL**: `/videos/{id}/publish/` +- **方法**: POST +- **请求参数**: +```json +{ + "video_url": "https://youtube.com/watch?v=123" +} +``` +- **响应示例**: +```json +{ + "code": 200, + "message": "视频已成功发布", + "data": { + "id": 1, + "title": "测试视频", + "status": "published", + "video_url": "https://youtube.com/watch?v=123", + "publish_time": "2023-09-02T10:00:00Z" + } +} +``` + +#### 3.8 上传视频 + +- **URL**: `/videos/upload_video/` +- **方法**: POST +- **请求参数**: + - `video_file`: 视频文件(multipart/form-data) + - `platform_account`: 平台账号ID + - `title`: 视频标题 + - `description`: 视频描述 + - `tags`: 视频标签 + +- **响应示例**: +```json +{ + "code": 200, + "message": "视频上传成功", + "data": { + "id": 1, + "title": "测试视频", + "status": "draft" + } +} +``` + +#### 3.9 手动发布视频 + +- **URL**: `/videos/{id}/manual_publish/` +- **方法**: POST +- **请求参数**: +```json +{ + "video_url": "https://youtube.com/watch?v=123" +} +``` +- **响应示例**: +```json +{ + "code": 200, + "message": "视频发布成功", + "data": { + "id": 1, + "title": "测试视频", + "status": "published", + "video_url": "https://youtube.com/watch?v=123", + "publish_time": "2023-09-02T10:00:00Z" + } +} +``` + +## 字段说明 + +### 运营账号(OperatorAccount) + +| 字段名 | 类型 | 说明 | +|------------|-----------|--------------------------------------------------| +| id | Integer | 自增主键ID | +| uuid | UUID | 唯一标识符 | +| username | String | 用户名 | +| password | String | 密码(创建/更新时传入,不会在响应中返回) | +| real_name | String | 真实姓名 | +| email | String | 邮箱 | +| phone | String | 电话号码 | +| position | String | 职位,可选值: editor(编辑)、planner(策划)、operator(运营)、admin(管理员) | +| department | String | 部门 | +| is_active | Boolean | 是否在职 | +| created_at | Datetime | 创建时间 | +| updated_at | Datetime | 更新时间 | + +### 平台账号(PlatformAccount) + +| 字段名 | 类型 | 说明 | +|----------------|-----------|--------------------------------------------------| +| id | Integer | 自增主键ID | +| operator | Integer | 关联运营账号ID | +| operator_name | String | 运营账号名称(只读) | +| name | String | 自定义账户名称,可用于分类和识别不同平台的账号 | +| platforms | Array | 平台信息数组,包含平台名称、账号名称、账号ID、账号URL | +| status | String | 账号状态,可选值: active(正常)、restricted(限流)、suspended(封禁)、inactive(未激活) | +| followers_count| Integer | 粉丝数 | +| description | String | 账号描述 | +| tags | Array | 标签数组 | +| profile_image | String | 账号头像URL | +| last_posting | Datetime | 最后发布时间 | +| created_at | Datetime | 创建时间 | +| updated_at | Datetime | 更新时间 | +| last_login | Datetime | 最后登录时间 | + +### 视频(Video) + +| 字段名 | 类型 | 说明 | +|----------------------|-----------|--------------------------------------------------| +| id | Integer | 自增主键ID | +| platform_account | Integer | 关联平台账号ID | +| platform_account_name| String | 平台账号名称(只读) | +| platform_name | String | 平台名称(只读) | +| title | String | 视频标题 | +| description | String | 视频描述 | +| video_url | String | 视频URL | +| local_path | String | 本地存储路径 | +| thumbnail_url | String | 缩略图URL | +| status | String | 视频状态,可选值: draft(草稿)、scheduled(已排期)、published(已发布)、failed(失败)、deleted(已删除) | +| views_count | Integer | 观看次数 | +| likes_count | Integer | 点赞数 | +| comments_count | Integer | 评论数 | +| shares_count | Integer | 分享数 | +| tags | Array | 标签数组 | +| publish_time | Datetime | 发布时间 | +| video_id | String | 视频ID | +| created_at | Datetime | 创建时间 | +| updated_at | Datetime | 更新时间 | + \ No newline at end of file diff --git a/operation/migrations/0002_alter_platformaccount_options_and_more.py b/operation/migrations/0002_alter_platformaccount_options_and_more.py new file mode 100644 index 0000000..cc185a4 --- /dev/null +++ b/operation/migrations/0002_alter_platformaccount_options_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 5.1.5 on 2025-05-15 03:09 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('operation', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='platformaccount', + options={'ordering': ['-created_at'], 'verbose_name': '平台账号', 'verbose_name_plural': '平台账号'}, + ), + migrations.AlterUniqueTogether( + name='platformaccount', + unique_together=set(), + ), + migrations.AddField( + model_name='platformaccount', + name='name', + field=models.CharField(blank=True, default='', max_length=100, verbose_name='账户名称'), + ), + migrations.AlterField( + model_name='platformaccount', + name='operator', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='platform_accounts', to='operation.operatoraccount', verbose_name='运营账号'), + ), + migrations.AlterField( + model_name='platformaccount', + name='platform_name', + field=models.CharField(choices=[('youtube', 'YouTube'), ('tiktok', 'TikTok'), ('bilibili', 'Bilibili'), ('facebook', 'Facebook'), ('instagram', 'Instagram'), ('twitter', 'Twitter'), ('other', '其他平台')], max_length=20, verbose_name='平台名称'), + ), + migrations.AlterField( + model_name='platformaccount', + name='profile_image', + field=models.URLField(blank=True, null=True, verbose_name='头像URL'), + ), + migrations.AlterField( + model_name='platformaccount', + name='tags', + field=models.CharField(blank=True, help_text='逗号分隔的标签列表', max_length=255, null=True, verbose_name='标签'), + ), + migrations.AddIndex( + model_name='platformaccount', + index=models.Index(fields=['platform_name'], name='operation_p_platfor_6e8678_idx'), + ), + migrations.AddIndex( + model_name='platformaccount', + index=models.Index(fields=['account_id'], name='operation_p_account_bdceaf_idx'), + ), + migrations.AddIndex( + model_name='platformaccount', + index=models.Index(fields=['status'], name='operation_p_status_167573_idx'), + ), + ] diff --git a/operation/models.py b/operation/models.py index 7a58c2b..1cebd1b 100644 --- a/operation/models.py +++ b/operation/models.py @@ -38,25 +38,26 @@ class OperatorAccount(models.Model): return f"{self.real_name} ({self.username})" class PlatformAccount(models.Model): - """平台账号信息表""" + """平台账号模型""" + PLATFORM_CHOICES = [ + ('youtube', 'YouTube'), + ('tiktok', 'TikTok'), + ('bilibili', 'Bilibili'), + ('facebook', 'Facebook'), + ('instagram', 'Instagram'), + ('twitter', 'Twitter'), + ('other', '其他平台') + ] STATUS_CHOICES = [ ('active', '正常'), ('restricted', '限流'), ('suspended', '封禁'), - ('inactive', '未激活'), + ('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='关联运营') + operator = models.ForeignKey(OperatorAccount, on_delete=models.CASCADE, related_name='platform_accounts', verbose_name='运营账号') + name = models.CharField(max_length=100, verbose_name='账户名称', default='', blank=True) 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') @@ -64,23 +65,25 @@ class PlatformAccount(models.Model): 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='账号头像') + tags = models.CharField(max_length=255, blank=True, null=True, verbose_name='标签', help_text='逗号分隔的标签列表') + profile_image = models.URLField(blank=True, null=True, verbose_name='头像URL') 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='最后登录时间') + def __str__(self): + return f"{self.platform_name} - {self.account_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()})" + ordering = ['-created_at'] + indexes = [ + models.Index(fields=['platform_name']), + models.Index(fields=['account_id']), + models.Index(fields=['status']), + ] class Video(models.Model): """视频信息表""" diff --git a/operation/pagination.py b/operation/pagination.py index b4a8671..7a6939a 100644 --- a/operation/pagination.py +++ b/operation/pagination.py @@ -8,12 +8,13 @@ class CustomPagination(PageNumberPagination): max_page_size = 100 def get_paginated_response(self, data): - # 为每个结果添加name字段,从platforms中提取平台名称 + # 为那些没有name字段或name字段为空的项目设置默认值 for item in data: if 'platforms' in item and len(item['platforms']) > 0: - # 添加name字段,只使用platform_name - platform = item['platforms'][0] - item['name'] = platform.get('platform_name', '') + # 只有当name为空或不存在时,才使用platform_name作为默认值 + if not item.get('name'): + platform = item['platforms'][0] + item['name'] = platform.get('platform_name', '') return Response({ "code": 200, diff --git a/operation/serializers.py b/operation/serializers.py index caebf04..8cc7b60 100644 --- a/operation/serializers.py +++ b/operation/serializers.py @@ -18,15 +18,20 @@ class OperatorAccountSerializer(serializers.ModelSerializer): class PlatformAccountSerializer(serializers.ModelSerializer): operator_name = serializers.CharField(source='operator.real_name', read_only=True) + platforms = serializers.SerializerMethodField() class Meta: model = PlatformAccount - fields = ['id', 'operator', 'operator_name', 'platform_name', 'account_name', 'account_id', + fields = ['id', 'operator', 'operator_name', 'name', 'platform_name', 'account_name', 'account_id', 'status', 'followers_count', 'account_url', 'description', 'tags', 'profile_image', 'last_posting', - 'created_at', 'updated_at', 'last_login'] + 'created_at', 'updated_at', 'last_login', 'platforms'] read_only_fields = ['id', 'created_at', 'updated_at'] + def get_platforms(self, obj): + # 不会在查询时调用,但需要实现该方法,默认返回空数组 + return [] + def to_internal_value(self, data): # 处理operator字段,可能是字符串格式的ID if 'operator' in data and isinstance(data['operator'], str): @@ -41,6 +46,28 @@ class PlatformAccountSerializer(serializers.ModelSerializer): # 其他类型的错误,如ID格式不正确等 pass + # 处理platforms字段,从数组中提取出第一个元素的信息 + if 'platforms' in data and isinstance(data['platforms'], list) and len(data['platforms']) > 0: + data = data.copy() + platform_data = data['platforms'][0] + # 将platforms中的字段移到顶层 + if 'platform_name' in platform_data: + data['platform_name'] = platform_data['platform_name'] + if 'account_url' in platform_data: + data['account_url'] = platform_data['account_url'] + if 'account_id' in platform_data: + data['account_id'] = platform_data['account_id'] + if 'account_name' in platform_data: + data['account_name'] = platform_data['account_name'] + + # 删除platforms字段,避免验证错误 + del data['platforms'] + + # 处理tags字段,将列表转换为逗号分隔的字符串 + if 'tags' in data and isinstance(data['tags'], list): + data = data.copy() if not isinstance(data, dict) else data + data['tags'] = ','.join(data['tags']) + return super().to_internal_value(data) def to_representation(self, instance): @@ -60,6 +87,7 @@ class PlatformDetailSerializer(serializers.Serializer): class MultiPlatformAccountSerializer(serializers.Serializer): """多平台账号创建序列化器""" operator = serializers.PrimaryKeyRelatedField(queryset=OperatorAccount.objects.all()) + name = serializers.CharField(max_length=100, required=False, allow_blank=True) account_name = serializers.CharField(max_length=100) account_id = serializers.CharField(max_length=100) status = serializers.ChoiceField(choices=PlatformAccount.STATUS_CHOICES, default='active') diff --git a/operation/views.py b/operation/views.py index 9963126..939900e 100644 --- a/operation/views.py +++ b/operation/views.py @@ -124,8 +124,9 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): } # 添加platforms字段作为数组 account_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - account_data["name"] = platform_info["platform_name"] + # 保留用户传入的name字段,如果没有则使用platform_name + if not account_data.get("name"): + account_data["name"] = platform_info["platform_name"] restructured_data.append(account_data) # 使用自定义分页器的响应,但替换数据 @@ -145,8 +146,9 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): } # 添加platforms字段作为数组 account_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - account_data["name"] = platform_info["platform_name"] + # 保留用户传入的name字段,如果没有则使用platform_name + if not account_data.get("name"): + account_data["name"] = platform_info["platform_name"] restructured_data.append(account_data) return Response({ @@ -170,8 +172,9 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): } # 添加platforms字段 account_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - account_data["name"] = platform_info["platform_name"] + # 保留用户传入的name字段,如果没有则使用platform_name + if not account_data.get("name"): + account_data["name"] = platform_info["platform_name"] return Response({ "code": 200, @@ -183,10 +186,50 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): """更新平台账号信息""" partial = kwargs.pop('partial', False) instance = self.get_object() - serializer = self.get_serializer(instance, data=request.data, partial=partial) + + # 单独处理platforms字段,多平台信息需要特殊处理 + data = request.data.copy() + platforms_data = None + + if 'platforms' in data and isinstance(data['platforms'], list): + platforms_data = data.pop('platforms') + + # 如果有platforms数据,先将第一个平台的信息移至顶层,保证基本平台信息能正常更新 + if platforms_data and len(platforms_data) > 0: + first_platform = platforms_data[0] + if 'platform_name' in first_platform: + data['platform_name'] = first_platform['platform_name'] + if 'account_url' in first_platform: + data['account_url'] = first_platform['account_url'] + if 'account_id' in first_platform: + data['account_id'] = first_platform['account_id'] + if 'account_name' in first_platform: + data['account_name'] = first_platform['account_name'] + + serializer = self.get_serializer(instance, data=data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) + # 处理多平台信息 + # 这里我们需要实现新的逻辑来处理额外的平台 + # 注意:这需要修改模型结构或添加关联模型来支持多平台 + # 由于当前模型结构限制,我们暂时只能在此记录其他平台数据 + # 用于未来扩展,目前会在日志中记录这些信息 + if platforms_data and len(platforms_data) > 1: + logger = logging.getLogger(__name__) + logger.info(f"接收到多平台数据,但当前版本仅支持一个平台。额外平台数据: {platforms_data[1:]}") + + # 这里应该添加创建关联平台记录的代码 + # 例如: + # for platform_data in platforms_data[1:]: + # RelatedPlatform.objects.create( + # primary_account=instance, + # platform_name=platform_data.get('platform_name', ''), + # account_id=platform_data.get('account_id', ''), + # account_name=platform_data.get('account_name', ''), + # account_url=platform_data.get('account_url', '') + # ) + # 处理数据结构 account_data = serializer.data # 提取平台信息并放入platforms字段 @@ -197,9 +240,15 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): "account_name": account_data.pop("account_name") } # 添加platforms字段 - account_data["platforms"] = [platform_info] - # 添加name字段 - account_data["name"] = platform_info["platform_name"] + " | " + platform_info["account_name"] + # 如果有platforms_data,使用原始请求中的数据,否则使用当前的单平台数据 + if platforms_data: + account_data["platforms"] = platforms_data + else: + account_data["platforms"] = [platform_info] + + # 保留用户传入的name字段,如果没有则使用platform_name + if not account_data.get("name"): + account_data["name"] = platform_info["platform_name"] return Response({ "code": 200, @@ -214,120 +263,69 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): def create(self, request, *args, **kwargs): """创建平台账号""" - # 检查请求数据中是否包含platforms字段,判断是否为多平台账号创建 - if 'platforms' in request.data and isinstance(request.data['platforms'], list): - # 使用多平台账号序列化器 - serializer = MultiPlatformAccountSerializer(data=request.data) + # 传统单平台账号创建流程 + print(f"{request.data}") + with transaction.atomic(): + # 处理operator字段,可能是字符串类型的ID + data = request.data.copy() + if 'operator' in data and isinstance(data['operator'], str): + try: + # 尝试通过ID查找运营账号 + operator_id = data['operator'] + try: + # 先尝试通过整数ID查找 + operator_id_int = int(operator_id) + operator = OperatorAccount.objects.get(id=operator_id_int) + except (ValueError, OperatorAccount.DoesNotExist): + # 如果无法转换为整数或找不到对应账号,尝试通过用户名或真实姓名查找 + operator = OperatorAccount.objects.filter( + Q(username=operator_id) | Q(real_name=operator_id) + ).first() + + if not operator: + return Response({ + "code": 404, + "message": f"未找到运营账号: {operator_id},请提供有效的ID、用户名或真实姓名", + "data": None + }, status=status.HTTP_404_NOT_FOUND) + + # 更新请求数据中的operator字段为找到的operator的ID + data['operator'] = operator.id + + except Exception as e: + return Response({ + "code": 400, + "message": f"处理运营账号ID时出错: {str(e)}", + "data": None + }, status=status.HTTP_400_BAD_REQUEST) + + # 创建平台账号 + serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) - created_accounts = [] + # 创建平台账号 + self.perform_create(serializer) - with transaction.atomic(): - # 获取基础账号信息 - base_data = serializer.validated_data.copy() - platforms_data = base_data.pop('platforms') - operator = base_data['operator'] - - # 遍历平台数据创建多个平台账号 - for platform_data in platforms_data: - # 创建平台账号数据 - platform_account_data = base_data.copy() - platform_account_data['platform_name'] = platform_data['platform_name'] - platform_account_data['account_url'] = platform_data['platform_url'] - - # 创建平台账号 - platform_account = PlatformAccount.objects.create(**platform_account_data) - created_accounts.append(platform_account) - - # 将创建的账号序列化返回 - 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"), - "account_id": account_data.pop("account_id"), - "account_name": account_data.pop("account_name") - } - - # 添加platforms字段 - account_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - account_data["name"] = platform_info["platform_name"] - restructured_data.append(account_data) - - return Response({ - "code": 200, - "message": "多平台账号创建成功", - "data": restructured_data - }, status=status.HTTP_201_CREATED) - else: - # 传统单平台账号创建流程 - with transaction.atomic(): - # 处理operator字段,可能是字符串类型的ID - data = request.data.copy() - if 'operator' in data and isinstance(data['operator'], str): - try: - # 尝试通过ID查找运营账号 - operator_id = data['operator'] - try: - # 先尝试通过整数ID查找 - operator_id_int = int(operator_id) - operator = OperatorAccount.objects.get(id=operator_id_int) - except (ValueError, OperatorAccount.DoesNotExist): - # 如果无法转换为整数或找不到对应账号,尝试通过用户名或真实姓名查找 - operator = OperatorAccount.objects.filter( - Q(username=operator_id) | Q(real_name=operator_id) - ).first() - - if not operator: - return Response({ - "code": 404, - "message": f"未找到运营账号: {operator_id},请提供有效的ID、用户名或真实姓名", - "data": None - }, status=status.HTTP_404_NOT_FOUND) - - # 更新请求数据中的operator字段为找到的operator的ID - data['operator'] = operator.id - - except Exception as e: - return Response({ - "code": 400, - "message": f"处理运营账号ID时出错: {str(e)}", - "data": None - }, status=status.HTTP_400_BAD_REQUEST) - - # 创建平台账号 - serializer = self.get_serializer(data=data) - serializer.is_valid(raise_exception=True) - - # 创建平台账号 - self.perform_create(serializer) - - # 处理响应数据 - account_data = serializer.data - # 提取平台信息并放入platforms字段 - platform_info = { - "platform_name": account_data.pop("platform_name"), - "account_url": account_data.pop("account_url"), - "account_id": account_data.pop("account_id"), - "account_name": account_data.pop("account_name") - } - # 添加platforms字段 - account_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name + # 处理响应数据 + account_data = serializer.data + # 提取平台信息并放入platforms字段 + platform_info = { + "platform_name": account_data.pop("platform_name"), + "account_url": account_data.pop("account_url"), + "account_id": account_data.pop("account_id"), + "account_name": account_data.pop("account_name") + } + # 添加platforms字段 + account_data["platforms"] = [platform_info] + # 保留用户传入的name字段,如果没有则使用platform_name + if not account_data.get("name"): account_data["name"] = platform_info["platform_name"] - - return Response({ - "code": 200, - "message": "平台账号创建成功", - "data": account_data - }, status=status.HTTP_201_CREATED) + + return Response({ + "code": 200, + "message": "平台账号创建成功", + "data": account_data + }, status=status.HTTP_201_CREATED) def destroy(self, request, *args, **kwargs): """删除平台账号""" @@ -368,8 +366,9 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): } # 添加platforms字段 platform_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - platform_data["name"] = platform_info["platform_name"] + # 保留用户传入的name字段,如果没有则使用platform_name + if not platform_data.get("name"): + platform_data["name"] = platform_info["platform_name"] return Response({ "code": 200, @@ -411,6 +410,10 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): "message": f"最后发布时间格式错误: {str(e)}", "data": None }, status=status.HTTP_400_BAD_REQUEST) + + # 处理名称 + if 'name' in request.data: + profile_data['name'] = request.data['name'] if not profile_data: return Response({ @@ -435,8 +438,9 @@ class PlatformAccountViewSet(viewsets.ModelViewSet): } # 添加platforms字段 platform_data["platforms"] = [platform_info] - # 添加name字段,只用platform_name - platform_data["name"] = platform_info["platform_name"] + # 保留用户传入的name字段,如果没有则使用platform_name + if not platform_data.get("name"): + platform_data["name"] = platform_info["platform_name"] return Response({ "code": 200,