2026-05-15 19:42:02 +08:00

174 lines
6.0 KiB
Go

package errorlog
import (
"context"
"net/http"
"strconv"
"strings"
"time"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/utils"
"gorm.io/gorm"
)
// ReportAgoraError persists one client-reported Agora error log.
func (s *Service) ReportAgoraError(ctx context.Context, user AuthUser, req AgoraErrorReportRequest, meta AgoraErrorReportContext) (*AgoraErrorReportResponse, error) {
if s == nil || s.db == nil {
return nil, NewAppError(http.StatusServiceUnavailable, "error_log_unavailable", "error log service is unavailable")
}
roomID := req.RoomID.String()
if roomID == "" {
return nil, NewAppError(http.StatusBadRequest, "room_id_required", "roomId is required")
}
reasonType := firstNonBlank(req.ConnectionChangedReasonType, req.ReasonType, req.Reason)
bannedByServer := isBannedByServer(reasonType)
id, err := utils.NextID()
if err != nil {
return nil, err
}
now := time.Now()
row := model.AgoraErrorLog{
ID: id,
SysOrigin: normalizeSysOrigin(user.SysOrigin),
UserID: user.UserID,
RoomID: trimMax(roomID, 64),
AgoraUID: trimMax(req.AgoraUID.String(), 64),
ErrorCodeType: trimMax(req.ErrorCodeType, 128),
ConnectionChangedReasonType: trimMax(reasonType, 128),
BannedByServer: bannedByServer,
IsTimeout: boolValue(req.Timeout, req.IsTimeout),
TokenRequestSuccess: boolPtrValue(req.TokenRequestSuccess, req.TokenSuccess),
NetworkType: trimMax(req.NetworkType, 64),
ReqClient: trimMax(meta.ReqClient, 32),
ReqAppIntel: trimMax(meta.ReqAppIntel, 255),
ClientIP: trimMax(meta.ClientIP, 64),
UserAgent: trimMax(meta.UserAgent, 255),
RawPayload: strings.TrimSpace(meta.RawPayload),
CreateTime: now,
UpdateTime: now,
}
if err := s.db.WithContext(ctx).Create(&row).Error; err != nil {
return nil, err
}
return &AgoraErrorReportResponse{
ID: strconv.FormatInt(id, 10),
BannedByServer: bannedByServer,
}, nil
}
// PageAgoraErrorLogs returns admin Agora error logs.
func (s *Service) PageAgoraErrorLogs(ctx context.Context, query AgoraErrorLogQuery) (*AgoraErrorLogPageResponse, error) {
if s == nil || s.db == nil {
return nil, NewAppError(http.StatusServiceUnavailable, "error_log_unavailable", "error log service is unavailable")
}
cursor, limit := normalizePage(query.Cursor, query.Limit)
startTime, endTime, err := parseAdminTimeRange(query.StartTime, query.EndTime)
if err != nil {
return nil, err
}
filter := query
filter.Cursor = cursor
filter.Limit = limit
filter.StartAt = startTime
filter.EndAt = endTime
base := s.agoraErrorLogQuery(ctx, filter)
var total int64
if err := base.Count(&total).Error; err != nil {
return nil, err
}
var rows []model.AgoraErrorLog
if err := s.agoraErrorLogQuery(ctx, filter).
Order("create_time desc, id desc").
Offset((cursor - 1) * limit).
Limit(limit).
Find(&rows).Error; err != nil {
return nil, err
}
return &AgoraErrorLogPageResponse{
Records: agoraErrorLogItems(rows),
Total: total,
Current: cursor,
Size: limit,
}, nil
}
// AgoraErrorLogQuery is the admin filter for Agora error logs.
type AgoraErrorLogQuery struct {
SysOrigin string
UserID int64
RoomID string
AgoraUID string
ErrorCodeType string
ReasonType string
BannedOnly bool
NetworkType string
StartTime string
EndTime string
Cursor int
Limit int
StartAt time.Time
EndAt time.Time
}
func (s *Service) agoraErrorLogQuery(ctx context.Context, filter AgoraErrorLogQuery) *gorm.DB {
query := s.db.WithContext(ctx).Model(&model.AgoraErrorLog{}).
Where("create_time >= ? AND create_time <= ?", filter.StartAt, filter.EndAt)
if sysOrigin := normalizeSysOrigin(filter.SysOrigin); sysOrigin != "" {
query = query.Where("sys_origin = ?", sysOrigin)
}
if filter.UserID > 0 {
query = query.Where("user_id = ?", filter.UserID)
}
if roomID := strings.TrimSpace(filter.RoomID); roomID != "" {
query = query.Where("room_id = ?", roomID)
}
if agoraUID := strings.TrimSpace(filter.AgoraUID); agoraUID != "" {
query = query.Where("agora_uid = ?", agoraUID)
}
if codeType := strings.TrimSpace(filter.ErrorCodeType); codeType != "" {
query = query.Where("error_code_type LIKE ?", "%"+codeType+"%")
}
if reasonType := strings.TrimSpace(filter.ReasonType); reasonType != "" {
query = query.Where("connection_changed_reason_type LIKE ?", "%"+reasonType+"%")
}
if filter.BannedOnly {
query = query.Where("banned_by_server = ?", true)
}
if networkType := strings.TrimSpace(filter.NetworkType); networkType != "" {
query = query.Where("network_type = ?", networkType)
}
return query
}
func agoraErrorLogItems(rows []model.AgoraErrorLog) []AgoraErrorLogItem {
items := make([]AgoraErrorLogItem, 0, len(rows))
for _, row := range rows {
items = append(items, AgoraErrorLogItem{
ID: strconv.FormatInt(row.ID, 10),
SysOrigin: row.SysOrigin,
UserID: strconv.FormatInt(row.UserID, 10),
RoomID: row.RoomID,
AgoraUID: row.AgoraUID,
ErrorCodeType: row.ErrorCodeType,
ConnectionChangedReasonType: row.ConnectionChangedReasonType,
BannedByServer: row.BannedByServer,
IsTimeout: row.IsTimeout,
TokenRequestSuccess: row.TokenRequestSuccess,
NetworkType: row.NetworkType,
ReqClient: row.ReqClient,
ReqAppIntel: row.ReqAppIntel,
ClientIP: row.ClientIP,
UserAgent: row.UserAgent,
RawPayload: row.RawPayload,
CreateTime: formatDateTime(row.CreateTime),
UpdateTime: formatDateTime(row.UpdateTime),
})
}
return items
}