Files
ToDoList/api/app/schemas/anniversary.py
祀梦 2979197b1c release: Elysia ToDo v1.0.0
鍏ㄦ爤涓汉淇℃伅绠$悊搴旂敤锛岄泦鎴愬緟鍔炰换鍔°€佷範鎯墦鍗°€佺邯蹇垫棩鎻愰啋銆佽祫浜ф€昏鍔熻兘銆

Made-with: Cursor
2026-03-14 22:21:26 +08:00

123 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from pydantic import BaseModel, Field, field_validator
from datetime import date, datetime, timezone
from typing import Optional, List
def parse_date(value):
"""解析日期字符串"""
if value is None or value == '':
return None
if isinstance(value, date):
return value
if isinstance(value, datetime):
return value.date()
formats = [
'%Y-%m-%d',
'%Y-%m-%dT%H:%M:%S',
'%Y-%m-%d %H:%M:%S',
'%Y-%m-%dT%H:%M:%S.%f',
]
for fmt in formats:
try:
return datetime.strptime(value, fmt).date()
except ValueError:
continue
try:
return date.fromisoformat(value)
except ValueError:
raise ValueError(f"无法解析日期: {value}")
# ============ 纪念日分类 Schema ============
class AnniversaryCategoryBase(BaseModel):
"""纪念日分类基础模型"""
name: str = Field(..., max_length=50)
icon: str = Field(default="calendar", max_length=50)
color: str = Field(default="#FFB7C5", max_length=20)
sort_order: int = Field(default=0)
class AnniversaryCategoryCreate(AnniversaryCategoryBase):
"""创建纪念日分类请求模型"""
pass
class AnniversaryCategoryUpdate(BaseModel):
"""更新纪念日分类请求模型"""
name: Optional[str] = Field(None, max_length=50)
icon: Optional[str] = Field(None, max_length=50)
color: Optional[str] = Field(None, max_length=20)
sort_order: Optional[int] = None
class AnniversaryCategoryResponse(AnniversaryCategoryBase):
"""纪念日分类响应模型"""
id: int
class Config:
from_attributes = True
# ============ 纪念日 Schema ============
class AnniversaryBase(BaseModel):
"""纪念日基础模型"""
title: str = Field(..., max_length=200)
date: date
year: Optional[int] = None
category_id: Optional[int] = None
description: Optional[str] = None
is_recurring: bool = Field(default=True)
remind_days_before: int = Field(default=3)
@field_validator('date', mode='before')
@classmethod
def parse_anniversary_date(cls, v):
result = parse_date(v)
if result is None:
raise ValueError("纪念日日期不能为空")
return result
class AnniversaryCreate(AnniversaryBase):
"""创建纪念日请求模型"""
pass
class AnniversaryUpdate(BaseModel):
"""更新纪念日请求模型"""
title: Optional[str] = Field(None, max_length=200)
date: Optional[date] = None
year: Optional[int] = None
category_id: Optional[int] = None
description: Optional[str] = None
is_recurring: Optional[bool] = None
remind_days_before: Optional[int] = None
@field_validator('date', mode='before')
@classmethod
def parse_anniversary_date(cls, v):
if v is None:
return None
return parse_date(v)
@property
def clearable_fields(self) -> set:
"""允许被显式清空(设为 None的字段集合"""
return {'description', 'category_id', 'year'}
class AnniversaryResponse(AnniversaryBase):
"""纪念日响应模型"""
id: int
created_at: datetime
updated_at: datetime
category: Optional[AnniversaryCategoryResponse] = None
next_date: Optional[date] = None
days_until: Optional[int] = None
year_count: Optional[int] = None
class Config:
from_attributes = True