From 97df85d4f8fa8d3b06f1d8a87e0a0974f70de61a Mon Sep 17 00:00:00 2001 From: hxuanyu <2252193204@qq.com> Date: Mon, 26 Jan 2026 23:32:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=AA=8C=E8=AF=81=E5=9B=BE=E7=89=87=E9=87=8D=E5=AE=9A?= =?UTF-8?q?=E5=90=91=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E9=A2=84=E8=A7=88=E9=93=BE=E6=8E=A5=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E4=BE=9D=E8=B5=96=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 4 ++ internal/http/handlers/image.go | 10 ++-- internal/http/handlers/image_test.go | 68 ++++++++++++++++++++++++++++ web/index.html | 23 ++++++++-- 4 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 internal/http/handlers/image_test.go diff --git a/go.mod b/go.mod index 7081167..6894fea 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/glebarez/sqlite v1.11.0 github.com/robfig/cron/v3 v3.0.1 github.com/spf13/viper v1.21.0 + github.com/stretchr/testify v1.11.1 github.com/studio-b12/gowebdav v0.12.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.1 @@ -29,6 +30,7 @@ require ( github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.1.0 // indirect @@ -61,6 +63,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.54.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect @@ -85,6 +88,7 @@ require ( golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect diff --git a/internal/http/handlers/image.go b/internal/http/handlers/image.go index 02b786d..87938de 100644 --- a/internal/http/handlers/image.go +++ b/internal/http/handlers/image.go @@ -172,9 +172,11 @@ func handleImageResponse(c *gin.Context, img *model.Image) { if mode == "redirect" { if selected.PublicURL != "" { c.Redirect(http.StatusFound, selected.PublicURL) + } else if img.URLBase != "" { + // 兜底重定向到原始 Bing + bingURL := fmt.Sprintf("https://www.bing.com%s_%s.jpg", img.URLBase, selected.Variant) + c.Redirect(http.StatusFound, bingURL) } else { - // 兜底重定向到原始 Bing (如果可能,但由于 URLBase 只有一部分,这里可能不工作) - // 这里我们更倾向于 local 转发,如果 PublicURL 为空 serveLocal(c, selected.StorageKey) } } else { @@ -201,7 +203,9 @@ func formatMeta(img *model.Image) gin.H { variants := []gin.H{} for _, v := range img.Variants { url := v.PublicURL - if cfg.API.Mode == "local" || url == "" { + if url == "" && cfg.API.Mode == "redirect" && img.URLBase != "" { + url = fmt.Sprintf("https://www.bing.com%s_%s.jpg", img.URLBase, v.Variant) + } else if cfg.API.Mode == "local" || url == "" { url = fmt.Sprintf("%s/api/v1/image/date/%s?variant=%s&format=%s", cfg.Server.BaseURL, img.Date, v.Variant, v.Format) } variants = append(variants, gin.H{ diff --git a/internal/http/handlers/image_test.go b/internal/http/handlers/image_test.go new file mode 100644 index 0000000..2ce56ac --- /dev/null +++ b/internal/http/handlers/image_test.go @@ -0,0 +1,68 @@ +package handlers + +import ( + "net/http" + "net/http/httptest" + "testing" + + "BingPaper/internal/config" + "BingPaper/internal/model" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +func TestHandleImageResponseRedirect(t *testing.T) { + gin.SetMode(gin.TestMode) + + // Setup config + err := config.Init("") + assert.NoError(t, err) + config.GetConfig().API.Mode = "redirect" + + // Mock Image and Variant + img := &model.Image{ + Date: "2026-01-26", + URLBase: "/th?id=OHR.TestImage", + Variants: []model.ImageVariant{ + { + Variant: "UHD", + Format: "jpg", + PublicURL: "", // Empty for local storage simulation + StorageKey: "2026-01-26/2026-01-26_UHD.jpg", + }, + }, + } + + t.Run("Redirect mode with empty PublicURL should redirect to Bing", func(t *testing.T) { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request, _ = http.NewRequest("GET", "/api/v1/image/today?variant=UHD", nil) + + handleImageResponse(c, img) + + assert.Equal(t, http.StatusFound, w.Code) + assert.Contains(t, w.Header().Get("Location"), "bing.com") + assert.Contains(t, w.Header().Get("Location"), "UHD") + }) + + t.Run("FormatMeta in redirect mode should return Bing URL if PublicURL is empty", func(t *testing.T) { + config.GetConfig().API.Mode = "redirect" + meta := formatMeta(img) + + variants := meta["variants"].([]gin.H) + assert.Equal(t, 1, len(variants)) + assert.Contains(t, variants[0]["url"].(string), "bing.com") + assert.Contains(t, variants[0]["url"].(string), "UHD") + }) + + t.Run("FormatMeta in local mode should return API URL", func(t *testing.T) { + config.GetConfig().API.Mode = "local" + config.GetConfig().Server.BaseURL = "http://myserver.com" + meta := formatMeta(img) + + variants := meta["variants"].([]gin.H) + assert.Equal(t, 1, len(variants)) + assert.Contains(t, variants[0]["url"].(string), "myserver.com") + assert.Contains(t, variants[0]["url"].(string), "/api/v1/image/date/") + }) +} diff --git a/web/index.html b/web/index.html index fd92c9e..fb46007 100644 --- a/web/index.html +++ b/web/index.html @@ -38,6 +38,21 @@ /* Info Section */ .info-card { background: var(--card-bg); padding: 2rem; border-radius: 12px; margin-bottom: 2rem; box-shadow: 0 2px 10px rgba(0,0,0,0.05); } + .info-card ul { list-style: none; padding: 0; } + .info-card li { margin-bottom: 12px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } + .preview-link { + text-decoration: none; + color: var(--primary-color); + font-size: 0.85rem; + padding: 2px 8px; + border: 1px solid var(--primary-color); + border-radius: 4px; + transition: all 0.2s; + } + .preview-link:hover { + background: var(--primary-color); + color: white; + } code { background: #f1f1f1; padding: 2px 5px; border-radius: 4px; color: #e83e8c; } pre { background: #f1f1f1; padding: 1rem; border-radius: 8px; overflow-x: auto; } @@ -82,10 +97,10 @@

使用说明

这是一个自动抓取必应每日一图并提供多分辨率管理的工具。您可以直接通过以下接口获取图片:

参数支持:variant=UHD|1920x1080|1366x768format=jpg