from typing import Optional from pydantic import BaseModel, Field, field_validator, ConfigDict from datetime import datetime class BlogQuery(BaseModel): """博客查询参数""" category: Optional[str] = Field(None, description="分类名称") title: Optional[str] = Field(None, description="标题关键词") year: Optional[int] = Field(None, description="发布年份", ge=2000, le=datetime.now().year) @field_validator('year') def validate_year(cls, v): if v is not None and v > datetime.now().year: raise ValueError('年份不能超过当前年份') return v class BlogBase(BaseModel): """博客基础模型""" title: str = Field(..., min_length=1, max_length=255, description="博客标题") top_value: int = Field(default=0, ge=0, description="置顶值,越大越靠前", alias="topValue") is_great: bool = Field(default=False, description="是否是精品", alias="isGreat") category: str = Field(..., min_length=1, max_length=45, description="分类名称") content: Optional[str] = Field(None, description="博客内容") is_approved: bool = Field(default=False, description="是否已发布", alias="isApproved") @field_validator('title') def title_not_empty(cls, v): if not v or not v.strip(): raise ValueError('标题不能为空') return v.strip() @field_validator('category') def category_not_empty(cls, v): if not v or not v.strip(): raise ValueError('分类不能为空') return v.strip() class BlogCreate(BlogBase): pass class BlogUpdate(BlogBase): pass class BlogResponse(BlogBase): id: int = Field(..., description="博客ID") summary: Optional[str] = Field(None, description="内容摘要") word_count: Optional[int] = Field(None, description="字数统计", alias="wordCount") read_duration: Optional[float] = Field(None, description="阅读时长", alias="readDuration") visit_count: Optional[int] = Field(0, description="访问次数", alias="visitCount") create_time: datetime = Field(..., description="创建时间", alias="createTime") update_time: datetime = Field(..., description="更新时间", alias="updateTime") model_config = ConfigDict( from_attributes=True, populate_by_name=True, json_encoders={ datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') } ) class BlogCategoryResponse(BaseModel): name: str = Field(..., description="分类名称") count: int = Field(..., description="博客数量") model_config = ConfigDict( from_attributes=True, populate_by_name=True ) class BlogStatsResponse(BaseModel): blog_count: int = Field(..., description="博客总数", alias="blogCount") category_count: int = Field(..., description="分类总数", alias="categoryCount") word_count: int = Field(..., description="总字数", alias="wordCount") class BlogVisitResponse(BaseModel): ip: str = Field(..., description="IP地址") os: str = Field(..., description="操作系统") browser: str = Field(..., description="浏览器") uri: str = Field(..., description="访问路径") title: str = Field(None, description="博客标题") visit_time: datetime = Field(..., description="访问时间", alias="visitTime") model_config = ConfigDict( from_attributes=True, populate_by_name=True, json_encoders={ datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') } ) class BlogLatestResponse(BaseModel): id: int = Field(..., description="博客ID") title: str = Field(..., description="博客标题") model_config = ConfigDict( from_attributes=True, populate_by_name=True ) class BlogAdjacentResponse(BaseModel): id: int = Field(..., description="博客ID") title: str = Field(..., description="博客标题") model_config = ConfigDict( from_attributes=True, populate_by_name=True ) class BlogCommentCreate(BaseModel): parent_id: int = Field(default=0, ge=0, description="父评论ID,0表示顶级评论", alias="parentId") name: str = Field(..., min_length=1, max_length=50, description="评论人昵称") website: Optional[str] = Field(None, description="评论人网站") content: str = Field(..., min_length=1, max_length=1000, description="评论内容") @field_validator('name') def name_not_empty(cls, v): if not v or not v.strip(): raise ValueError('昵称不能为空') return v.strip() @field_validator('content') def content_not_empty(cls, v): if not v or not v.strip(): raise ValueError('评论内容不能为空') return v.strip() class BlogCommentResponse(BlogCommentCreate): id: int = Field(..., description="评论ID") ip_address: str = Field(..., description="IP地址", alias="ipAddress") user_agent: str = Field(..., description="浏览器信息", alias="userAgent") create_time: datetime = Field(..., description="创建时间", alias="createTime") model_config = ConfigDict( from_attributes=True, populate_by_name=True, json_encoders={ datetime: lambda dt: dt.strftime('%Y-%m-%d %H:%M:%S') } )