新增单元测试验证图片重定向逻辑,优化前端预览链接样式,调整依赖配置

This commit is contained in:
2026-01-26 23:32:00 +08:00
parent 4795960fad
commit 97df85d4f8
4 changed files with 98 additions and 7 deletions

4
go.mod
View File

@@ -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

View File

@@ -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{

View File

@@ -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/")
})
}

View File

@@ -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 @@
<h2>使用说明</h2>
<p>这是一个自动抓取必应每日一图并提供多分辨率管理的工具。您可以直接通过以下接口获取图片:</p>
<ul>
<li>今日图片:<code>/api/v1/image/today</code></li>
<li>今日元数据:<code>/api/v1/image/today/meta</code></li>
<li>随机图片:<code>/api/v1/image/random</code></li>
<li>指定日期:<code>/api/v1/image/date/2026-01-26</code></li>
<li><span>今日图片:</span><code>/api/v1/image/today</code> <a href="/api/v1/image/today" target="_blank" class="preview-link">立即预览</a></li>
<li><span>今日元数据:</span><code>/api/v1/image/today/meta</code> <a href="/api/v1/image/today/meta" target="_blank" class="preview-link">立即预览</a></li>
<li><span>随机图片:</span><code>/api/v1/image/random</code> <a href="/api/v1/image/random" target="_blank" class="preview-link">立即预览</a></li>
<li><span>指定日期:</span><code>/api/v1/image/date/2026-01-26</code> <a href="/api/v1/image/date/2026-01-26" target="_blank" class="preview-link">立即预览</a></li>
</ul>
<p>参数支持:<code>variant=UHD|1920x1080|1366x768</code><code>format=jpg</code></p>
</div>