feat: client 增删改查

This commit is contained in:
Blizzard
2025-05-08 23:03:00 +08:00
parent 75e9157e5e
commit ab51ba91bc
41 changed files with 1093 additions and 57 deletions
+76
View File
@@ -0,0 +1,76 @@
package system
import (
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"go.uber.org/zap"
"sundynix-go/global"
"sundynix-go/model/commom/response"
"sundynix-go/model/system"
systemReq "sundynix-go/model/system/request"
systemRes "sundynix-go/model/system/response"
"sundynix-go/utils"
)
var store = base64Captcha.DefaultMemStore
type AuthApi struct{}
// Login api
func (a *AuthApi) Login(c *gin.Context) {
var l systemReq.Login
err := c.ShouldBindJSON(&l)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
if l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true) {
u := &system.User{Account: l.Account, Password: l.Password}
user, err := UserService.Login(u)
if err != nil {
global.Logger.Error("登录失败! 用户名不存在或者密码错误!", zap.Error(err))
response.FailWithMsg("用户名不存在或者密码错误", c)
return
}
a.GetToken(c, *user)
return
}
response.FailWithMsg("验证码错误", c)
}
// Captcha api 生成验证码
func (u *AuthApi) Captcha(c *gin.Context) {
var driver = base64Captcha.DriverString{
Height: 80,
Width: 240,
NoiseCount: 2,
ShowLineOptions: 4,
Length: 6,
Source: "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM",
}
cp := base64Captcha.NewCaptcha(&driver, store)
id, b64s, _, err := cp.Generate()
if err != nil {
global.Logger.Error("GenerateCaptcha err", zap.Error(err))
response.FailWithMsg("GenerateCaptcha err", c)
return
}
response.OkWithData(systemRes.CaptchaRes{
CaptchaId: id,
Captcha: b64s,
}, c)
}
func (a *AuthApi) GetToken(c *gin.Context, user system.User) {
token, claims, err := utils.GetLoginToken(&user)
if err != nil {
global.Logger.Error("GetToken err", zap.Error(err))
response.FailWithMsg("GetToken err", c)
}
response.OkWithData(systemRes.LoginResponse{
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
Token: token,
User: user,
}, c)
}
+9 -1
View File
@@ -1,6 +1,14 @@
package system package system
import "sundynix-go/service"
type ApiGroup struct { type ApiGroup struct {
LoginApi AuthApi
UserApi UserApi
ClientApi
} }
var (
UserService = service.ServiceGroupApp.SystemServiceGroup.UserService
ClientService = service.ServiceGroupApp.SystemServiceGroup.ClientService
)
-15
View File
@@ -1,15 +0,0 @@
package system
import "github.com/gin-gonic/gin"
type LoginApi struct{}
// Login
func (u LoginApi) Login(c *gin.Context) {
c.String(200, "Hello Admin")
}
// Captcha
func (u LoginApi) Captcha(c *gin.Context) {
c.String(200, "Hello Admin")
}
+100
View File
@@ -0,0 +1,100 @@
package system
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"sundynix-go/global"
"sundynix-go/model/commom/request"
"sundynix-go/model/commom/response"
"sundynix-go/model/system"
systemReq "sundynix-go/model/system/request"
)
type ClientApi struct {
}
func (s *ClientApi) SaveClient(c *gin.Context) {
var client system.Client
err := c.ShouldBindJSON(&client)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
err = ClientService.SaveClient(client)
if err != nil {
global.Logger.Error("保存客户端失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithMsg("保存客户端成功!", c)
}
func (s *ClientApi) UpdateClient(c *gin.Context) {
var client system.Client
err := c.ShouldBindJSON(&client)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
err = ClientService.UpdateClient(client)
if err != nil {
global.Logger.Error("更新客户端失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithMsg("更新客户端成功!", c)
}
func (s *ClientApi) GetClientList(c *gin.Context) {
var pageInfo systemReq.GetClientList
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
list, total, err := ClientService.GetClientList(pageInfo)
if err != nil {
global.Logger.Error("获取客户端列表失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithData(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Current,
PageSize: pageInfo.PageSize,
}, c)
}
func (s *ClientApi) Delete(c *gin.Context) {
var ids request.IdsReq
err := c.ShouldBindJSON(&ids)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
err = ClientService.DeleteClientByIds(ids)
if err != nil {
global.Logger.Error("删除客户端失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithMsg("删除客户端成功!", c)
}
func (s *ClientApi) Detail(c *gin.Context) {
var idInfo request.GetById
err := c.ShouldBindJSON(&idInfo)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
client, err := ClientService.GetClientById(idInfo.ID)
if err != nil {
global.Logger.Error("获取客户端详情失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithData(client, c)
}
+29
View File
@@ -1,4 +1,33 @@
package system package system
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"sundynix-go/global"
"sundynix-go/model/commom/response"
systemReq "sundynix-go/model/system/request"
)
type UserApi struct { type UserApi struct {
} }
func (u *UserApi) GetUserList(c *gin.Context) {
var pageInfo systemReq.GetUserList
err := c.ShouldBindJSON(&pageInfo)
if err != nil {
response.FailWithMsg(err.Error(), c)
return
}
list, total, err := UserService.GetUserList(pageInfo)
if err != nil {
global.Logger.Error("获取用户列表失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithData(response.PageResult{
List: list,
Total: total,
Page: pageInfo.Current,
PageSize: pageInfo.PageSize,
}, c)
}
+9
View File
@@ -2,6 +2,15 @@ system:
addr: 8888 addr: 8888
db-type: mysql db-type: mysql
router-prefix: "api" router-prefix: "api"
enable-captcha: 0
jwt:
buffer-time: 1d
expires-time: 7d
issuer: sunfynix
signing-key: 9149f2eb-d517-4a50-a03a-231dbcf0d872
mysql: mysql:
config: charset=utf8mb4&parseTime=True&loc=Local config: charset=utf8mb4&parseTime=True&loc=Local
+7
View File
@@ -2,6 +2,13 @@ system:
addr: 8888 addr: 8888
db-type: mysql db-type: mysql
captcha:
img-height: 80
img-width: 240
key-long: 6
open-captcha: 0
open-captcha-timeout: 3600
mysql: mysql:
config: charset=utf8mb4&parseTime=True&loc=Local config: charset=utf8mb4&parseTime=True&loc=Local
db-name: sundynix_go db-name: sundynix_go
+1
View File
@@ -1,6 +1,7 @@
package config package config
type Config struct { type Config struct {
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
System System `mapstructure:"system" json:"system" yaml:"system"` System System `mapstructure:"system" json:"system" yaml:"system"`
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"` Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"` Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
+4 -3
View File
@@ -1,7 +1,8 @@
package config package config
type System struct { type System struct {
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` Addr int `mapstructure:"addr" json:"addr" yaml:"addr"`
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"` DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"`
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"` RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
EnableCaptcha int `mapstructure:"enable-captcha" json:"enable-captcha" yaml:"enable-captcha"`
} }
+7 -5
View File
@@ -4,15 +4,17 @@ import (
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/sync/singleflight"
"gorm.io/gorm" "gorm.io/gorm"
"sundynix-go/config" "sundynix-go/config"
) )
// 全局变量 加载在内存中 // 全局变量 加载在内存中
var ( var (
Viper *viper.Viper Viper *viper.Viper
Logger *zap.Logger Logger *zap.Logger
Config *config.Config Config *config.Config
DB *gorm.DB DB *gorm.DB
Redis redis.UniversalClient Redis redis.UniversalClient
ConcurrencyControl = &singleflight.Group{}
) )
+4 -4
View File
@@ -6,10 +6,10 @@ import (
) )
type BaseModel struct { type BaseModel struct {
ID uint `gorm:"primarykey" json:"ID"` // 主键ID Id uint `gorm:"primarykey" json:"id"` // 主键ID
CreatedAt time.Time // 创建时间 CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time // 更新时间 UpdatedAt time.Time `json:"updatedAt"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"` // 删除时间
} }
// BeforeCreate 定义一个钩子,在创建之前执行自动插入字段 // BeforeCreate 定义一个钩子,在创建之前执行自动插入字段
+4
View File
@@ -31,6 +31,8 @@ require (
github.com/go-sql-driver/mysql v1.9.2 // indirect github.com/go-sql-driver/mysql v1.9.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -44,6 +46,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mojocn/base64Captcha v1.3.8 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/redis/go-redis/v9 v9.7.3 // indirect github.com/redis/go-redis/v9 v9.7.3 // indirect
@@ -60,6 +63,7 @@ require (
golang.org/x/arch v0.16.0 // indirect golang.org/x/arch v0.16.0 // indirect
golang.org/x/crypto v0.37.0 // indirect golang.org/x/crypto v0.37.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/net v0.39.0 // indirect golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect golang.org/x/sys v0.32.0 // indirect
+69
View File
@@ -45,6 +45,10 @@ github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIx
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 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-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -83,6 +87,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
@@ -125,6 +131,7 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 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/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -133,23 +140,85 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U= golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+2
View File
@@ -31,6 +31,8 @@ func MigrateTable() {
db := global.DB db := global.DB
err := db.AutoMigrate( err := db.AutoMigrate(
system.User{}, system.User{},
system.Client{},
system.SysOperationRecord{},
) )
if err != nil { if err != nil {
global.Logger.Error("Migrate table failed,err:", zap.Error(err)) global.Logger.Error("Migrate table failed,err:", zap.Error(err))
+5 -3
View File
@@ -5,6 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
"sundynix-go/global" "sundynix-go/global"
"sundynix-go/middleware"
"sundynix-go/router" "sundynix-go/router"
) )
@@ -24,15 +25,16 @@ func Routers() {
PublicGroup := Router.Group(global.Config.System.RouterPrefix) PublicGroup := Router.Group(global.Config.System.RouterPrefix)
//鉴权中间件 //鉴权中间件
//NeedAuthGroup.Use(middleware.JWTAuthMiddleware()) NeedAuthGroup.Use(middleware.AuthMiddleware())
{ {
//无须鉴权的路由 //无须鉴权的路由
systemRouter.InitLoginRouter(PublicGroup) //登录和验证码不需要鉴权 systemRouter.InitAuthRouter(PublicGroup) //登录和验证码不需要鉴权
} }
{ {
//需要鉴权的路由 //需要鉴权的路由
systemRouter.InitUserRouter(NeedAuthGroup) systemRouter.InitUserRouter(NeedAuthGroup) //用户相关
systemRouter.InitClientRouter(NeedAuthGroup) //客户端相关
} }
address := fmt.Sprintf(":%d", global.Config.System.Addr) address := fmt.Sprintf(":%d", global.Config.System.Addr)
+38
View File
@@ -0,0 +1,38 @@
package middleware
import (
"errors"
"github.com/gin-gonic/gin"
"sundynix-go/model/commom/response"
"sundynix-go/utils"
)
// AuthMiddleware 验证token有效性
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := utils.GetToken(c)
if token == "" {
response.NoAuth("未登录或非法访问", c)
c.Abort()
return
}
//todo 黑名单处理
j := utils.NewJWT()
// 解析token信息
claims, err := j.ParseToken(token)
if err != nil {
if errors.Is(err, utils.TokenExpired) {
response.NoAuth("登录过期", c)
utils.ClearToken(c)
c.Abort()
return
}
response.NoAuth(err.Error(), c)
utils.ClearToken(c)
c.Abort()
return
}
c.Set("claims", claims)
}
}
+48
View File
@@ -0,0 +1,48 @@
package request
import (
"gorm.io/gorm"
)
// PageInfo Paging common input parameter structure
type PageInfo struct {
Current int `json:"current" form:"current"` // 页码
PageSize int `json:"pageSize" form:"pageSize"` // 每页大小
Keyword string `json:"keyword" form:"keyword"` // 关键字
}
func (r *PageInfo) Paginate() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if r.Current <= 0 {
r.Current = 1
}
switch {
case r.PageSize > 100:
r.PageSize = 100
case r.PageSize <= 0:
r.PageSize = 10
}
offset := (r.Current - 1) * r.PageSize
return db.Offset(offset).Limit(r.PageSize)
}
}
// GetById Find by id structure
type GetById struct {
ID int `json:"id" form:"id"` // 主键ID
}
func (r *GetById) Uint() uint {
return uint(r.ID)
}
type IdsReq struct {
Ids []int `json:"ids" form:"ids"`
}
// GetAuthorityId Get role by id structure
type GetAuthorityId struct {
AuthorityId uint `json:"authorityId" form:"authorityId"` // 角色ID
}
type Empty struct{}
+8
View File
@@ -0,0 +1,8 @@
package response
type PageResult struct {
List interface{} `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
+60
View File
@@ -0,0 +1,60 @@
package response
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Response struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}
const (
SUCCESS = 200
ERROR = 7
)
// Result 返回结果
func Result(code int, data interface{}, msg string, c *gin.Context) {
c.JSON(http.StatusOK, Response{
code,
data,
msg,
})
}
// Ok 成功返回
func Ok(c *gin.Context) {
Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
}
// OkWithData 带数据成功返回
func OkWithData(data interface{}, c *gin.Context) {
Result(SUCCESS, data, "操作成功", c)
}
// OkWithMsg 带信息成功返回
func OkWithMsg(msg string, c *gin.Context) {
Result(SUCCESS, map[string]interface{}{}, msg, c)
}
// Fail 失败返回
func Fail(code int, msg string, c *gin.Context) {
Result(code, map[string]interface{}{}, "操作失败", c)
}
// FailWithMsg 带信息失败返回
func FailWithMsg(msg string, c *gin.Context) {
Result(ERROR, map[string]interface{}{}, msg, c)
}
// NoAuth 未授权返回
func NoAuth(message string, c *gin.Context) {
c.JSON(http.StatusUnauthorized, Response{
7,
nil,
message,
})
}
+15
View File
@@ -0,0 +1,15 @@
package request
import "github.com/mojocn/base64Captcha"
// configJsonBody json request body.
type CaptchaReqBody struct {
Id string
CaptchaType string
VerifyValue string
DriverAudio *base64Captcha.DriverAudio
DriverString *base64Captcha.DriverString
DriverChinese *base64Captcha.DriverChinese
DriverMath *base64Captcha.DriverMath
DriverDigit *base64Captcha.DriverDigit
}
+9
View File
@@ -0,0 +1,9 @@
package request
import common "sundynix-go/model/commom/request"
type GetClientList struct {
common.PageInfo
ClientId string `json:"clientId" form:"clientId"`
Name string `json:"name" form:"name"`
}
+16
View File
@@ -0,0 +1,16 @@
package request
import common "sundynix-go/model/commom/request"
type Login struct {
Account string `json:"account"`
Password string `json:"password"`
Captcha string `json:"captcha"`
CaptchaId string `json:"captchaId"`
}
type GetUserList struct {
common.PageInfo
Account string `json:"account" form:"account"`
Phone string `json:"phone" form:"phone"`
}
+6
View File
@@ -0,0 +1,6 @@
package response
type CaptchaRes struct {
CaptchaId string `json:"captchaId"`
Captcha string `json:"captcha"`
}
+13
View File
@@ -0,0 +1,13 @@
package response
import "sundynix-go/model/system"
type SysUserResponse struct {
User system.User `json:"user"`
}
type LoginResponse struct {
User system.User `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
}
+12
View File
@@ -0,0 +1,12 @@
package system
import "sundynix-go/global"
type Client struct {
global.BaseModel
ClientId string `gorm:"size:20;" json:"clientId"`
Name string `gorm:"size:50;" json:"name"`
GrantType string `gorm:"size:50;" json:"grantType"`
AdditionalInfo string `gorm:"type:text" json:"additionalInfo"`
ActiveTimeout uint `json:"activeTimeout"`
}
+21
View File
@@ -0,0 +1,21 @@
package system
import (
"sundynix-go/global"
"time"
)
type SysOperationRecord struct {
global.BaseModel
Ip string `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"` // 请求ip
Method string `json:"method" form:"method" gorm:"column:method;comment:请求方法"` // 请求方法
Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径
Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态
Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
Agent string `json:"agent" form:"agent" gorm:"type:text;column:agent;comment:代理"` // 代理
ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息
Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body
Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body
UserID int `json:"user_id" form:"user_id" gorm:"column:user_id;comment:用户id"` // 用户id
User User `json:"user"`
}
+19 -1
View File
@@ -1,7 +1,14 @@
package system package system
import "sundynix-go/global" import (
"sundynix-go/global"
)
type Login interface {
GetAccount() string
GetUserId() uint
GetUserInfo() any
}
type User struct { type User struct {
global.BaseModel global.BaseModel
ClientId string `gorm:"size:20;" json:"clientId"` ClientId string `gorm:"size:20;" json:"clientId"`
@@ -9,3 +16,14 @@ type User struct {
Password string `gorm:"size:100;" json:"-" form:"password"` Password string `gorm:"size:100;" json:"-" form:"password"`
Phone string `gorm:"size:11;" json:"phone" form:"phone"` Phone string `gorm:"size:11;" json:"phone" form:"phone"`
} }
func (u *User) GetAccount() string {
return u.Account
}
func (u *User) GetUserId() uint {
return u.Id
}
func (u *User) GetUserInfo() any {
return *u
}
+17
View File
@@ -0,0 +1,17 @@
package system
import (
"github.com/gin-gonic/gin"
)
type AuthRouter struct {
}
// InitAuthRouter 初始化登录路由
func (s *AuthRouter) InitAuthRouter(Router *gin.RouterGroup) {
loginRouter := Router.Group("auth")
{
loginRouter.POST("login", authApi.Login)
loginRouter.GET("captcha", authApi.Captcha)
}
}
+17
View File
@@ -0,0 +1,17 @@
package system
import "github.com/gin-gonic/gin"
type ClientRouter struct {
}
func (s *ClientRouter) InitClientRouter(Router *gin.RouterGroup) {
clientRouter := Router.Group("client")
{
clientRouter.POST("save", clientApi.SaveClient)
clientRouter.POST("update", clientApi.UpdateClient)
clientRouter.POST("getClientList", clientApi.GetClientList)
clientRouter.GET("delete", clientApi.Delete)
clientRouter.GET("detail", clientApi.Detail)
}
}
+5 -3
View File
@@ -3,12 +3,14 @@ package system
import v1 "sundynix-go/api/v1" import v1 "sundynix-go/api/v1"
type RouterGroup struct { type RouterGroup struct {
LoginRouter AuthRouter
UserRouter UserRouter
ClientRouter
} }
// 初始化路由 // 初始化路由
var ( var (
loginApi = v1.ApiGroupApp.SystemApiGroup.LoginApi authApi = v1.ApiGroupApp.SystemApiGroup.AuthApi
userApi = v1.ApiGroupApp.SystemApiGroup.UserApi userApi = v1.ApiGroupApp.SystemApiGroup.UserApi
clientApi = v1.ApiGroupApp.SystemApiGroup.ClientApi
) )
-17
View File
@@ -1,17 +0,0 @@
package system
import (
"github.com/gin-gonic/gin"
)
type LoginRouter struct {
}
// InitLoginRouter 初始化登录路由
func (s *LoginRouter) InitLoginRouter(Router *gin.RouterGroup) {
loginRouter := Router.Group("login")
{
loginRouter.POST("login", loginApi.Login)
loginRouter.POST("captcha", loginApi.Captcha)
}
}
+4
View File
@@ -8,4 +8,8 @@ type UserRouter struct {
} }
func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
userRouter := Router.Group("user")
{
userRouter.POST("getUserList", userApi.GetUserList)
}
} }
+9
View File
@@ -0,0 +1,9 @@
package service
import "sundynix-go/service/system"
var ServiceGroupApp = new(ServiceGroup)
type ServiceGroup struct {
SystemServiceGroup system.ServiceGroup
}
+6
View File
@@ -0,0 +1,6 @@
package system
type ServiceGroup struct {
UserService
ClientService
}
+53
View File
@@ -0,0 +1,53 @@
package system
import (
"errors"
"gorm.io/gorm"
"sundynix-go/global"
common "sundynix-go/model/commom/request"
"sundynix-go/model/system"
systemReq "sundynix-go/model/system/request"
)
type ClientService struct{}
var ClientServiceApp = new(ClientService)
func (s *ClientService) SaveClient(client system.Client) error {
if !errors.Is(global.DB.Where("client_id = ?", client.ClientId).First(&system.Client{}).Error, gorm.ErrRecordNotFound) {
return errors.New("存在重复clientId,请修改clientId")
}
return global.DB.Create(&client).Error
}
func (s *ClientService) UpdateClient(client system.Client) error {
return global.DB.Model(&client).Where("id = ?", client.Id).Updates(&client).Error
}
func (s *ClientService) GetClientList(info systemReq.GetClientList) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Current - 1)
db := global.DB.Model(&system.Client{})
var clientList []system.Client
if info.ClientId != "" {
db = db.Where("client_id = ?", info.ClientId)
}
if info.Name != "" {
db = db.Where("name LIKE ?", "%"+info.Name+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Find(&clientList).Error
return clientList, total, err
}
func (s *ClientService) DeleteClientByIds(ids common.IdsReq) (err error) {
return global.DB.Delete(&system.Client{}, "id IN ?", ids.Ids).Error
}
func (s *ClientService) GetClientById(id int) (client system.Client, err error) {
err = global.DB.Where("id = ?", id).First(&client).Error
return client, err
}
+44
View File
@@ -0,0 +1,44 @@
package system
import (
"errors"
"sundynix-go/global"
"sundynix-go/model/system"
systemReq "sundynix-go/model/system/request"
"sundynix-go/utils"
)
type UserService struct{}
var UserServiceApp = new(UserService)
func (userService *UserService) Login(u *system.User) (userInfo *system.User, err error) {
var user system.User
err = global.DB.Where("account = ?", u.Account).First(&user).Error
if err == nil {
if ok := utils.BcryptCheck(u.Password, user.Password); !ok {
return nil, errors.New("密码错误")
}
}
return &user, err
}
func (userService *UserService) GetUserList(info systemReq.GetUserList) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Current - 1)
db := global.DB.Model(&system.User{})
var userList []system.User
if info.Account != "" {
db = db.Where("account LIKE ?", "%"+info.Account+"%")
}
if info.Phone != "" {
db = db.Where("phone LIKE ?", "%"+info.Phone+"%")
}
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Find(&userList).Error
return userList, total, err
}
+5
View File
@@ -0,0 +1,5 @@
package captcha
import "github.com/mojocn/base64Captcha"
var CaptchaStore = base64Captcha.DefaultMemStore
+13
View File
@@ -78,6 +78,19 @@ func GetToken(c *gin.Context) string {
return token return token
} }
// ClearToken 清除Cookie中的token
func ClearToken(c *gin.Context) {
host, _, err := net.SplitHostPort(c.Request.Host)
if err != nil {
host = c.Request.Host
}
if net.ParseIP(host) != nil {
c.SetCookie("x-token", "", -1, "/", "", false, false)
} else {
c.SetCookie("x-token", "", -1, "/", host, false, false)
}
}
// GetUserInfo 从 gin.Context 中获取用户信息,并返回 CustomClaims 类型的指针。 // GetUserInfo 从 gin.Context 中获取用户信息,并返回 CustomClaims 类型的指针。
// 该函数首先尝试从上下文中获取已存在的 claims,如果不存在,则调用 GetClaims 函数获取 claims。 // 该函数首先尝试从上下文中获取已存在的 claims,如果不存在,则调用 GetClaims 函数获取 claims。
// 如果获取 claims 失败,则返回 nil。 // 如果获取 claims 失败,则返回 nil。
+5 -5
View File
@@ -1,14 +1,14 @@
package utils package utils
import ( import (
"fmt"
"testing" "testing"
) )
func TestHashPwd(t *testing.T) { func TestHashPwd(t *testing.T) {
hash := BcryptHash("admin") //hash := BcryptHash("admin")
fmt.Println(hash) // $2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2 //fmt.Println(hash) // $2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2
//
//check := BcryptCheck("admin", "$2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2")
//fmt.Println(check)
check := BcryptCheck("admin", "$2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2")
fmt.Println(check)
} }
+30
View File
@@ -0,0 +1,30 @@
package utils
import (
"strconv"
"strings"
"time"
)
// ParseDuration 解析时间
func ParseDuration(d string) (time.Duration, error) {
d = strings.TrimSpace(d)
dr, err := time.ParseDuration(d)
if err == nil {
return dr, nil
}
if strings.Contains(d, "d") {
index := strings.Index(d, "d")
hour, _ := strconv.Atoi(d[:index])
dr = time.Hour * 24 * time.Duration(hour)
ndr, err := time.ParseDuration(d[index+1:])
if err != nil {
return dr, nil
}
return dr + ndr, nil
}
dv, err := strconv.ParseInt(d, 10, 64)
return time.Duration(dv), err
}
+294
View File
@@ -0,0 +1,294 @@
package utils
import (
"errors"
"reflect"
"regexp"
"strconv"
"strings"
)
type Rules map[string][]string
type RulesMap map[string]Rules
var CustomizeMap = make(map[string]Rules)
//@author: [piexlmax](https://github.com/piexlmax)
//@function: RegisterRule
//@description: 注册自定义规则方案建议在路由初始化层即注册
//@param: key string, rule Rules
//@return: err error
func RegisterRule(key string, rule Rules) (err error) {
if CustomizeMap[key] != nil {
return errors.New(key + "已注册,无法重复注册")
} else {
CustomizeMap[key] = rule
return nil
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: NotEmpty
//@description: 非空 不能为其对应类型的0值
//@return: string
func NotEmpty() string {
return "notEmpty"
}
// @author: [zooqkl](https://github.com/zooqkl)
// @function: RegexpMatch
// @description: 正则校验 校验输入项是否满足正则表达式
// @param: rule string
// @return: string
func RegexpMatch(rule string) string {
return "regexp=" + rule
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Lt
//@description: 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Lt(mark string) string {
return "lt=" + mark
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Le
//@description: 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Le(mark string) string {
return "le=" + mark
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Eq
//@description: 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Eq(mark string) string {
return "eq=" + mark
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Ne
//@description: 不等于入参(!=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Ne(mark string) string {
return "ne=" + mark
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Ge
//@description: 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Ge(mark string) string {
return "ge=" + mark
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Gt
//@description: 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
//@param: mark string
//@return: string
func Gt(mark string) string {
return "gt=" + mark
}
//
//@author: [piexlmax](https://github.com/piexlmax)
//@function: Verify
//@description: 校验方法
//@param: st interface{}, roleMap Rules(入参实例,规则map)
//@return: err error
func Verify(st interface{}, roleMap Rules) (err error) {
compareMap := map[string]bool{
"lt": true,
"le": true,
"eq": true,
"ne": true,
"ge": true,
"gt": true,
}
typ := reflect.TypeOf(st)
val := reflect.ValueOf(st) // 获取reflect.Type类型
kd := val.Kind() // 获取到st对应的类别
if kd != reflect.Struct {
return errors.New("expect struct")
}
num := val.NumField()
// 遍历结构体的所有字段
for i := 0; i < num; i++ {
tagVal := typ.Field(i)
val := val.Field(i)
if tagVal.Type.Kind() == reflect.Struct {
if err = Verify(val.Interface(), roleMap); err != nil {
return err
}
}
if len(roleMap[tagVal.Name]) > 0 {
for _, v := range roleMap[tagVal.Name] {
switch {
case v == "notEmpty":
if isBlank(val) {
return errors.New(tagVal.Name + "值不能为空")
}
case strings.Split(v, "=")[0] == "regexp":
if !regexpMatch(strings.Split(v, "=")[1], val.String()) {
return errors.New(tagVal.Name + "格式校验不通过")
}
case compareMap[strings.Split(v, "=")[0]]:
if !compareVerify(val, v) {
return errors.New(tagVal.Name + "长度或值不在合法范围," + v)
}
}
}
}
}
return nil
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: compareVerify
//@description: 长度和数字的校验方法 根据类型自动校验
//@param: value reflect.Value, VerifyStr string
//@return: bool
func compareVerify(value reflect.Value, VerifyStr string) bool {
switch value.Kind() {
case reflect.String:
return compare(len([]rune(value.String())), VerifyStr)
case reflect.Slice, reflect.Array:
return compare(value.Len(), VerifyStr)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return compare(value.Uint(), VerifyStr)
case reflect.Float32, reflect.Float64:
return compare(value.Float(), VerifyStr)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return compare(value.Int(), VerifyStr)
default:
return false
}
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: isBlank
//@description: 非空校验
//@param: value reflect.Value
//@return: bool
func isBlank(value reflect.Value) bool {
switch value.Kind() {
case reflect.String, reflect.Slice:
return value.Len() == 0
case reflect.Bool:
return !value.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return value.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return value.Uint() == 0
case reflect.Float32, reflect.Float64:
return value.Float() == 0
case reflect.Interface, reflect.Ptr:
return value.IsNil()
}
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: compare
//@description: 比较函数
//@param: value interface{}, VerifyStr string
//@return: bool
func compare(value interface{}, VerifyStr string) bool {
VerifyStrArr := strings.Split(VerifyStr, "=")
val := reflect.ValueOf(value)
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
VInt, VErr := strconv.ParseInt(VerifyStrArr[1], 10, 64)
if VErr != nil {
return false
}
switch {
case VerifyStrArr[0] == "lt":
return val.Int() < VInt
case VerifyStrArr[0] == "le":
return val.Int() <= VInt
case VerifyStrArr[0] == "eq":
return val.Int() == VInt
case VerifyStrArr[0] == "ne":
return val.Int() != VInt
case VerifyStrArr[0] == "ge":
return val.Int() >= VInt
case VerifyStrArr[0] == "gt":
return val.Int() > VInt
default:
return false
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
VInt, VErr := strconv.Atoi(VerifyStrArr[1])
if VErr != nil {
return false
}
switch {
case VerifyStrArr[0] == "lt":
return val.Uint() < uint64(VInt)
case VerifyStrArr[0] == "le":
return val.Uint() <= uint64(VInt)
case VerifyStrArr[0] == "eq":
return val.Uint() == uint64(VInt)
case VerifyStrArr[0] == "ne":
return val.Uint() != uint64(VInt)
case VerifyStrArr[0] == "ge":
return val.Uint() >= uint64(VInt)
case VerifyStrArr[0] == "gt":
return val.Uint() > uint64(VInt)
default:
return false
}
case reflect.Float32, reflect.Float64:
VFloat, VErr := strconv.ParseFloat(VerifyStrArr[1], 64)
if VErr != nil {
return false
}
switch {
case VerifyStrArr[0] == "lt":
return val.Float() < VFloat
case VerifyStrArr[0] == "le":
return val.Float() <= VFloat
case VerifyStrArr[0] == "eq":
return val.Float() == VFloat
case VerifyStrArr[0] == "ne":
return val.Float() != VFloat
case VerifyStrArr[0] == "ge":
return val.Float() >= VFloat
case VerifyStrArr[0] == "gt":
return val.Float() > VFloat
default:
return false
}
default:
return false
}
}
func regexpMatch(rule, matchStr string) bool {
return regexp.MustCompile(rule).MatchString(matchStr)
}