From e0bedd1cd1474a660eccf18892814b6cc52e9278 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Wed, 30 Sep 2026 09:48:11 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AE=B0=E5=BD=95=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=A2=84=E8=A7=88=E4=B8=8E=E5=85=A5=E5=8F=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 9 ++++++-- app_upload.go | 20 +++++++++++++++- app_upload_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 819079e..856215b 100644 --- a/app.go +++ b/app.go @@ -960,7 +960,12 @@ func (a *App) DownloadVideos(productIDs []string) error { // GetUploadPreview 只扫描本地磁盘并执行本地预检;批量确认框不发远端请求。 // 单商品保留 R5 首版的覆盖警告,因此额外读取一次远端当前状态。 -func (a *App) GetUploadPreview(productIDs []string) (UploadPreview, error) { +func (a *App) GetUploadPreview(productIDs []string) (preview UploadPreview, previewErr error) { + defer func() { + if previewErr != nil && a.log != nil { + a.log.Error("上传预览失败:%s", uploadRequestSummary(previewErr)) + } + }() if a.db == nil { return UploadPreview{}, fmt.Errorf("数据库未就绪,请查看运行日志") } @@ -968,7 +973,7 @@ func (a *App) GetUploadPreview(productIDs []string) (UploadPreview, error) { if len(ids) == 0 { return UploadPreview{}, fmt.Errorf("请先勾选要上传的商品") } - preview := UploadPreview{Total: len(ids)} + preview = UploadPreview{Total: len(ids)} var single store.Product for _, productID := range ids { product, found, err := a.db.GetProduct(productID) diff --git a/app_upload.go b/app_upload.go index a4a74d2..8ef9343 100644 --- a/app_upload.go +++ b/app_upload.go @@ -16,7 +16,12 @@ import ( ) // UploadVideos 只在使用者确认后逐件执行;未决操作只能查询,不能生成新键重放写入。 -func (a *App) UploadVideos(productIDs []string) error { +func (a *App) UploadVideos(productIDs []string) (uploadErr error) { + defer func() { + if uploadErr != nil && a.log != nil { + a.log.Error("启动视频上传失败:%s", uploadRequestSummary(uploadErr)) + } + }() if a.db == nil { return fmt.Errorf("数据库未就绪,请查看运行日志") } @@ -221,6 +226,19 @@ func videoCode(err error) string { return "VIDEO_REQUEST_FAILED" } +// 上传入口错误只记稳定字段;尤其不要把原始上游响应、请求地址或凭据写入日志。 +func uploadRequestSummary(err error) string { + var videoErr *erpgo.VideoError + if errors.As(err, &videoErr) { + return fmt.Sprintf("%s (HTTP %d, requestId=%s)", videoCode(err), videoErr.Status, videoErr.RequestID) + } + var bridgeErr *erpgo.Error + if errors.As(err, &bridgeErr) { + return erpgo.LogSummary(err) + } + return "LOCAL_UPLOAD_ERROR" +} + func safeUploadCode(value string) bool { if len(value) == 0 || len(value) > 64 { return false diff --git a/app_upload_test.go b/app_upload_test.go index d46a527..511eaec 100644 --- a/app_upload_test.go +++ b/app_upload_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "strings" "testing" "cmsp/internal/downloader" @@ -92,3 +93,59 @@ func TestVideoUploadUsesERPGoAndPersistsOutcome(t *testing.T) { }) } } + +func TestUploadPreviewFailureIsLoggedWithoutKey(t *testing.T) { + const key = "fictional-secret-key" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprint(w, `{"code":401,"errorCode":"API_KEY_INVALID","requestId":"fictional-request"}`) + })) + defer server.Close() + db, err := store.Open(":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + if err := db.UpsertProducts([]store.Product{{ID: "internal-1", ItemID: "123"}}, "2026-09-30 00:00:00"); err != nil { + t.Fatal(err) + } + a := NewApp() + a.db = db + a.cfg.ERPGo.BaseURL, a.cfg.ERPGo.APIKey = server.URL, key + a.cfg.Download.VideoDir = t.TempDir() + a.probe = func(context.Context, string) (downloader.ProbeResult, error) { + return downloader.ProbeResult{Duration: 20, FormatName: "mp4", Width: 640, Height: 480}, nil + } + dir := filepath.Join(a.cfg.Download.VideoDir, "123") + if err := os.MkdirAll(dir, 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "video.mp4"), []byte("fictional-mp4"), 0600); err != nil { + t.Fatal(err) + } + if _, err := a.GetUploadPreview([]string{"internal-1"}); err == nil { + t.Fatal("expected preview failure") + } + entries := a.GetLogs() + if len(entries) != 1 || !strings.Contains(entries[0].Message, "API_KEY_INVALID") || strings.Contains(entries[0].Message, key) { + t.Fatalf("wrong log: %+v", entries) + } +} + +func TestUploadStartFailureIsLogged(t *testing.T) { + a := NewApp() + db, err := store.Open(":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + a.db = db + if err := a.UploadVideos([]string{"internal-1"}); err == nil { + t.Fatal("expected unconfigured erpgo") + } + entries := a.GetLogs() + if len(entries) != 1 || !strings.Contains(entries[0].Message, "ERPGo_NOT_CONFIGURED") { + t.Fatalf("wrong log: %+v", entries) + } +}