u
This commit is contained in:
1 parent
b116690769
commit
1d6bb2b7f5
23 files changed
+922
-116
No files matched your search
@@ -1,25 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/api"
|
||||
"base-go-v2/internal/config"
|
||||
"base-go-v2/internal/database"
|
||||
"base-go-v2/internal/logx"
|
||||
"fmt"
|
||||
"github.com/valyala/fasthttp"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化配置
|
||||
err := config.InitAppConfig("./configs/app.yaml")
|
||||
if err != nil {
|
||||
if err := config.InitAppConfig("./configs/app.yaml"); err != nil {
|
||||
log.Fatalf("加载配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化数据库
|
||||
err = database.InitDb()
|
||||
if err != nil {
|
||||
if err := database.InitDb(); err != nil {
|
||||
log.Fatalf("加载数据库配置失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化日志
|
||||
logx.InitLogger()
|
||||
|
||||
// 初始化路由
|
||||
r := api.InitAppRouter()
|
||||
addr := fmt.Sprintf(":%d", config.App.Server.Port)
|
||||
|
||||
log.Printf("✅ 服务器启动成功,端口%s", addr)
|
||||
|
||||
// 启动服务器(简洁写法)
|
||||
if err := fasthttp.ListenAndServe(addr, r.Handler); err != nil {
|
||||
log.Fatalf("❌ 服务器启动失败: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
server:
|
||||
port: 8085
|
||||
baseUrl: '/app'
|
||||
|
||||
snowflake:
|
||||
datacenterID: 0
|
||||
@@ -23,5 +24,5 @@ log:
|
||||
daily: true # 是否按天滚动
|
||||
maxDays: 30 # 保留天数,0表示无限
|
||||
caller: true # 是否记录调用文件行号
|
||||
levelDirs: false # 是否按级别目录
|
||||
levelDirs: true # 是否按级别目录
|
||||
console: true # 是否打印到控制台
|
||||
@@ -6,6 +6,7 @@ require (
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/spf13/viper v1.21.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -23,7 +24,6 @@ require (
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package auth
|
||||
|
||||
func InitAuthRouter() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import "base-go-v2/internal/router"
|
||||
|
||||
func CORSMiddleware() router.HandlerFunc {
|
||||
return func(c *router.Context) error {
|
||||
origin := string(c.RequestCtx.Request.Header.Peek("Origin"))
|
||||
if origin == "" {
|
||||
origin = "*"
|
||||
}
|
||||
|
||||
c.RequestCtx.Response.Header.Set("Access-Control-Allow-Origin", origin)
|
||||
c.RequestCtx.Response.Header.Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
c.RequestCtx.Response.Header.Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
c.RequestCtx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
if string(c.RequestCtx.Method()) == "OPTIONS" {
|
||||
c.RequestCtx.SetStatusCode(200)
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/logx"
|
||||
"base-go-v2/internal/reponse"
|
||||
"base-go-v2/internal/router"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func ErrorMiddleware() router.HandlerFunc {
|
||||
return func(c *router.Context) error {
|
||||
err := c.Next()
|
||||
|
||||
if err != nil {
|
||||
stack := string(debug.Stack())
|
||||
|
||||
logx.Logger.Error("请求错误",
|
||||
logx.String("path", string(c.RequestCtx.Path())),
|
||||
logx.String("method", string(c.RequestCtx.Method())),
|
||||
logx.String("error", err.Error()),
|
||||
logx.String("stack", stack),
|
||||
)
|
||||
|
||||
return response.Error(c).Message(err.Error()).Send()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/logx"
|
||||
"base-go-v2/internal/router"
|
||||
"base-go-v2/internal/routinex"
|
||||
"base-go-v2/internal/utils/uid"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TrackTime 记录开始时间到现在时间的耗时
|
||||
func TrackTime(pre time.Time) time.Duration {
|
||||
elapsed := time.Since(pre)
|
||||
|
||||
return elapsed
|
||||
}
|
||||
|
||||
func LoggerMiddleware() router.HandlerFunc {
|
||||
return func(c *router.Context) error {
|
||||
start := time.Now() // 记录开始时间
|
||||
|
||||
// 生成 trackID 并存入 routinex
|
||||
trackID := uid.NextID()
|
||||
routinex.Set(logx.TrackID, trackID)
|
||||
|
||||
// 执行业务逻辑,并捕获错误
|
||||
err := c.Next()
|
||||
|
||||
// 请求结束后计算耗时
|
||||
elapsed := TrackTime(start)
|
||||
|
||||
ip := c.RequestCtx.RemoteIP().String()
|
||||
|
||||
logx.Logger.Info("接口请求",
|
||||
logx.String("path", string(c.RequestCtx.Path())),
|
||||
logx.String("method", string(c.RequestCtx.Method())),
|
||||
logx.String("ip", ip),
|
||||
logx.Duration("elapsed", elapsed),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/logx"
|
||||
response "base-go-v2/internal/reponse"
|
||||
"base-go-v2/internal/router"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func RecoveryMiddleware() router.HandlerFunc {
|
||||
return func(c *router.Context) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
stack := string(debug.Stack()) // 获取堆栈
|
||||
|
||||
logx.Logger.Error("请求 panic",
|
||||
logx.String("path", string(c.RequestCtx.Path())),
|
||||
logx.String("method", string(c.RequestCtx.Method())),
|
||||
logx.String("panic", r.(error).Error()),
|
||||
logx.String("stack", stack),
|
||||
)
|
||||
|
||||
// 返回统一 JSON 响应
|
||||
_ = response.Error(c).Message("系统 Panic").Send()
|
||||
}
|
||||
}()
|
||||
|
||||
// 调用后续 handler
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/api/middleware"
|
||||
"base-go-v2/internal/config"
|
||||
response "base-go-v2/internal/reponse"
|
||||
"base-go-v2/internal/router"
|
||||
)
|
||||
|
||||
func InitAppRouter() *router.Router {
|
||||
r := router.NewRouter(config.App.Server.BaseUrl)
|
||||
|
||||
r.Use(middleware.RecoveryMiddleware())
|
||||
r.Use(middleware.CORSMiddleware())
|
||||
r.Use(middleware.LoggerMiddleware())
|
||||
r.Use(middleware.ErrorMiddleware())
|
||||
|
||||
r.GET("/test", func(c *router.Context) error {
|
||||
return response.Success(c).Send()
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Field = zap.Field
|
||||
|
||||
func String(key, val string) zap.Field { return zap.String(key, val) }
|
||||
func Stringp(key string, val *string) zap.Field { return zap.Stringp(key, val) }
|
||||
func Strings(key string, val []string) zap.Field { return zap.Strings(key, val) }
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"base-go-v2/internal/logx"
|
||||
"base-go-v2/internal/router"
|
||||
"base-go-v2/internal/routinex"
|
||||
"base-go-v2/internal/utils/strutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
TrackId string `json:"trackId"`
|
||||
}
|
||||
|
||||
type Code struct {
|
||||
Value string
|
||||
Message string
|
||||
}
|
||||
|
||||
var (
|
||||
CodeSuccess = Code{"0000", "请求成功"}
|
||||
CodeFail = Code{"0001", "请求失败"}
|
||||
CodeNoLogin = Code{"1001", "未登录"}
|
||||
CodeLoginExpired = Code{"1002", "登录已过期"}
|
||||
CodeInvalidToken = Code{"1003", "Token 无效"}
|
||||
CodeInvalidAccount = Code{"1005", "账号或密码错误"}
|
||||
)
|
||||
|
||||
const defaultMessage = "未知错误"
|
||||
|
||||
type Builder struct {
|
||||
c *router.Context
|
||||
result Result
|
||||
}
|
||||
|
||||
func Success(c *router.Context) *Builder {
|
||||
return &Builder{
|
||||
c: c,
|
||||
result: Result{
|
||||
Code: CodeSuccess.Value,
|
||||
Message: CodeSuccess.Message,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Error(c *router.Context) *Builder {
|
||||
return &Builder{
|
||||
c: c,
|
||||
result: Result{
|
||||
Code: CodeFail.Value,
|
||||
Message: CodeFail.Message,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) Code(code string) *Builder {
|
||||
b.result.Code = code
|
||||
b.result.Message = GetMessage(code)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) CodeObj(code Code) *Builder {
|
||||
b.result.Code = code.Value
|
||||
b.result.Message = code.Message
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) Message(msg string) *Builder {
|
||||
b.result.Message = msg
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) Data(data interface{}) *Builder {
|
||||
b.result.Data = data
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Builder) Send() error {
|
||||
// 默认 Data 为空
|
||||
if b.result.Data == nil {
|
||||
b.result.Data = ""
|
||||
}
|
||||
|
||||
// 从 routinex 获取 trackId
|
||||
trackId := routinex.Get(logx.TrackID)
|
||||
if trackId != nil {
|
||||
b.result.TrackId = strutil.ToString(trackId)
|
||||
}
|
||||
|
||||
// 发送响应
|
||||
return b.c.JSON(http.StatusOK, b.result)
|
||||
}
|
||||
|
||||
func GetMessage(value string) string {
|
||||
for _, c := range []Code{
|
||||
CodeSuccess, CodeFail, CodeNoLogin,
|
||||
CodeLoginExpired, CodeInvalidToken, CodeInvalidAccount,
|
||||
} {
|
||||
if c.Value == value {
|
||||
return c.Message
|
||||
}
|
||||
}
|
||||
return defaultMessage
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// BodyCache 缓存 fasthttp 请求体
|
||||
type BodyCache struct {
|
||||
once sync.Once
|
||||
Data []byte
|
||||
Err error
|
||||
}
|
||||
|
||||
// Load 读取并缓存请求体
|
||||
func (b *BodyCache) Load(ctx *fasthttp.RequestCtx) ([]byte, error) {
|
||||
b.once.Do(func() {
|
||||
b.Data = ctx.PostBody()
|
||||
})
|
||||
return b.Data, b.Err
|
||||
}
|
||||
|
||||
// Context fasthttp 上下文封装
|
||||
type Context struct {
|
||||
RequestCtx *fasthttp.RequestCtx
|
||||
params map[string]string
|
||||
index int
|
||||
handlers []HandlerFunc
|
||||
|
||||
bodyCache BodyCache
|
||||
keys map[string]interface{}
|
||||
aborted bool
|
||||
}
|
||||
|
||||
// Next 执行下一个中间件
|
||||
func (c *Context) Next() error {
|
||||
c.index++
|
||||
if c.index < len(c.handlers) {
|
||||
return c.handlers[c.index](c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Abort 中止执行
|
||||
func (c *Context) Abort() {
|
||||
c.aborted = true
|
||||
}
|
||||
|
||||
// Param 获取路由参数
|
||||
func (c *Context) Param(key string) string {
|
||||
return c.params[key]
|
||||
}
|
||||
|
||||
// Header 获取请求头
|
||||
func (c *Context) Header(key string) string {
|
||||
return string(c.RequestCtx.Request.Header.Peek(key))
|
||||
}
|
||||
|
||||
// Body 获取请求体
|
||||
func (c *Context) Body() ([]byte, error) {
|
||||
return c.bodyCache.Load(c.RequestCtx)
|
||||
}
|
||||
|
||||
// BindJSON 反序列化 JSON 请求体
|
||||
func (c *Context) BindJSON(obj interface{}) error {
|
||||
body, err := c.Body()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(body, obj)
|
||||
}
|
||||
|
||||
// PostForm 获取表单参数
|
||||
func (c *Context) PostForm(key string) string {
|
||||
return string(c.RequestCtx.FormValue(key))
|
||||
}
|
||||
|
||||
// Set 存储键值
|
||||
func (c *Context) Set(key string, value interface{}) {
|
||||
if c.keys == nil {
|
||||
c.keys = make(map[string]interface{})
|
||||
}
|
||||
c.keys[key] = value
|
||||
}
|
||||
|
||||
// Get 获取键值
|
||||
func (c *Context) Get(key string) (interface{}, bool) {
|
||||
if c.keys == nil {
|
||||
return nil, false
|
||||
}
|
||||
val, ok := c.keys[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// JSON 返回 JSON 响应,支持返回 error
|
||||
func (c *Context) JSON(statusCode int, data interface{}) error {
|
||||
c.RequestCtx.SetStatusCode(statusCode)
|
||||
c.RequestCtx.SetContentType("application/json; charset=utf-8")
|
||||
|
||||
if err := json.NewEncoder(c.RequestCtx).Encode(data); err != nil {
|
||||
c.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
c.Abort()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Text 返回纯文本,支持返回 error
|
||||
func (c *Context) Text(statusCode int, msg string) error {
|
||||
c.RequestCtx.SetStatusCode(statusCode)
|
||||
c.RequestCtx.SetContentType("text/plain; charset=utf-8")
|
||||
|
||||
if _, err := c.RequestCtx.WriteString(msg); err != nil {
|
||||
c.Abort()
|
||||
return err
|
||||
}
|
||||
|
||||
c.Abort()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func (n *node) matchChild(part string) *node {
|
||||
for _, child := range n.children {
|
||||
if child.part == part || child.isParam {
|
||||
return child
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) matchChildren(part string) []*node {
|
||||
nodes := make([]*node, 0)
|
||||
for _, child := range n.children {
|
||||
if child.part == part || child.isParam {
|
||||
nodes = append(nodes, child)
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (n *node) insert(pattern string, parts []string, height int, handler HandlerFunc) {
|
||||
if len(parts) == height {
|
||||
n.pattern = pattern
|
||||
n.handler = handler
|
||||
return
|
||||
}
|
||||
part := parts[height]
|
||||
child := n.matchChild(part)
|
||||
if child == nil {
|
||||
child = &node{
|
||||
part: part,
|
||||
isParam: len(part) > 0 && part[0] == ':',
|
||||
}
|
||||
n.children = append(n.children, child)
|
||||
}
|
||||
child.insert(pattern, parts, height+1, handler)
|
||||
}
|
||||
|
||||
func (n *node) search(parts []string, height int, params map[string]string) *node {
|
||||
if len(parts) == height || n.part == "*" {
|
||||
if n.pattern == "" {
|
||||
return nil
|
||||
}
|
||||
return n
|
||||
}
|
||||
part := parts[height]
|
||||
children := n.matchChildren(part)
|
||||
|
||||
for _, child := range children {
|
||||
if child.isParam {
|
||||
params[child.part[1:]] = part
|
||||
}
|
||||
res := child.search(parts, height+1, params)
|
||||
if res != nil {
|
||||
return res
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 中间件条目
|
||||
type middlewareEntry struct {
|
||||
handler HandlerFunc
|
||||
excludePaths map[string]struct{}
|
||||
}
|
||||
|
||||
// Router 主结构
|
||||
type Router struct {
|
||||
roots map[string]*node
|
||||
middleware []middlewareEntry
|
||||
basePath string
|
||||
}
|
||||
|
||||
// NewRouter 创建
|
||||
func NewRouter(basePath ...string) *Router {
|
||||
path := "/"
|
||||
if len(basePath) > 0 && basePath[0] != "" {
|
||||
path = basePath[0]
|
||||
}
|
||||
return &Router{
|
||||
roots: make(map[string]*node),
|
||||
basePath: path,
|
||||
}
|
||||
}
|
||||
|
||||
type MiddlewareHandle struct {
|
||||
router *Router
|
||||
entryIdx int
|
||||
}
|
||||
|
||||
func (r *Router) Use(handler HandlerFunc) *MiddlewareHandle {
|
||||
entry := middlewareEntry{handler: handler}
|
||||
r.middleware = append(r.middleware, entry)
|
||||
return &MiddlewareHandle{
|
||||
router: r,
|
||||
entryIdx: len(r.middleware) - 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (mh *MiddlewareHandle) ExcludePaths(paths ...string) *Router {
|
||||
excludeMap := make(map[string]struct{}, len(paths))
|
||||
for _, p := range paths {
|
||||
excludeMap[p] = struct{}{}
|
||||
}
|
||||
mh.router.middleware[mh.entryIdx].excludePaths = excludeMap
|
||||
return mh.router
|
||||
}
|
||||
|
||||
func (r *Router) Group(prefix string, m ...HandlerFunc) *Router {
|
||||
newMiddleware := make([]middlewareEntry, len(r.middleware))
|
||||
copy(newMiddleware, r.middleware)
|
||||
for _, handler := range m {
|
||||
entry := middlewareEntry{handler: handler}
|
||||
newMiddleware = append(newMiddleware, entry)
|
||||
}
|
||||
return &Router{
|
||||
roots: r.roots,
|
||||
middleware: newMiddleware,
|
||||
basePath: joinPaths(r.basePath, prefix),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) GET(path string, handler HandlerFunc) *Router {
|
||||
return r.handle("GET", path, handler)
|
||||
}
|
||||
|
||||
func (r *Router) POST(path string, handler HandlerFunc) *Router {
|
||||
return r.handle("POST", path, handler)
|
||||
}
|
||||
|
||||
func (r *Router) handle(method, path string, handler HandlerFunc) *Router {
|
||||
fullPath := joinPaths(r.basePath, path)
|
||||
parts := parsePattern(fullPath)
|
||||
if r.roots[method] == nil {
|
||||
r.roots[method] = &node{}
|
||||
}
|
||||
r.roots[method].insert(fullPath, parts, 0, handler)
|
||||
return r
|
||||
}
|
||||
|
||||
// Handler 入口函数(fasthttp)
|
||||
func (r *Router) Handler(ctx *fasthttp.RequestCtx) {
|
||||
method := string(ctx.Method())
|
||||
root := r.roots[method]
|
||||
if root == nil {
|
||||
ctx.Error("404 Not Found", fasthttp.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
searchParts := parsePattern(string(ctx.Path()))
|
||||
params := make(map[string]string)
|
||||
n := root.search(searchParts, 0, params)
|
||||
if n == nil || n.handler == nil {
|
||||
ctx.Error("404 Not Found", fasthttp.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 组装中间件链
|
||||
handlers := make([]HandlerFunc, 0)
|
||||
path := string(ctx.Path())
|
||||
for _, m := range r.middleware {
|
||||
if m.excludePaths != nil {
|
||||
if _, excluded := m.excludePaths[path]; excluded {
|
||||
continue
|
||||
}
|
||||
}
|
||||
handlers = append(handlers, m.handler)
|
||||
}
|
||||
handlers = append(handlers, n.handler) // 最终 handler
|
||||
|
||||
c := &Context{
|
||||
RequestCtx: ctx,
|
||||
params: params,
|
||||
index: -1,
|
||||
handlers: handlers,
|
||||
}
|
||||
|
||||
_ = c.Next()
|
||||
}
|
||||
|
||||
func parsePattern(pattern string) []string {
|
||||
vs := strings.Split(strings.Trim(pattern, "/"), "/")
|
||||
parts := make([]string, 0, len(vs))
|
||||
for _, item := range vs {
|
||||
if item != "" {
|
||||
parts = append(parts, item)
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func joinPaths(a, b string) string {
|
||||
if a == "" {
|
||||
if b == "" {
|
||||
return "/"
|
||||
}
|
||||
if !strings.HasPrefix(b, "/") {
|
||||
return "/" + b
|
||||
}
|
||||
return b
|
||||
}
|
||||
if b == "" {
|
||||
return a
|
||||
}
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
case aslash && bslash:
|
||||
return a + b[1:]
|
||||
case !aslash && !bslash:
|
||||
return a + "/" + b
|
||||
default:
|
||||
return a + b
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package router
|
||||
|
||||
type HandlerFunc func(c *Context) error
|
||||
|
||||
// node 路由树节点
|
||||
type node struct {
|
||||
pattern string
|
||||
part string
|
||||
children []*node
|
||||
isParam bool
|
||||
handler HandlerFunc
|
||||
}
|
||||
@@ -12,7 +12,8 @@ type AppConfig struct {
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port int
|
||||
Port int
|
||||
BaseUrl string
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"level":"ERROR","ts":"2025-11-01 13:46:05.593","caller":"middleware/recovery.go:16","msg":"请求 panic","path":"/app/test","method":"GET","panic":"interface conversion: interface {} is uint64, not string","stack":"goroutine 35 [running]:\nruntime/debug.Stack()\n\tD:/devtool/golang/go1.25.0/src/runtime/debug/stack.go:26 +0x5e\nbase-go-v2/internal/api.InitAppRouter.RecoveryMiddleware.func2.1()\n\tD:/workspace/code/base-project/base-go-v2/internal/api/middleware/recovery.go:14 +0x4b\npanic({0x7ff7c009b840?, 0xc000094240?})\n\tD:/devtool/golang/go1.25.0/src/runtime/panic.go:783 +0x132\nbase-go-v2/internal/reponse.(*Builder).Send(0xc0001b7640)\n\tD:/workspace/code/base-project/base-go-v2/internal/reponse/response.go:89 +0xeb\nbase-go-v2/internal/api.InitAppRouter.func1(0xa0?)\n\tD:/workspace/code/base-project/base-go-v2/internal/api/router.go:19 +0x6b\nbase-go-v2/internal/router.(*Context).Next(0x7ff7bfd30a85?)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/context.go:41 +0x35\nbase-go-v2/internal/api.InitAppRouter.ErrorMiddleware.func5(0xc0000a4080)\n\tD:/workspace/code/base-project/base-go-v2/internal/api/middleware/error.go:12 +0x2a\nbase-go-v2/internal/router.(*Context).Next(0x7ff7c010426d?)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/context.go:41 +0x35\nbase-go-v2/internal/api.InitAppRouter.LoggerMiddleware.func4(0xc0000a4080)\n\tD:/workspace/code/base-project/base-go-v2/internal/api/middleware/logger.go:27 +0x77\nbase-go-v2/internal/router.(*Context).Next(0xc0000a2660?)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/context.go:41 +0x35\nbase-go-v2/internal/api.InitAppRouter.CORSMiddleware.func3(0xc0000a4080)\n\tD:/workspace/code/base-project/base-go-v2/internal/api/middleware/cors.go:22 +0x159\nbase-go-v2/internal/router.(*Context).Next(0xc0001840a0?)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/context.go:41 +0x35\nbase-go-v2/internal/api.InitAppRouter.RecoveryMiddleware.func2(0xc0000900a0?)\n\tD:/workspace/code/base-project/base-go-v2/internal/api/middleware/recovery.go:29 +0x45\nbase-go-v2/internal/router.(*Context).Next(...)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/context.go:41\nbase-go-v2/internal/router.(*Router).Handler(0xc0001a81e0, 0xc0000a2608)\n\tD:/workspace/code/base-project/base-go-v2/internal/router/router.go:185 +0x402\ngithub.com/valyala/fasthttp.(*Server).serveConn(0xc0001d0008, {0x7ff7c01a5358, 0xc000096008})\n\tD:/devtool/golang/gopath/pkg/mod/github.com/valyala/fasthttp@v1.68.0/server.go:2465 +0x124b\ngithub.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0001d2000, 0xc000090040)\n\tD:/devtool/golang/gopath/pkg/mod/github.com/valyala/fasthttp@v1.68.0/workerpool.go:225 +0x92\ngithub.com/valyala/fasthttp.(*workerPool).getCh.func1()\n\tD:/devtool/golang/gopath/pkg/mod/github.com/valyala/fasthttp@v1.68.0/workerpool.go:197 +0x32\ncreated by github.com/valyala/fasthttp.(*workerPool).getCh in goroutine 1\n\tD:/devtool/golang/gopath/pkg/mod/github.com/valyala/fasthttp@v1.68.0/workerpool.go:196 +0x194\n","trackId":"375158538101587968"}
|
||||
@@ -0,0 +1,9 @@
|
||||
{"level":"INFO","ts":"2025-11-01 11:02:11.221","caller":"middleware/logger.go:31","msg":"请求结束","logType":"request","trackID":375117289755770880,"path":"/ping","method":"GET","elapsed":0.0009988,"trackId":"375117289755770880"}
|
||||
{"level":"INFO","ts":"2025-11-01 11:03:01.633","caller":"middleware/logger.go:31","msg":"请求结束","logType":"request","path":"/ping","method":"GET","elapsed":0.0008635,"trackId":"375117501199024128"}
|
||||
{"level":"INFO","ts":"2025-11-01 11:06:32.290","caller":"middleware/logger.go:34","msg":"请求结束","logType":"request","path":"/ping","method":"GET","clientIP":"\u0000\u0000\u0001","elapsed":0.0005278,"trackId":"375118384762716160"}
|
||||
{"level":"INFO","ts":"2025-11-01 11:07:13.255","caller":"middleware/logger.go:34","msg":"请求结束","logType":"request","path":"/ping","method":"GET","clientIP":"127.0.0.1","elapsed":0.0006027,"trackId":"375118556582379520"}
|
||||
{"level":"INFO","ts":"2025-11-01 11:07:55.599","caller":"middleware/logger.go:41","msg":"请求结束","logType":"request","path":"/ping","method":"GET","ip":"127.0.0.1","elapsed":0.0010004,"trackId":"375118734181793792"}
|
||||
{"level":"INFO","ts":"2025-11-01 13:19:29.451","caller":"middleware/logger.go:34","msg":"请求结束","path":"/app/app/ping","method":"GET","ip":"127.0.0.1","elapsed":0.0005252,"trackId":"375151843401007104"}
|
||||
{"level":"INFO","ts":"2025-11-01 13:26:31.817","caller":"middleware/logger.go:34","msg":"请求结束","path":"/app/test","method":"GET","ip":"127.0.0.1","elapsed":0.0019991,"trackId":"375153614924021760"}
|
||||
{"level":"INFO","ts":"2025-11-01 13:43:51.649","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/test","method":"GET","ip":"127.0.0.1","elapsed":0.0005048,"trackId":"375157976299732992"}
|
||||
{"level":"INFO","ts":"2025-11-01 13:46:59.502","caller":"middleware/logger.go:34","msg":"接口请求","path":"/app/test","method":"GET","ip":"127.0.0.1","elapsed":0.001,"trackId":"375158764212322304"}
|
||||
Whitespace-only changes.
Reference in new issue
Block a user