feat: 用户被多次举报合并
This commit is contained in:
@@ -61,9 +61,10 @@ def create_app(config_name='default'):
|
||||
app.logger.info('PT黑名单系统启动')
|
||||
|
||||
# 注册自定义过滤器
|
||||
from .filters import translate_status, translate_reason
|
||||
from .filters import translate_status, translate_reason, translate_reasons_list
|
||||
app.jinja_env.filters['translate_status'] = translate_status
|
||||
app.jinja_env.filters['translate_reason'] = translate_reason
|
||||
app.jinja_env.filters['translate_reasons_list'] = translate_reasons_list
|
||||
|
||||
# 注册蓝图
|
||||
from .routes import main as main_blueprint
|
||||
|
||||
@@ -38,3 +38,9 @@ def translate_status(status):
|
||||
def translate_reason(reason):
|
||||
"""违规原因翻译过滤器"""
|
||||
return REASON_TRANSLATIONS.get(reason, reason)
|
||||
|
||||
def translate_reasons_list(reasons):
|
||||
"""违规原因列表翻译过滤器"""
|
||||
if not reasons:
|
||||
return []
|
||||
return [REASON_TRANSLATIONS.get(r, r) for r in reasons]
|
||||
|
||||
@@ -64,7 +64,6 @@ class Report(db.Model):
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
comments = db.relationship('Comment', backref='report', lazy='dynamic', cascade='all, delete-orphan')
|
||||
evidences = db.relationship('Evidence', backref='report', lazy='dynamic', cascade='all, delete-orphan')
|
||||
blacklist_entry = db.relationship('Blacklist', backref='report', uselist=False)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Report {self.id}>'
|
||||
@@ -96,7 +95,8 @@ class Blacklist(db.Model):
|
||||
normalized_email = db.Column(db.String(120), index=True)
|
||||
pt_site = db.Column(db.String(100), index=True)
|
||||
uid = db.Column(db.String(50))
|
||||
report_id = db.Column(db.Integer, db.ForeignKey('reports.id'), unique=True)
|
||||
report_ids = db.Column(db.JSON, default=list, nullable=False)
|
||||
reason_categories = db.Column(db.JSON, default=list, nullable=False)
|
||||
status = db.Column(db.String(16), default='active', index=True)
|
||||
created_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from flask import abort, Blueprint, render_template, request, flash, redirect, url_for, current_app
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
from flask_login import login_required, current_user
|
||||
from app import db
|
||||
from app.forms import SearchForm, ReportForm, UpdateUserForm, CommentForm, RevokeForm, AppealForm, AppealMessageForm, PartnerSiteForm
|
||||
@@ -24,13 +25,12 @@ def index():
|
||||
search_term = form.search_term.data
|
||||
normalized_email = normalize_email(search_term)
|
||||
|
||||
search_result = Blacklist.query.join(Report).filter(
|
||||
search_result = Blacklist.query.filter(
|
||||
or_(
|
||||
Blacklist.normalized_email == normalized_email,
|
||||
Blacklist.username == search_term
|
||||
),
|
||||
Blacklist.status == 'active',
|
||||
Report.status == 'approved'
|
||||
Blacklist.status == 'active'
|
||||
).first()
|
||||
|
||||
if search_result:
|
||||
@@ -46,6 +46,9 @@ def index():
|
||||
@login_required
|
||||
def create_report():
|
||||
"""创建新举报"""
|
||||
if current_user.status != 'active':
|
||||
flash('您的账户尚未激活,无法提交举报。请等待管理员审核。', 'warning')
|
||||
return redirect(url_for('main.index'))
|
||||
form = ReportForm()
|
||||
active_sites = PartnerSite.query.filter_by(is_active=True).order_by(PartnerSite.name).all()
|
||||
form.reported_pt_site.choices = [(site.name, site.name) for site in active_sites]
|
||||
@@ -170,17 +173,33 @@ def process_report(report_id):
|
||||
|
||||
if action == 'confirm':
|
||||
report.status = 'approved'
|
||||
existing_blacklist = Blacklist.query.filter_by(report_id=report.id).first()
|
||||
normalized = normalize_email(report.reported_email)
|
||||
existing_blacklist = Blacklist.query.filter_by(normalized_email=normalized, status='active').first()
|
||||
|
||||
if not existing_blacklist:
|
||||
new_blacklist_entry = Blacklist(
|
||||
email=report.reported_email,
|
||||
normalized_email=normalize_email(report.reported_email),
|
||||
normalized_email=normalized,
|
||||
pt_site=report.reported_pt_site,
|
||||
uid=report.reported_username,
|
||||
report_id=report.id,
|
||||
report_ids=[report.id],
|
||||
reason_categories=[report.reason_category],
|
||||
username=report.reported_username or None
|
||||
)
|
||||
db.session.add(new_blacklist_entry)
|
||||
current_app.logger.info(f'举报批准: #{report.id} - {report.reported_email} by {current_user.username}')
|
||||
flash('举报已批准,并已将相关信息添加到黑名单。', 'success')
|
||||
else:
|
||||
if report.reason_category not in existing_blacklist.reason_categories:
|
||||
existing_blacklist.reason_categories.append(report.reason_category)
|
||||
existing_blacklist.report_ids.append(report.id)
|
||||
flag_modified(existing_blacklist, 'reason_categories')
|
||||
flag_modified(existing_blacklist, 'report_ids')
|
||||
current_app.logger.info(f'举报合并: #{report.id} 合并到黑名单#{existing_blacklist.id} - 新增原因: {report.reason_category}')
|
||||
flash(f'举报已批准并合并到现有黑名单记录(新增违规原因:{report.reason_category})。', 'success')
|
||||
else:
|
||||
current_app.logger.info(f'举报批准: #{report.id} - 相同原因已存在,不合并')
|
||||
flash('举报已批准。该用户已有相同违规原因的记录,未进行合并。', 'info')
|
||||
|
||||
other_pending = Report.query.filter(
|
||||
Report.reported_email == report.reported_email,
|
||||
@@ -188,8 +207,16 @@ def process_report(report_id):
|
||||
Report.status == 'pending'
|
||||
).all()
|
||||
|
||||
merged_count = 0
|
||||
for other_report in other_pending:
|
||||
other_report.status = 'approved'
|
||||
bl = existing_blacklist or new_blacklist_entry
|
||||
if other_report.reason_category not in bl.reason_categories:
|
||||
bl.reason_categories.append(other_report.reason_category)
|
||||
bl.report_ids.append(other_report.id)
|
||||
flag_modified(bl, 'reason_categories')
|
||||
flag_modified(bl, 'report_ids')
|
||||
merged_count += 1
|
||||
comment = Comment(
|
||||
body=f'该举报已自动批准(关联举报 #{report.id} 已确认违规)',
|
||||
report=other_report,
|
||||
@@ -197,14 +224,9 @@ def process_report(report_id):
|
||||
)
|
||||
db.session.add(comment)
|
||||
|
||||
current_app.logger.info(f'举报批准: #{report.id} - {report.reported_email} by {current_user.username}')
|
||||
if other_pending:
|
||||
current_app.logger.info(f'自动批准关联举报: {len(other_pending)}个')
|
||||
flash(f'举报已批准,并已将相关信息添加到黑名单。同时自动处理了 {len(other_pending)} 个相关举报。', 'success')
|
||||
else:
|
||||
flash('举报已批准,并已将相关信息添加到黑名单。', 'success')
|
||||
else:
|
||||
flash('举报状态已更新为"批准"。该举报已在黑名单中,无需重复添加。', 'info')
|
||||
current_app.logger.info(f'自动批准关联举报: {len(other_pending)}个,合并{merged_count}个不同原因')
|
||||
flash(f'同时自动处理了 {len(other_pending)} 个相关举报(其中 {merged_count} 个不同原因已合并)。', 'info')
|
||||
elif action == 'invalidate':
|
||||
report.status = 'rejected'
|
||||
current_app.logger.info(f'举报驳回: #{report.id} by {current_user.username}')
|
||||
@@ -224,9 +246,23 @@ def revoke_report(report_id):
|
||||
return redirect(url_for('main.report_detail', report_id=report.id))
|
||||
form = RevokeForm()
|
||||
if form.validate_on_submit():
|
||||
blacklist_entry = Blacklist.query.filter_by(report_id=report.id).first()
|
||||
if blacklist_entry:
|
||||
normalized = normalize_email(report.reported_email)
|
||||
blacklist_entry = Blacklist.query.filter_by(normalized_email=normalized, status='active').first()
|
||||
|
||||
if blacklist_entry and report.id in blacklist_entry.report_ids:
|
||||
blacklist_entry.report_ids.remove(report.id)
|
||||
if report.reason_category in blacklist_entry.reason_categories:
|
||||
blacklist_entry.reason_categories.remove(report.reason_category)
|
||||
flag_modified(blacklist_entry, 'report_ids')
|
||||
flag_modified(blacklist_entry, 'reason_categories')
|
||||
|
||||
if len(blacklist_entry.report_ids) == 0:
|
||||
db.session.delete(blacklist_entry)
|
||||
current_app.logger.warning(f'举报撤销: #{report.id} - 黑名单记录已删除')
|
||||
flash('举报已成功撤销,并已从黑名单中移除。', 'success')
|
||||
else:
|
||||
current_app.logger.warning(f'举报撤销: #{report.id} - 从黑名单中移除该举报')
|
||||
flash(f'举报已成功撤销,已从黑名单中移除该违规原因(剩余 {len(blacklist_entry.report_ids)} 个举报)。', 'success')
|
||||
|
||||
report.status = 'revoked'
|
||||
revocation_comment = Comment(
|
||||
@@ -237,7 +273,6 @@ def revoke_report(report_id):
|
||||
db.session.add(revocation_comment)
|
||||
db.session.commit()
|
||||
current_app.logger.warning(f'举报撤销: #{report.id} by {current_user.username} - 理由: {form.reason.data[:50]}')
|
||||
flash('举报已成功撤销,并已从黑名单中移除。', 'success')
|
||||
else:
|
||||
flash('撤销失败:' + ' '.join(form.reason.errors), 'danger')
|
||||
return redirect(url_for('main.report_detail', report_id=report.id))
|
||||
@@ -398,9 +433,11 @@ def decide_appeal(appeal_id):
|
||||
blacklist_entry = appeal.blacklist_entry
|
||||
blacklist_entry.status = 'revoked'
|
||||
appeal.status = 'approved'
|
||||
if blacklist_entry.report:
|
||||
blacklist_entry.report.status = 'overturned'
|
||||
db.session.add(blacklist_entry.report)
|
||||
for report_id in blacklist_entry.report_ids:
|
||||
report = Report.query.get(report_id)
|
||||
if report:
|
||||
report.status = 'overturned'
|
||||
db.session.add(report)
|
||||
db.session.add(blacklist_entry)
|
||||
db.session.add(appeal)
|
||||
db.session.commit()
|
||||
|
||||
@@ -24,6 +24,18 @@
|
||||
{% if appeal.blacklist_entry %}
|
||||
<p class="mb-0"><strong>站点:</strong> {{ appeal.blacklist_entry.pt_site }}</p>
|
||||
<p class="mb-0"><strong>UID:</strong> {{ appeal.blacklist_entry.uid }}</p>
|
||||
<p class="mb-1"><strong>违规原因:</strong></p>
|
||||
{% if appeal.blacklist_entry.reason_categories and appeal.blacklist_entry.reason_categories|length > 0 %}
|
||||
<ul class="mb-0">
|
||||
{% for reason in appeal.blacklist_entry.reason_categories %}
|
||||
<li>{{ reason | translate_reason }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif appeal.blacklist_entry.report %}
|
||||
<p class="mb-0">{{ appeal.blacklist_entry.report.reason_category | translate_reason }}</p>
|
||||
{% else %}
|
||||
<p class="mb-0 text-muted">未知</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="mb-0 text-muted">黑名单记录已删除</p>
|
||||
{% endif %}
|
||||
|
||||
@@ -14,9 +14,20 @@
|
||||
<li class="list-group-item"><strong>UID:</strong> {{ entry.uid }}</li>
|
||||
<li class="list-group-item"><strong>邮箱:</strong> {{ entry.email }}</li>
|
||||
<li class="list-group-item"><strong>站点:</strong> {{ entry.pt_site }}</li>
|
||||
{% if entry.report %}
|
||||
<li class="list-group-item"><strong>违规原因:</strong> {{ entry.report.reason_category | translate_reason }}</li>
|
||||
<li class="list-group-item">
|
||||
<strong>违规原因:</strong>
|
||||
{% if entry.reason_categories and entry.reason_categories|length > 0 %}
|
||||
<ul class="mb-0 mt-1">
|
||||
{% for reason in entry.reason_categories %}
|
||||
<li>{{ reason | translate_reason }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif entry.report %}
|
||||
{{ entry.report.reason_category | translate_reason }}
|
||||
{% else %}
|
||||
未知
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form method="POST" novalidate>
|
||||
|
||||
@@ -45,9 +45,18 @@
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item"><strong>违规站点:</strong> {{ search_result.pt_site }}</li>
|
||||
{% if search_result.report %}
|
||||
<li class="list-group-item"><strong>违规原因:</strong> {{ search_result.report.reason_category | translate_reason }}</li>
|
||||
<li class="list-group-item">
|
||||
<strong>违规原因:</strong>
|
||||
{% if search_result.reason_categories and search_result.reason_categories|length > 0 %}
|
||||
<ul class="mb-0 mt-1">
|
||||
{% for reason in search_result.reason_categories %}
|
||||
<li>{{ reason | translate_reason }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
未知
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="list-group-item"><strong>记录时间:</strong> {{ search_result.created_at.strftime('%Y-%m-%d') }}</li>
|
||||
</ul>
|
||||
<p class="text-muted small mt-3">为保护隐私,仅展示必要的脱敏信息。具体违规描述不对外公开。</p>
|
||||
|
||||
Reference in New Issue
Block a user