199 lines
5.4 KiB
Go
199 lines
5.4 KiB
Go
// Package router 负责 HTTP 路由的注册与管理
|
|
// 按功能模块分为前台路由、后台管理路由和 API 路由三类
|
|
package router
|
|
|
|
import (
|
|
"html/template"
|
|
"net/http"
|
|
"resume-platform/internal/handler"
|
|
"resume-platform/internal/middleware"
|
|
"resume-platform/internal/model"
|
|
"resume-platform/pkg/config"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Router 路由管理器,持有各模块 Handler 实例和全局配置
|
|
type Router struct {
|
|
resumeHandler *handler.ResumeHandler
|
|
adminHandler *handler.AdminHandler
|
|
aiHandler *handler.AIHandler
|
|
documentHandler *handler.DocumentHandler
|
|
chatHandler *handler.ChatHandler
|
|
imHandler *handler.IMHandler
|
|
knowledgeHandler *handler.KnowledgeHandler
|
|
cfg *config.Config
|
|
}
|
|
|
|
// NewRouter 创建路由管理器实例
|
|
// @param resumeHandler 简历相关处理器
|
|
// @param adminHandler 后台管理相关处理器
|
|
// @param aiHandler AI 相关处理器
|
|
// @param documentHandler 文档相关处理器
|
|
// @param chatHandler 聊天相关处理器
|
|
// @param cfg 全局配置
|
|
// @return *Router 路由管理器实例
|
|
// @author sunct
|
|
func NewRouter(resumeHandler *handler.ResumeHandler, adminHandler *handler.AdminHandler, aiHandler *handler.AIHandler, documentHandler *handler.DocumentHandler, chatHandler *handler.ChatHandler, imHandler *handler.IMHandler, knowledgeHandler *handler.KnowledgeHandler, cfg *config.Config) *Router {
|
|
return &Router{
|
|
resumeHandler: resumeHandler,
|
|
adminHandler: adminHandler,
|
|
aiHandler: aiHandler,
|
|
documentHandler: documentHandler,
|
|
chatHandler: chatHandler,
|
|
imHandler: imHandler,
|
|
knowledgeHandler: knowledgeHandler,
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
// Setup 初始化 Gin 引擎并注册所有路由、中间件和模板函数
|
|
// 注册顺序:中间件 → 模板函数 → 静态资源 → API 路由 → 后台路由 → 前台路由 → 404 处理
|
|
// @return *gin.Engine 配置完成的 Gin 引擎
|
|
// @author sunct
|
|
func (r *Router) Setup() *gin.Engine {
|
|
gin.SetMode(r.cfg.Server.Mode)
|
|
engine := gin.New()
|
|
|
|
engine.Use(middleware.Tracing("resume-platform"))
|
|
engine.Use(middleware.RequestLogger())
|
|
engine.Use(gin.Recovery())
|
|
|
|
engine.SetFuncMap(template.FuncMap{
|
|
"dict": dict,
|
|
"set": set,
|
|
"merge": merge,
|
|
"slice": slice,
|
|
"append": appendFunc,
|
|
"substr": substr,
|
|
"first": func(n int, s interface{}) interface{} {
|
|
switch v := s.(type) {
|
|
case []*model.Resume:
|
|
if n > len(v) {
|
|
return v
|
|
}
|
|
return v[:n]
|
|
default:
|
|
return s
|
|
}
|
|
},
|
|
})
|
|
|
|
engine.LoadHTMLGlob("templates/web/*.html")
|
|
engine.Static("/static", "./static")
|
|
|
|
r.setupAPIRoutes(engine)
|
|
r.setupAdminRoutes(engine)
|
|
r.setupFrontendRoutes(engine)
|
|
|
|
engine.NoRoute(func(c *gin.Context) {
|
|
if len(c.Request.URL.Path) > 10 && c.Request.URL.Path[:10] == "/dashboard" {
|
|
r.adminHandler.DashboardNotFound(c)
|
|
return
|
|
}
|
|
c.Status(http.StatusNotFound)
|
|
c.HTML(http.StatusNotFound, "404.html", nil)
|
|
})
|
|
|
|
return engine
|
|
}
|
|
|
|
// dict 模板函数,根据传入的键值对构建 map[string]interface{}
|
|
// 传入参数必须为偶数个,按 key, value 交替排列
|
|
// @param values 键值对列表,格式:key1, value1, key2, value2...
|
|
// @return map[string]interface{} 构建的字典
|
|
// @author sunct
|
|
func dict(values ...interface{}) (map[string]interface{}, error) {
|
|
if len(values)%2 != 0 {
|
|
return nil, nil
|
|
}
|
|
m := make(map[string]interface{}, len(values)/2)
|
|
for i := 0; i < len(values); i += 2 {
|
|
key, ok := values[i].(string)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
m[key] = values[i+1]
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// set 模板函数,向 map 中设置键值对
|
|
// @param m 目标 map
|
|
// @param key 键名
|
|
// @param value 值
|
|
// @return interface{} 始终返回 nil
|
|
// @author sunct
|
|
func set(m map[string]interface{}, key string, value interface{}) interface{} {
|
|
m[key] = value
|
|
return nil
|
|
}
|
|
|
|
// merge 模板函数,将 src map 中的所有键值合并到 dest map
|
|
// @param dest 目标 map
|
|
// @param src 源 map
|
|
// @return map[string]interface{} 合并后的 dest
|
|
// @author sunct
|
|
func merge(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
|
for k, v := range src {
|
|
dest[k] = v
|
|
}
|
|
return dest
|
|
}
|
|
|
|
// slice 模板函数,将传入的多个参数组装为切片
|
|
// @param values 任意数量的参数
|
|
// @return []interface{} 参数切片
|
|
// @author sunct
|
|
func slice(values ...interface{}) []interface{} {
|
|
return values
|
|
}
|
|
|
|
// appendFunc 模板函数,向切片末尾追加一个元素
|
|
// @param slice 目标切片
|
|
// @param value 待追加元素
|
|
// @return []interface{} 追加后的切片
|
|
// @author sunct
|
|
func appendFunc(slice []interface{}, value interface{}) []interface{} {
|
|
return append(slice, value)
|
|
}
|
|
|
|
// substr 模板函数,按 rune 截取字符串,支持中文等 Unicode 字符
|
|
// @param s 源字符串
|
|
// @param start 起始位置,支持负数(从末尾倒数)
|
|
// @param length 可选,截取长度;不传则截取到末尾
|
|
// @return string 截取后的子串
|
|
// @author sunct
|
|
func substr(s string, start int, length ...int) string {
|
|
if len(s) == 0 {
|
|
return ""
|
|
}
|
|
|
|
runes := []rune(s)
|
|
runeLen := len(runes)
|
|
|
|
startIdx := 0
|
|
if start < 0 {
|
|
startIdx = runeLen + start
|
|
if startIdx < 0 {
|
|
startIdx = 0
|
|
}
|
|
} else {
|
|
startIdx = start
|
|
}
|
|
|
|
if startIdx >= runeLen {
|
|
return ""
|
|
}
|
|
|
|
if len(length) == 0 {
|
|
return string(runes[startIdx:])
|
|
}
|
|
|
|
endIdx := startIdx + length[0]
|
|
if endIdx > runeLen {
|
|
endIdx = runeLen
|
|
}
|
|
|
|
return string(runes[startIdx:endIdx])
|
|
} |