panghu пре 8 месеци
комит
cea178bbe5
11 измењених фајлова са 494 додато и 0 уклоњено
  1. 8 0
      .idea/.gitignore
  2. 6 0
      .idea/git_toolbox_blame.xml
  3. 9 0
      .idea/go_zh.iml
  4. 8 0
      .idea/modules.xml
  5. 6 0
      .idea/vcs.xml
  6. 43 0
      go.mod
  7. 98 0
      go.sum
  8. 76 0
      main.go
  9. 108 0
      middleware/logger.go
  10. 110 0
      pkg/logger/logger.go
  11. 22 0
      router/init.go

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 6 - 0
.idea/git_toolbox_blame.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GitToolBoxBlameSettings">
+    <option name="version" value="2" />
+  </component>
+</project>

+ 9 - 0
.idea/go_zh.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/go_zh.iml" filepath="$PROJECT_DIR$/.idea/go_zh.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 43 - 0
go.mod

@@ -0,0 +1,43 @@
+module go_zh
+
+go 1.25.6
+
+require go.uber.org/zap v1.27.1
+
+require (
+	github.com/bytedance/gopkg v0.1.3 // indirect
+	github.com/bytedance/sonic v1.15.0 // indirect
+	github.com/bytedance/sonic/loader v0.5.0 // indirect
+	github.com/cloudwego/base64x v0.1.6 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.13 // indirect
+	github.com/gin-contrib/sse v1.1.0 // indirect
+	github.com/gin-gonic/gin v1.11.0 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.30.1 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/goccy/go-yaml v1.19.2 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+	github.com/quic-go/qpack v0.6.0 // indirect
+	github.com/quic-go/quic-go v0.59.0 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.3.1 // indirect
+	go.uber.org/mock v0.6.0 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	golang.org/x/arch v0.23.0 // indirect
+	golang.org/x/crypto v0.47.0 // indirect
+	golang.org/x/mod v0.32.0 // indirect
+	golang.org/x/net v0.49.0 // indirect
+	golang.org/x/sync v0.19.0 // indirect
+	golang.org/x/sys v0.40.0 // indirect
+	golang.org/x/text v0.33.0 // indirect
+	golang.org/x/tools v0.41.0 // indirect
+	google.golang.org/protobuf v1.36.11 // indirect
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+)

+ 98 - 0
go.sum

@@ -0,0 +1,98 @@
+github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
+github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
+github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
+github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
+github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
+github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
+github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
+github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
+github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
+github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
+github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
+github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
+github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
+github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
+github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
+github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
+github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
+go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
+golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
+golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
+golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
+golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
+golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
+golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
+golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
+golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
+golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
+golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
+golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 76 - 0
main.go

@@ -0,0 +1,76 @@
+package main
+
+import (
+	"context"
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"go_zh/middleware"
+	"go_zh/pkg/logger"
+	"go_zh/router"
+	"net/http"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+)
+
+func main() {
+
+	// 1. 初始化日志系统
+	if err := logger.InitLogger(); err != nil {
+		panic("初始化日志失败: " + err.Error())
+	}
+	// 确保程序退出前刷新日志缓冲区
+	defer logger.Sync()
+	// 初始化Gin引擎
+	//gin.SetMode(gin.ReleaseMode)
+	engine := gin.Default()
+
+	engine.Use(middleware.GinLoggerMiddleware())   //日志中间件
+	engine.Use(middleware.GinRecoveryMiddleware()) //recover中间件
+
+	// 设置路由
+	router.SetupRoutes(engine)
+
+	// 启动服务
+	//_ = engine.Run(":8080")
+	srv := &http.Server{
+		Addr:    ":8080",
+		Handler: engine.Handler(),
+	}
+	// 10. 优雅启动和关闭
+	startServer(srv)
+}
+
+// startServer 启动服务器
+func startServer(srv *http.Server) {
+
+	// 在goroutine中启动服务器
+	go func() {
+		zap.L().Info("启动HTTP服务器",
+			zap.String("address", srv.Addr),
+		)
+
+		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+			zap.L().Fatal("启动服务器失败", zap.Error(err))
+		}
+	}()
+
+	// 等待中断信号
+	quit := make(chan os.Signal, 1)
+	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+	<-quit
+
+	zap.L().Info("收到关闭信号,开始优雅关闭...")
+
+	// 创建关闭超时上下文
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	// 关闭服务器
+	if err := srv.Shutdown(ctx); err != nil {
+		zap.L().Error("服务器关闭失败", zap.Error(err))
+	}
+
+	zap.L().Info("服务器已关闭")
+}

