From bf525906e4d52754ec7b8b64e899a3ab0e6d86bc Mon Sep 17 00:00:00 2001 From: lulz1 Date: Thu, 31 Oct 2024 16:56:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=87=E4=BB=BD=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/http/kernel.go | 9 +- app/http/module/user/api.go | 3 +- app/http/module/user/api_user_get.go | 30 +++ app/http/module/user/api_user_save.go | 44 +++++ app/http/module/user/user_test.http | 18 ++ app/http/result/dto.go | 46 +++++ app/infrastructure/infrastructure.go | 7 + app/infrastructure/user.go | 24 ++- app/provider/user/contract.go | 9 + app/provider/user/service.go | 3 +- config/development/cache.yaml | 4 +- config/development/redis.yaml | 2 +- config/testing/redis.yaml | 14 +- framework/provider/cache/services/redis.go | 5 +- framework/provider/infrastructure/service.go | 8 +- framework/provider/repository/provider.go | 2 +- framework/provider/repository/service.go | 6 +- framework/util/request/request.go | 197 +++++++++++++++++++ 18 files changed, 396 insertions(+), 35 deletions(-) create mode 100644 app/http/module/user/api_user_get.go create mode 100644 app/http/module/user/api_user_save.go create mode 100644 app/http/module/user/user_test.http create mode 100644 app/http/result/dto.go create mode 100644 app/infrastructure/infrastructure.go create mode 100644 framework/util/request/request.go diff --git a/app/http/kernel.go b/app/http/kernel.go index 5bd5f27..fddccda 100644 --- a/app/http/kernel.go +++ b/app/http/kernel.go @@ -1,6 +1,7 @@ package http import ( + "github.com/Superdanda/hade/app/infrastructure" "github.com/Superdanda/hade/app/provider/database_connect" "github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework/gin" @@ -13,8 +14,7 @@ func NewHttpEngine(container *framework.HadeContainer) (*gin.Engine, error) { // 默认启动一个Web引擎 r := gin.Default() r.SetContainer(container) - // 业务绑定路由操作 - Routes(r) + // 返回绑定路由后的Web引擎 // 对业务模型进行注册,通过注册名获取业务模型类型信息 @@ -23,5 +23,10 @@ func NewHttpEngine(container *framework.HadeContainer) (*gin.Engine, error) { //绑定服务 container.Bind(&database_connect.DatabaseConnectProvider{}) + //注册 infrastructure 包的实例 + infrastructure.NewOrmRepositoryAndRegister(container) + + // 业务绑定路由操作 + Routes(r) return r, nil } diff --git a/app/http/module/user/api.go b/app/http/module/user/api.go index 9a77f29..b9ece44 100644 --- a/app/http/module/user/api.go +++ b/app/http/module/user/api.go @@ -23,7 +23,8 @@ func RegisterRoutes(r *gin.Engine) error { { userGroup.POST("/login", api.UserLogin) - + userGroup.POST("/get", api.UserGet) + userGroup.POST("/save", api.UserSave) } } diff --git a/app/http/module/user/api_user_get.go b/app/http/module/user/api_user_get.go new file mode 100644 index 0000000..e72f315 --- /dev/null +++ b/app/http/module/user/api_user_get.go @@ -0,0 +1,30 @@ +package user + +import ( + "github.com/Superdanda/hade/app/http/result" + "github.com/Superdanda/hade/app/provider/user" + "github.com/Superdanda/hade/framework/gin" + "github.com/spf13/cast" + "net/http" +) + +type UserGetParam struct { + ID string `json:"id"` +} + +func (api *UserApi) UserGet(c *gin.Context) { + userService := c.MustMake(user.UserKey).(user.Service) + param := &UserGetParam{} + err := c.ShouldBindJSON(param) + if err != nil { + c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误")) + return + } + + userQuery, err := userService.GetUser(c, cast.ToInt64(param.ID)) + if err != nil { + c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差")) + return + } + c.ISetOkStatus().IJson(result.Success(userQuery)) +} diff --git a/app/http/module/user/api_user_save.go b/app/http/module/user/api_user_save.go new file mode 100644 index 0000000..1531846 --- /dev/null +++ b/app/http/module/user/api_user_save.go @@ -0,0 +1,44 @@ +package user + +import ( + "github.com/Superdanda/hade/app/http/result" + "github.com/Superdanda/hade/app/provider/database_connect" + "github.com/Superdanda/hade/app/provider/user" + "github.com/Superdanda/hade/framework/gin" + "net/http" +) + +type UserSaveParam struct { + ID int64 `gorm:"column:id;primary_key;auto_increment" json:"id"` // 代表用户id, 只有注册成功之后才有这个id,唯一表示一个用户 + UserName string `gorm:"column:username;type:varchar(255);comment:用户名;not null" json:"username"` + NickName string `gorm:"column:username;type:varchar(255);comment:昵称;not null" json:"nickname"` + Email string `gorm:"column:email;type:varchar(255);comment:邮箱;not null" json:"email"` +} + +func (u *UserSaveParam) conventUser() *user.User { + return &user.User{ + ID: u.ID, + UserName: u.UserName, + NickName: u.NickName, + Email: u.Email, + } +} + +func (api *UserApi) UserSave(c *gin.Context) { + + service := c.MustMake(database_connect.DatabaseConnectKey).(database_connect.Service) + service.DefaultDatabaseConnect().AutoMigrate(&user.User{}) + + userService := c.MustMake(user.UserKey).(user.Service) + param := &UserSaveParam{} + err := c.ShouldBindJSON(param) + if err != nil { + c.ISetStatus(http.StatusBadRequest).IJson(result.Fail("参数错误")) + } + conventUser := param.conventUser() + err = userService.SaveUser(c, conventUser) + if err != nil { + c.ISetStatus(http.StatusInternalServerError).IJson(result.Fail("网络开小差")) + } + c.ISetOkStatus().IJson(result.SuccessWithOKMessage()) +} diff --git a/app/http/module/user/user_test.http b/app/http/module/user/user_test.http new file mode 100644 index 0000000..050ab0f --- /dev/null +++ b/app/http/module/user/user_test.http @@ -0,0 +1,18 @@ +### GET request to example server +POST http://127.0.0.1:8888/user/get +Content-Type: application/json + +{ + "id": "1" +} + + +### POST request to example server +POST http://127.0.0.1:8888/user/save +Content-Type: application/json + +{ + "username": "testuser", + "nickname": "Test", + "email": "test@example.com" +} diff --git a/app/http/result/dto.go b/app/http/result/dto.go new file mode 100644 index 0000000..621bc0a --- /dev/null +++ b/app/http/result/dto.go @@ -0,0 +1,46 @@ +package result + +type Result struct { + Code int `json:"code"` + Success bool `json:"success"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +// Success 方法,封装成功响应的结构体 +func Success(data interface{}) Result { + return Result{ + Code: 1, + Success: true, + Message: "Success", + Data: data, + } +} + +// SuccessWithMessage 方法,封装成功响应的结构体 +func SuccessWithMessage(message string) Result { + return Result{ + Code: 1, + Success: true, + Message: message, + } +} + +// SuccessWithOKMessage 方法,封装成功响应的结构体, message 操作成功 +func SuccessWithOKMessage() Result { + return Result{ + Code: 1, + Success: true, + Message: "操作成功", + } +} + +// Fail 方法,封装失败响应的结构体 +func Fail(message string) Result { + return Result{ + Code: 0, + Success: false, + Message: message, + Data: nil, + } +} diff --git a/app/infrastructure/infrastructure.go b/app/infrastructure/infrastructure.go new file mode 100644 index 0000000..9caafbe --- /dev/null +++ b/app/infrastructure/infrastructure.go @@ -0,0 +1,7 @@ +package infrastructure + +import "github.com/Superdanda/hade/framework" + +func NewOrmRepositoryAndRegister(container framework.Container) { + NewOrmUserRepositoryAndRegister(container) // 用户仓储实例 +} diff --git a/app/infrastructure/user.go b/app/infrastructure/user.go index 1a45eef..271163c 100644 --- a/app/infrastructure/user.go +++ b/app/infrastructure/user.go @@ -6,6 +6,7 @@ import ( userModule "github.com/Superdanda/hade/app/provider/user" "github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework/contract" + "github.com/Superdanda/hade/framework/provider/repository" "gorm.io/gorm" ) @@ -16,15 +17,18 @@ type UserRepository struct { userModule.Repository } -func NewUserRepository(container framework.Container) contract.OrmRepository[userModule.User, int64] { +func NewOrmUserRepositoryAndRegister(container framework.Container) { + //获取必要服务对象 connectService := container.MustMake(database_connect.DatabaseConnectKey).(database_connect.Service) + infrastructureService := container.MustMake(contract.InfrastructureKey).(contract.InfrastructureService) + repositoryService := container.MustMake(contract.RepositoryKey).(contract.RepositoryService) + connect := connectService.DefaultDatabaseConnect() userOrmService := &UserRepository{container: container, db: connect} - infrastructureService := container.MustMake(contract.InfrastructureKey).(contract.InfrastructureService) infrastructureService.RegisterOrmRepository(userModule.UserKey, userOrmService) - //repository.RegisterRepository[userModule.User, int64](userModule.UserKey,) - return userOrmService + //注册通用仓储对象 + repository.RegisterRepository[userModule.User, int64](repositoryService, userModule.UserKey, userOrmService) } func (u *UserRepository) SaveToDB(entity *userModule.User) error { @@ -38,7 +42,7 @@ func (u *UserRepository) FindByIDFromDB(id int64) (*userModule.User, error) { return user, nil } -func (u *UserRepository) FindByID64sFromDB(ids []int64) ([]*userModule.User, error) { +func (u *UserRepository) FindByIDsFromDB(ids []int64) ([]*userModule.User, error) { var users []*userModule.User // 使用 GORM 的 Where 方法查询用户 ID 在给定 ID 列表中的记录 if err := u.db.Where("id IN ?", ids).Find(&users).Error; err != nil { @@ -126,16 +130,18 @@ func (u *UserRepository) GetFieldValueFunc(fieldName string) (func(entity *userM } func (u *UserRepository) Save(ctx context.Context, user *userModule.User) error { - repository := u.container.MustMake(contract.RepositoryKey).(contract.Repository[userModule.User, int64]) - if err := repository.Save(ctx, userModule.UserKey, user); err != nil { + repositoryService := u.container.MustMake(contract.RepositoryKey).(contract.RepositoryService) + genericRepository := repositoryService.GetGenericRepositoryByKey(userModule.UserKey).(contract.GenericRepository[userModule.User, int64]) + if err := genericRepository.Save(ctx, user); err != nil { return err } return nil } func (u *UserRepository) FindById(ctx context.Context, id int64) (*userModule.User, error) { - repository := u.container.MustMake(contract.RepositoryKey).(contract.Repository[userModule.User, int64]) - byID, err := repository.FindByID(ctx, userModule.UserKey, id) + repositoryService := u.container.MustMake(contract.RepositoryKey).(contract.RepositoryService) + genericRepository := repositoryService.GetGenericRepositoryByKey(userModule.UserKey).(contract.GenericRepository[userModule.User, int64]) + byID, err := genericRepository.FindByID(ctx, id) if err != nil { return nil, err } diff --git a/app/provider/user/contract.go b/app/provider/user/contract.go index c3f5285..3a5cbbd 100644 --- a/app/provider/user/contract.go +++ b/app/provider/user/contract.go @@ -2,6 +2,7 @@ package user import ( "context" + "encoding/json" "time" ) @@ -24,3 +25,11 @@ type User struct { Email string `gorm:"column:email;type:varchar(255);comment:邮箱;not null" json:"email"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;comment:创建时间;not null;<-:create" json:"createdAt"` } + +func (u *User) MarshalBinary() ([]byte, error) { + return json.Marshal(u) +} + +func (u *User) UnmarshalBinary(data []byte) error { + return json.Unmarshal(data, u) +} diff --git a/app/provider/user/service.go b/app/provider/user/service.go index aac3dc1..4aee3fc 100644 --- a/app/provider/user/service.go +++ b/app/provider/user/service.go @@ -4,7 +4,6 @@ import ( "context" "github.com/Superdanda/hade/framework" "github.com/Superdanda/hade/framework/contract" - "github.com/Superdanda/hade/framework/provider/infrastructure" ) type UserService struct { @@ -30,7 +29,7 @@ func (s *UserService) SaveUser(ctx context.Context, user *User) error { func NewUserService(params ...interface{}) (interface{}, error) { container := params[0].(framework.Container) - infrastructureService := container.MustMake(contract.InfrastructureKey).(infrastructure.Service) + infrastructureService := container.MustMake(contract.InfrastructureKey).(contract.InfrastructureService) ormRepository := infrastructureService.GetModuleOrmRepository(UserKey).(Repository) return &UserService{container: container, repository: ormRepository}, nil } diff --git a/config/development/cache.yaml b/config/development/cache.yaml index a312892..e8de1e0 100644 --- a/config/development/cache.yaml +++ b/config/development/cache.yaml @@ -1,4 +1,6 @@ -driver: memory +driver: redis + + repository: expire: 6h \ No newline at end of file diff --git a/config/development/redis.yaml b/config/development/redis.yaml index a27d9ea..d4b13af 100644 --- a/config/development/redis.yaml +++ b/config/development/redis.yaml @@ -4,4 +4,4 @@ write_timeout: 2s # 写超时 host: 10.10.103.131 # ip地址 port: 6379 # 端口 password: Worktask@Redis2023 -db: 0 #db \ No newline at end of file +db: 6 #db \ No newline at end of file diff --git a/config/testing/redis.yaml b/config/testing/redis.yaml index 0ed4bf0..04e6660 100644 --- a/config/testing/redis.yaml +++ b/config/testing/redis.yaml @@ -2,11 +2,9 @@ timeout: 10s # 连接超时 read_timeout: 2s # 读超时 write_timeout: 2s # 写超时 -write: - host: 10.10.103.131 # ip地址 - port: 6379 # 端口 - password: Worktask@Redis2023 - db: 0 #db - timeout: 10s # 连接超时 - read_timeout: 2s # 读超时 - write_timeout: 2s # 写超时 + +host: 10.10.103.131 # ip地址 +port: 6379 # 端口 +password: Worktask@Redis2023 +db: 6 #db + diff --git a/framework/provider/cache/services/redis.go b/framework/provider/cache/services/redis.go index 53cc333..6845be1 100644 --- a/framework/provider/cache/services/redis.go +++ b/framework/provider/cache/services/redis.go @@ -30,7 +30,7 @@ func NewRedisCache(params ...interface{}) (interface{}, error) { // 获取redis服务配置,并且实例化redis.Client redisService := container.MustMake(contract.RedisKey).(contract.RedisService) - client, err := redisService.GetClient(redis.WithConfigPath("cache")) + client, err := redisService.GetClient(redis.WithConfigPath("redis")) if err != nil { return nil, err } @@ -111,7 +111,8 @@ func (r *RedisCache) Set(ctx context.Context, key string, val string, timeout ti // SetObj 设置某个key和对象到缓存, 对象必须实现 https://pkg.go.dev/encoding#BinaryMarshaler func (r *RedisCache) SetObj(ctx context.Context, key string, val interface{}, timeout time.Duration) error { - return r.client.Set(ctx, key, val, timeout).Err() + set := r.client.Set(ctx, key, val, timeout) + return set.Err() } // SetMany 设置多个key和值到缓存 diff --git a/framework/provider/infrastructure/service.go b/framework/provider/infrastructure/service.go index 51923de..a7c2759 100644 --- a/framework/provider/infrastructure/service.go +++ b/framework/provider/infrastructure/service.go @@ -2,18 +2,18 @@ package infrastructure import ( "github.com/Superdanda/hade/framework" - "github.com/Superdanda/hade/framework/contract" _ "github.com/Superdanda/hade/framework/provider/repository" ) type Service struct { - container framework.Container - contract.InfrastructureService + container framework.Container ormRepositoryMap map[string]interface{} } func NewInfrastructureService(params ...interface{}) (interface{}, error) { - return &Service{container: params[0].(framework.Container)}, nil + infrastructureService := &Service{container: params[0].(framework.Container), + ormRepositoryMap: make(map[string]interface{})} + return infrastructureService, nil } func (i *Service) GetModuleOrmRepository(moduleName string) interface{} { diff --git a/framework/provider/repository/provider.go b/framework/provider/repository/provider.go index ff664ef..efed527 100644 --- a/framework/provider/repository/provider.go +++ b/framework/provider/repository/provider.go @@ -16,7 +16,7 @@ func (r RepositoryProvider) Boot(container framework.Container) error { } func (r RepositoryProvider) IsDefer() bool { - return true + return false } func (r RepositoryProvider) Params(container framework.Container) []interface{} { diff --git a/framework/provider/repository/service.go b/framework/provider/repository/service.go index f10cce5..810ffd1 100644 --- a/framework/provider/repository/service.go +++ b/framework/provider/repository/service.go @@ -10,7 +10,7 @@ import ( "time" ) -func RegisterRepository[T any, ID comparable](service contract.RepositoryService, key string, ormRepository interface{}) { +func RegisterRepository[T any, ID comparable](service contract.RepositoryService, key string, ormRepository interface{}) *HadeGenericRepository[T, ID] { container := service.GetContainer() cacheService := container.MustMake(contract.CacheKey).(contract.CacheService) configService := container.MustMake(contract.ConfigKey).(contract.Config) @@ -32,6 +32,7 @@ func RegisterRepository[T any, ID comparable](service contract.RepositoryService ormRepository.(contract.OrmRepository[T, ID]), ) service.GetGenericRepositoryMap()[key] = genericRepository + return genericRepository } type HadeRepositoryService struct { @@ -41,9 +42,6 @@ type HadeRepositoryService struct { } func NewHadeRepositoryService(params ...interface{}) (interface{}, error) { - if len(params) < 2 { - return nil, errors.New("insufficient parameters") - } container, ok := params[0].(framework.Container) if !ok { return nil, errors.New("invalid container parameter") diff --git a/framework/util/request/request.go b/framework/util/request/request.go new file mode 100644 index 0000000..0053a24 --- /dev/null +++ b/framework/util/request/request.go @@ -0,0 +1,197 @@ +package request + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "time" +) + +// DefaultClient 默认定义一个60秒超时的请求 +var DefaultClient = NewClient(60 * time.Second) + +// Client 定义了一个包含 http.Client 的结构体,可以设置超时时间等参数 +type Client struct { + httpClient *http.Client +} + +type Headers = map[string]string + +// NewClient 返回一个新的 Client 实例,可以设置请求超时时间 +func NewClient(timeout time.Duration) *Client { + return &Client{ + httpClient: &http.Client{ + Timeout: timeout, + }, + } +} + +// Request 封装了 HTTP 请求,返回响应的字节数组 +func (c *Client) Request(method, url string, headers map[string]string, body io.Reader) ([]byte, error) { + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + + // 设置请求头 + if headers != nil && len(headers) > 0 { + for key, value := range headers { + req.Header.Set(key, value) + } + } + + // 发起请求 + resp, err := DefaultClient.httpClient.Do(req) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + + } + }(resp.Body) + + // 读取响应体 + return io.ReadAll(resp.Body) +} + +// requestJSON 使用泛型,将响应解析为指定的类型 T +func requestJSON[T any](method, url string, headers map[string]string, body io.Reader) (T, error) { + var result T + // 发起请求,获取字节数组 + data, err := DefaultClient.Request(method, url, headers, body) + if err != nil { + return result, err + } + // 解析 JSON 到指定类型 + err = json.Unmarshal(data, &result) + return result, err +} + +// Get 快捷 GET 请求,返回字节数组 +func Get(url string, headers Headers) ([]byte, error) { + return DefaultClient.Request(http.MethodGet, url, headers, nil) +} + +// GetJSON 快捷 GET 请求,使用泛型返回指定类型 +func GetJSON[T any](url string, headers Headers) (T, error) { + return requestJSON[T](http.MethodGet, url, headers, nil) +} + +// Post 快捷 POST 请求,返回字节数组 +func Post(url string, headers Headers, body interface{}) ([]byte, error) { + jsonBody, err := JSONBody(body) + if err != nil { + return nil, err + } + return DefaultClient.Request(http.MethodPost, url, headers, jsonBody) +} + +// PostJSON 快捷 POST 请求,使用泛型返回指定类型 +func PostJSON[T any](url string, headers Headers, body interface{}) (T, error) { + var result T + jsonBody, err := JSONBody(body) + if err != nil { + return result, err + } + return requestJSON[T](http.MethodPost, url, headers, jsonBody) +} + +// JSONBody 接受任意结构体数据,将其序列化为 JSON 格式并作为请求体 +func JSONBody(data interface{}) (*bytes.Buffer, error) { + jsonBytes, err := json.Marshal(data) + if err != nil { + return nil, err + } + return bytes.NewBuffer(jsonBytes), nil +} + +// RequestBuilder 定义了一个请求构建器,支持链式调用 +type RequestBuilder struct { + client *Client + method string + url string + headers Headers + body io.Reader +} + +// NewRequest 创建一个新的 RequestBuilder 实例 +func NewRequest() *RequestBuilder { + return &RequestBuilder{ + client: DefaultClient, + headers: make(map[string]string), + } +} + +// WithClient 设置自定义的 Client +func (rb *RequestBuilder) WithClient(client *Client) *RequestBuilder { + rb.client = client + return rb +} + +// Get 设置请求方法为 GET,并指定 URL +func (rb *RequestBuilder) Get(url string) *RequestBuilder { + rb.method = http.MethodGet + rb.url = url + return rb +} + +// Post 设置请求方法为 POST,并指定 URL +func (rb *RequestBuilder) Post(url string) *RequestBuilder { + rb.method = http.MethodPost + rb.url = url + return rb +} + +// Header 添加一个请求头 +func (rb *RequestBuilder) Header(key, value string) *RequestBuilder { + rb.headers[key] = value + return rb +} + +// Headers 批量添加请求头,接受任意数量的键值对 +func (rb *RequestBuilder) Headers(headers map[string]string) *RequestBuilder { + for k, v := range headers { + rb.headers[k] = v + } + return rb +} + +// JSONBody 设置请求体为 JSON 格式,接受任意可被 json.Marshal 的类型 +func (rb *RequestBuilder) JSONBody(body interface{}) *RequestBuilder { + data, _ := json.Marshal(body) + rb.body = bytes.NewBuffer(data) + rb.Header("Content-Type", "application/json") + return rb +} + +// Body 设置请求体,接受一个 io.Reader +func (rb *RequestBuilder) Body(body io.Reader) *RequestBuilder { + rb.body = body + return rb +} + +// Do 发送请求,返回响应的字节数组 +func (rb *RequestBuilder) Do() ([]byte, error) { + req, err := http.NewRequest(rb.method, rb.url, rb.body) + if err != nil { + return nil, err + } + + // 设置请求头 + for key, value := range rb.headers { + req.Header.Set(key, value) + } + + // 发起请求 + resp, err := rb.client.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // 读取响应体 + return io.ReadAll(resp.Body) +}