Files
HarborForge.Backend/app/models/models.py

146 lines
5.7 KiB
Python

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean, JSON
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.config import Base
import enum
class IssueType(str, enum.Enum):
MEETING = "meeting"
SUPPORT = "support"
ISSUE = "issue"
MAINTENANCE = "maintenance"
RESEARCH = "research"
REVIEW = "review"
STORY = "story"
TEST = "test"
RESOLUTION = "resolution" # 决议案 - 用于 Agent 僵局提交
TASK = "task" # legacy generic type
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(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)
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)
project_code = Column(String(16), unique=True, index=True, nullable=True)
owner_name = Column(String(128), nullable=False)
sub_projects = Column(String(512), nullable=True)
related_projects = Column(String(512), nullable=True)
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")
class ProjectCodeCounter(Base):
__tablename__ = "project_code_counters"
id = Column(Integer, primary_key=True, index=True)
prefix = Column(String(16), unique=True, index=True, nullable=False)
next_value = Column(Integer, default=0)