+ 108 - 0
middleware/logger.go

@@ -0,0 +1,108 @@
+package middleware
+
+import (
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+// GinLoggerMiddleware Gin日志中间件
+func GinLoggerMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// 记录请求开始时间
+		start := time.Now()
+
+		// 记录请求路径
+		path := c.Request.URL.Path
+		// 记录查询参数
+		query := c.Request.URL.RawQuery
+
+		// 处理请求
+		c.Next()
+
+		// 计算请求处理耗时
+		latency := time.Since(start)
+
+		// 获取响应状态码
+		status := c.Writer.Status()
+		// 获取客户端IP
+		clientIP := c.ClientIP()
+		// 获取请求方法
+		method := c.Request.Method
+		// 获取用户代理
+		userAgent := c.Request.UserAgent()
+		// 获取响应体大小
+		bodySize := c.Writer.Size()
+
+		// 获取错误信息(如果有)
+		var errorMsg string
+		if len(c.Errors) > 0 {
+			errorMsg = c.Errors.String()
+		}
+
+		// 构建日志字段
+		fields := []zapcore.Field{
+			zap.Int("status", status),                                    // 响应状态码
+			zap.String("method", method),                                 // 请求方法
+			zap.String("path", path),                                     // 请求路径
+			zap.String("query", query),                                   // 查询参数
+			zap.String("ip", clientIP),                                   // 客户端IP
+			zap.String("user-agent", userAgent),                          // 用户代理
+			zap.Duration("latency", latency),                             // 处理耗时
+			zap.Int("body_size", bodySize),                               // 响应体大小
+			zap.String("time", time.Now().Format("2006-01-02 15:04:05")), // 请求时间
+		}
+
+		// 如果存在错误,添加错误信息字段
+		if errorMsg != "" {
+			fields = append(fields, zap.String("error", errorMsg))
+		}
+		// 根据状态码级别记录日志
+		switch {
+		case status >= 500:
+			// 服务器错误(5xx):使用Error级别
+			zap.L().Error("服务器错误", fields...)
+		case status >= 400:
+			// 客户端错误(4xx):使用Warn级别
+			zap.L().Warn("客户端错误", fields...)
+		default:
+			// 成功请求(2xx/3xx):使用Info级别
+			zap.L().Info("请求完成", fields...)
+		}
+	}
+}
+
+// GinRecoveryMiddleware Gin恢复中间件(捕获panic)
+func GinRecoveryMiddleware() gin.HandlerFunc {
+
+	return func(c *gin.Context) {
+		defer func() {
+			if err := recover(); err != nil {
+				// 获取客户端IP
+				clientIP := c.ClientIP()
+				// 获取请求信息
+				path := c.Request.URL.Path
+				method := c.Request.Method
+
+				// 记录panic详细信息
+				zap.L().Error("请求发生panic",
+					zap.Any("error", err),                             // panic错误
+					zap.String("method", method),                      // 请求方法
+					zap.String("path", path),                          // 请求路径
+					zap.String("ip", clientIP),                        // 客户端IP
+					zap.String("stack", string(zap.Stack("").String)), // 堆栈信息
+				)
+
+				// 返回500错误响应
+				c.AbortWithStatusJSON(500, gin.H{
+					"code":    500,
+					"message": "服务器内部错误",
+				})
+			}
+		}()
+
+		c.Next()
+	}
+}

+ 110 - 0
pkg/logger/logger.go

