feat: 全面升级项目架构 - PostgreSQL迁移 + 配置化改造
主要变更: - 数据库: SQLite → PostgreSQL (yzr_nr) - 选题系统: 硬编码字段 → 配置化 (TopicField/TopicConfigField/TopicStatusConfig) - 新增模型: ContentCalendar, ContentMetrics, MediaAsset, PlatformConfig, ContentTask - 新增 API: topic-config, calendar, metrics, assets, tasks, platform-config - 数据迁移: 现有选题数据迁移到新 schema (field_id/tags/custom_data/scoring_data) - 初始化数据: 10个领域, 5种状态, 3个平台配置 服务运行: http://localhost:8001 默认账号: admin / admin123
This commit is contained in:
+361
-66
@@ -1,7 +1,336 @@
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
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
|
||||
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 = 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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
class PlatformConfigCreate(PlatformConfigBase):
|
||||
pass
|
||||
|
||||
|
||||
class PlatformConfigUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
icon: 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
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
@@ -13,85 +342,51 @@ class AuditLogBase(BaseModel):
|
||||
user_agent: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class AuditLogResponse(AuditLogBase):
|
||||
id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
username: str
|
||||
role: str = "user"
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str
|
||||
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
username: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
role: Optional[str] = None
|
||||
|
||||
|
||||
class UserResponse(UserBase):
|
||||
id: int
|
||||
created_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
token: str
|
||||
role: str
|
||||
user: UserResponse
|
||||
|
||||
class TopicBase(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
field: str
|
||||
priority_score: int = 0
|
||||
status: str = "pending"
|
||||
compliance_score: Optional[int] = None
|
||||
ready_at: Optional[date] = None
|
||||
published_at: Optional[date] = None
|
||||
generated_at: Optional[datetime] = None
|
||||
platform_urls: Optional[Dict[str, str]] = None
|
||||
|
||||
class TopicResponse(TopicBase):
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class ArticleBase(BaseModel):
|
||||
id: str
|
||||
topic_id: str
|
||||
platform: str
|
||||
file_path: str
|
||||
status: str = "draft"
|
||||
compliance_score: Optional[int] = None
|
||||
created_at: Optional[datetime] = None
|
||||
|
||||
class ArticleResponse(ArticleBase):
|
||||
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 OptimizationRequest(BaseModel):
|
||||
topic_ids: Optional[List[str]] = None
|
||||
|
||||
class PublishRequest(BaseModel):
|
||||
topic_id: str
|
||||
platform_urls: Dict[str, str]
|
||||
|
||||
|
||||
class PublishActionRequest(BaseModel):
|
||||
action: str
|
||||
platform: Optional[str] = None
|
||||
@@ -102,6 +397,7 @@ class PublishActionRequest(BaseModel):
|
||||
url: Optional[str] = None
|
||||
error_msg: Optional[str] = None
|
||||
|
||||
|
||||
class PublishRecordResponse(BaseModel):
|
||||
id: int
|
||||
topic_id: str
|
||||
@@ -117,26 +413,7 @@ class PublishRecordResponse(BaseModel):
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
class BatchPublishRequest(BaseModel):
|
||||
date: str
|
||||
|
||||
|
||||
class AuditLogResponse(BaseModel):
|
||||
id: int
|
||||
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: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# --- 案例库 Schema ---
|
||||
class CaseBase(BaseModel):
|
||||
title: str
|
||||
field: str
|
||||
@@ -148,6 +425,7 @@ class CaseBase(BaseModel):
|
||||
credibility_rating: Optional[str] = None
|
||||
china_applicability: Optional[str] = None
|
||||
|
||||
|
||||
class CaseResponse(CaseBase):
|
||||
id: int
|
||||
created_at: Optional[datetime] = None
|
||||
@@ -156,7 +434,6 @@ class CaseResponse(CaseBase):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# --- 任务日志 Schema ---
|
||||
class TaskLogBase(BaseModel):
|
||||
task_name: str
|
||||
topic_id: Optional[str] = None
|
||||
@@ -166,13 +443,13 @@ class TaskLogBase(BaseModel):
|
||||
finished_at: Optional[datetime] = None
|
||||
duration_seconds: Optional[int] = None
|
||||
|
||||
|
||||
class TaskLogResponse(TaskLogBase):
|
||||
id: int
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# --- LLM 配置 Schema ---
|
||||
class LLMConfigBase(BaseModel):
|
||||
name: str
|
||||
system_prompt: Optional[str] = None
|
||||
@@ -182,6 +459,7 @@ class LLMConfigBase(BaseModel):
|
||||
model: Optional[str] = None
|
||||
is_active: bool = True
|
||||
|
||||
|
||||
class LLMConfigResponse(LLMConfigBase):
|
||||
id: int
|
||||
created_at: Optional[datetime] = None
|
||||
@@ -190,14 +468,31 @@ class LLMConfigResponse(LLMConfigBase):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# --- 系统配置 Schema ---
|
||||
class SystemConfigBase(BaseModel):
|
||||
key: str
|
||||
value: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class SystemConfigResponse(SystemConfigBase):
|
||||
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 OptimizationRequest(BaseModel):
|
||||
topic_ids: Optional[List[str]] = None
|
||||
|
||||
|
||||
class BatchPublishRequest(BaseModel):
|
||||
date: str
|
||||
Reference in New Issue
Block a user