package dataReport import ( "91porn-server/common/log" "91porn-server/middleware/ua" "91porn-server/models/commod" "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strconv" "strings" "time" "github.com/google/uuid" "github.com/moul/http2curl" ) var config = Config{} func Init(c Config) { config = c } // GenerateGlobalEventID 生成全局唯一的 event_id func GenerateGlobalEventID() string { // 生成一个新的 UUID return strings.ReplaceAll(uuid.New().String(), "-", "") } func ReportAppId() string { if commod.KFK_APPID < 10 { return fmt.Sprintf("JHA-00%v", commod.KFK_APPID) } if commod.KFK_APPID < 100 { return fmt.Sprintf("JHA-0%v", commod.KFK_APPID) } return fmt.Sprintf("JHA-%v", commod.KFK_APPID) } // 上报事件 func BatchReport(data interface{}) error { apiUrl := config.ApiUrl + "/api/eventTracking/batchReport.json" reqBody, err := json.Marshal(data) if err != nil { log.Error("Event report json.Marshal fail", log.E(err)) return err } req, err := http.NewRequest("POST", apiUrl, bytes.NewReader(reqBody)) if err != nil { log.Error("Event report NewRequest fail", log.E(err)) return fmt.Errorf("failed to create request: %v", err) } req.Header.Set("Content-Type", "application/json") curl, _ := http2curl.GetCurlCommand(req) client := &http.Client{ Timeout: 5 * time.Second, // 设置超时时间为 5 秒 } resp, err := client.Do(req) if err != nil { log.Error("Event report fail", log.Any("curl", curl), log.E(err)) return fmt.Errorf("failed to send request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { log.Error("Event report fail", log.Any("curl", curl), log.Any("http.code", resp.StatusCode)) return fmt.Errorf("failed to report event, status code: %d", resp.StatusCode) } // 读取返回的 body body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Error("Event report fail", log.Any("curl", curl), log.E(err)) return fmt.Errorf("failed to read response body: %v", err) } // 输出响应状态和返回的 body log.Info("Event report Response", log.Any("body", string(body))) return nil } func structToFormData(data interface{}) (string, error) { // 序列化结构体为 JSON 字符串 jsonData, err := json.Marshal(data) if err != nil { return "", err } // 反序列化为 map var dataMap map[string]interface{} err = json.Unmarshal(jsonData, &dataMap) if err != nil { return "", err } var formData []string // 遍历 map,将其转换为 form 数据 for key, value := range dataMap { if value == nil { continue // 如果值为 nil,跳过 } // 将值转换为字符串 valueStr := fmt.Sprintf("%v", value) // 使用 URL 编码 formData = append(formData, fmt.Sprintf( "%s=%s", url.QueryEscape(key), url.QueryEscape(valueStr), )) } return strings.Join(formData, "&"), nil } func NewCommonField(eventId string, event string, uid uint64, districtCode string, ua ua.UA, ip string) CommonField { device := normalizeDevice(ua.SysType) deviceID := CutTo50(strings.TrimSpace(ua.DevID)) deviceModel := strings.TrimSpace(ua.DeviceModel) if deviceModel == "" { deviceModel = strings.TrimSpace(ua.DevType) } systemName := strings.TrimSpace(ua.SystemName) if systemName == "" { systemName = device } return CommonField{ UID: fmt.Sprintf("%v", uid), EventID: eventId, Event: event, // 事件类型由外部事件对象指定 Channel: strings.TrimSpace(districtCode), SID: strings.TrimSpace(ua.SID), AppID: ReportAppId(), ClientTS: JsonNumber(time.Now().Unix()), Device: device, DeviceID: deviceID, UserAgent: strings.TrimSpace(ua.UserAgent), DeviceModel: deviceModel, DeviceBrand: strings.TrimSpace(ua.DeviceBrand), SystemName: systemName, SystemVersion: strings.TrimSpace(ua.SystemVersion), IP: ip, } } func Device(ua ua.UA) string { return normalizeDevice(ua.SysType) } func normalizeDevice(value string) string { lower := strings.ToLower(strings.TrimSpace(value)) switch { case lower == "": return "" case strings.Contains(lower, "ios"), strings.Contains(lower, "iphone"), strings.Contains(lower, "ipad"), strings.Contains(lower, "apple"): return "iOS" case strings.Contains(lower, "android"): return "Android" case strings.Contains(lower, "pc"), strings.Contains(lower, "windows"), strings.Contains(lower, "mac"), strings.Contains(lower, "h5"): return "PC" } return strings.TrimSpace(value) } // CutTo50 截取前 50 个“字符”(支持中文、-、_ 等特殊字符) func CutTo50(s string) string { runes := []rune(s) if len(runes) <= 50 { return s } return string(runes[:50]) } func JsonNumber(v int64) json.Number { return json.Number(strconv.FormatInt(v, 10)) } func JsonRawMessage(v interface{}) json.RawMessage { b, _ := json.Marshal(v) return b }