@@ -0,0 +1,110 @@
+package logger
+
+import (
+	"fmt"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"gopkg.in/natefinch/lumberjack.v2"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+// 全局日志实例
+var Logger *zap.Logger
+var SugarLogger *zap.SugaredLogger
+
+// InitLogger 初始化日志配置
+func InitLogger() error {
+	// 创建核心日志写入器配置
+	encoderConfig := zapcore.EncoderConfig{
+		TimeKey:       "time",                    // 时间字段的键名
+		LevelKey:      "level",                   // 级别字段的键名
+		NameKey:       "logger",                  // 记录器名字段的键名
+		CallerKey:     "caller",                  // 调用者字段的键名
+		MessageKey:    "msg",                     // 消息字段的键名
+		StacktraceKey: "stacktrace",              // 堆栈跟踪字段的键名
+		LineEnding:    zapcore.DefaultLineEnding, // 行结束符
+		// 时间格式配置
+		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+			enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
+		},
+		// 日志级别格式配置(大写显示)
+		EncodeLevel: zapcore.CapitalLevelEncoder,
+		// 调用者信息格式配置
+		EncodeCaller: zapcore.ShortCallerEncoder,
+		// 日志持续时间格式配置
+		EncodeDuration: zapcore.SecondsDurationEncoder,
+	}
+	logDir := "logs"
+	// 创建日志目录
+	_ = os.MkdirAll(logDir, 0755)
+
+	dateStr := time.Now().Format("2006-01-02")
+	logFileName := filepath.Join(logDir, fmt.Sprintf("app-%s.log", dateStr))
+	lumberJackLogger := &lumberjack.Logger{
+		Filename:   logFileName, // 日志文件路径
+		MaxSize:    100,         // 每个日志文件最大大小(MB)
+		MaxBackups: 30,          // 保留的旧日志文件最大数量
+		MaxAge:     30,          // 保留日志的最大天数
+		Compress:   true,        // 是否压缩旧的日志文件
+		LocalTime:  true,        // 使用本地时间
+	}
+	// 创建控制台输出编码器(适合开发环境)
+	//consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)
+	// 控制台写入器
+	//consoleWriteSyncer := zapcore.AddSync(os.Stdout)
+
+	// 创建文件输出编码器(适合生产环境)
+	fileEncoder := zapcore.NewJSONEncoder(encoderConfig)
+
+	// 设置日志级别
+	logLevel := zapcore.InfoLevel
+
+	// 创建多个日志写入目标
+	// 文件写入器(使用Lumberjack)
+	fileWriteSyncer := zapcore.AddSync(lumberJackLogger)
+
+	// 创建核心日志器,同时输出到文件和控制台
+	core := zapcore.NewTee(
+		// 文件核心:JSON格式,包含所有级别日志
+		zapcore.NewCore(fileEncoder, fileWriteSyncer, logLevel),
+		// 控制台核心:控制台格式,只输出INFO及以上级别
+		//zapcore.NewCore(consoleEncoder, consoleWriteSyncer, zapcore.InfoLevel),
+	)
+
+	// 创建Logger实例
+	Logger = zap.New(core,
+		zap.AddCaller(),                       // 添加调用者信息
+		zap.AddCallerSkip(1),                  // 跳过一层调用堆栈
+		zap.AddStacktrace(zapcore.ErrorLevel), // 错误级别添加堆栈跟踪
+	)
+
+	// 创建SugaredLogger(更方便的日志接口)
+	SugarLogger = Logger.Sugar()
+
+	// 替换zap全局Logger
+	zap.ReplaceGlobals(Logger)
+
+	// 记录日志初始化完成
+	SugarLogger.Info("日志系统初始化完成")
+
+	return nil
+}
+
+// Sync 刷新日志缓冲区(程序退出前调用)
+func Sync() {
+	if Logger != nil {
+		_ = Logger.Sync()
+	}
+}
+
+// GetLogger 获取Logger实例
+func GetLogger() *zap.Logger {
+	return Logger
+}
+
+// GetSugarLogger 获取SugarLogger实例
+func GetSugarLogger() *zap.SugaredLogger {
+	return SugarLogger
+}

+ 22 - 0
router/init.go

@@ -0,0 +1,22 @@
+package router
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+// 路由配置函数类型
+type RouteSetupFunc func(*gin.Engine)
+
+var routeRegistrations []RouteSetupFunc
+
+// 注册路由配置(自动被各个路由文件调用)
+func RegisterRoute(setup RouteSetupFunc) {
+	routeRegistrations = append(routeRegistrations, setup)
+}
+
+// 统一设置路由入口
+func SetupRoutes(engine *gin.Engine) {
+	for _, setup := range routeRegistrations {
+		setup(engine)
+	}
+}