ComfyUI-Ranking-API / models.py
ZHIWEI666's picture
Upload 15 files
be93389 verified
Raw History Blame Contribute Delete
12.5 kB
# models.py
import re
from pydantic import BaseModel, Field, field_validator # 🧹 P2修复:pydantic v2 已废弃 v1 风格 validator
from typing import Optional, List, Literal
from profile_media import normalize_profile_media
def _validate_email_format(email: str) -> str:
"""邮箱格式校验"""
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
raise ValueError('邮箱格式不正确')
return email
def _validate_password_length(password: str) -> str:
"""密码长度校验:6-64字符(兼容旧用户短密码)"""
if len(password) < 6:
raise ValueError('密码长度不能少于6个字符')
if len(password) > 64:
raise ValueError('密码长度不能超过64个字符')
return password
class SendCodeRequest(BaseModel):
contact: str = Field(..., min_length=1, max_length=254)
contact_type: Literal["email", "phone"]
action_type: Literal["register", "reset", "recover", "withdraw"]
account: Optional[str] = None
class UserRegister(BaseModel):
account: str
password: str
email: str
phone: Optional[str] = ""
name: str
gender: str
code: str
avatarDataUrl: Optional[str] = None
age: Optional[int] = None
country: Optional[str] = None
region: Optional[str] = None
intro: Optional[str] = None
# 🧹 P2修复:v2 中下划线前缀属性会被当作私有属性而非校验器,改用 field_validator 显式挂载(校验逻辑不变)
@field_validator('password')
@classmethod
def _check_password(cls, v):
return _validate_password_length(v)
@field_validator('email')
@classmethod
def _check_email(cls, v):
return _validate_email_format(v)
@field_validator('avatarDataUrl')
@classmethod
def _check_avatar_url(cls, value):
return normalize_profile_media(value)
class UserLogin(BaseModel):
account: str
password: str
remember: Optional[bool] = True # 保持登录选项:True=30天, False=24小时
@field_validator('password')
@classmethod
def _check_password(cls, v):
return _validate_password_length(v)
class UserUpdate(BaseModel):
name: Optional[str] = None
intro: Optional[str] = None
age: Optional[int] = None
birthday: Optional[str] = None # 🚀 出生日期
country: Optional[str] = None
region: Optional[str] = None
gender: Optional[str] = None
avatarDataUrl: Optional[str] = None
bannerUrl: Optional[str] = None # 🚀 个人资料卡背景图云端URL
@field_validator('avatarDataUrl', 'bannerUrl')
@classmethod
def _check_media_url(cls, value):
return normalize_profile_media(value)
class SettingsPrivacy(BaseModel):
model_config = {"extra": "forbid"}
follows: bool = Field(strict=True)
likes: bool = Field(strict=True)
favorites: bool = Field(strict=True)
downloads: bool = Field(strict=True)
class UserSettings(UserUpdate):
model_config = {"extra": "forbid"}
privacy: SettingsPrivacy
# 🧹 P2清理:移除零引用的 PasswordReset 模型(重置密码实际走 UserUpdate/专用端点)
class CommentCreate(BaseModel):
item_id: str
author: str
content: str
reply_to_user: Optional[str] = None
parent_id: Optional[str] = None
class ItemCreate(BaseModel):
request_id: Optional[str] = Field(default=None, strict=True, pattern=r'^[0-9a-f]{32}$')
type: str
title: str
shortDesc: str
fullDesc: str
link: str
coverUrl: Optional[str] = None
imageUrls: Optional[List[str]] = []
author: str
price: int = Field(default=0, ge=0, strict=True)
github_token: Optional[str] = None
netdisk_password: Optional[str] = None # ☁️ 网盘提取码(加密存储,购买后解密)
is_netdisk: Optional[bool] = False # ☁️ 是否为网盘资源
is_original: Optional[bool] = False # 🎨 是否为原创作品
allow_refund: Optional[bool] = True # 💸 是否支持退款(默认支持)
class ItemUpdate(BaseModel):
expected_revision: Optional[int] = Field(default=None, ge=0, le=9007199254740991, strict=True)
title: Optional[str] = None
shortDesc: Optional[str] = None
fullDesc: Optional[str] = None
link: Optional[str] = None
coverUrl: Optional[str] = None
imageUrls: Optional[List[str]] = None
price: Optional[int] = Field(default=None, ge=0, strict=True)
github_token: Optional[str] = None
netdisk_password: Optional[str] = None # ☁️ 网盘提取码
is_netdisk: Optional[bool] = None # ☁️ 是否为网盘资源
is_original: Optional[bool] = None # 🎨 是否为原创作品
allow_refund: Optional[bool] = None # 💸 是否支持退款
class FollowToggle(BaseModel):
user_id: str
target_account: str
is_active: bool
class PrivateMessage(BaseModel):
sender: str
receiver: str
content: str
class IdempotentPrivateMessage(PrivateMessage):
client_request_id: str = Field(..., strict=True, min_length=32, max_length=32, pattern=r"^[0-9a-f]{32}$")
class PrivacySettings(BaseModel):
follows: bool
likes: bool
favorites: bool
downloads: bool
class InteractionToggle(BaseModel):
item_id: str
user_id: str
action_type: str
is_active: bool
class RatingRequest(BaseModel):
score: int # 1-5
@field_validator("score")
@classmethod
def validate_score(cls, v):
if not isinstance(v, int):
raise ValueError("score must be an integer between 1 and 5")
if v < 1 or v > 5:
raise ValueError("score must be between 1 and 5")
return v
class RechargeRequest(BaseModel):
account: str
amount: int = Field(..., ge=1, le=10000, strict=True) # 单次充值1-10000
method: Optional[str] = "alipay"
class PurchaseRequest(BaseModel):
account: str
item_id: str
expected_price: Optional[int] = Field(default=None, ge=0, strict=True)
class PromptPurchaseRequest(BaseModel):
expected_price: Optional[int] = Field(default=None, ge=0, strict=True)
class TipRequest(BaseModel):
sender_account: str
target_account: str
amount: int
is_anonymous: bool
item_id: Optional[str] = None # 🚀 新增:记录是为哪个作品打赏的
class WithdrawRequest(BaseModel):
account: str
amount: int = Field(..., ge=1, le=5000, strict=True) # 单次提现1-5000
alipayAccount: str = Field(..., min_length=1, max_length=256)
real_name: str = Field(..., min_length=1, max_length=128)
code: str = Field(..., pattern=r"^[0-9]{6}$")
# ==========================================
# 📝 任务榜数据模型
# ==========================================
class TaskCreate(BaseModel):
""" 发布新任务 """
title: str # 任务标题
description: str # 任务详情
referenceImages: Optional[List[str]] = [] # 参考图(最多6张)
referenceLink: Optional[str] = None # 参考链接
totalPrice: int = Field(..., ge=0, le=2147483647, strict=True) # SQL整数范围
depositRatio: int = Field(..., strict=True) # 订金比例(10/20/30/50)
deadline: str # 截止时间 (ISO格式)
publisher: str # 发布者账号
class TaskUpdate(BaseModel):
""" 更新任务 """
title: Optional[str] = None
description: Optional[str] = None
referenceImages: Optional[List[str]] = None
referenceLink: Optional[str] = None
totalPrice: Optional[int] = Field(None, ge=0, le=2147483647, strict=True)
depositRatio: Optional[int] = Field(None, strict=True)
deadline: Optional[str] = None
# 🔧 P2修复:以下三个 body 模型修复前后端参数通道不匹配
# (前端以 JSON body 提交,旧版端点用裸标量参数会被 FastAPI 解析为 query 导致 422)
class TaskSubmit(BaseModel):
""" 接单者提交成果 """
deliverables: List[str] # 交付成果列表
note: Optional[str] = None # 提交说明
class TaskDispute(BaseModel):
""" 发起申诉 """
reason: str # 申诉理由
evidence: Optional[List[str]] = None # 证据图片URL列表(最多6张)
class TaskDisputeResponse(BaseModel):
""" 被申诉方回应申诉 """
response: str # 回应内容
evidence: Optional[List[str]] = None # 证据图片URL列表(最多6张)
# 🧹 P2清理:移除零引用的 TaskApply/TaskAssign/TaskAccept 模型(对应端点使用 query 参数且前端匹配)
# ==========================================
# 💬 讨论区数据模型(小红书风格图文社区)
# ==========================================
class PostCreate(BaseModel):
""" 创建帖子 """
title: str # 标题
content: str # 正文/文案
cover_image: str # 封面图(第一张)
images: Optional[List[str]] = [] # 图片列表(最多9张)
author: str # 作者账号
is_original: Optional[bool] = False # 🎨 是否为原创作品
post_type: str = "image" # "image" 或 "video"
video_url: Optional[str] = None # 视频 URL(仅 video 类型)
class PostUpdate(BaseModel):
""" 更新帖子 """
title: Optional[str] = None
content: Optional[str] = None
cover_image: Optional[str] = None
images: Optional[List[str]] = None
is_original: Optional[bool] = None # 🎨 是否为原创作品
post_type: Optional[str] = None # "image" 或 "video"
video_url: Optional[str] = None # 视频 URL(仅 video 类型)
# ==========================================
# ✨ 提示词市场数据模型(图像/视频提示词)
# ==========================================
class PromptCreate(BaseModel):
""" 发布提示词 """
prompt_type: str # "image"、"video" 或 "music"(所属子界面)
category: str # 所属分类(人物/动作等)
title: str # 标题
content: str # 公开介绍文案
prompt_text: str # 提示词正文(付费内容,服务端脱敏下发)
cover_image: str # 封面图
media_type: str = "image" # "image" 或 "video"(示例媒体类型)
video_url: Optional[str] = None # 示例视频 URL(仅 media_type=video)
audio_url: Optional[str] = None # 示例音频 URL(仅音乐提示词,图片+音频方案;视频可选)
images: Optional[List[str]] = None # 图像提示词图片列表(最多9张,首张为封面)
is_original: bool = False # 原创作品标记
price: int = Field(default=0, ge=0, le=10000, strict=True) # 提示词定价(0=免费)
tags: Optional[List[str]] = None # 自定义标签(最多10个,每个≤12字)
class PromptUpdate(BaseModel):
""" 更新提示词 """
category: Optional[str] = None
title: Optional[str] = None
content: Optional[str] = None
prompt_text: Optional[str] = None
cover_image: Optional[str] = None
media_type: Optional[str] = None
video_url: Optional[str] = None
audio_url: Optional[str] = None # 示例音频 URL(仅音乐提示词;传 null 表示清除)
images: Optional[List[str]] = None # 图像提示词图片列表(最多9张,首张为封面)
is_original: Optional[bool] = None # 原创作品标记
price: Optional[int] = Field(default=None, ge=0, le=10000, strict=True)
tags: Optional[List[str]] = None
class PromptCategoryAdd(BaseModel):
""" 添加提示词分类 """
prompt_type: str # "image"、"video" 或 "music"
name: str # 分类名称(≤8字,去重)