- Docker Compose with MySQL + FastAPI backend - Issue model with RESOLUTION type (for agent deadlock resolution) - Project, User, Comment models - Basic CRUD API endpoints - .env.example for configuration
123 lines
4.8 KiB
Python
123 lines
4.8 KiB
Python
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.sql import func
|
|
from app.core.config import Base
|
|
import enum
|
|
|
|
|
|
class IssueType(str, enum.Enum):
|
|
TASK = "task"
|
|
STORY = "story"
|
|
TEST = "test"
|
|
RESOLUTION = "resolution" # 决议案 - 用于 Agent 僵局提交
|
|
|
|
|
|
class IssueStatus(str, enum.Enum):
|
|
OPEN = "open"
|
|
IN_PROGRESS = "in_progress"
|
|
RESOLVED = "resolved"
|
|
CLOSED = "closed"
|
|
BLOCKED = "blocked"
|
|
|
|
|
|
class IssuePriority(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(Enum(IssueType), default=IssueType.TASK)
|
|
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)
|
|
|
|
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)
|
|
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")
|
|
|
|
|
|
class Project(Base):
|
|
__tablename__ = "projects"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
name = Column(String(100), unique=True, nullable=False)
|
|
description = Column(Text, nullable=True)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
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")
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
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
|
|
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")
|
|
|
|
|
|
class ProjectMember(Base):
|
|
__tablename__ = "project_members"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
role = Column(String(20), default="dev") # admin, dev, mgr, ops
|
|
|
|
project = relationship("Project", back_populates="members")
|
|
user = relationship("User", back_populates="project_memberships")
|