feat: 新增日程面板、复盘页面与 Agent 读写接口

- 日程:单次/每日/每周/每月重复,JSON 存储,月历按优先级着色圆点 + 悬停提示
- 复盘页:可自定义折线图卡片(SVG) + 每日心得总览(按日期/任务展开)
- Agent 接口:GET /api/agent/overview 读日程+进度,POST /api/agent/schedules 建日程(PPM_API_TOKEN Bearer 鉴权)
- 导出/导入/统计纳入日程与图表;47 项测试全绿
This commit is contained in:
MobKBK
2026-09-04 14:46:41 +08:00
parent 5f50eb54ed
commit 8340d1dbbd
13 changed files with 1027 additions and 19 deletions

View File

@@ -121,3 +121,59 @@ class PurgeInput(BaseModel):
model_config = ConfigDict(extra="forbid")
confirmation: str
class ScheduleRecurrence(str, Enum):
none = "none"
daily = "daily"
weekly = "weekly"
monthly = "monthly"
class ScheduleInput(BaseModel):
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
title: Annotated[str, Field(min_length=1, max_length=120)]
description: Annotated[str, Field(max_length=2000)] = ""
date: date
time: Annotated[str | None, Field(max_length=5)] = None
recurrence: ScheduleRecurrence = ScheduleRecurrence.none
priority: Annotated[int, Field(ge=1, le=5)] = 3
state: Literal["active", "archived"] = "active"
@field_validator("time")
@classmethod
def validate_time(cls, value: str | None) -> str | None:
if value is None or value == "":
return None
if len(value) != 5 or value[2] != ":" or not value[:2].isdigit() or not value[3:].isdigit():
raise ValueError("时间必须是 HH:MM 格式")
hour, minute = int(value[:2]), int(value[3:])
if hour > 23 or minute > 59:
raise ValueError("时间必须是合法的时分")
return value
class ScheduleUpdate(ScheduleInput):
expectedVersion: Annotated[int, Field(ge=1)]
class ScheduleCompleteInput(BaseModel):
model_config = ConfigDict(extra="forbid")
date: date
completed: bool = True
expectedVersion: Annotated[int, Field(ge=1)]
class ChartInput(BaseModel):
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
title: Annotated[str, Field(min_length=1, max_length=80)]
taskId: str | None = None
metricId: str | None = None
days: Annotated[int, Field(ge=7, le=365)] = 30
class ChartUpdate(ChartInput):
expectedVersion: Annotated[int, Field(ge=1)]