refactor: replace issues backend with milestone tasks

This commit is contained in:
zhi
2026-03-16 13:22:14 +00:00
parent dc5d06489d
commit 214a9b109d
20 changed files with 836 additions and 1066 deletions

View File

@@ -7,8 +7,8 @@ class ActivityLog(Base):
__tablename__ = "activity_logs"
id = Column(Integer, primary_key=True, index=True)
action = Column(String(50), nullable=False) # e.g. "issue.created", "comment.added"
entity_type = Column(String(50), nullable=False) # "issue", "project", "comment"
action = Column(String(50), nullable=False) # e.g. "task.created", "comment.added"
entity_type = Column(String(50), nullable=False) # "task", "project", "comment"
entity_id = Column(Integer, nullable=False)
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
details = Column(Text, nullable=True) # JSON string

View File

@@ -6,96 +6,42 @@ from app.models.role_permission import Role
import enum
class IssueType(str, enum.Enum):
MEETING = "meeting"
SUPPORT = "support"
class TaskType(str, enum.Enum):
"""Task type enum — 'issue' is a subtype of task, not the other way around."""
ISSUE = "issue"
MAINTENANCE = "maintenance"
RESEARCH = "research"
REVIEW = "review"
STORY = "story"
TEST = "test"
RESOLUTION = "resolution" # 决议案 - 用于 Agent 僵局提交
TASK = "task" # legacy generic type
RESOLUTION = "resolution"
TASK = "task"
class IssueStatus(str, enum.Enum):
class TaskStatus(str, enum.Enum):
OPEN = "open"
IN_PROGRESS = "in_progress"
RESOLVED = "resolved"
PENDING = "pending"
PROGRESSING = "progressing"
CLOSED = "closed"
BLOCKED = "blocked"
class IssuePriority(str, enum.Enum):
class TaskPriority(str, enum.Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class Issue(Base):
__tablename__ = "issues"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False)
description = Column(Text, nullable=True)
issue_type = Column(String(32), default=IssueType.ISSUE.value)
issue_subtype = Column(String(64), nullable=True)
status = Column(Enum(IssueStatus), default=IssueStatus.OPEN)
priority = Column(Enum(IssuePriority), default=IssuePriority.MEDIUM)
# Relationships
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
reporter_id = Column(Integer, ForeignKey("users.id"), nullable=False)
assignee_id = Column(Integer, ForeignKey("users.id"), nullable=True)
# Resolution specific fields (for RESOLUTION type)
resolution_summary = Column(Text, nullable=True) # 僵局摘要
positions = Column(Text, nullable=True) # 各方立场 (JSON)
pending_matters = Column(Text, nullable=True) # 待决事项
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Tags (comma-separated for simplicity)
tags = Column(String(500), nullable=True)
# Dependencies
depends_on_id = Column(Integer, ForeignKey("issues.id"), nullable=True)
# Due date and milestone
due_date = Column(DateTime(timezone=True), nullable=True)
milestone_id = Column(Integer, ForeignKey("milestones.id"), nullable=True)
# Task-specific fields
task_code = Column(String(64), nullable=True, unique=True, index=True)
depend_on = Column(Text, nullable=True) # JSON list of task codes
estimated_effort = Column(Integer, nullable=True) # 1-10
estimated_working_time = Column(Time(timezone=True), nullable=True)
task_status = Column(String(32), default="open") # open, closed, pending, progressing
started_on = Column(DateTime(timezone=True), nullable=True)
finished_on = Column(DateTime(timezone=True), nullable=True)
related_tasks = Column(Text, nullable=True) # JSON list of task codes
created_by_id = Column(Integer, ForeignKey("users.id"), nullable=True)
project = relationship("Project", back_populates="issues")
reporter = relationship("User", foreign_keys=[reporter_id], back_populates="reported_issues")
assignee = relationship("User", foreign_keys=[assignee_id], back_populates="assigned_issues")
comments = relationship("Comment", back_populates="issue", cascade="all, delete-orphan")
class Comment(Base):
__tablename__ = "comments"
id = Column(Integer, primary_key=True, index=True)
content = Column(Text, nullable=False)
issue_id = Column(Integer, ForeignKey("issues.id"), nullable=False)
task_id = Column(Integer, ForeignKey("tasks.id"), nullable=False)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
issue = relationship("Issue", back_populates="comments")
author = relationship("User", back_populates="comments")
@@ -114,7 +60,6 @@ class Project(Base):
owner_id = Column(Integer, ForeignKey("users.id"), nullable=False)
issues = relationship("Issue", back_populates="project", cascade="all, delete-orphan")
members = relationship("ProjectMember", back_populates="project", cascade="all, delete-orphan")
owner = relationship("User", back_populates="owned_projects")
@@ -125,15 +70,13 @@ class User(Base):
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, nullable=False)
email = Column(String(100), unique=True, nullable=False)
hashed_password = Column(String(255), nullable=True) # Nullable for OAuth users
hashed_password = Column(String(255), nullable=True)
full_name = Column(String(100), nullable=True)
is_active = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
owned_projects = relationship("Project", back_populates="owner")
reported_issues = relationship("Issue", foreign_keys=[Issue.reporter_id], back_populates="reporter")
assigned_issues = relationship("Issue", foreign_keys=[Issue.assignee_id], back_populates="assignee")
comments = relationship("Comment", back_populates="author")
project_memberships = relationship("ProjectMember", back_populates="user")

