- Remove BOM from all Go source files - Standardize import ordering and whitespace across handlers and services - Replace PostgreSQL full-text search with ILIKE pattern matching in post and user repositories - Enhance JPush client with TLS transport, rate limit monitoring, and simplified API - Refactor PushService to include userID in device management methods - Add MaxDevicesPerUser limit and extract helper functions for push payload construction
218 lines
6.5 KiB
Go
218 lines
6.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"with_you/internal/config"
|
|
"with_you/internal/model"
|
|
"with_you/internal/repository"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// ReportService 举报服务接口(用户端)
|
|
type ReportService interface {
|
|
// CreateReport 创建举报
|
|
CreateReport(ctx context.Context, reporterID, targetType, targetID, reason, description string) (*model.Report, error)
|
|
// HasUserReported 检查用户是否已举报
|
|
HasUserReported(ctx context.Context, reporterID, targetType, targetID string) (bool, error)
|
|
}
|
|
|
|
// reportServiceImpl 举报服务实现
|
|
type reportServiceImpl struct {
|
|
reportRepo repository.ReportRepository
|
|
postRepo repository.PostRepository
|
|
commentRepo repository.CommentRepository
|
|
messageRepo repository.MessageRepository
|
|
userRepo repository.UserRepository
|
|
notifyRepo repository.SystemNotificationRepository
|
|
pushService PushService
|
|
txManager repository.TransactionManager
|
|
logService OperationLogService
|
|
config *config.ReportConfig
|
|
}
|
|
|
|
// NewReportService 创建举报服务
|
|
func NewReportService(
|
|
reportRepo repository.ReportRepository,
|
|
postRepo repository.PostRepository,
|
|
commentRepo repository.CommentRepository,
|
|
messageRepo repository.MessageRepository,
|
|
userRepo repository.UserRepository,
|
|
notifyRepo repository.SystemNotificationRepository,
|
|
pushService PushService,
|
|
txManager repository.TransactionManager,
|
|
logService OperationLogService,
|
|
cfg *config.Config,
|
|
) ReportService {
|
|
reportConfig := &cfg.Report
|
|
if reportConfig.AutoHideThreshold == 0 {
|
|
reportConfig.AutoHideThreshold = 3
|
|
}
|
|
return &reportServiceImpl{
|
|
reportRepo: reportRepo,
|
|
postRepo: postRepo,
|
|
commentRepo: commentRepo,
|
|
messageRepo: messageRepo,
|
|
userRepo: userRepo,
|
|
notifyRepo: notifyRepo,
|
|
pushService: pushService,
|
|
txManager: txManager,
|
|
logService: logService,
|
|
config: reportConfig,
|
|
}
|
|
}
|
|
|
|
// CreateReport 创建举报
|
|
func (s *reportServiceImpl) CreateReport(ctx context.Context, reporterID, targetType, targetID, reason, description string) (*model.Report, error) {
|
|
// 验证举报类型
|
|
reportTargetType := model.ReportTargetType(targetType)
|
|
if reportTargetType != model.ReportTargetTypePost &&
|
|
reportTargetType != model.ReportTargetTypeComment &&
|
|
reportTargetType != model.ReportTargetTypeMessage {
|
|
return nil, errors.New("invalid target type")
|
|
}
|
|
|
|
// 验证举报原因
|
|
reportReason := model.ReportReason(reason)
|
|
if reportReason != model.ReportReasonSpam &&
|
|
reportReason != model.ReportReasonInappropriate &&
|
|
reportReason != model.ReportReasonHarassment &&
|
|
reportReason != model.ReportReasonMisinformation &&
|
|
reportReason != model.ReportReasonOther {
|
|
return nil, errors.New("invalid reason")
|
|
}
|
|
|
|
// 检查目标内容是否存在
|
|
if err := s.validateTargetExists(ctx, reportTargetType, targetID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 检查用户是否已举报过
|
|
hasReported, err := s.reportRepo.HasUserReported(reporterID, reportTargetType, targetID)
|
|
if err != nil {
|
|
zap.L().Error("failed to check if user has reported", zap.Error(err))
|
|
return nil, errors.New("failed to check report status")
|
|
}
|
|
if hasReported {
|
|
return nil, errors.New("you have already reported this content")
|
|
}
|
|
|
|
// 创建举报
|
|
report := &model.Report{
|
|
ReporterID: reporterID,
|
|
TargetType: reportTargetType,
|
|
TargetID: targetID,
|
|
Reason: reportReason,
|
|
Description: description,
|
|
Status: model.ReportStatusPending,
|
|
}
|
|
|
|
// 使用事务处理
|
|
var createdReport *model.Report
|
|
err = s.txManager.RunInTransaction(ctx, func(ctx context.Context) error {
|
|
// 创建举报记录
|
|
if err := s.reportRepo.CreateWithContext(ctx, report); err != nil {
|
|
return fmt.Errorf("failed to create report: %w", err)
|
|
}
|
|
|
|
createdReport = report
|
|
|
|
// 检查举报次数是否达到阈值
|
|
count, err := s.reportRepo.GetReportCount(reportTargetType, targetID)
|
|
if err != nil {
|
|
zap.L().Error("failed to get report count", zap.Error(err))
|
|
return nil // 不阻止举报创建
|
|
}
|
|
|
|
// 达到阈值,自动隐藏内容
|
|
if int(count) >= s.config.AutoHideThreshold {
|
|
if err := s.hideTargetContent(ctx, reportTargetType, targetID); err != nil {
|
|
zap.L().Error("failed to hide target content", zap.Error(err))
|
|
return nil // 不阻止举报创建
|
|
}
|
|
zap.L().Info("content auto-hidden due to report threshold",
|
|
zap.String("target_type", targetType),
|
|
zap.String("target_id", targetID),
|
|
zap.Int64("report_count", count),
|
|
)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 记录操作日志
|
|
if s.logService != nil {
|
|
s.logService.RecordOperation(&model.OperationLog{
|
|
UserID: reporterID,
|
|
Operation: "create_report",
|
|
TargetType: "report",
|
|
TargetID: report.ID,
|
|
Module: "report",
|
|
})
|
|
}
|
|
|
|
return createdReport, nil
|
|
}
|
|
|
|
// HasUserReported 检查用户是否已举报
|
|
func (s *reportServiceImpl) HasUserReported(ctx context.Context, reporterID, targetType, targetID string) (bool, error) {
|
|
return s.reportRepo.HasUserReported(reporterID, model.ReportTargetType(targetType), targetID)
|
|
}
|
|
|
|
// validateTargetExists 验证目标内容是否存在
|
|
func (s *reportServiceImpl) validateTargetExists(ctx context.Context, targetType model.ReportTargetType, targetID string) error {
|
|
switch targetType {
|
|
case model.ReportTargetTypePost:
|
|
post, err := s.postRepo.GetByID(targetID)
|
|
if err != nil || post == nil {
|
|
return errors.New("post not found")
|
|
}
|
|
case model.ReportTargetTypeComment:
|
|
comment, err := s.commentRepo.GetByID(targetID)
|
|
if err != nil || comment == nil {
|
|
return errors.New("comment not found")
|
|
}
|
|
case model.ReportTargetTypeMessage:
|
|
_, err := s.messageRepo.GetMessageByID(targetID)
|
|
if err != nil {
|
|
return errors.New("message not found")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// hideTargetContent 隐藏目标内容
|
|
func (s *reportServiceImpl) hideTargetContent(ctx context.Context, targetType model.ReportTargetType, targetID string) error {
|
|
switch targetType {
|
|
case model.ReportTargetTypePost:
|
|
post, err := s.postRepo.GetByID(targetID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if post != nil && post.Status != model.PostStatusDeleted {
|
|
post.Status = model.PostStatusDeleted
|
|
return s.postRepo.Update(post)
|
|
}
|
|
case model.ReportTargetTypeComment:
|
|
comment, err := s.commentRepo.GetByID(targetID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if comment != nil && comment.Status != model.CommentStatusDeleted {
|
|
comment.Status = model.CommentStatusDeleted
|
|
return s.commentRepo.Update(comment)
|
|
}
|
|
case model.ReportTargetTypeMessage:
|
|
// 消息隐藏通过更新状态实现
|
|
return s.messageRepo.UpdateMessageStatus(targetID, model.MessageStatusDeleted)
|
|
}
|
|
return nil
|
|
}
|