Files
mce/internal/theme/loader.go
Victor_Jay e98bfe193b fix: 修复发帖后链接404 + 通知卡片跳转错误
- post_repo.go: FindByIDWithAuthor/Restore 中 posts.uid 改为 posts.id(posts 表主键是 id,不存在 uid 列)
- theme/loader.go: 新增 derefUint 模板函数,显式解引用 *uint 指针(Go 1.26 模板引擎传参给 printf 时不自动解引用)
- messages/index.html: 通知卡片链接使用 derefUint 包裹 RelatedID 避免打印指针地址
2026-05-31 13:58:14 +08:00

92 lines
2.4 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 theme
import (
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
"metazone.cc/metalab/internal/common"
)
// TemplateRoot 模板根目录配置
type TemplateRoot struct {
Dir string // 目录路径
Prefix string // 模板名前缀(如 "admin/" 表示管理后台)
}
// LoadTemplates 加载多个根目录的 .html 模板到同一模板集
// 模板名 = prefix + 相对于 root.Dir 的相对路径
func LoadTemplates(roots ...TemplateRoot) (*template.Template, error) {
funcMap := template.FuncMap{
"add": func(a, b int) int { return a + b },
"subtract": func(a, b int) int { return a - b },
"formatTTL": formatTTL,
"assetV": common.AssetV,
// derefUint 安全解引用 *uint 指针。
// Go 1.26 的模板引擎在将 *uint 传给 printf/print 时不会自动解引用,
// 导致打印指针地址而非值。通过此函数显式解引用后传给 printf。
"derefUint": func(p *uint) uint {
if p == nil {
return 0
}
return *p
},
}
t := template.New("").Funcs(funcMap)
for _, root := range roots {
dir := filepath.Clean(root.Dir) + string(os.PathSeparator)
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() || filepath.Ext(path) != ".html" {
return nil
}
content, err := os.ReadFile(path)
if err != nil {
return err
}
// 模板名prefix + 相对路径
name := root.Prefix + strings.TrimPrefix(path, dir)
_, err = t.New(name).Parse(string(content))
return err
})
if err != nil {
return nil, err
}
}
return t, nil
}
// LoadContent 读取主题内容文件(准则等),返回不转义的 template.HTML。
// 后期改为查数据库时,只需修改此函数实现,调用方不变。
func LoadContent(path string) (template.HTML, error) {
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
return template.HTML(content), nil
}
// formatTTL 将剩余分钟数格式化为人类可读的时间(如 "23 小时"、"3 天"、"15 分钟"
func formatTTL(minutes int) string {
if minutes <= 0 {
return "即将过期"
}
if minutes < 60 {
return fmt.Sprintf("%d 分钟", minutes)
}
hours := minutes / 60
if hours < 24 {
return fmt.Sprintf("%d 小时", hours)
}
days := hours / 24
remainHours := hours % 24
if remainHours == 0 {
return fmt.Sprintf("%d 天", days)
}
return fmt.Sprintf("%d 天 %d 小时", days, remainHours)
}