feat: add safety reporting APIs

This commit is contained in:
richjohnrambo
2026-08-14 00:01:54 +08:00
parent fa75c0f0f9
commit 170bbdca76
10 changed files with 1344 additions and 6 deletions
+90
View File
@@ -1520,6 +1520,95 @@ func (devicePushTokenMapper) Upsert(tenantID types.TenantID, token *types.Device
return tenantAdp.TenantDevicePushTokenUpsert(tenantID, token)
}
type SafetyPersistenceInterface interface {
CreateRiskChallenge(tenantID types.TenantID, challenge *types.RiskChallenge) (*types.RiskChallenge, error)
ConfirmRiskChallenge(tenantID types.TenantID, uid types.Uid, challengeID, decision, actionTokenID string, now time.Time) (*types.RiskChallenge, error)
AbuseReasons(tenantID types.TenantID, targetType string) ([]types.AbuseReason, error)
CreateAbuseReport(tenantID types.TenantID, report *types.AbuseReport, idempotencyKey string) (*types.AbuseReport, error)
FeedbackConfig(tenantID types.TenantID, uid types.Uid) (*types.FeedbackConfig, error)
CreateFeedback(tenantID types.TenantID, feedback *types.FeedbackSubmission, idempotencyKey string) (*types.FeedbackSubmission, error)
}
type safetyMapper struct{}
var Safety SafetyPersistenceInterface
func (safetyMapper) CreateRiskChallenge(tenantID types.TenantID, challenge *types.RiskChallenge) (*types.RiskChallenge, error) {
if challenge == nil || challenge.UserID.IsZero() || strings.TrimSpace(challenge.ChallengeID) == "" ||
strings.TrimSpace(challenge.Action) == "" || challenge.ExpiresAt.IsZero() {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantRiskChallengeCreate(tenantID, challenge)
}
func (safetyMapper) ConfirmRiskChallenge(tenantID types.TenantID, uid types.Uid, challengeID, decision, actionTokenID string, now time.Time) (*types.RiskChallenge, error) {
decision = strings.ToLower(strings.TrimSpace(decision))
if uid.IsZero() || strings.TrimSpace(challengeID) == "" || (decision != "continue" && decision != "cancel") {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantRiskChallengeConfirm(tenantID, uid, strings.TrimSpace(challengeID), decision, strings.TrimSpace(actionTokenID), now)
}
func (safetyMapper) AbuseReasons(tenantID types.TenantID, targetType string) ([]types.AbuseReason, error) {
targetType = strings.ToLower(strings.TrimSpace(targetType))
if targetType == "" {
targetType = "message"
}
if targetType != "user" && targetType != "group" && targetType != "message" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantAbuseReasons(tenantID, targetType)
}
func (safetyMapper) CreateAbuseReport(tenantID types.TenantID, report *types.AbuseReport, idempotencyKey string) (*types.AbuseReport, error) {
if report == nil || report.ReporterID.IsZero() || strings.TrimSpace(report.ReportID) == "" ||
strings.TrimSpace(report.TargetType) == "" || strings.TrimSpace(report.ReasonCode) == "" ||
strings.TrimSpace(idempotencyKey) == "" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantAbuseReportCreate(tenantID, report, strings.TrimSpace(idempotencyKey))
}
func (safetyMapper) FeedbackConfig(tenantID types.TenantID, uid types.Uid) (*types.FeedbackConfig, error) {
if uid.IsZero() {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFeedbackConfigGet(tenantID, uid)
}
func (safetyMapper) CreateFeedback(tenantID types.TenantID, feedback *types.FeedbackSubmission, idempotencyKey string) (*types.FeedbackSubmission, error) {
if feedback == nil || feedback.UserID.IsZero() || strings.TrimSpace(feedback.FeedbackID) == "" ||
strings.TrimSpace(feedback.Category) == "" || strings.TrimSpace(feedback.Content) == "" ||
strings.TrimSpace(idempotencyKey) == "" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFeedbackCreate(tenantID, feedback, strings.TrimSpace(idempotencyKey))
}
func (profileMapper) Get(tenantID types.TenantID, uid types.Uid) (*types.UserProfile, error) {
if uid.IsZero() {
return nil, types.ErrMalformed
@@ -2781,6 +2870,7 @@ func init() {
Profiles = profileMapper{}
AppReleases = appReleaseMapper{}
DevicePushTokens = devicePushTokenMapper{}
Safety = safetyMapper{}
Devices = deviceMapper{}
Files = fileMapper{}
PCache = pcacheMapper{}
+86
View File
@@ -0,0 +1,86 @@
package types
import "time"
const (
RiskChallengeStatePending = "pending"
RiskChallengeStateContinued = "continued"
RiskChallengeStateCanceled = "canceled"
RiskChallengeStateExpired = "expired"
AbuseReportStatusReceived = "received"
FeedbackStatusReceived = "received"
)
type RiskChallenge struct {
ID int64
ChallengeID string
UserID Uid
Action string
Level string
NoticeCode string
NoticeParams map[string]any
State string
ExpiresAt time.Time
Decision string
ActionTokenID string
CreatedAt time.Time
UpdatedAt time.Time
}
type AbuseReason struct {
TargetType string
ReasonCode string
Label string
RequiresDescription bool
AllowsEvidence bool
SortOrder int
}
type AbuseTarget struct {
Type string `json:"type"`
UserID string `json:"user_id,omitempty"`
Topic string `json:"topic,omitempty"`
Seq string `json:"seq,omitempty"`
}
type AbuseReport struct {
ID int64
ReportID string
ReporterID Uid
TargetType string
TargetUserID Uid
TargetTopic string
TargetSeq int
ReasonCode string
Description string
EvidenceFileIDs []string
Snapshot map[string]any
Status string
CreatedAt time.Time
UpdatedAt time.Time
}
type FeedbackConfig struct {
Categories []string
MaxLength int
AttachmentPolicy map[string]any
AllowContact bool
DiagnosticsNotice string
UpdatedAt time.Time
}
type FeedbackSubmission struct {
ID int64
FeedbackID string
UserID Uid
Category string
Content string
Contact string
AttachmentFileIDs []string
ClientContext map[string]any
IncludeDiagnostics bool
Status string
CreatedAt time.Time
UpdatedAt time.Time
}