diff --git a/app/api/routers/rbac_admin.py b/app/api/routers/rbac_admin.py index 6361706..211a6fc 100644 --- a/app/api/routers/rbac_admin.py +++ b/app/api/routers/rbac_admin.py @@ -177,23 +177,113 @@ async def update_user( return {"ok": True, "user": await db.users.to_profile(user)} -@router.delete("/users/{user_id}", summary="删除用户") +@router.delete("/users/{user_id}", summary="删除用户(级联删除所有相关数据)") async def delete_user( user_id: str, request: Request, db: Database = Depends(get_db), actor: dict = Depends(require_permission("action:user.manage")), ): + from sqlalchemy import text as sql_text + user = await db.users.get_by_id(user_id) + if user is None: + raise HTTPException(status_code=404, detail="用户不存在") + + # 防止删除自己 + if actor.get("id") == user_id: + raise HTTPException(status_code=400, detail="不能删除当前登录账号") + + username = user.get("username", "") + + # ═══════════════════════════════════════════════════════════ + # 级联删除所有与用户相关的数据 + # ═══════════════════════════════════════════════════════════ + cascade_tables = [ + # 认证与信用 + "certifications", "skill_tests", "credit_scores", "credit_ledger", + "credit_dimensions", "user_badges", + # IM/消息/通知 + "messages", "dm_messages", "notifications", + # 服务大厅 + "opc_services", "service_inquiries", "service_orders", + "community_posts", "community_comments", "community_likes", "community_follows", + # 培训 + "training_enrollments", "training_course_enrollments", + "training_lesson_progress", "training_certificates", + # 招聘 + "jobs", "job_applications", + # 人才 + "talent_profiles", + # 用户主页 + "user_pages", "user_page_review_logs", + # 邀请 + "invite_records", + # 财务 + "finance_records", "escrows", "contracts", "invoices", + "disputes", "ratings", + # 任务 + "tasks", "task_claims", "task_deliveries", "task_milestones", "task_acceptances", + # 园区/企业成员关系 + "park_members", "company_members", + "park_admissions", "park_transfers", + # 内容互动 + "content_likes", "content_comments", "content_comment_likes", + # 其他 + "sessions", "agents", "opc_profiles", + "investor_preferences", "roadshow_registrations", "investment_intents", + "bids", "subsidy_applications", "service_referrals", + "bookings", "tests", "policy_logs", "plan_logs", "survey_logs", + "incubator_monthly_reports", "incubator_area_reservations", + "market_purchase_orders", "market_purchases", + ] + + deleted_count = 0 + for table in cascade_tables: + try: + result = await db.session.execute( + sql_text(f"DELETE FROM {table} WHERE user_id = :uid"), + {"uid": user_id} + ) + deleted_count += result.rowcount or 0 + except Exception as e: + # 表不存在或字段不存在时跳过,不影响主流程 + print(f"[delete_user] 跳过表 {table}: {e}") + await db.session.rollback() + + # 删除作为邀请人的邀请记录(inviter_id 字段) + try: + await db.session.execute( + sql_text("DELETE FROM invite_records WHERE inviter_id = :uid"), + {"uid": user_id} + ) + except Exception: + await db.session.rollback() + + # 删除作为负责人的企业(owner_user_id 字段) + try: + await db.session.execute( + sql_text("DELETE FROM park_companies WHERE owner_user_id = :uid"), + {"uid": user_id} + ) + except Exception: + await db.session.rollback() + + # 提交级联删除 + await db.session.commit() + + # 硬删除用户主记录 ok = await db.users.delete(user_id) if not ok: - raise HTTPException(status_code=404, detail="User not found") - # 平台用户删除 → 引擎镜像同步删除 - if user: - await _sync_compute(compute_client.sync_delete_user(user.get("username"))) + raise HTTPException(status_code=404, detail="用户删除失败") + + # 平台用户删除 → 引擎镜像同步删除(算力、IM等) + if username: + await _sync_compute(compute_client.sync_delete_user(username)) + await write_audit(db, action="user.delete", resource="user", resource_id=user_id, - detail="deleted", user=actor, request=request) - return {"ok": True, "user_id": user_id} + detail=f"deleted, cascade_deleted={deleted_count} records", user=actor, request=request) + return {"ok": True, "user_id": user_id, "username": username, "cascade_deleted": deleted_count} @router.get("/roles", summary="角色列表")