# api/departments.py from typing import List from django.db import IntegrityError from django.shortcuts import get_object_or_404 from ninja import Router from ninja.errors import HttpError from users.models import Department from users.permission import has_permission, require_permissions from users.schema import DepartmentOut, DepartmentCreate, DepartmentUpdate router = Router(tags=['depts']) @router.get("/", response=List[DepartmentOut], summary="获取部门列表(扁平)") @require_permissions("dept:list") def list_departments(request): return Department.objects.all() @router.get("/tree/", response=List[dict], summary="获取部门树形结构") @require_permissions("dept:list") def get_department_tree(request): depts = Department.objects.all().order_by('order') return build_dept_tree(list(depts)) @router.post("/", response=DepartmentOut, summary="创建部门") @require_permissions("dept:create") def create_department(request, payload: DepartmentCreate): if not has_permission(request.auth.id, "dept:create"): raise HttpError(403, "权限不足") # 👇 新增:校验根部门唯一性 if payload.parent_id is None: if Department.objects.filter(parent__isnull=True).exists(): raise HttpError(400, "根部门已存在,无法创建新的根部门") data = payload.dict() parent_id = data.pop('parent_id', None) if parent_id: # 可以额外校验 parent_id 是否真实存在 if not Department.objects.filter(id=parent_id).exists(): raise HttpError(400, "指定的上级部门不存在") data['parent_id'] = parent_id try: dept = Department.objects.create(**data) return dept except IntegrityError as e: # 捕获数据库层面的唯一性冲突,提供统一错误信息 if "unique_root_department" in str(e): raise HttpError(400, "根部门已存在,无法创建新的根部门") else: raise HttpError(400, "创建部门失败,请检查数据") @router.get("/{dept_id}/", response=DepartmentOut, summary="获取部门详情") @require_permissions("dept:view") def get_department(request, dept_id: int): dept = get_object_or_404(Department, id=dept_id) return dept @router.put("/{dept_id}/", response=DepartmentOut, summary="更新部门") @require_permissions("dept:update") def update_department(request, dept_id: int, payload: DepartmentUpdate): dept = get_object_or_404(Department, id=dept_id) data = payload.dict() parent_id = data.pop('parent_id', None) if parent_id: data['parent_id'] = parent_id else: data['parent'] = None for attr, value in data.items(): setattr(dept, attr, value) dept.save() return dept @router.delete("/{dept_id}/", summary="删除部门") @require_permissions("dept:delete") def delete_department(request, dept_id: int): dept = get_object_or_404(Department, id=dept_id) # 可选:检查是否有子部门或用户 if dept.user_set.exists() or Department.objects.filter(parent=dept).exists(): return 400, {"detail": "部门包含用户或子部门,无法删除"} dept.delete() return {"success": True} # 工具函数:构建部门树 def build_dept_tree(depts, parent_id=None): tree = [] for dept in depts: if dept.parent_id == parent_id: node = { 'id': dept.id, 'name': dept.name, 'parent_id': dept.parent_id, 'order': dept.order, 'children': build_dept_tree(depts, dept.id) or None } tree.append(node) return tree