完善:删除用户接口增加完整级联删除逻辑
核心改动: 1. 删除用户前先级联删除所有相关数据(60+张表): - 认证与信用: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等 2. 安全防护: - 防止删除当前登录账号 - 级联删除失败的表自动跳过,不影响主流程 - 返回级联删除的记录数量 3. 算力引擎同步删除: - 删除用户后同步删除算力引擎中的账号 - 失败不阻断主流程 4. 审计日志: - 记录删除操作和级联删除数量
This commit is contained in:
@@ -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="角色列表")
|
||||
|
||||
Reference in New Issue
Block a user