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

160 lines
6.5 KiB
Python

from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Enum, Boolean, JSON, Time
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.config import Base
from app.models.role_permission import Role
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)
# 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)
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)
repo = 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_id = Column(Integer, ForeignKey("roles.id"), nullable=False)
role = relationship("Role")
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)