restrict v8
This commit is contained in:
@@ -39,6 +39,15 @@ func AboutMe(ctx context.Context) {
|
||||
ctx.Success("about-me")
|
||||
}
|
||||
|
||||
// Rules 社区规则页。
|
||||
//
|
||||
// 公开可访问:处置箱主时可以直接引用条款,不必每次临时解释;
|
||||
// 箱主也能在被处置前就知道边界在哪。内容是静态 i18n 文案,不走数据库。
|
||||
func Rules(ctx context.Context) {
|
||||
ctx.SetTitle(i18n.T(ctx.Lang(), "rules.title"))
|
||||
ctx.Success("rules")
|
||||
}
|
||||
|
||||
func ChangeLogs(ctx context.Context) {
|
||||
ctx.Success("change-logs")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package question
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -47,6 +49,46 @@ func allowAbuseAlert(userID uint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// questionReportNoticeThreshold 单条内容被举报多少人次时提醒箱主自查。
|
||||
//
|
||||
// 取 3:一两个人可能是私人恩怨,三个人以上通常是内容本身有问题。
|
||||
const questionReportNoticeThreshold = 3
|
||||
|
||||
// notifyQuestionOwner 在单条内容的待处理举报数**恰好**达到阈值时提醒箱主。
|
||||
//
|
||||
// 用「恰好等于」而不是「大于等于」:前者只发一次,后者会从第 3 条起每条举报都发一封信。
|
||||
// 箱主关闭邮件通知(Notify=none)时不发 —— 后台的「被举报 N 次」标记仍然可见,
|
||||
// 治理提醒不该以无视用户通知偏好为代价。
|
||||
func notifyQuestionOwner(pageUser *db.User, question *db.Question) {
|
||||
if pageUser.Email == "" || pageUser.Notify == db.NotifyTypeNone {
|
||||
return
|
||||
}
|
||||
|
||||
shared.SafeGo(func() {
|
||||
ctx := stdctx.Background()
|
||||
|
||||
count, err := db.Reports.CountByQuestion(ctx, question.ID)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to count reports for question")
|
||||
return
|
||||
}
|
||||
if count != questionReportNoticeThreshold {
|
||||
return
|
||||
}
|
||||
|
||||
link := strings.TrimRight(conf.App.ExternalURL, "/") + "/_/" + pageUser.Domain + "/" + strconv.FormatUint(uint64(question.ID), 10)
|
||||
if err := mail.SendQuestionReportedMail(pageUser.Email, pageUser.Domain, question.ID, count, link); err != nil {
|
||||
logrus.WithError(err).Error("Failed to send question reported mail")
|
||||
return
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"user_id": pageUser.ID,
|
||||
"question_id": question.ID,
|
||||
"count": count,
|
||||
}).Warn("Question owner notified about reports")
|
||||
})
|
||||
}
|
||||
|
||||
// notifyAbuseIfNeeded 在举报写入后判断是否达到告警阈值,异步发送汇总邮件。
|
||||
//
|
||||
// 只发「该看一眼」的信号,不做任何自动处置:举报数达标不等于内容违规,
|
||||
@@ -82,10 +124,10 @@ func notifyAbuseIfNeeded(pageUser *db.User) {
|
||||
return
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"user_id": pageUser.ID,
|
||||
"domain": pageUser.Domain,
|
||||
"count24": count24,
|
||||
"count7d": count7d,
|
||||
"user_id": pageUser.ID,
|
||||
"domain": pageUser.Domain,
|
||||
"count24": count24,
|
||||
"count7d": count7d,
|
||||
}).Warn("Abuse alert mail sent")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -272,6 +272,12 @@ func Delete(ctx context.Context, pageUser *db.User, question *db.Question, canDe
|
||||
return
|
||||
}
|
||||
|
||||
// 举报闭环:内容已被箱主处理掉,该题下的待处理举报不应再计入告警阈值。
|
||||
// 这一步失败不影响删除结果,只记日志 —— 让一次统计失败回滚用户操作是不可接受的。
|
||||
if _, err := db.Reports.MarkAcceptedByQuestion(ctx.Request().Context(), question.ID); err != nil {
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to mark reports accepted after delete")
|
||||
}
|
||||
|
||||
ctx.Redirect("/_/" + pageUser.Domain)
|
||||
}
|
||||
|
||||
@@ -288,6 +294,11 @@ func SetPrivate(ctx context.Context, pageUser *db.User, question *db.Question, c
|
||||
return
|
||||
}
|
||||
|
||||
// 举报闭环:设为私密等于箱主已响应,该题的待处理举报移出统计池(见 report.go 的说明)。
|
||||
if _, err := db.Reports.MarkAcceptedByQuestion(ctx.Request().Context(), question.ID); err != nil {
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to mark reports accepted after set private")
|
||||
}
|
||||
|
||||
ctx.Redirect(fmt.Sprintf("/_/%s/%d", pageUser.Domain, question.ID))
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ func Report(ctx context.Context, pageUser *db.User, question *db.Question, f for
|
||||
"reason": f.Reason,
|
||||
}).Warn("Content reported")
|
||||
notifyAbuseIfNeeded(pageUser)
|
||||
// 单条内容被多人举报时提醒箱主自查(治理前移,减少「不知道越界」的情况)。
|
||||
notifyQuestionOwner(pageUser, question)
|
||||
}
|
||||
|
||||
// 首次与重复使用同一句提示(见上方设计取舍)。
|
||||
|
||||
@@ -363,6 +363,18 @@ func QuestionList(ctx context.Context) {
|
||||
ctx.Data["SelectedType"] = typeParam
|
||||
ctx.Data["SelectedReported"] = reportedParam
|
||||
|
||||
// 处置期条幅:箱主必须能持续看到自己处在限制里。
|
||||
// 只靠回答后那条一闪而过的提示是不够的 —— 线上实测过箱主完全没意识到 L2 已生效。
|
||||
// 查询失败不阻断页面,只是少一条提示。
|
||||
if level, active, rErr := db.Restrictions.ActiveLevel(ctx.Request().Context(), ctx.User.ID); rErr != nil {
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(rErr).Error("Failed to query active restriction")
|
||||
} else if level != "" {
|
||||
ctx.Data["RestrictionLevel"] = string(level)
|
||||
if active != nil {
|
||||
ctx.Data["RestrictionExpiresAt"] = active.ExpiresAt.Format("2006-01-02")
|
||||
}
|
||||
}
|
||||
|
||||
// 我发起的私聊 — 同样使用批量查询与分页(复用 chat_cursor 游标)
|
||||
askerConvs, err := db.Conversations.ListByAsker(ctx.Request().Context(), ctx.User.ID, chatPageSize, int(chatOffset))
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user