Files
yu-zhi-ran/platform/backend/app/schemas.py
T
Yuzhiran Dev 23ff63baa9 fix: 三平台内容差异化 + admin敏感词管理表格化
- writer.py: _expand_section() 去除 <100字阈值,始终调用 LLM 平台专属扩写
- prompt_loader.py: 新增 section_expansion_zhihu/wechat/xiaohongshu 三个独立 prompt
- admin.html: 配置管理标签页 + 敏感词/清理规则子标签 + 敏感词表格化管理(编辑/删除)
- config_items.py: PUT /sensitive-words/{id} 支持更新 word/category
- compliance_checker.py: AI 套话从 DB 加载 + 人称规则修正
- initial_data.py: PlatformConfig 字数迁移 + 新种子
- 各前端页面: LLM 配置 rate_limit 字段 + 供应商列表排序
2026-06-08 13:51:35 +08:00

614 lines
15 KiB
Python

from pydantic import BaseModel, ConfigDict, Field
from datetime import datetime, date
from typing import Optional, List, Dict, Any
class TopicFieldBase(BaseModel):
name: str
icon: Optional[str] = None
color: Optional[str] = None
description: Optional[str] = None
parent_id: Optional[int] = None
sort_order: int = 0
is_active: bool = True
class TopicFieldResponse(TopicFieldBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class TopicConfigFieldBase(BaseModel):
name: str
key: str
field_type: str = "number"
weight: float = 1.0
options: List[Any] = []
min_value: Optional[float] = None
max_value: Optional[float] = None
is_required: bool = False
sort_order: int = 0
class TopicConfigFieldResponse(TopicConfigFieldBase):
id: int
field_id: int
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class TopicStatusConfigBase(BaseModel):
status: str
label: str
color: Optional[str] = None
icon: Optional[str] = None
sort_order: int = 0
is_default: bool = False
class TopicStatusConfigResponse(TopicStatusConfigBase):
id: int
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class TopicBase(BaseModel):
id: str
field_id: Optional[int] = None
field_name: Optional[str] = None
org_id: Optional[str] = None
title: str
format: Optional[str] = None
core_concept: Optional[str] = None
audience_pain: Optional[str] = None
unique_angle: Optional[str] = None
priority: Optional[str] = None
priority_score: int | float = 0
total_score: Optional[float] = None
status: str = "pending"
tags: List[str] = []
custom_data: Dict[str, Any] = {}
scoring_data: Dict[str, Any] = {}
lock_by: Optional[str] = None
lock_at: Optional[datetime] = None
class TopicCreate(BaseModel):
id: Optional[str] = None
field_id: Optional[int] = None
title: str = Field(..., min_length=1)
format: Optional[str] = None
core_concept: Optional[str] = None
audience_pain: Optional[str] = None
unique_angle: Optional[str] = None
priority: Optional[str] = None
tags: List[str] = []
custom_data: Dict[str, Any] = {}
scoring_data: Dict[str, Any] = {}
class TopicUpdate(BaseModel):
field_id: Optional[int] = None
title: Optional[str] = None
format: Optional[str] = None
core_concept: Optional[str] = None
audience_pain: Optional[str] = None
unique_angle: Optional[str] = None
priority: Optional[str] = None
status: Optional[str] = None
tags: Optional[List[str]] = None
custom_data: Optional[Dict[str, Any]] = None
scoring_data: Optional[Dict[str, Any]] = None
priority_score: Optional[int] = None
total_score: Optional[float] = None
class TopicResponse(TopicBase):
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
generated_at: Optional[datetime] = None
reviewed_at: Optional[datetime] = None
ready_at: Optional[date] = None
published_at: Optional[date] = None
compliance_score: Optional[int] = None
platform_urls: Dict[str, str] = {}
cases: List[Any] = []
model_config = ConfigDict(from_attributes=True)
class TopicScoreRequest(BaseModel):
scoring_data: Dict[str, Any]
class ArticleBase(BaseModel):
id: str
topic_id: str
platform: str
file_path: str
status: str = "draft"
compliance_score: Optional[int] = None
word_count: Optional[int] = None
outline: Optional[str] = None
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class ArticleResponse(ArticleBase):
html_content: Optional[str] = None
model_config = ConfigDict(from_attributes=True)
class ContentCalendarBase(BaseModel):
topic_id: Optional[str] = None
field_id: Optional[int] = None
title: str
planned_date: date
platform: Optional[str] = None
status: str = "planned"
reminder_time: Optional[datetime] = None
notes: Optional[str] = None
class ContentCalendarCreate(ContentCalendarBase):
created_by: Optional[str] = None
class ContentCalendarUpdate(BaseModel):
topic_id: Optional[str] = None
title: Optional[str] = None
planned_date: Optional[date] = None
published_date: Optional[date] = None
platform: Optional[str] = None
status: Optional[str] = None
reminder_time: Optional[datetime] = None
notes: Optional[str] = None
class ContentCalendarResponse(ContentCalendarBase):
id: int
published_date: Optional[date] = None
created_by: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
topic_status: Optional[str] = None
platform_icon: Optional[str] = None
model_config = ConfigDict(from_attributes=True)
class ContentMetricsBase(BaseModel):
topic_id: str
platform: str
publish_url: Optional[str] = None
views: int = 0
likes: int = 0
favorites: int = 0
comments: int = 0
shares: int = 0
class ContentMetricsCreate(ContentMetricsBase):
data_snapshot: Optional[Dict[str, Any]] = None
class ContentMetricsUpdate(BaseModel):
publish_url: Optional[str] = None
views: Optional[int] = None
likes: Optional[int] = None
favorites: Optional[int] = None
comments: Optional[int] = None
shares: Optional[int] = None
data_snapshot: Optional[Dict[str, Any]] = None
class ContentMetricsResponse(ContentMetricsBase):
id: int
engagement_rate: float = 0
last_fetched: Optional[datetime] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class MediaAssetBase(BaseModel):
filename: str
file_path: str
file_url: Optional[str] = None
file_type: str
mime_type: Optional[str] = None
size: Optional[int] = None
width: Optional[int] = None
height: Optional[int] = None
thumbnail_path: Optional[str] = None
alt_text: Optional[str] = None
tags: List[str] = []
topic_ids: List[str] = []
class MediaAssetCreate(MediaAssetBase):
uploaded_by: Optional[str] = None
class MediaAssetUpdate(BaseModel):
alt_text: Optional[str] = None
tags: Optional[List[str]] = None
topic_ids: Optional[List[str]] = None
class MediaAssetResponse(MediaAssetBase):
id: int
usage_count: int = 0
uploaded_by: Optional[str] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class PlatformConfigBase(BaseModel):
platform: str
name: str
icon: Optional[str] = None
website_url: Optional[str] = None
api_endpoint: Optional[str] = None
auth_config: Dict[str, Any] = {}
format_template: Dict[str, Any] = {}
compliance_rules: Dict[str, Any] = {}
default_format: Optional[str] = None
is_active: bool = True
requires_image: bool = False
image_count_min: int = 0
image_count_max: int = 0
image_width: int = 0
image_height: int = 0
min_words: int = 0
max_words: int = 0
class PlatformConfigCreate(PlatformConfigBase):
pass
class PlatformConfigUpdate(BaseModel):
name: Optional[str] = None
icon: Optional[str] = None
website_url: Optional[str] = None
api_endpoint: Optional[str] = None
auth_config: Optional[Dict[str, Any]] = None
format_template: Optional[Dict[str, Any]] = None
compliance_rules: Optional[Dict[str, Any]] = None
default_format: Optional[str] = None
is_active: Optional[bool] = None
requires_image: Optional[bool] = None
image_count_min: Optional[int] = None
image_count_max: Optional[int] = None
image_width: Optional[int] = None
image_height: Optional[int] = None
min_words: Optional[int] = None
max_words: Optional[int] = None
class PlatformConfigResponse(PlatformConfigBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class ContentTaskBase(BaseModel):
topic_id: Optional[str] = None
stage: str
status: str = "pending"
progress: int = 0
message: Optional[str] = None
class ContentTaskCreate(BaseModel):
topic_id: Optional[str] = None
stage: str
created_by: Optional[str] = None
class ContentTaskResponse(ContentTaskBase):
id: int
task_id: str
topic_id: Optional[str] = None
topic_title: Optional[str] = None
result_data: Dict[str, Any] = {}
error_msg: Optional[str] = None
started_at: Optional[datetime] = None
finished_at: Optional[datetime] = None
duration: Optional[int] = None
created_by: Optional[str] = None
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class MetricsDashboard(BaseModel):
total_topics: int
topics_by_status: Dict[str, int]
total_published: int
total_views: int
total_likes: int
avg_engagement_rate: float
top_topics: List[Dict[str, Any]]
recent_metrics: List[ContentMetricsResponse]
class CalendarMonthResponse(BaseModel):
year: int
month: int
entries: List[ContentCalendarResponse]
stats: Dict[str, int]
class AuditLogBase(BaseModel):
user_id: Optional[int] = None
username: str
action: str
resource_type: Optional[str] = None
resource_id: Optional[str] = None
details: Optional[Dict[str, Any]] = None
ip_address: Optional[str] = None
user_agent: Optional[str] = None
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class AuditLogResponse(AuditLogBase):
id: int
class UserBase(BaseModel):
username: str
role: str = "user"
class UserCreate(UserBase):
password: str
org_id: Optional[str] = None
class UserUpdate(BaseModel):
username: Optional[str] = None
password: Optional[str] = None
role: Optional[str] = None
class UserResponse(UserBase):
id: int
org_id: Optional[str] = None
last_login: Optional[datetime] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class ChangePasswordRequest(BaseModel):
old_password: str
new_password: str
class LoginRequest(BaseModel):
username: str
password: str
class TokenResponse(BaseModel):
token: str
role: str
user: UserResponse
class PublishRequest(BaseModel):
topic_id: str
platform_urls: Dict[str, str]
class PublishActionRequest(BaseModel):
action: str
platform: Optional[str] = None
status: str
operator: Optional[str] = None
description: Optional[str] = None
suggestion: Optional[str] = None
url: Optional[str] = None
error_msg: Optional[str] = None
class PublishRecordResponse(BaseModel):
id: int
topic_id: str
platform: str
action: str
status: str
operator: Optional[str] = None
description: Optional[str] = None
suggestion: Optional[str] = None
url: Optional[str] = None
error_msg: Optional[str] = None
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class CaseBase(BaseModel):
title: str
field: str
summary: str
key_metrics: Optional[str] = None
date: Optional[str] = None
source: str
source_url: Optional[str] = None
credibility_rating: Optional[str] = None
china_applicability: Optional[str] = None
class CaseResponse(CaseBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class TaskLogBase(BaseModel):
module_id: str
task_name: str
topic_id: Optional[str] = None
status: str
message: Optional[str] = None
error_trace: Optional[str] = None
triggered_by: str = "scheduler"
result_data: Dict[str, Any] = {}
started_at: Optional[datetime] = None
finished_at: Optional[datetime] = None
duration: Optional[int] = None
next_run_time: Optional[datetime] = None
class TaskLogResponse(TaskLogBase):
id: int
model_config = ConfigDict(from_attributes=True)
class TaskConfigBase(BaseModel):
module_id: str
enabled: bool = True
params: Dict[str, Any] = {}
schedule: Optional[str] = None
class TaskConfigUpdate(BaseModel):
enabled: Optional[bool] = None
params: Optional[Dict[str, Any]] = None
schedule: Optional[str] = None
last_modified_by: Optional[str] = None
class TaskConfigResponse(TaskConfigBase):
id: int
last_modified_by: Optional[str] = None
last_run_at: Optional[datetime] = None
result_data: Optional[Dict[str, Any]] = None
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class LLMConfigBase(BaseModel):
name: str
system_prompt: Optional[str] = None
user_prompt_template: str
temperature: float = 0.7
max_tokens: int = 131072
model: Optional[str] = None
provider: str = "opencode-go"
base_url: Optional[str] = None
api_key: Optional[str] = None
is_active: bool = True
is_default: bool = False
rate_limit: int = 0
rate_limit_window_minutes: int = 300
class LLMConfigResponse(LLMConfigBase):
id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class SystemConfigBase(BaseModel):
key: str
value: Optional[str] = None
description: Optional[str] = None
class SystemConfigResponse(SystemConfigBase):
value: Optional[Any] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class SystemStatus(BaseModel):
total_topics: int
topics_by_status: Dict[str, int]
ready_topics: List[TopicResponse]
today_articles: int
compliance_rate: float
last_optimization: Optional[datetime] = None
execution_time: Optional[float] = None
class RoleBase(BaseModel):
name: str
description: str = ""
is_system: bool = False
class RoleCreate(RoleBase):
pass
class RoleUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
class RoleResponse(RoleBase):
id: int
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class MenuBase(BaseModel):
parent_id: Optional[int] = None
name: str
path: str
icon: str = ""
sort_order: int = 0
roles: List[str] = []
is_active: bool = True
class MenuCreate(MenuBase):
pass
class MenuUpdate(BaseModel):
parent_id: Optional[int] = None
name: Optional[str] = None
path: Optional[str] = None
icon: Optional[str] = None
sort_order: Optional[int] = None
roles: Optional[List[str]] = None
is_active: Optional[bool] = None
class MenuResponse(MenuBase):
id: int
created_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)