341 lines
11 KiB
Python
341 lines
11 KiB
Python
"""
|
|
项目管理相关 API
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select, or_
|
|
from typing import List
|
|
import uuid
|
|
|
|
from app.core.database import get_db
|
|
from app.core.deps import get_current_user
|
|
from app.models.user import User
|
|
from app.models.project import Project, ProjectMember, ProjectMemberRole
|
|
from app.schemas.project import (
|
|
ProjectCreate,
|
|
ProjectUpdate,
|
|
ProjectResponse,
|
|
ProjectMemberAdd,
|
|
ProjectMemberUpdate,
|
|
ProjectMemberResponse,
|
|
ProjectShareSettings,
|
|
ProjectShareInfo,
|
|
)
|
|
from app.schemas.response import success_response
|
|
from app.services.storage import storage_service
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=dict)
|
|
async def get_my_projects(
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""获取我的项目列表(包括创建的和协作的)"""
|
|
# 查询我创建的项目
|
|
owned_result = await db.execute(
|
|
select(Project).where(Project.owner_id == current_user.id, Project.status == 1)
|
|
)
|
|
owned_projects = owned_result.scalars().all()
|
|
|
|
# 查询我协作的项目
|
|
member_result = await db.execute(
|
|
select(Project)
|
|
.join(ProjectMember, ProjectMember.project_id == Project.id)
|
|
.where(
|
|
ProjectMember.user_id == current_user.id,
|
|
Project.owner_id != current_user.id,
|
|
Project.status == 1
|
|
)
|
|
)
|
|
member_projects = member_result.scalars().all()
|
|
|
|
# 合并结果
|
|
all_projects = owned_projects + member_projects
|
|
projects_data = [ProjectResponse.from_orm(p).dict() for p in all_projects]
|
|
|
|
return success_response(data=projects_data)
|
|
|
|
|
|
@router.post("/", response_model=dict)
|
|
async def create_project(
|
|
project_in: ProjectCreate,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""创建新项目"""
|
|
# 生成 UUID 作为存储键
|
|
storage_key = str(uuid.uuid4())
|
|
|
|
# 创建项目记录
|
|
db_project = Project(
|
|
name=project_in.name,
|
|
description=project_in.description,
|
|
storage_key=storage_key,
|
|
owner_id=current_user.id,
|
|
is_public=project_in.is_public,
|
|
status=1,
|
|
)
|
|
db.add(db_project)
|
|
await db.commit()
|
|
await db.refresh(db_project)
|
|
|
|
# 创建物理文件夹结构
|
|
try:
|
|
storage_service.create_project_structure(storage_key)
|
|
except Exception as e:
|
|
# 如果文件夹创建失败,回滚数据库记录
|
|
await db.delete(db_project)
|
|
await db.commit()
|
|
raise HTTPException(status_code=500, detail=f"项目文件夹创建失败: {str(e)}")
|
|
|
|
# 添加项目所有者为管理员成员
|
|
db_member = ProjectMember(
|
|
project_id=db_project.id,
|
|
user_id=current_user.id,
|
|
role=ProjectMemberRole.ADMIN,
|
|
)
|
|
db.add(db_member)
|
|
await db.commit()
|
|
|
|
project_data = ProjectResponse.from_orm(db_project)
|
|
return success_response(data=project_data.dict(), message="项目创建成功")
|
|
|
|
|
|
@router.get("/{project_id}", response_model=dict)
|
|
async def get_project(
|
|
project_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""获取项目详情"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 检查权限(项目所有者或成员可访问)
|
|
if project.owner_id != current_user.id:
|
|
member_result = await db.execute(
|
|
select(ProjectMember).where(
|
|
ProjectMember.project_id == project_id,
|
|
ProjectMember.user_id == current_user.id
|
|
)
|
|
)
|
|
member = member_result.scalar_one_or_none()
|
|
if not member and project.is_public != 1:
|
|
raise HTTPException(status_code=403, detail="无权访问该项目")
|
|
|
|
# 增加访问次数
|
|
project.visit_count += 1
|
|
await db.commit()
|
|
|
|
project_data = ProjectResponse.from_orm(project)
|
|
return success_response(data=project_data.dict())
|
|
|
|
|
|
@router.put("/{project_id}", response_model=dict)
|
|
async def update_project(
|
|
project_id: int,
|
|
project_in: ProjectUpdate,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""更新项目信息"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 只有项目所有者可以更新
|
|
if project.owner_id != current_user.id:
|
|
raise HTTPException(status_code=403, detail="无权修改该项目")
|
|
|
|
# 更新字段
|
|
update_data = project_in.dict(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(project, field, value)
|
|
|
|
await db.commit()
|
|
await db.refresh(project)
|
|
|
|
project_data = ProjectResponse.from_orm(project)
|
|
return success_response(data=project_data.dict(), message="项目更新成功")
|
|
|
|
|
|
@router.delete("/{project_id}", response_model=dict)
|
|
async def delete_project(
|
|
project_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""删除项目(归档)"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 只有项目所有者可以删除
|
|
if project.owner_id != current_user.id:
|
|
raise HTTPException(status_code=403, detail="无权删除该项目")
|
|
|
|
# 软删除(归档)
|
|
project.status = 0
|
|
await db.commit()
|
|
|
|
return success_response(message="项目已归档")
|
|
|
|
|
|
@router.get("/{project_id}/members", response_model=dict)
|
|
async def get_project_members(
|
|
project_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""获取项目成员列表"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 检查权限
|
|
if project.owner_id != current_user.id:
|
|
member_result = await db.execute(
|
|
select(ProjectMember).where(
|
|
ProjectMember.project_id == project_id,
|
|
ProjectMember.user_id == current_user.id
|
|
)
|
|
)
|
|
member = member_result.scalar_one_or_none()
|
|
if not member:
|
|
raise HTTPException(status_code=403, detail="无权访问该项目")
|
|
|
|
# 查询成员列表
|
|
members_result = await db.execute(
|
|
select(ProjectMember).where(ProjectMember.project_id == project_id)
|
|
)
|
|
members = members_result.scalars().all()
|
|
|
|
members_data = [ProjectMemberResponse.from_orm(m).dict() for m in members]
|
|
return success_response(data=members_data)
|
|
|
|
|
|
@router.post("/{project_id}/members", response_model=dict)
|
|
async def add_project_member(
|
|
project_id: int,
|
|
member_in: ProjectMemberAdd,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""添加项目成员"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 只有项目所有者和管理员可以添加成员
|
|
if project.owner_id != current_user.id:
|
|
member_result = await db.execute(
|
|
select(ProjectMember).where(
|
|
ProjectMember.project_id == project_id,
|
|
ProjectMember.user_id == current_user.id,
|
|
ProjectMember.role == ProjectMemberRole.ADMIN
|
|
)
|
|
)
|
|
member = member_result.scalar_one_or_none()
|
|
if not member:
|
|
raise HTTPException(status_code=403, detail="无权添加成员")
|
|
|
|
# 检查用户是否已是成员
|
|
existing_result = await db.execute(
|
|
select(ProjectMember).where(
|
|
ProjectMember.project_id == project_id,
|
|
ProjectMember.user_id == member_in.user_id
|
|
)
|
|
)
|
|
existing_member = existing_result.scalar_one_or_none()
|
|
if existing_member:
|
|
raise HTTPException(status_code=400, detail="用户已是项目成员")
|
|
|
|
# 添加成员
|
|
db_member = ProjectMember(
|
|
project_id=project_id,
|
|
user_id=member_in.user_id,
|
|
role=member_in.role,
|
|
invited_by=current_user.id,
|
|
)
|
|
db.add(db_member)
|
|
await db.commit()
|
|
await db.refresh(db_member)
|
|
|
|
member_data = ProjectMemberResponse.from_orm(db_member)
|
|
return success_response(data=member_data.dict(), message="成员添加成功")
|
|
|
|
|
|
@router.get("/{project_id}/share", response_model=dict)
|
|
async def get_project_share_info(
|
|
project_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""获取项目分享信息"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 只有项目所有者可以获取分享信息
|
|
if project.owner_id != current_user.id:
|
|
raise HTTPException(status_code=403, detail="只有项目所有者可以查看分享信息")
|
|
|
|
# 构建分享链接
|
|
share_url = f"/preview/{project_id}"
|
|
|
|
share_info = ProjectShareInfo(
|
|
share_url=share_url,
|
|
has_password=bool(project.access_pass),
|
|
access_pass=project.access_pass # 返回实际密码给项目所有者
|
|
)
|
|
|
|
return success_response(data=share_info.dict())
|
|
|
|
|
|
@router.post("/{project_id}/share/settings", response_model=dict)
|
|
async def update_share_settings(
|
|
project_id: int,
|
|
settings: ProjectShareSettings,
|
|
current_user: User = Depends(get_current_user),
|
|
db: AsyncSession = Depends(get_db)
|
|
):
|
|
"""更新分享设置(设置或取消访问密码)"""
|
|
# 查询项目
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="项目不存在")
|
|
|
|
# 只有项目所有者可以修改分享设置
|
|
if project.owner_id != current_user.id:
|
|
raise HTTPException(status_code=403, detail="只有项目所有者可以修改分享设置")
|
|
|
|
# 更新访问密码
|
|
project.access_pass = settings.access_pass
|
|
await db.commit()
|
|
|
|
message = "访问密码已取消" if not settings.access_pass else "访问密码已设置"
|
|
return success_response(message=message)
|