package yuyuecha import ( "bytes" "encoding/json" "fmt" "os" "path/filepath" "strings" "sync" "time" ) const debugExchangeFileName = "dev_curl.log" var debugExchangeMu sync.Mutex type debugExchangeRecord struct { RequestID string TransactionID string API string Status int Duration time.Duration Curl string ResponseBody []byte Error string } // writeDebugExchange 开发环境写入单一排查文件:一次调用一条记录,含 curl + response func writeDebugExchange(logDir string, rec debugExchangeRecord) { if strings.TrimSpace(logDir) == "" || strings.TrimSpace(rec.Curl) == "" { return } var b strings.Builder b.WriteString(strings.Repeat("=", 72)) b.WriteByte('\n') b.WriteString("time: ") b.WriteString(time.Now().Format("2006-01-02 15:04:05.000")) b.WriteByte('\n') b.WriteString("request_id: ") b.WriteString(rec.RequestID) b.WriteByte('\n') // 与上游签名头 nonce 一致,对外排查只提供 request_id 即可 b.WriteString("nonce: ") b.WriteString(rec.RequestID) b.WriteByte('\n') if rec.TransactionID != "" { b.WriteString("transaction_id: ") b.WriteString(rec.TransactionID) b.WriteByte('\n') } b.WriteString("api: ") b.WriteString(rec.API) b.WriteByte('\n') if rec.Status > 0 { b.WriteString(fmt.Sprintf("status: %d\n", rec.Status)) } b.WriteString(fmt.Sprintf("duration: %s\n", rec.Duration.Round(time.Millisecond))) if rec.Error != "" { b.WriteString("error: ") b.WriteString(rec.Error) b.WriteByte('\n') } b.WriteByte('\n') b.WriteString("[curl]\n") b.WriteString(rec.Curl) b.WriteByte('\n') b.WriteByte('\n') b.WriteString("[response]\n") if len(rec.ResponseBody) == 0 { b.WriteString("(empty)\n") } else { b.WriteString(prettyJSONForLog(rec.ResponseBody)) b.WriteByte('\n') } b.WriteString(strings.Repeat("=", 72)) b.WriteString("\n\n") debugExchangeMu.Lock() defer debugExchangeMu.Unlock() if err := os.MkdirAll(logDir, 0755); err != nil { return } path := filepath.Join(logDir, debugExchangeFileName) f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return } defer f.Close() _, _ = f.WriteString(b.String()) } func prettyJSONForLog(raw []byte) string { trimmed := bytes.TrimSpace(raw) if len(trimmed) == 0 { return "(empty)" } var buf bytes.Buffer if err := json.Indent(&buf, trimmed, "", " "); err != nil { return string(raw) } return buf.String() }