from __future__ import annotations from .shared import * from .helpers_user import _iter_import_rows, _create_user_with_defaults @router.put("/{user_id}/workload") async def update_user_workload( user_id: int, workload: int = Query(..., ge=0, le=200, description="Workload value (0-200)"), tenant_id: int = Depends(get_tenant_id), current_user: User = Depends(require_self_or_manager), db: AsyncSession = Depends(get_db), ): """手动更新用户 workload 百分比。""" _ = current_user result = await db.execute( select(User).where(and_(User.tenant_id == tenant_id, User.id == user_id)) ) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") user.workload = max(0, min(200, int(workload))) user.updated_at = now_cn_naive() await db.commit() await db.refresh(user) return _serialize_user(user) @router.post("/import") async def import_users( file: UploadFile = File(...), tenant_id: int = Depends(get_tenant_id), current_user: User = Depends(require_roles("OWNER", "REVIEWER")), db: AsyncSession = Depends(get_db), ): """批量导入用户(最多 50 行)。""" rows = await _iter_import_rows(file) if not rows: raise HTTPException(status_code=400, detail="Import file is empty") if len(rows) > 50: raise HTTPException(status_code=400, detail="Import rows exceed max limit: 50") results: list[dict[str, Any]] = [] success_count = 0 for idx, raw in enumerate(rows, start=1): row = _normalize_import_row(raw) try: if row["role"] not in {r.value for r in UserRole}: raise ValueError(f"Invalid role: {row['role']}") if not row["username"] or not row["password"]: raise ValueError("username/password required") data = UserCreate( username=row["username"], password=row["password"], name=row["name"] or row["username"], email=row["email"], role=UserRole(row["role"]), assigned_domains=[], project_ids=row["project_ids"], ) user = await _create_user_with_defaults( db, tenant_id=tenant_id, user_data=data, actor_id=int(current_user.id), commit=True, ) success_count += 1 results.append( { "row": idx, "status": "success", "user_id": str(user.id), "message": "created", } ) except Exception as exc: await db.rollback() results.append( { "row": idx, "status": "failed", "message": str(exc), } ) return { "total": len(rows), "success": success_count, "failed": len(rows) - success_count, "results": results, }