完善:删除用户接口增加完整级联删除逻辑

核心改动:
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:
Pine
2026-09-06 12:07:33 +08:00
parent e03f2c5fb8
commit 102c49654a
+97 -7
View File
@@ -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="角色列表")