feat: 用户行为信誉分系统

- User 新增 credit_score 字段(0-200,默认100)
- 信誉分影响检测阈值系数:高分降低敏感度,低分提高敏感度
- 发布成功+1分,被拦截-2分;申诉通过+10分,驳回-5分
- 新增手动调整和批量重算信誉分接口
- admin-users 页面显示信誉分进度条,支持编辑调整

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
刘正航
2026-04-21 23:52:47 +08:00
parent 5279816452
commit 6d62120443
8 changed files with 166 additions and 10 deletions

View File

@@ -309,11 +309,17 @@ def process_appeal(post_id: int):
row.prediction = "ham"
row.manual_review_status = "approved_ham"
_upsert_manual_sample(row.text, "ham", admin.id if admin else None)
# 申诉通过,增加用户信誉分
if row.author:
row.author.credit_score = min(200, (row.author.credit_score or 100) + 10)
else:
row.status = "blocked"
row.prediction = "spam"
row.manual_review_status = "confirmed_spam"
_upsert_manual_sample(row.text, "spam", admin.id if admin else None)
# 申诉驳回,减少用户信誉分
if row.author:
row.author.credit_score = max(0, (row.author.credit_score or 100) - 5)
db.session.commit()
return ok(_serialize_post(row), "申诉处理完成")
@@ -404,6 +410,12 @@ def update_user(user_id: int):
user.phone = (payload.get("phone") or "").strip()
if "is_admin" in payload:
user.is_admin = bool(payload.get("is_admin"))
if "credit_score" in payload:
try:
credit = int(payload.get("credit_score", 100))
user.credit_score = max(0, min(200, credit))
except Exception:
pass
if payload.get("password"):
if len(payload["password"]) < 6:
return fail("密码至少6位", 400)
@@ -427,3 +439,73 @@ def delete_user(user_id: int):
db.session.commit()
return ok({}, "用户已删除")
@admin_bp.put("/users/<int:user_id>/credit")
@admin_required
def update_user_credit(user_id: int):
"""手动调整用户信誉分"""
user = User.query.get(user_id)
if not user:
return fail("用户不存在", 404)
payload = request.get_json(silent=True) or {}
try:
credit = int(payload.get("credit_score", user.credit_score or 100))
credit = max(0, min(200, credit))
except Exception:
return fail("信誉分必须是0-200之间的整数", 400)
user.credit_score = credit
db.session.commit()
return ok(user.to_dict(), "信誉分已更新")
@admin_bp.post("/users/recalculate-credit")
@admin_required
def recalculate_all_credit():
"""根据用户发布历史和申诉通过率重新计算信誉分"""
users = User.query.filter_by(is_admin=False).all()
updated_count = 0
for user in users:
posts = ContentPost.query.filter_by(user_id=user.id).all()
if not posts:
continue
# 计算发布成功率
published_count = sum(1 for p in posts if p.status == "published")
blocked_count = sum(1 for p in posts if p.status == "blocked")
total_count = len(posts)
if total_count == 0:
continue
publish_ratio = published_count / total_count
# 计算申诉通过率
appeals = [p for p in posts if p.appeal_status != "none"]
approved_appeals = sum(1 for p in appeals if p.appeal_status == "approved")
appeal_ratio = approved_appeals / len(appeals) if appeals else 0
# 基础信誉分:发布成功率贡献
base_score = 100
if publish_ratio >= 0.9:
base_score += 30 # 90%以上发布成功,+30
elif publish_ratio >= 0.7:
base_score += 15 # 70%以上,+15
elif publish_ratio < 0.5:
base_score -= 20 # 低于50%-20
# 申诉通过率贡献
if appeal_ratio >= 0.8 and len(appeals) >= 3:
base_score += 20 # 80%以上申诉通过且有3次以上申诉+20
elif appeal_ratio >= 0.5 and len(appeals) >= 2:
base_score += 10
# 限制范围
user.credit_score = max(0, min(200, base_score))
updated_count += 1
db.session.commit()
return ok({"updated_count": updated_count}, "信誉分批量重算完成")