13 Commits

11 changed files with 480 additions and 54 deletions

22
go.mod
View File

@ -6,7 +6,7 @@ toolchain go1.23.1
require (
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250122075709-5ecf3edb4a00
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20240930081343-1e7f84ed8465
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd
@ -23,20 +23,20 @@ require (
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 // indirect
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 // indirect
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
github.com/bytedance/sonic v1.12.5 // indirect
github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/bytedance/sonic v1.12.8 // indirect
github.com/bytedance/sonic/loader v0.2.3 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-contrib/sse v1.0.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
@ -58,12 +58,12 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/arch v0.14.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

37
go.sum
View File

@ -6,6 +6,10 @@ git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 h1:s6d4b
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4/go.mod h1:V4Dfg1v/JVIZGEKCm6/aehs8hK+Xow1dkL1yiQymXlQ=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20240930081343-1e7f84ed8465 h1:j5EB0hamTMT5fY+xmjJ51oBvll+vS2inNPi+3/UBj60=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20240930081343-1e7f84ed8465/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091353-907c20662792 h1:BehqU7W+FF39xCcrbrlPBggbKVTIguDDXfABn8l5RB4=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091353-907c20662792/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06 h1:XsjGMkBCi93h56oCg5Lrz5zVpUxify/CQVhQU9+qLWM=
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250207091724-ca151fbc1f06/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad h1:6BI3QiDI64SlER1006UJbTJyOCXxB8KCmCK+Kr7FzQo=
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20241125083316-eab7bab9d7ad/go.mod h1:+jPQTyCEQqMWhq4p1LowQWq15emisON+++87ArTgwNA=
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 h1:1eBf0C0gdpBQOqjTK3UCw/mwzQ/SCodx3iTQtidx9eE=
@ -18,6 +22,8 @@ git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6Cc
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 h1:P2kuhU2TFWk9mPbJlZCK6FMDVS7S3NGafWKD4eNqKiI=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2/go.mod h1:7KaMpQmWgiNLpEkaV7oDtep7geI1f/VoCoPVcyvMjAE=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e h1:YE2Gi+M03UDImIpWa3I7jzSesyfu2RL8x/4ONs5v0oE=
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e/go.mod h1:L/7JugxKZL3JP9JP/XDvPAPz0FQXG1u181Su1+u/d1c=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9 h1:yF770WIDNwyiKL0nwmBGmjZvNCLXtHQL4xJyffPjTMU=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9/go.mod h1:I76wxEsWq7KnMQ84elpwTjEqq4I49QFw60tp5h7iGBs=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
@ -26,22 +32,33 @@ github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+I
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.12.7 h1:CQU8pxOy9HToxhndH0Kx/S1qU/CuS9GnKYrGioDcU1Q=
github.com/bytedance/sonic v1.12.7/go.mod h1:tnbal4mxOMju17EGfknm2XyYcpyCnIROYOEYuemj13I=
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
@ -56,6 +73,10 @@ github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
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/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -115,13 +136,17 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@ -141,6 +166,10 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
@ -148,12 +177,20 @@ golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

25
router/config.go Normal file
View File

@ -0,0 +1,25 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-07 17:41
package router
import "strings"
var defaultValidateErrTag = "err"
// SetValidateErrTag 设置验证失败时, 获取错误信息的tag字段
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:42 2025/2/7
func SetValidateErrTag(tagName string) {
tagName = strings.TrimSpace(tagName)
if tagName == "" {
return
}
defaultValidateErrTag = tagName
}

175
router/controller.go Normal file
View File

@ -0,0 +1,175 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 15:29
package router
import (
"reflect"
"strings"
)
// controller 解析controller有哪些方法要注册为接口
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:30 2025/1/27
type controller struct {
}
// Parse 执行解析
//
// 符合注册为借口的方法具有如下特征:
//
// 1. 函数接受两个入参, 第一个参数为 gin.Context , 第二个参数为 任意结构体指针, 但是必须声明 Meta 相关信息, 否则会报错
//
// 2. 函数有两个返回值, 第一个返回值为任意结构体/结构体指针(限制死不能为map/slice, 方便后续统一标准化) , 第二个返回值为 error , 代表处理的异常, 会自动适配 exception.IException 类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:31 2025/1/27
func (c controller) Parse(inputController any) map[string]UriConfig {
parseRes := make(map[string]UriConfig)
if nil == inputController {
return parseRes
}
controllerType := reflect.TypeOf(inputController)
controllerValue := reflect.ValueOf(inputController)
/*if controllerType.Kind() == reflect.Func {
// 直接函数注册
cfg, isNeedRegister := c.methodConfig(controllerType)
if isNeedRegister {
parseRes[cfg.Path] = cfg
}
return parseRes
}*/
if controllerType.Kind() == reflect.Ptr {
controllerValue = controllerValue.Elem()
}
/*if controllerValue.IsNil() {
return parseRes
}*/
for methodIdx := 0; methodIdx < controllerType.NumMethod(); methodIdx++ {
uriCfg, needRegister := c.methodConfig(controllerType.Method(methodIdx))
if !needRegister {
continue
}
uriCfg.ApiStructValue = controllerValue
parseRes[uriCfg.Path] = uriCfg
}
return parseRes
}
// methodConfig 解析方法配置, 要求函数格式, 两个参数, 两个返回值, 格式 : func(ctx *gin.Context, formData anyStruct[组合Meta]) (anyStruct|map[response], error)
//
// 参数 : 方法反射结果
//
// 返回值 : 第一个 -> 解析出的接口配置 第二个 -> 是否要注册为接口
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:05 2025/1/27
func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, needRegister bool) {
methodType := reflectMethod.Type
// num0: 函数声明
// num1: 第一个参数
// num2: 第二个参数
if methodType.NumIn() != 3 {
needRegister = false
return
}
// 第一个参数必须是 *gin.Context
if methodType.In(1).String() != GinContextType {
needRegister = false
return
}
// 解析第二个参数是组合Meta的form表单
formType := methodType.In(2)
if formType.Kind() == reflect.Ptr {
formType = methodType.In(2).Elem()
}
cfg.FormDataType = formType
metaField, metaFieldExist := formType.FieldByName(FieldNameMeta)
if !metaFieldExist {
needRegister = false
return
}
if methodType.Out(1).Kind().String() != ErrorType {
// 判断是否是实现 error接口的方法
outputErrParse := false
for j := 0; j < methodType.Out(1).NumMethod(); j++ {
if methodType.Out(1).Method(j).Name == ErrorInterfaceFuncName && // 实现Error方法
methodType.Out(1).Method(j).Type.NumIn() == 0 && // 没有任何参数
methodType.Out(1).Method(j).Type.NumOut() == 1 && // 一个返回值
methodType.Out(1).Method(j).Type.Out(0).Kind().String() == reflect.String.String() {
outputErrParse = true
break
}
}
if !outputErrParse {
needRegister = false
return
}
}
// 解析meta信息
cfg.Path = metaField.Tag.Get(TagNamePath)
cfg.RequestMethod = metaField.Tag.Get(TagNameMethod)
cfg.Desc = metaField.Tag.Get(TagNameDesc)
cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")
// 解析第一个返回值, 要求必须是结构体或者是map
outputStrictModel := metaField.Tag.Get(TagNameOutputStrict)
cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
if cfg.OutputStrict {
// 开启输出严格模式校验
if methodType.Out(0).Kind() != reflect.Struct && methodType.Out(0).Kind() != reflect.Map {
panic(cfg.Path + " : 接口配置输出严格校验, 输出数据类型必须为 struct 或 *struct 或 map, 实际返回数据类型 : " + methodType.Out(0).Kind().String())
return
}
}
// 解析参数配置
cfg.ParamList = c.parseParamConfig(formType)
cfg.ApiLogicFunc = reflectMethod
needRegister = true
return
}
// parseParamConfig 解析参数配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:35 2025/2/7
func (c controller) parseParamConfig(formDataType reflect.Type) []UriParam {
res := make([]UriParam, 0)
for i := 0; i < formDataType.NumField(); i++ {
structField := formDataType.Field(i)
if structField.Name == FieldNameMeta {
// Meta 字段, 忽略
continue
}
jsonTag := structField.Tag.Get("json")
if jsonTag == "" {
jsonTag = structField.Name
}
validate := strings.TrimSpace(structField.Tag.Get(TagNameBinding))
if len(validate) == 0 {
validate = strings.TrimSpace(structField.Tag.Get(TagNameValidate))
}
title := strings.TrimSpace(structField.Tag.Get(TagNameDesc))
if len(title) == 0 {
title = jsonTag
}
res = append(res, UriParam{
Field: structField.Name,
Name: jsonTag,
Title: title,
Type: structField.Type.String(),
Validate: validate,
ErrorMsg: "",
})
}
return res
}

View File

@ -12,14 +12,20 @@ import "reflect"
const (
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
GinContextType = "*gin.Context" // gin context 类型名称
ErrorType = "error" // error类型
ErrorInterfaceFuncName = "Error" // error接口需要实现的方法名称
)
const (
TagNamePath = "path" // 接口的请求路径
TagNameMethod = "method" // 接口的请求方法
TagNameUriTag = "tag" // 接口的tag
TagNameDesc = "desc" // 接口的描述
TagNameStrict = "strict" // 接口是否为严格模式 : 不配置, 则为严格模式.严格模式 : POST 仅解析 BODY , GET 仅解析 QUERY
TagNameDesc = "desc" // 接口/接口参数的描述
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map非严格模式返回任意值
TagNameBinding = "binding" // gin 内置的验证规则tag
TagNameValidate = "validate" // validator v10 默认的验证规则tag
TagNameErrMsg = "err" // 验证失败错误信息tag
)
// UriConfig 接口配置
@ -29,9 +35,30 @@ const (
// Date : 15:41 2024/7/21
type UriConfig struct {
Path string `json:"path"` // 接口路由, 必须配置
Method string `json:"method"` // 接口请求方法, 必须配置
RequestMethod string `json:"request_method"` // 接口请求方法, 必须配置
TagList []string `json:"tag_list"` // 接口分组
Desc string `json:"desc"` // 接口描述
Strict bool `json:"strict"` // 接口是否为严格模式 : 不配置, 则为严格模式.严格模式 : POST 仅解析 BODY , GET 仅解析 QUERY
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
ParamList []UriParam `json:"param_list"` // 参数信息表
FormDataType reflect.Type `json:"-"` // 表单数据类型
ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值
ApiLogicFunc reflect.Method `json:"-"` // 自定义的接口逻辑
}
// UriParam 接口参数配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:40 2025/1/27
type UriParam struct {
Field string `json:"field"` // 结构体字段
Name string `json:"name"` // 参数名称
Title string `json:"title"` // 参数标题
Type string `json:"type"` // 参数类型
Validate string `json:"validate"` // 验证规则: validator/v10 库
ErrorMsg string `json:"error_msg"` // 验证失败的错误信息
}
const (
FieldNameMeta = "Meta" // 元信息字段
)

51
router/group.go Normal file
View File

@ -0,0 +1,51 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 19:33
package router
import (
"errors"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
// Group 注册接口路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:35 2025/1/27
func Group(router *gin.Engine, routerPrefix string, middlewareList []gin.HandlerFunc, cList ...any) error {
g := router.Group(routerPrefix)
g.Use(middlewareList...)
cParser := controller{}
for _, c := range cList {
urlTable := cParser.Parse(c)
for _, itemUriCfg := range urlTable {
method := strings.ToUpper(itemUriCfg.RequestMethod)
switch method {
case http.MethodGet:
g.GET(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodHead:
g.HEAD(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPost:
g.POST(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPut:
g.PUT(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodPatch:
g.PATCH(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodDelete:
g.DELETE(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodOptions:
g.OPTIONS(itemUriCfg.Path, RequestHandler(itemUriCfg))
case http.MethodTrace:
return errors.New(`method Trace is not supported`)
}
}
}
return nil
}

63
router/handler.go Normal file
View File

@ -0,0 +1,63 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 19:42
package router
import (
"git.zhangdeman.cn/zhangdeman/exception"
"git.zhangdeman.cn/zhangdeman/gin/request"
"git.zhangdeman.cn/zhangdeman/gin/response"
"github.com/gin-gonic/gin"
"reflect"
)
// RequestHandler 获取请求处理方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:44 2025/1/27
func RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
return func(ctx *gin.Context) {
var (
err error
ok bool
e exception.IException
)
formData := reflect.New(uriCfg.FormDataType).Interface()
// 表单解析
if err = request.Form.Parse(ctx, formData); nil != err {
// 格式化验证错误的信息
err = GetValidateErr(formData, err)
e = exception.NewFromError(400, err)
response.SendWithException(ctx, e, nil)
return
}
// 执行逻辑
resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, reflect.ValueOf(ctx), reflect.ValueOf(formData)})
if resList[1].IsNil() {
// 请求成功
response.Success(ctx, resList[0].Interface())
return
}
// 请求失败
if e, ok = resList[1].Interface().(exception.IException); ok {
response.SendWithException(ctx, e, nil)
return
}
if err, ok = resList[1].Interface().(error); ok {
e = exception.NewFromError(-1, err)
response.SendWithException(ctx, e, nil)
return
}
e = exception.NewWithCodeAndData(-1, map[string]any{
"err": resList[1].Interface(),
})
response.SendWithException(ctx, e, nil)
return
}
}

View File

@ -7,7 +7,11 @@
// Date : 2024-07-20 21:40
package router
// Meta 接口的元信息
// Meta 接口的元信息, 主要包含如下信息:
//
// uri: 接口路由(不包含group前缀)
//
// method: 请求方法: get/post 等
//
// Author : go_developer@163.com<白茶清欢>
//

View File

@ -118,16 +118,16 @@ func parseUriConfig(methodType reflect.Type, routerPrefix string) (*UriConfig, e
if formType.Kind() == reflect.Ptr {
formType = methodType.In(2).Elem()
}
metaField, metaFieldExist := formType.FieldByName("Meta")
metaField, metaFieldExist := formType.FieldByName(FieldNameMeta)
if !metaFieldExist {
return nil, nil
}
uriConfig := &UriConfig{
Path: strings.TrimRight(routerPrefix, "/") + "/" + strings.TrimLeft(metaField.Tag.Get(TagNamePath), "/"),
Method: strings.ToUpper(metaField.Tag.Get(TagNameMethod)),
RequestMethod: strings.ToUpper(metaField.Tag.Get(TagNameMethod)),
TagList: strings.Split(metaField.Tag.Get(TagNameUriTag), "|"),
Desc: metaField.Tag.Get(TagNameDesc),
Strict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameStrict))) >= 0,
OutputStrict: wrapper.ArrayType([]string{"", "true"}).Has(strings.ToLower(metaField.Tag.Get(TagNameOutputStrict))) >= 0,
FormDataType: methodType.In(2).Elem(),
}
// 校验 FormDataType
@ -163,10 +163,10 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
}
err := errData.Interface()
if e, ok := err.(exception.IException); ok {
response.SendWithException(ctx, e, businessData)
response.SendWithException(ctx, e, map[string]any{"business_data": businessData})
return
} else {
response.SendWithException(ctx, exception.NewFromError(-1, errData.Interface().(error)), businessData)
response.SendWithException(ctx, exception.NewFromError(-1, errData.Interface().(error)), map[string]any{"business_data": businessData})
return
}
}
@ -174,7 +174,7 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
middlewareList = append([]gin.HandlerFunc{
middleware.InitRequest(),
}, middlewareList...)
switch uriConfig.Method {
switch uriConfig.RequestMethod {
case http.MethodGet:
ginRouter.GET(uriConfig.Path, middlewareList...)
case http.MethodHead:
@ -196,7 +196,7 @@ func registerUri(uriConfig *UriConfig, methodValue reflect.Value, middlewareList
case "ANY":
ginRouter.Any(uriConfig.Path, middlewareList...)
default:
panic(uriConfig.Path + " : " + uriConfig.Method + " is not support")
panic(uriConfig.Path + " : " + uriConfig.RequestMethod + " is not support")
}
}

View File

@ -15,24 +15,13 @@ import (
type TestController struct{}
func (t *TestController) RouterPrefix() string {
return "/uri/prefix"
}
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
return []gin.HandlerFunc{
func(ctx *gin.Context) {
},
}
}
func (t *TestController) Uri(ctx *gin.Context, formData *TestForm) (any, error) {
func (t TestController) Logic(ctx *gin.Context, formData *TestForm) (any, error) {
return formData, nil
}
type TestForm struct {
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
Age int `json:"age" form:"age"`
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get"`
Age int `json:"age" form:"age" binding:"min=20" err_msg:"年龄不能小于20"`
Name string `json:"name" form:"name"`
Test *Test `json:"test" form:"test"`
Num *int64 `json:"num" form:"num"`
@ -42,8 +31,8 @@ type Test struct {
}
func Test_parseController(t *testing.T) {
type args struct {
controller any
}
Register(8080, &TestController{})
SetValidateErrTag("err_msg")
r := gin.Default()
Group(r, "test", nil, TestController{})
r.Run(":8080")
}

55
router/validator.go Normal file
View File

@ -0,0 +1,55 @@
// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-02-07 17:36
package router
import (
"errors"
"fmt"
"github.com/go-playground/validator/v10"
"reflect"
"strings"
)
// GetValidateErr 格式化错误信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:19 2025/1/15
func GetValidateErr(obj any, rawErr error) error {
if nil == rawErr {
return nil
}
if nil == obj {
return rawErr
}
var (
ok bool
validationErrs validator.ValidationErrors
errString []string
field reflect.StructField
)
if ok = errors.As(rawErr, &validationErrs); !ok {
return rawErr
}
objType := reflect.TypeOf(obj)
if objType.Kind() == reflect.Ptr {
objType = objType.Elem()
}
for _, validationErr := range validationErrs {
if field, ok = objType.FieldByName(validationErr.Field()); ok {
if e := field.Tag.Get(defaultValidateErrTag); e != "" {
errString = append(errString, fmt.Sprintf("%s: %s", field.Tag.Get("json"), e))
continue
} else {
errString = append(errString, fmt.Sprintf("%s: %v", field.Tag.Get("json"), validationErr.Value()))
}
}
errString = append(errString, validationErr.Error())
}
return errors.New(strings.Join(errString, "\n"))
}