mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 14:06:28 +00:00
d4d6f87730
BREAKING CHANGE: root backend/frontend directories and old run/manage entrypoints were removed. Use apps/backend, apps/frontend, and python main.py commands instead.
149 lines
5.1 KiB
Python
149 lines
5.1 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class TaskBase(BaseModel):
|
|
"""打卡任务基础 Schema"""
|
|
payload_config: str = Field(..., description="完整的 payload 配置 JSON(包含 ThreadId 和所有字段)")
|
|
name: Optional[str] = Field("", max_length=100, description="任务名称(用户自定义)")
|
|
is_active: Optional[bool] = Field(True, description="是否启用自动打卡")
|
|
|
|
@field_validator('payload_config')
|
|
@classmethod
|
|
def validate_payload_config(cls, v: str) -> str:
|
|
"""
|
|
验证 payload_config 是否为有效的 JSON,并且包含必需的 ThreadId 字段
|
|
"""
|
|
from backend.utils.json_helpers import safe_parse_json, extract_thread_id
|
|
|
|
if not v or not v.strip():
|
|
raise ValueError("payload_config 不能为空")
|
|
|
|
payload = safe_parse_json(v)
|
|
if payload is None:
|
|
raise ValueError("payload_config 必须是有效的 JSON 格式")
|
|
|
|
# 检查是否为字典类型
|
|
if not isinstance(payload, dict):
|
|
raise ValueError("payload_config 必须是 JSON 对象(字典)")
|
|
|
|
# 检查必需字段 ThreadId
|
|
thread_id = extract_thread_id(v)
|
|
if not thread_id or not str(thread_id).strip():
|
|
raise ValueError("payload_config 必须包含有效的 ThreadId 字段")
|
|
|
|
return v
|
|
|
|
|
|
class TaskCreate(TaskBase):
|
|
"""创建打卡任务 Schema"""
|
|
cron_expression: Optional[str] = Field(
|
|
None,
|
|
max_length=100,
|
|
description="Crontab 表达式(例如 '0 20 * * *' 表示每天 20:00)。NULL 表示禁用定时打卡"
|
|
)
|
|
|
|
@field_validator('cron_expression')
|
|
@classmethod
|
|
def validate_cron_expression(cls, v: Optional[str]) -> Optional[str]:
|
|
"""验证 Crontab 表达式格式"""
|
|
if v is None:
|
|
return v # NULL 允许(表示禁用定时打卡)
|
|
|
|
if not v.strip():
|
|
raise ValueError("cron_expression 不能为空字符串,应该使用 NULL")
|
|
|
|
try:
|
|
from croniter import croniter
|
|
if not croniter.is_valid(v):
|
|
raise ValueError(f"无效的 Crontab 表达式: '{v}'")
|
|
except Exception as e:
|
|
raise ValueError(f"Crontab 表达式验证失败: {str(e)}")
|
|
|
|
return v
|
|
|
|
|
|
class TaskUpdate(BaseModel):
|
|
"""更新打卡任务 Schema"""
|
|
payload_config: Optional[str] = None
|
|
name: Optional[str] = None
|
|
is_active: Optional[bool] = None
|
|
cron_expression: Optional[str] = Field(
|
|
None,
|
|
max_length=100,
|
|
description="Crontab 表达式。NULL 表示禁用定时打卡"
|
|
)
|
|
|
|
@field_validator('payload_config')
|
|
@classmethod
|
|
def validate_payload_config(cls, v: Optional[str]) -> Optional[str]:
|
|
"""
|
|
验证 payload_config 是否为有效的 JSON(如果提供的话)
|
|
"""
|
|
from backend.utils.json_helpers import safe_parse_json, extract_thread_id
|
|
|
|
if v is None:
|
|
return v
|
|
|
|
if not v.strip():
|
|
raise ValueError("payload_config 不能为空字符串")
|
|
|
|
payload = safe_parse_json(v)
|
|
if payload is None:
|
|
raise ValueError("payload_config 必须是有效的 JSON 格式")
|
|
|
|
# 检查是否为字典类型
|
|
if not isinstance(payload, dict):
|
|
raise ValueError("payload_config 必须是 JSON 对象(字典)")
|
|
|
|
# 检查必需字段 ThreadId
|
|
thread_id = extract_thread_id(v)
|
|
if not thread_id or not str(thread_id).strip():
|
|
raise ValueError("payload_config 必须包含有效的 ThreadId 字段")
|
|
|
|
return v
|
|
|
|
@field_validator('cron_expression')
|
|
@classmethod
|
|
def validate_cron_expression(cls, v: Optional[str]) -> Optional[str]:
|
|
"""验证 Crontab 表达式(与 TaskCreate 相同)"""
|
|
if v is None:
|
|
return v
|
|
|
|
if not v.strip():
|
|
raise ValueError("cron_expression 不能为空字符串,应该使用 NULL")
|
|
|
|
try:
|
|
from croniter import croniter
|
|
if not croniter.is_valid(v):
|
|
raise ValueError(f"无效的 Crontab 表达式: '{v}'")
|
|
except Exception as e:
|
|
raise ValueError(f"Crontab 表达式验证失败: {str(e)}")
|
|
|
|
return v
|
|
|
|
|
|
class TaskResponse(TaskBase):
|
|
"""打卡任务响应 Schema"""
|
|
id: int
|
|
user_id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
cron_expression: Optional[str] = Field(
|
|
None,
|
|
description="当前 Crontab 表达式(NULL = 禁用定时打卡)"
|
|
)
|
|
is_scheduled_enabled: Optional[bool] = Field(
|
|
None,
|
|
description="是否启用了定时打卡"
|
|
)
|
|
|
|
# 新增字段:最后一次打卡信息
|
|
last_check_in_time: Optional[datetime] = Field(None, description="最后一次打卡时间")
|
|
last_check_in_status: Optional[str] = Field(None, description="最后一次打卡状态")
|
|
thread_id: Optional[str] = Field(None, description="接龙 ID(从 payload_config 中提取)")
|
|
|
|
class Config:
|
|
from_attributes = True
|