diff --git a/internal/timesync/sync.go b/internal/timesync/sync.go index 7dff5a0..585a6b5 100644 --- a/internal/timesync/sync.go +++ b/internal/timesync/sync.go @@ -21,10 +21,17 @@ type TimeSync struct { mu sync.RWMutex } -// ChinaTimeAPIResponse 中国时间API响应结构(type=1返回时间戳) -type ChinaTimeAPIResponse struct { - Code int `json:"code"` - Msg string `json:"msg"` // 时间戳字符串(秒) +// K780TimeAPIResponse K780时间API响应结构 +type K780TimeAPIResponse struct { + Success string `json:"success"` + Msgid string `json:"msgid"` + Msg string `json:"msg"` + Result struct { + Timestamp string `json:"timestamp"` // 时间戳(秒) + TimestampMs string `json:"timestamp_ms"` // 时间戳(毫秒) + Datetime1 string `json:"datetime_1"` // 日期时间格式1 + Datetime2 string `json:"datetime_2"` // 日期时间格式2 + } `json:"result"` } // NewTimeSync 创建时间同步器 @@ -44,95 +51,119 @@ func NewTimeSync(logger *zap.Logger) (*TimeSync, error) { // syncTime 同步时间(从HTTP API获取北京时间) func (ts *TimeSync) syncTime() error { - // 使用中国时间API(必须使用,大陆无法访问海外API) - chinaAPIs := []string{ - "http://101.35.2.25/api/time/getapi.php", - "http://124.222.204.22/api/time/getapi.php", - "http://81.68.149.132/api/time/getapi.php", - "https://cn.apihz.cn/api/time/getapi.php", + // 使用K780时间API + apiURL := "https://sapi.k780.com/?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json" + + client := &http.Client{ + Timeout: 5 * time.Second, } - var lastErr error - - // 尝试所有中国时间API - for _, baseURL := range chinaAPIs { - apiURL := baseURL + "?id=88888888&key=88888888&type=1" - - client := &http.Client{ - Timeout: 5 * time.Second, - } - - resp, err := client.Get(apiURL) - if err != nil { - lastErr = err - ts.logger.Debug("API请求失败", zap.String("api", baseURL), zap.Error(err)) - continue - } - - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - lastErr = fmt.Errorf("API返回状态码: %d", resp.StatusCode) - ts.logger.Debug("API返回错误状态码", zap.String("api", baseURL), zap.Int("status", resp.StatusCode)) - continue - } - - var result ChinaTimeAPIResponse - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - resp.Body.Close() - lastErr = err - ts.logger.Debug("解析API响应失败", zap.String("api", baseURL), zap.Error(err)) - continue - } - resp.Body.Close() - - // 检查返回码 - if result.Code != 200 { - lastErr = fmt.Errorf("API返回错误: %s", result.Msg) - ts.logger.Debug("API返回错误码", zap.String("api", baseURL), zap.Int("code", result.Code), zap.String("msg", result.Msg)) - continue - } - - // 从msg字段解析时间戳字符串 - if result.Msg == "" { - lastErr = fmt.Errorf("API返回的时间戳为空") - continue - } - - // 解析时间戳字符串为int64 - var timestamp int64 - if _, err := fmt.Sscanf(result.Msg, "%d", ×tamp); err != nil { - lastErr = fmt.Errorf("解析时间戳失败: %w", err) - ts.logger.Debug("解析时间戳失败", zap.String("api", baseURL), zap.String("msg", result.Msg), zap.Error(err)) - continue - } - - // 使用时间戳转换为北京时间 - beijingTime := time.Unix(timestamp, 0).In(ts.beijingTZ) - - // 计算时间差 - localTime := time.Now() - timeDiff := beijingTime.Sub(localTime) - + // 创建请求,添加浏览器请求头 + req, err := http.NewRequest("GET", apiURL, nil) + if err != nil { ts.mu.Lock() - ts.lastSyncTime = beijingTime - ts.lastSyncError = nil + ts.lastSyncError = err ts.mu.Unlock() - - ts.logger.Info("时间同步成功", - zap.String("api", baseURL), - zap.String("remote_time", beijingTime.Format("2006-01-02 15:04:05")), - zap.String("local_time", localTime.Format("2006-01-02 15:04:05")), - zap.Duration("time_diff", timeDiff), - zap.Int64("timestamp", timestamp)) - - return nil + return fmt.Errorf("创建请求失败: %w", err) } + // 添加浏览器请求头,模拟浏览器访问 + req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7") + req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") + req.Header.Set("Cache-Control", "max-age=0") + req.Header.Set("Connection", "keep-alive") + req.Header.Set("Referer", "https://www.nowapi.com/") + req.Header.Set("Sec-Fetch-Dest", "document") + req.Header.Set("Sec-Fetch-Mode", "navigate") + req.Header.Set("Sec-Fetch-Site", "cross-site") + req.Header.Set("Sec-Fetch-User", "?1") + req.Header.Set("Upgrade-Insecure-Requests", "1") + req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36") + req.Header.Set("sec-ch-ua", `"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"`) + req.Header.Set("sec-ch-ua-mobile", "?0") + req.Header.Set("sec-ch-ua-platform", `"macOS"`) + + resp, err := client.Do(req) + if err != nil { + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + return fmt.Errorf("API请求失败: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + err := fmt.Errorf("API返回状态码: %d", resp.StatusCode) + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + return err + } + + var result K780TimeAPIResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + return fmt.Errorf("解析API响应失败: %w", err) + } + + // 检查返回状态 + if result.Success != "1" { + errMsg := fmt.Sprintf("API返回失败状态: success=%s", result.Success) + if result.Msg != "" { + errMsg += fmt.Sprintf(", msg=%s", result.Msg) + } + if result.Msgid != "" { + errMsg += fmt.Sprintf(", msgid=%s", result.Msgid) + } + err := fmt.Errorf(errMsg) + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + ts.logger.Warn("时间API返回失败", zap.String("success", result.Success), zap.String("msgid", result.Msgid), zap.String("msg", result.Msg)) + return err + } + + // 从result.timestamp字段解析时间戳字符串 + if result.Result.Timestamp == "" { + err := fmt.Errorf("API返回的时间戳为空") + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + return err + } + + // 解析时间戳字符串为int64 + var timestamp int64 + if _, err := fmt.Sscanf(result.Result.Timestamp, "%d", ×tamp); err != nil { + err := fmt.Errorf("解析时间戳失败: %w", err) + ts.mu.Lock() + ts.lastSyncError = err + ts.mu.Unlock() + return err + } + + // 使用时间戳转换为北京时间 + beijingTime := time.Unix(timestamp, 0).In(ts.beijingTZ) + + // 计算时间差 + localTime := time.Now() + timeDiff := beijingTime.Sub(localTime) + ts.mu.Lock() - ts.lastSyncError = lastErr + ts.lastSyncTime = beijingTime + ts.lastSyncError = nil ts.mu.Unlock() - return fmt.Errorf("所有中国时间API同步失败: %w", lastErr) + ts.logger.Info("时间同步成功", + zap.String("api", apiURL), + zap.String("remote_time", beijingTime.Format("2006-01-02 15:04:05")), + zap.String("local_time", localTime.Format("2006-01-02 15:04:05")), + zap.Duration("time_diff", timeDiff), + zap.Int64("timestamp", timestamp)) + + return nil } // Start 启动定期时间同步