View File

@@ -9,10 +9,10 @@ class Notification(Base):
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
type = Column(String(50), nullable=False) # issue.assigned, issue.mentioned, comment.added, milestone.due
type = Column(String(50), nullable=False) # task.assigned, task.mentioned, comment.added, milestone.due
title = Column(String(255), nullable=False)
message = Column(Text, nullable=True)
entity_type = Column(String(50), nullable=True) # issue, comment, milestone
entity_type = Column(String(50), nullable=True) # task, comment, milestone
entity_id = Column(Integer, nullable=True)
is_read = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -26,18 +26,35 @@ class Task(Base):
priority = Column(Enum(TaskPriority), default=TaskPriority.MEDIUM)
task_code = Column(String(64), nullable=True, unique=True, index=True)
# Task type/subtype (replaces old issue_type/issue_subtype)
task_type = Column(String(32), default="task")
task_subtype = Column(String(64), nullable=True)
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
milestone_id = Column(Integer, ForeignKey("milestones.id"), nullable=False)
reporter_id = Column(Integer, ForeignKey("users.id"), nullable=False)
assignee_id = Column(Integer, ForeignKey("users.id"), nullable=True)
created_by_id = Column(Integer, ForeignKey("users.id"), nullable=True)
# Tags (comma-separated)
tags = Column(String(500), nullable=True)
# Dependencies
depend_on = Column(Text, nullable=True)
related_tasks = Column(Text, nullable=True)
# Effort tracking
estimated_effort = Column(Integer, nullable=True)
estimated_working_time = Column(Time, nullable=True)
started_on = Column(DateTime(timezone=True), nullable=True)
finished_on = Column(DateTime(timezone=True), nullable=True)
related_tasks = Column(Text, nullable=True)
# Resolution specific fields (for task_type="resolution")
resolution_summary = Column(Text, nullable=True)
positions = Column(Text, nullable=True)
pending_matters = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
comments = relationship("Comment", foreign_keys="Comment.task_id", cascade="all, delete-orphan")

View File

@@ -5,10 +5,10 @@ import enum
class WebhookEvent(str, enum.Enum):
ISSUE_CREATED = "issue.created"
ISSUE_UPDATED = "issue.updated"
ISSUE_CLOSED = "issue.closed"
ISSUE_DELETED = "issue.deleted"
TASK_CREATED = "task.created"
TASK_UPDATED = "task.updated"
TASK_CLOSED = "task.closed"
TASK_DELETED = "task.deleted"
COMMENT_CREATED = "comment.created"
RESOLUTION_CREATED = "resolution.created"
MEMBER_ADDED = "member.added"

View File

@@ -7,9 +7,9 @@ class WorkLog(Base):
__tablename__ = "work_logs"
id = Column(Integer, primary_key=True, index=True)
issue_id = Column(Integer, ForeignKey("issues.id"), nullable=False)
task_id = Column(Integer, ForeignKey("tasks.id"), nullable=False)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
hours = Column(Float, nullable=False) # Hours spent
hours = Column(Float, nullable=False)
description = Column(Text, nullable=True)
logged_date = Column(DateTime(timezone=True), nullable=False) # When the work was done
logged_date = Column(DateTime(timezone=True), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())