feat: initial HarborForge project structure
- 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
This commit is contained in:
122
backend/app/models/models.py
Normal file
122
backend/app/models/models.py
Normal file
@@ -0,0 +1,122 @@
|
||||
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")
|
||||
Reference in New Issue
Block a user