feat(backend): enhance task and schedule center APIs with expanded endpoints

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-11 08:47:39 +08:00
parent 8c7cf0732b
commit 3e39b40a50
6 changed files with 1015 additions and 107 deletions

View File

@@ -1,7 +1,8 @@
from sqlalchemy import Column, String, Text, Integer, ForeignKey, DateTime, Enum
from sqlalchemy.orm import relationship
from datetime import datetime
from enum import Enum as PyEnum
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
@@ -19,26 +20,144 @@ class TaskPriority(str, PyEnum):
URGENT = "urgent"
class TaskSource(str, PyEnum):
MANUAL = "manual"
CHAT = "chat"
SCHEDULE_CENTER = "schedule_center"
TODAY_STATUS = "today_status"
COMMANDER = "commander"
class TaskQuadrant(str, PyEnum):
URGENT_IMPORTANT = "urgent-important"
NOT_URGENT_IMPORTANT = "not-urgent-important"
URGENT_NOT_IMPORTANT = "urgent-not-important"
NOT_URGENT_NOT_IMPORTANT = "not-urgent-not-important"
class TaskAssigneeType(str, PyEnum):
USER = "user"
COMMANDER = "commander"
AGENT = "agent"
PLANNER = "planner"
EXECUTOR = "executor"
KNOWLEDGE = "knowledge"
ANALYST = "analyst"
CODER = "coder"
RESEARCHER = "researcher"
class TaskDispatchStatus(str, PyEnum):
IDLE = "idle"
QUEUED = "queued"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
DispatchStatus = TaskDispatchStatus
DispatchStatus = TaskDispatchStatus
class TaskHistoryAction(str, PyEnum):
CREATED = "created"
CREATED_FROM_CHAT = "created_from_chat"
UPDATED = "updated"
STATUS_CHANGED = "status_changed"
ASSIGNED = "assigned"
DELETED = "deleted"
SUBTASK_CREATED = "subtask_created"
SUBTASK_UPDATED = "subtask_updated"
SUBTASK_DELETED = "subtask_deleted"
SUBTASK_REORDERED = "subtask_reordered"
DISPATCHED_TO_COMMANDER = "dispatched_to_commander"
DISPATCH_STATUS_CHANGED = "dispatch_status_changed"
def enum_values(enum_cls: type[PyEnum]) -> list[str]:
return [item.value for item in enum_cls]
TASK_STATUS_ENUM = Enum(TaskStatus, values_callable=enum_values)
TASK_PRIORITY_ENUM = Enum(TaskPriority, values_callable=enum_values)
TASK_SOURCE_ENUM = Enum(TaskSource, values_callable=enum_values)
TASK_QUADRANT_ENUM = Enum(TaskQuadrant, values_callable=enum_values)
TASK_ASSIGNEE_TYPE_ENUM = Enum(TaskAssigneeType, values_callable=enum_values)
TASK_DISPATCH_STATUS_ENUM = Enum(TaskDispatchStatus, values_callable=enum_values)
class Task(BaseModel):
__tablename__ = "tasks"
user_id = Column(String(36), ForeignKey("users.id"), nullable=False, index=True)
title = Column(String(500), nullable=False)
description = Column(Text, nullable=True)
status = Column(Enum(TaskStatus), default=TaskStatus.TODO, nullable=False, index=True)
priority = Column(Enum(TaskPriority), default=TaskPriority.MEDIUM, nullable=False)
due_date = Column(DateTime, nullable=True)
status = Column(TASK_STATUS_ENUM, default=TaskStatus.TODO, nullable=False, index=True)
priority = Column(TASK_PRIORITY_ENUM, default=TaskPriority.MEDIUM, nullable=False)
due_date = Column(DateTime, nullable=True, index=True)
completed_at = Column(DateTime, nullable=True)
tags = Column(String(1000), nullable=True) # JSON 数组
tags = Column(String(1000), nullable=True) # JSON array
source = Column(TASK_SOURCE_ENUM, default=TaskSource.MANUAL, nullable=False, index=True)
conversation_id = Column(String(36), nullable=True, index=True)
quadrant = Column(TASK_QUADRANT_ENUM, nullable=True, index=True)
assignee_type = Column(TASK_ASSIGNEE_TYPE_ENUM, nullable=True, index=True)
assignee_id = Column(String(255), nullable=True, index=True)
dispatch_status = Column(
TASK_DISPATCH_STATUS_ENUM,
default=TaskDispatchStatus.IDLE,
nullable=False,
index=True,
)
dispatch_run_id = Column(String(64), nullable=True, index=True)
result_summary = Column(Text, nullable=True)
started_at = Column(DateTime, nullable=True)
last_synced_at = Column(DateTime, nullable=True)
history = relationship("TaskHistory", back_populates="task", cascade="all, delete-orphan")
subtasks = relationship(
"TaskSubTask",
back_populates="task",
cascade="all, delete-orphan",
order_by="TaskSubTask.order_index.asc()",
)
history = relationship(
"TaskHistory",
back_populates="task",
cascade="all, delete-orphan",
order_by="TaskHistory.created_at.desc()",
)
class TaskSubTask(BaseModel):
__tablename__ = "task_subtasks"
task_id = Column(String(36), ForeignKey("tasks.id"), nullable=False, index=True)
title = Column(String(500), nullable=False)
description = Column(Text, nullable=True)
status = Column(TASK_STATUS_ENUM, default=TaskStatus.TODO, nullable=False, index=True)
order_index = Column(Integer, default=0, nullable=False, index=True)
assignee_type = Column(TASK_ASSIGNEE_TYPE_ENUM, nullable=True, index=True)
assignee_id = Column(String(255), nullable=True, index=True)
dispatch_status = Column(
TASK_DISPATCH_STATUS_ENUM,
default=TaskDispatchStatus.IDLE,
nullable=False,
index=True,
)
dispatch_run_id = Column(String(64), nullable=True, index=True)
result_summary = Column(Text, nullable=True)
completed_at = Column(DateTime, nullable=True)
task = relationship("Task", back_populates="subtasks")
class TaskHistory(BaseModel):
__tablename__ = "task_histories"
task_id = Column(String(36), ForeignKey("tasks.id"), nullable=False, index=True)
action = Column(String(100), nullable=False) # created, status_changed, updated, deleted
subtask_id = Column(String(36), ForeignKey("task_subtasks.id"), nullable=True, index=True)
action = Column(String(100), nullable=False)
old_value = Column(Text, nullable=True)
new_value = Column(Text, nullable=True)