首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// setupAdminRoutes 配置后台管理路由
|
||||
// 登录入口(需配置路径):
|
||||
// - GET /my-secret-admin-123/login 登录页面
|
||||
// - POST /my-secret-admin-123/login 登录验证
|
||||
// - GET /my-secret-admin-123/captcha 验证码
|
||||
//
|
||||
// 后台管理路由(登录后访问):
|
||||
// - GET /dashboard 后台首页(仪表盘)
|
||||
// - GET /dashboard/home 后台首页
|
||||
// - GET /dashboard/users 人员列表页
|
||||
// - GET /dashboard/user/:id/resume 用户简历管理页
|
||||
// - GET /dashboard/user/:id/portfolio 用户作品集管理页
|
||||
// - GET /dashboard/user/:id/quiz 用户面试题库页
|
||||
// - GET /dashboard/menus 菜单管理页
|
||||
// - GET /dashboard/settings 系统设置页
|
||||
// - GET /dashboard/logout 退出登录
|
||||
func (r *Router) setupAdminRoutes(engine *gin.Engine) {
|
||||
adminPath := r.cfg.Auth.AdminPath
|
||||
if adminPath == "" {
|
||||
adminPath = "/admin"
|
||||
}
|
||||
|
||||
fmt.Printf("Setting up admin login routes at: %s\n", adminPath)
|
||||
|
||||
loginGroup := engine.Group(adminPath)
|
||||
{
|
||||
loginGroup.GET("/login", r.adminHandler.LoginPage)
|
||||
loginGroup.POST("/login", r.adminHandler.Login)
|
||||
loginGroup.GET("/captcha", r.adminHandler.Captcha)
|
||||
}
|
||||
|
||||
dashboard := engine.Group("/dashboard")
|
||||
dashboard.Use(r.adminHandler.AuthMiddleware())
|
||||
{
|
||||
dashboard.GET("/", r.adminHandler.HomePage)
|
||||
dashboard.GET("/home", r.adminHandler.HomePage)
|
||||
|
||||
dashboard.GET("/users", r.adminHandler.UsersPage)
|
||||
dashboard.GET("/user", func(c *gin.Context) {
|
||||
c.Redirect(http.StatusMovedPermanently, "/dashboard/users")
|
||||
})
|
||||
|
||||
dashboard.GET("/resumes", r.adminHandler.ResumesPage)
|
||||
dashboard.GET("/resume/add", r.adminHandler.ResumeAddPage)
|
||||
dashboard.GET("/resume/:id/edit", r.adminHandler.ResumeEditPageWithoutUser)
|
||||
dashboard.GET("/user/:id/resume", r.adminHandler.UserResumePage)
|
||||
dashboard.GET("/user/:id/resume/:resume_id/edit", r.adminHandler.ResumeEditPage)
|
||||
|
||||
dashboard.GET("/portfolios", r.adminHandler.PortfoliosPage)
|
||||
dashboard.GET("/user/:id/portfolio", r.adminHandler.UserPortfolioPage)
|
||||
dashboard.GET("/portfolio/add", r.adminHandler.PortfolioAddPage)
|
||||
dashboard.GET("/portfolio/:id/edit", r.adminHandler.PortfolioEditPage)
|
||||
|
||||
dashboard.GET("/quiz", r.adminHandler.QuizPage)
|
||||
dashboard.GET("/user/:id/quiz", r.adminHandler.UserQuizPage)
|
||||
dashboard.GET("/user/:id/quiz/generate", r.adminHandler.QuizGeneratePage)
|
||||
dashboard.GET("/user/:id/quiz/record/:record_id", r.adminHandler.QuizRecordDetailPage)
|
||||
dashboard.POST("/api/quiz/favorite/:id/toggle", r.adminHandler.ToggleFavoriteAPI)
|
||||
dashboard.DELETE("/api/quiz/favorite/:id", r.resumeHandler.DeleteFavoriteQuestionAPI)
|
||||
|
||||
dashboard.GET("/settings", r.adminHandler.SettingsPage)
|
||||
|
||||
dashboard.GET("/logout", r.adminHandler.Logout)
|
||||
|
||||
dashboard.GET("/api/user/:id", r.resumeHandler.GetUserAPI)
|
||||
dashboard.POST("/api/user", r.resumeHandler.CreateUserAPI)
|
||||
dashboard.PUT("/api/user/:id", r.resumeHandler.UpdateUserAPI)
|
||||
dashboard.DELETE("/api/user/:id", r.resumeHandler.DeleteUserAPI)
|
||||
|
||||
dashboard.GET("/api/users", r.resumeHandler.GetAllUsersAPI)
|
||||
|
||||
dashboard.GET("/api/settings", r.adminHandler.GetSettingsAPI)
|
||||
dashboard.POST("/api/settings", r.adminHandler.UpdateSettingsAPI)
|
||||
dashboard.POST("/api/settings/logo", r.adminHandler.UploadLogoAPI)
|
||||
dashboard.GET("/api/settings/system-info", r.adminHandler.SystemInfoAPI)
|
||||
dashboard.GET("/api/settings/login-history", r.adminHandler.LoginHistoryAPI)
|
||||
|
||||
dashboard.POST("/api/portfolio", r.resumeHandler.CreatePortfolioItemAPI)
|
||||
dashboard.GET("/api/portfolio/:id", r.resumeHandler.GetPortfolioItemAPI)
|
||||
dashboard.PUT("/api/portfolio/:id", r.resumeHandler.UpdatePortfolioItemAPI)
|
||||
dashboard.DELETE("/api/portfolio/:id", r.resumeHandler.DeletePortfolioItemAPI)
|
||||
|
||||
dashboard.POST("/api/resume/create", r.resumeHandler.AdminCreateResumeAPI)
|
||||
dashboard.POST("/api/resume/generate-from-document", r.resumeHandler.GenerateResumeFromDocumentAPI)
|
||||
dashboard.GET("/api/resume/:id", r.resumeHandler.GetResumeByIDAPI)
|
||||
dashboard.PUT("/api/resume/:id", r.resumeHandler.UpdateResumeByIDAPI)
|
||||
dashboard.PUT("/api/resume/:id/show_in_home", r.resumeHandler.UpdateResumeShowInHomeAPI)
|
||||
dashboard.PUT("/api/resume/:id/password", r.resumeHandler.UpdateResumePasswordAPI)
|
||||
dashboard.PUT("/api/resume/:id/user", r.resumeHandler.UpdateResumeUserIDAPI)
|
||||
dashboard.DELETE("/api/resume/:id", r.resumeHandler.DeleteResumeAPI)
|
||||
|
||||
dashboard.GET("/api/search", r.adminHandler.SearchAPI)
|
||||
dashboard.GET("/api/notifications", r.adminHandler.GetNotificationsAPI)
|
||||
dashboard.POST("/api/notifications/:id/read", r.adminHandler.MarkNotificationAsReadAPI)
|
||||
dashboard.POST("/api/notifications/read-all", r.adminHandler.MarkAllNotificationsAsReadAPI)
|
||||
dashboard.DELETE("/api/notifications", r.adminHandler.DeleteAllNotificationsAPI)
|
||||
dashboard.GET("/api/notifications/unread-count", r.adminHandler.GetUnreadNotificationCountAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupAPIRoutes 配置 API 路由,所有接口前缀为 /api
|
||||
// 按模块分组:
|
||||
// - /api/resume 简历相关接口
|
||||
// - /api/portfolio 作品集相关接口
|
||||
// - /api/user 用户相关接口
|
||||
// - /api/quiz 面试题库相关接口
|
||||
// - /api/ai AI 相关接口
|
||||
// - /api/document 文档相关接口
|
||||
// - /api/chat 聊天相关接口
|
||||
// - /api/system 系统配置相关接口
|
||||
// - /api/menu 菜单相关接口
|
||||
// - /api/im 实时聊天/IM相关接口
|
||||
func (r *Router) setupAPIRoutes(engine *gin.Engine) {
|
||||
api := engine.Group("/api")
|
||||
{
|
||||
r.setupResumeAPIRoutes(api)
|
||||
r.setupPortfolioAPIRoutes(api)
|
||||
r.setupUserAPIRoutes(api)
|
||||
r.setupQuizAPIRoutes(api)
|
||||
r.setupAIAPIRoutes(api)
|
||||
r.setupDocumentAPIRoutes(api)
|
||||
r.setupChatAPIRoutes(api)
|
||||
r.setupSystemAPIRoutes(api)
|
||||
r.setupIMRoutes(api)
|
||||
r.setupKnowledgeAPIRoutes(api)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupAIAPIRoutes 配置 AI 相关 API 路由
|
||||
func (r *Router) setupAIAPIRoutes(api *gin.RouterGroup) {
|
||||
aiGroup := api.Group("/ai")
|
||||
{
|
||||
aiGroup.POST("/polish", r.aiHandler.PolishText)
|
||||
aiGroup.GET("/provider", r.aiHandler.GetProviderInfo)
|
||||
|
||||
agentGroup := aiGroup.Group("/agent")
|
||||
{
|
||||
agentGroup.POST("/polish", r.aiHandler.AgentPolish)
|
||||
agentGroup.POST("/analyze", r.aiHandler.AgentAnalyze)
|
||||
agentGroup.POST("/generate-questions", r.aiHandler.GenerateQuestions)
|
||||
agentGroup.POST("/evaluate-answers", r.aiHandler.EvaluateAnswers)
|
||||
agentGroup.POST("/submit-answers", r.aiHandler.SubmitAnswers)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupChatAPIRoutes 配置聊天相关 API 路由
|
||||
func (r *Router) setupChatAPIRoutes(api *gin.RouterGroup) {
|
||||
chatGroup := api.Group("/chat")
|
||||
{
|
||||
chatGroup.POST("", r.chatHandler.Chat)
|
||||
chatGroup.GET("/history", r.chatHandler.GetChatHistory)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupDocumentAPIRoutes 配置文档相关 API 路由
|
||||
func (r *Router) setupDocumentAPIRoutes(api *gin.RouterGroup) {
|
||||
documentGroup := api.Group("/document")
|
||||
{
|
||||
documentGroup.POST("/upload", r.documentHandler.UploadDocument)
|
||||
documentGroup.GET("/list", r.documentHandler.GetDocuments)
|
||||
documentGroup.GET("/:id", r.documentHandler.GetDocument)
|
||||
documentGroup.DELETE("/:id", r.documentHandler.DeleteDocument)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func (r *Router) setupIMRoutes(api *gin.RouterGroup) {
|
||||
imGroup := api.Group("/im")
|
||||
{
|
||||
imGroup.GET("/ws", r.imHandler.WebSocket)
|
||||
imGroup.POST("/chat", r.imHandler.Chat)
|
||||
imGroup.POST("/chat/stream", r.imHandler.ChatStream)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func (r *Router) setupKnowledgeAPIRoutes(api *gin.RouterGroup) {
|
||||
if r.knowledgeHandler == nil {
|
||||
return
|
||||
}
|
||||
knowledgeGroup := api.Group("/knowledge")
|
||||
{
|
||||
knowledgeGroup.GET("/search", r.knowledgeHandler.Search)
|
||||
knowledgeGroup.GET("/stats", r.knowledgeHandler.GetStats)
|
||||
knowledgeGroup.POST("/docs", r.knowledgeHandler.AddDocument)
|
||||
knowledgeGroup.POST("/resume", r.knowledgeHandler.AddResume)
|
||||
knowledgeGroup.POST("/text", r.knowledgeHandler.AddText)
|
||||
knowledgeGroup.POST("/chat", r.knowledgeHandler.Chat)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupPortfolioAPIRoutes 配置作品集相关 API 路由
|
||||
func (r *Router) setupPortfolioAPIRoutes(api *gin.RouterGroup) {
|
||||
portfolioGroup := api.Group("/portfolio")
|
||||
{
|
||||
portfolioGroup.GET("", r.resumeHandler.GetPortfolioItemsAPI)
|
||||
portfolioGroup.POST("", r.resumeHandler.CreatePortfolioItemAPI)
|
||||
portfolioGroup.GET("/:id", r.resumeHandler.GetPortfolioItemAPI)
|
||||
portfolioGroup.PUT("/:id", r.resumeHandler.UpdatePortfolioItemAPI)
|
||||
portfolioGroup.DELETE("/:id", r.resumeHandler.DeletePortfolioItemAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupQuizAPIRoutes 配置面试题库相关 API 路由
|
||||
func (r *Router) setupQuizAPIRoutes(api *gin.RouterGroup) {
|
||||
quizGroup := api.Group("/quiz")
|
||||
{
|
||||
quizGroup.POST("/record", r.resumeHandler.CreateQuizRecordAPI)
|
||||
quizGroup.GET("/records", r.resumeHandler.GetQuizRecordsAPI)
|
||||
quizGroup.GET("/record/:id", r.resumeHandler.GetQuizRecordAPI)
|
||||
quizGroup.DELETE("/record/:id", r.resumeHandler.DeleteQuizRecordAPI)
|
||||
|
||||
quizGroup.POST("/favorite", r.resumeHandler.CreateFavoriteQuestionAPI)
|
||||
quizGroup.GET("/favorites", r.resumeHandler.GetFavoriteQuestionsAPI)
|
||||
quizGroup.DELETE("/favorite/:id", r.resumeHandler.DeleteFavoriteQuestionAPI)
|
||||
quizGroup.PUT("/favorite/:id/toggle", r.resumeHandler.ToggleFavoriteAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupResumeAPIRoutes 配置简历相关 API 路由
|
||||
func (r *Router) setupResumeAPIRoutes(api *gin.RouterGroup) {
|
||||
resumeGroup := api.Group("/resume")
|
||||
{
|
||||
resumeGroup.GET("", r.resumeHandler.GetResumeAPI)
|
||||
resumeGroup.POST("", r.resumeHandler.CreateResumeAPI)
|
||||
resumeGroup.PUT("", r.resumeHandler.UpdateResumeAPI)
|
||||
resumeGroup.DELETE("/:id", r.resumeHandler.DeleteResumeAPI)
|
||||
resumeGroup.GET("/list", r.resumeHandler.GetAllResumesAPI)
|
||||
resumeGroup.GET("/route-check", r.resumeHandler.RouteCheckAPI)
|
||||
resumeGroup.POST("/generate-from-document", r.resumeHandler.GenerateResumeFromDocumentAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupSystemAPIRoutes 配置系统配置和菜单相关 API 路由
|
||||
func (r *Router) setupSystemAPIRoutes(api *gin.RouterGroup) {
|
||||
systemGroup := api.Group("/system")
|
||||
{
|
||||
systemGroup.GET("/config", r.resumeHandler.GetSystemConfigAPI)
|
||||
systemGroup.POST("/config", r.resumeHandler.UpdateSystemConfigAPI)
|
||||
}
|
||||
|
||||
menuGroup := api.Group("/menu")
|
||||
{
|
||||
menuGroup.GET("/list", r.resumeHandler.GetAllMenusAPI)
|
||||
menuGroup.POST("", r.resumeHandler.CreateMenuAPI)
|
||||
menuGroup.PUT("/:id", r.resumeHandler.UpdateMenuAPI)
|
||||
menuGroup.DELETE("/:id", r.resumeHandler.DeleteMenuAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupUserAPIRoutes 配置用户相关 API 路由
|
||||
func (r *Router) setupUserAPIRoutes(api *gin.RouterGroup) {
|
||||
userGroup := api.Group("/user")
|
||||
{
|
||||
userGroup.POST("", r.resumeHandler.CreateUserAPI)
|
||||
userGroup.GET("/list", r.resumeHandler.GetAllUsersAPI)
|
||||
userGroup.GET("/:id", r.resumeHandler.GetUserAPI)
|
||||
userGroup.PUT("/:id", r.resumeHandler.UpdateUserAPI)
|
||||
userGroup.DELETE("/:id", r.resumeHandler.DeleteUserAPI)
|
||||
userGroup.GET("/resumes", r.resumeHandler.GetResumesByUserAPI)
|
||||
userGroup.GET("/portfolio", r.resumeHandler.GetPortfolioItemsByUserAPI)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package router
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// setupFrontendRoutes 配置前端页面路由
|
||||
// 路由结构:
|
||||
// - / 产品首页,展示项目介绍和所有简历列表
|
||||
// - /:route 个人简历页面,如 /zhangsan
|
||||
// - /:route/portfolio 个人作品集页面,如 /zhangsan/portfolio
|
||||
// - /:route/contact 个人联系方式页面,如 /zhangsan/contact
|
||||
// - /sitemap.xml 站点地图XML,用于搜索引擎优化
|
||||
func (r *Router) setupFrontendRoutes(engine *gin.Engine) {
|
||||
engine.GET("/", r.resumeHandler.ShowHome)
|
||||
engine.GET("/sitemap.xml", r.resumeHandler.Sitemap)
|
||||
engine.GET("/:route", r.resumeHandler.ShowResume)
|
||||
engine.POST("/:route/verify-password", r.resumeHandler.VerifyPassword)
|
||||
engine.GET("/:route/portfolio", r.resumeHandler.ShowPortfolio)
|
||||
engine.GET("/:route/contact", r.resumeHandler.ShowContact)
|
||||
|
||||
engine.NoRoute(func(c *gin.Context) {
|
||||
c.HTML(404, "web/404", nil)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// 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])
|
||||
}
|
||||
Reference in New Issue
Block a user