Files
qitongxue-api/internal/logic/event_code_qrcode_test.go
李琦 2bce19b05b 小程序码落图片库并返回绝对地址
码图原先落在 qrcode 私有目录且返回相对路径,后台无法统一管理、
换部署域名还要改数据。现迁到图片库 manifest/static/qrcode/,
与头像/封面/附件共用同一套 /static 路由,并登记附件表
(scene=qrcode)使后台附件管理可统一查看管理。

库内仍只存相对路径、域名由 absoluteURL 按配置统一拼,与既有
约定一致;按内容 md5 命名,重复生成同一张码不新增文件。
活动码与项目绑定码共用 saveQrcodeImage helper。

另修正 .gitignore 漏掉的仓库根二进制(go build -o 的产物)。
2026-10-13 16:23:21 +08:00

101 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/gogf/gf/v2/frame/g"
)
// TestSaveQrcodeImage 验证「小程序码落图片库」契约(迭代-2026-10-09)。
//
// 关注四点:
// 1. 落盘位置在图片库 manifest/static/qrcode/<ym>/<md5>.png(不是 qrcode 私有目录);
// 2. 返回的 relPath 以 /static/ 开头(库内只存相对路径);
// 3. 同一份字节重复调用**不新增文件**(md5 命名天然去重,不会在图片库堆垃圾);
// 4. 无 site.base_url 且无请求上下文时,absURL 回退为相对路径本身(本地联调零配置)。
func TestSaveQrcodeImage(t *testing.T) {
ctx := context.Background()
// storeStatic 会写 manifest/static/...,测试必须在仓库根跑(go test 的 CWD 是包目录)
// 故这里显式切到仓库根。
wd, err := os.Getwd()
if err != nil {
t.Fatalf("Getwd 失败:%v", err)
}
if err = os.Chdir("../.."); err != nil {
t.Fatalf("切到仓库根失败:%v", err)
}
defer func() { _ = os.Chdir(wd) }()
// 造一张最小合法 PNG(1x1),只为验证落盘与命名,不校验图片内容
data := []byte{
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
}
relPath, absURL, err := saveQrcodeImage(ctx, "event_probe.png", data)
if err != nil {
t.Fatalf("saveQrcodeImage 失败:%v", err)
}
if !strings.HasPrefix(relPath, staticURLPrefix+"/") {
t.Fatalf("relPath 必须以 %s/ 开头(库内只存相对路径),实际=%q", staticURLPrefix, relPath)
}
// 落在图片库的 qrcode 子目录下(storeStatic 里 dirName = scene = "qrcode")
if !strings.Contains(relPath, "/qrcode/") {
t.Fatalf("relPath 应落在图片库 qrcode 子目录,实际=%q", relPath)
}
// 落盘文件必须真实存在
disk := filepath.Join(StaticDir, filepath.FromSlash(strings.TrimPrefix(relPath, staticURLPrefix+"/")))
if _, err = os.Stat(disk); err != nil {
t.Fatalf("码图未落盘:%s(%v)", disk, err)
}
// 去重:同一份字节再调一次,文件数不应增加
before := countQrcodeFiles(t)
if _, _, err = saveQrcodeImage(ctx, "event_probe.png", data); err != nil {
t.Fatalf("第二次 saveQrcodeImage 失败:%v", err)
}
after := countQrcodeFiles(t)
if after != before {
t.Fatalf("重复生成应复用同一文件(md5 去重),文件数 %d → %d", before, after)
}
// 无 site.base_url + 无请求上下文 → absURL 回退为相对路径本身
if absURL != relPath {
t.Logf("absURL=%q(未配置 base_url 时回退相对路径,符合预期)", absURL)
}
if !strings.HasPrefix(absURL, "http://") && !strings.HasPrefix(absURL, "https://") && absURL != relPath {
t.Fatalf("absURL 既不是绝对 URL 也不是 relPath:%q", absURL)
}
// 附件表登记(recordAttachment 失败不阻断,故只告警不 Fatal;但成功时应能查到)
if n, e := g.Model("attachments").Where("file_path", relPath).WhereNull("deleted_at").Count(); e == nil {
t.Logf("附件表登记条数=%d(0 表示未登记,可能库未连通,不影响落盘结论)", n)
} else {
t.Logf("附件表查询失败(可能未连库):%v", e)
}
_ = os.Remove(disk)
t.Logf("relPath=%s absURL=%s disk=%s", relPath, absURL, disk)
}
// countQrcodeFiles 统计图片库 qrcode 子目录下的文件数。
func countQrcodeFiles(t *testing.T) int {
t.Helper()
dir := filepath.Join(StaticDir, "qrcode")
var n int
_ = filepath.Walk(dir, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() {
n++
}
return nil
})
return n
}