# Design: Schedule Items API **Date:** 2026-02-27 **Status:** Approved ## Overview 实现日历事项(Schedule Items)的后端 CRUD API,支持用户创建、查询、更新、删除日历事项。 ## Scope - 仅后端 API,不涉及前端 - 全量 CRUD - 查询按时间范围筛选 - 暂不支持重复日程(recurrence_rule 留空) ## API Endpoints ### 1. 创建日历事项 ``` POST /api/v1/schedule-items ``` **Request:** ```json { "title": "string (1-255 chars, required)", "description": "string? (max 2000 chars)", "start_at": "string (ISO 8601 datetime, required)", "end_at": "string? (ISO 8601 datetime, must be after start_at)", "timezone": "string? (default: UTC)", "metadata": { "color": "#FF6B6B", "location": "会议室A", "notes": "记得带身份证", "attachments": [], "version": 1 } } ``` **Response:** 201 Created ```json { "id": "uuid", "title": "string", "description": "string?", "start_at": "string", "end_at": "string?", "timezone": "string", "metadata": {...}, "status": "active", "source_type": "manual", "created_at": "string", "updated_at": "string" } ``` ### 2. 查询日历事项列表 ``` GET /api/v1/schedule-items?start_at=2026-02-01&end_at=2026-02-28 ``` **Query Parameters:** - `start_at`: ISO 8601 date/datetime(查询范围起始) - `end_at`: ISO 8601 date/datetime(查询范围结束) **Response:** 200 OK ```json [ { "id": "uuid", "title": "string", "start_at": "string", "end_at": "string?", "timezone": "string", "status": "active" } ] ``` ### 3. 获取单个事项 ``` GET /api/v1/schedule-items/{id} ``` **Response:** 200 OK(完整字段,同创建响应) ### 4. 更新事项 ``` PATCH /api/v1/schedule-items/{id} ``` **Request:** 支持 `title`/`description`/`start_at`/`end_at`/`timezone`/`metadata`/`status` 部分更新 **Response:** 200 OK ### 5. 删除事项 ``` DELETE /api/v1/schedule-items/{id} ``` **Response:** 204 No Content(软删除) ## Data Models ### Metadata 结构(Pydantic) ```python from enum import Enum from pydantic import BaseModel from uuid import UUID class AttachmentType(str, Enum): DOCUMENT = "document" REMINDER = "reminder" class ScheduleItemMetadataAttachment(BaseModel): name: str type: AttachmentType visible_to: list[UUID] = [] # document 类型 url: str | None = None note: str | None = None # reminder 类型 content: str | None = None class ScheduleItemMetadata(BaseModel): color: str | None = None location: str | None = None notes: str | None = None attachments: list[ScheduleItemMetadataAttachment] = [] version: int = 1 ``` ### 数据库模型(已有) 参见 `backend/src/models/schedule_items.py`: - `id`: UUID - `owner_id`: UUID - `title`: String(255) - `description`: Text - `start_at`: DateTime(timezone=True) - `end_at`: DateTime(timezone=True) - `timezone`: String(50) - `extra_metadata`: JSONB (mapped as "metadata") - `recurrence_rule`: String(255) - `source_type`: Enum (MANUAL/IMPORTED/AGENT_GENERATED) - `status`: Enum (ACTIVE/COMPLETED/CANCELED/ARCHIVED) - `created_by`: UUID ## Architecture 遵循项目 `schemas / repository / service / router` 分层模式: ``` backend/src/v1/schedule_items/ ├── __init__.py ├── schemas.py # Pydantic 请求/响应模型 ├── repository.py # CRUD 操作(无 auth,无 commit) ├── service.py # 业务逻辑 + 授权 + 事务边界 ├── router.py # FastAPI 路由定义 └── dependencies.py # DI(如有) ``` ## Security - 所有端点需要认证(JWT) - `owner_id` 从 JWT `sub` 提取,不从请求体读取 - 用户只能操作自己的日历事项(`owner_id` 过滤) - RLS 已在数据库层启用(防御边界) ## Error Handling 使用 RFC 7807 `application/problem+json` 格式: - 400: 请求参数无效 - 401: 未认证 - 404: 事项不存在或无权限访问 - 422: 验证失败 ## Out of Scope - 重复日程(recurrence_rule) - 日程订阅与协作(schedule_subscriptions) - 待办事项联动(todos/todo_sources) - 前端实现