Files
PROJECT-CONTORL/backend/app/models/project.py
beabigegg daca7798e3 feat: implement task management module
Backend (FastAPI):
- Database migration for spaces, projects, task_statuses, tasks tables
- SQLAlchemy models with relationships
- Pydantic schemas for CRUD operations
- Spaces API: CRUD with soft delete
- Projects API: CRUD with auto-created default statuses
- Tasks API: CRUD, status change, assign, subtask support
- Permission middleware with Security Level filtering
- Subtask depth limit (max 2 levels)

Frontend (React + Vite):
- Layout component with navigation
- Spaces list page
- Projects list page
- Tasks list page with status management

Fixes:
- auth_client.py: use 'username' field for external API
- config.py: extend JWT expiry to 7 days
- auth/router.py: sync Redis session with JWT expiry

Tests: 36 passed (unit + integration)
E2E: All APIs verified with real authentication

OpenSpec: add-task-management archived

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 00:31:34 +08:00

41 lines
1.8 KiB
Python

from sqlalchemy import Column, String, Text, Boolean, DateTime, Date, Numeric, Enum, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
import enum
class SecurityLevel(str, enum.Enum):
PUBLIC = "public"
DEPARTMENT = "department"
CONFIDENTIAL = "confidential"
class Project(Base):
__tablename__ = "pjctrl_projects"
id = Column(String(36), primary_key=True)
space_id = Column(String(36), ForeignKey("pjctrl_spaces.id", ondelete="CASCADE"), nullable=False)
title = Column(String(200), nullable=False)
description = Column(Text, nullable=True)
owner_id = Column(String(36), ForeignKey("pjctrl_users.id"), nullable=False)
budget = Column(Numeric(15, 2), nullable=True)
start_date = Column(Date, nullable=True)
end_date = Column(Date, nullable=True)
security_level = Column(
Enum("public", "department", "confidential", name="security_level_enum"),
default="department",
nullable=False
)
status = Column(String(50), default="active", nullable=False)
department_id = Column(String(36), ForeignKey("pjctrl_departments.id"), nullable=True)
created_at = Column(DateTime, server_default=func.now(), nullable=False)
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False)
# Relationships
space = relationship("Space", back_populates="projects")
owner = relationship("User", foreign_keys=[owner_id], back_populates="owned_projects")
department = relationship("Department", back_populates="projects")
task_statuses = relationship("TaskStatus", back_populates="project", cascade="all, delete-orphan")
tasks = relationship("Task", back_populates="project", cascade="all, delete-orphan")