""" 部門成員管理 API 記錄員工與部門的多對多關係 (純粹組織歸屬,與 RBAC 權限無關) """ from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query, status, Request from sqlalchemy.orm import Session from app.db.session import get_db from app.models.department_member import DepartmentMember from app.models.employee import Employee from app.models.department import Department from app.schemas.response import MessageResponse from app.services.audit_service import audit_service router = APIRouter() def get_current_tenant_id() -> int: """取得當前租戶 ID (暫時寫死為 1, 未來從 JWT token realm 取得)""" return 1 @router.get("/") def get_department_members( db: Session = Depends(get_db), employee_id: Optional[int] = Query(None, description="員工 ID 篩選"), department_id: Optional[int] = Query(None, description="部門 ID 篩選"), include_inactive: bool = False, ): """ 取得部門成員列表 可依員工 ID 或部門 ID 篩選 """ tenant_id = get_current_tenant_id() query = db.query(DepartmentMember).filter(DepartmentMember.tenant_id == tenant_id) if employee_id: query = query.filter(DepartmentMember.employee_id == employee_id) if department_id: query = query.filter(DepartmentMember.department_id == department_id) if not include_inactive: query = query.filter(DepartmentMember.is_active == True) members = query.all() result = [] for m in members: dept = m.department emp = m.employee result.append({ "id": m.id, "employee_id": m.employee_id, "employee_name": emp.legal_name if emp else None, "employee_number": emp.employee_id if emp else None, "department_id": m.department_id, "department_name": dept.name if dept else None, "department_code": dept.code if dept else None, "department_depth": dept.depth if dept else None, "position": m.position, "membership_type": m.membership_type, "is_active": m.is_active, "joined_at": m.joined_at, "ended_at": m.ended_at, }) return result @router.post("/", status_code=status.HTTP_201_CREATED) def add_employee_to_department( data: dict, request: Request, db: Session = Depends(get_db), ): """ 將員工加入部門 Body: { "employee_id": 1, "department_id": 3, "position": "資深工程師", "membership_type": "permanent" } """ tenant_id = get_current_tenant_id() employee_id = data.get("employee_id") department_id = data.get("department_id") position = data.get("position") membership_type = data.get("membership_type", "permanent") if not employee_id or not department_id: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="employee_id and department_id are required" ) # 驗證員工存在 employee = db.query(Employee).filter( Employee.id == employee_id, Employee.tenant_id == tenant_id, ).first() if not employee: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Employee with id {employee_id} not found" ) # 驗證部門存在 department = db.query(Department).filter( Department.id == department_id, Department.tenant_id == tenant_id, ).first() if not department: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Department with id {department_id} not found" ) # 檢查是否已存在 existing = db.query(DepartmentMember).filter( DepartmentMember.employee_id == employee_id, DepartmentMember.department_id == department_id, ).first() if existing: if existing.is_active: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Employee {employee_id} is already a member of department {department_id}" ) else: # 重新啟用 existing.is_active = True existing.position = position existing.membership_type = membership_type existing.ended_at = None db.commit() db.refresh(existing) return { "id": existing.id, "employee_id": existing.employee_id, "department_id": existing.department_id, "position": existing.position, "membership_type": existing.membership_type, "is_active": existing.is_active, } member = DepartmentMember( tenant_id=tenant_id, employee_id=employee_id, department_id=department_id, position=position, membership_type=membership_type, ) db.add(member) db.commit() db.refresh(member) audit_service.log_action( request=request, db=db, action="add_department_member", resource_type="department_member", resource_id=member.id, details={ "employee_id": employee_id, "department_id": department_id, "position": position, }, ) return { "id": member.id, "employee_id": member.employee_id, "department_id": member.department_id, "position": member.position, "membership_type": member.membership_type, "is_active": member.is_active, "joined_at": member.joined_at, } @router.delete("/{member_id}", response_model=MessageResponse) def remove_employee_from_department( member_id: int, request: Request, db: Session = Depends(get_db), ): """將員工從部門移除 (軟刪除)""" tenant_id = get_current_tenant_id() member = db.query(DepartmentMember).filter( DepartmentMember.id == member_id, DepartmentMember.tenant_id == tenant_id, ).first() if not member: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Department member with id {member_id} not found" ) from datetime import datetime member.is_active = False member.ended_at = datetime.utcnow() db.commit() audit_service.log_action( request=request, db=db, action="remove_department_member", resource_type="department_member", resource_id=member_id, details={ "employee_id": member.employee_id, "department_id": member.department_id, }, ) return MessageResponse(message=f"Employee removed from department successfully")