diff --git a/go.mod b/go.mod index ddcd38b..3166e68 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,12 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/golang-jwt/jwt/v5 v5.0.0 github.com/joho/godotenv v1.5.1 - github.com/labstack/echo-jwt/v4 v4.2.0 github.com/labstack/echo/v4 v4.11.1 github.com/labstack/gommon v0.4.0 github.com/mattn/go-isatty v0.0.19 - github.com/mattn/go-sqlite3 v1.14.17 - github.com/mitchellh/mapstructure v1.5.0 github.com/rs/xid v1.5.0 + github.com/spf13/cobra v1.7.0 github.com/swaggo/echo-swagger v1.4.1 - github.com/swaggo/swag v1.16.2 golang.org/x/time v0.3.0 gorm.io/driver/mysql v1.5.1 gorm.io/driver/postgres v1.5.2 @@ -37,6 +34,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.3.1 // indirect @@ -45,10 +43,13 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/microsoft/go-mssqldb v1.1.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.2 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect + github.com/swaggo/swag v1.16.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect golang.org/x/crypto v0.11.0 // indirect diff --git a/go.sum b/go.sum index 2f63c4d..e21076f 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -46,6 +47,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -74,8 +77,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= -github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU= github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= @@ -96,8 +97,6 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6 github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/microsoft/go-mssqldb v1.1.0 h1:jsV+tpvcPTbNNKW0o3kiCD69kOHICsfjZ2VcVu2lKYc= github.com/microsoft/go-mssqldb v1.1.0/go.mod h1:LzkFdl4z2Ck+Hi+ycGOTbL56VEfgoyA2DvYejrNGbRk= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -108,6 +107,11 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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= diff --git a/internal/services/company/controller/company_controller.go b/internal/services/company/controller/company_controller.go index 37545e5..a6f9068 100644 --- a/internal/services/company/controller/company_controller.go +++ b/internal/services/company/controller/company_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/company/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type CompanyController struct { - util.Controller[entities.Company, request.CompanyUpsertRequest] + app.Controller[entities.Company, request.CompanyUpsertRequest] } func (ctr *CompanyController) InitRoutes(r *echo.Group) { diff --git a/internal/services/company/controller/company_department_controller.go b/internal/services/company/controller/company_department_controller.go index e9eae72..c20f7d9 100644 --- a/internal/services/company/controller/company_department_controller.go +++ b/internal/services/company/controller/company_department_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/company/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type CompanyDepartmentController struct { - util.Controller[entities.CompanyDepartment, request.CompanyDepartmentUpsertRequest] + app.Controller[entities.CompanyDepartment, request.CompanyDepartmentUpsertRequest] } func (c *CompanyDepartmentController) InitRoutes(r *echo.Group) { diff --git a/internal/services/company/controller/company_staff_controller.go b/internal/services/company/controller/company_staff_controller.go index d1fac85..8135744 100644 --- a/internal/services/company/controller/company_staff_controller.go +++ b/internal/services/company/controller/company_staff_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/company/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type CompanyStaffController struct { - util.Controller[entities.CompanyStaff, request.CompanyStaffUpsertRequest] + app.Controller[entities.CompanyStaff, request.CompanyStaffUpsertRequest] } func (c *CompanyStaffController) InitRoutes(r *echo.Group) { diff --git a/internal/services/config/controller/config_controller.go b/internal/services/config/controller/config_controller.go index 6c62b6f..8381a72 100644 --- a/internal/services/config/controller/config_controller.go +++ b/internal/services/config/controller/config_controller.go @@ -4,13 +4,13 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/config/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type ConfigController struct { - util.Controller[entities.Config, request.ConfigUpsertRequest] + app.Controller[entities.Config, request.ConfigUpsertRequest] } func (ctr *ConfigController) InitRoutes(r *echo.Group) { - ctr.RegisterRoutes("/config", r) + ctr.RegisterRoutes("/configs", r) } diff --git a/internal/services/config/controller/config_group_controller.go b/internal/services/config/controller/config_group_controller.go index 4ff0b53..185e911 100644 --- a/internal/services/config/controller/config_group_controller.go +++ b/internal/services/config/controller/config_group_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/config/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type ConfigGroupController struct { - util.Controller[entities.ConfigGroup, request.ConfigGroupUpsertRequest] + app.Controller[entities.ConfigGroup, request.ConfigGroupUpsertRequest] } // InitRoutes 实现路由注册接口 diff --git a/internal/services/feature/controller/feature_category_controller.go b/internal/services/feature/controller/feature_category_controller.go index feb6416..473ed43 100644 --- a/internal/services/feature/controller/feature_category_controller.go +++ b/internal/services/feature/controller/feature_category_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureCategoryController struct { - util.Controller[entities.FeatureCategory, request.FeatureCategoryUpsertRequest] + app.Controller[entities.FeatureCategory, request.FeatureCategoryUpsertRequest] } func (f *FeatureCategoryController) InitRoutes(r *echo.Group) { diff --git a/internal/services/feature/controller/feature_config_controller.go b/internal/services/feature/controller/feature_config_controller.go index 267890a..9006c9c 100644 --- a/internal/services/feature/controller/feature_config_controller.go +++ b/internal/services/feature/controller/feature_config_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureConfigController struct { - util.Controller[entities.FeatureConfig, request.FeatureConfigUpsertRequest] + app.Controller[entities.FeatureConfig, request.FeatureConfigUpsertRequest] } func (f *FeatureConfigController) InitRoutes(r *echo.Group) { diff --git a/internal/services/feature/controller/feature_content_chapter_controller.go b/internal/services/feature/controller/feature_content_chapter_controller.go index 2c4caa9..15db1ca 100644 --- a/internal/services/feature/controller/feature_content_chapter_controller.go +++ b/internal/services/feature/controller/feature_content_chapter_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureContentChapterController struct { - util.Controller[entities.FeatureContentChapter, request.FeatureContentChapterUpsertRequest] + app.Controller[entities.FeatureContentChapter, request.FeatureContentChapterUpsertRequest] } func (f *FeatureContentChapterController) InitRoutes(r *echo.Group) { diff --git a/internal/services/feature/controller/feature_content_controller.go b/internal/services/feature/controller/feature_content_controller.go index 365ab89..1a05c4c 100644 --- a/internal/services/feature/controller/feature_content_controller.go +++ b/internal/services/feature/controller/feature_content_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureContentController struct { - util.Controller[entities.FeatureContent, request.FeatureContentUpsertRequest] + app.Controller[entities.FeatureContent, request.FeatureContentUpsertRequest] } func (f *FeatureContentController) InitRoutes(r *echo.Group) { diff --git a/internal/services/feature/controller/feature_content_detail_controller.go b/internal/services/feature/controller/feature_content_detail_controller.go index e7bd84d..0cbfd9c 100644 --- a/internal/services/feature/controller/feature_content_detail_controller.go +++ b/internal/services/feature/controller/feature_content_detail_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureContentDetailController struct { - util.Controller[entities.FeatureContentDetail, request.FeatureContentDetailUpsertRequest] + app.Controller[entities.FeatureContentDetail, request.FeatureContentDetailUpsertRequest] } func (f *FeatureContentDetailController) InitRoutes(r *echo.Group) { diff --git a/internal/services/feature/controller/feature_controller.go b/internal/services/feature/controller/feature_controller.go index f8bf938..eb6c3f5 100644 --- a/internal/services/feature/controller/feature_controller.go +++ b/internal/services/feature/controller/feature_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/feature/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type FeatureController struct { - util.Controller[entities.Feature, request.FeatureUpsertRequest] + app.Controller[entities.Feature, request.FeatureUpsertRequest] } func (f *FeatureController) InitRoutes(r *echo.Group) { diff --git a/internal/services/resource/controller/resource_category_controller.go b/internal/services/resource/controller/resource_category_controller.go index f3b5c44..55e0588 100644 --- a/internal/services/resource/controller/resource_category_controller.go +++ b/internal/services/resource/controller/resource_category_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/resource/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type ResourceCategoryController struct { - util.Controller[entities.Resource, request.ResourceCategoryUpsertRequest] + app.Controller[entities.Resource, request.ResourceCategoryUpsertRequest] } func (ctr *ResourceCategoryController) InitRoutes(r *echo.Group) { diff --git a/internal/services/resource/controller/resource_controller.go b/internal/services/resource/controller/resource_controller.go index 38b3f45..0b8bcf0 100644 --- a/internal/services/resource/controller/resource_controller.go +++ b/internal/services/resource/controller/resource_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/resource/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type ResourceController struct { - util.Controller[entities.Resource, request.ResourceUpsertRequest] + app.Controller[entities.Resource, request.ResourceUpsertRequest] } func (ctr *ResourceController) InitRoutes(r *echo.Group) { diff --git a/internal/services/service.go b/internal/services/service.go index dd422c8..d839f18 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -1,24 +1,49 @@ package services import ( + "context" + "github.com/labstack/echo/v4" + "sorbet/internal/services/company" + "sorbet/internal/services/config" + "sorbet/internal/services/feature" + "sorbet/internal/services/resource" + "sorbet/internal/services/system" "sorbet/pkg/app" ) -type Service struct { - inners []Service -} +var applets []app.Applet -func (s Service) Init(ctx *app.Context) error { - //TODO implement me - panic("implement me") +func Init() { + applets = []app.Applet{ + &config.Service{}, + &company.Service{}, + &resource.Service{}, + &feature.Service{}, + &system.Service{}, + } } -func (s Service) Start() error { - //TODO implement me - panic("implement me") +func Start(ctx context.Context) error { + e := ctx.Value("echo_framework").(*echo.Echo) + for _, service := range applets { + err := service.Init(app.NewContext(ctx, e.Group(""))) + if err != nil { + return err + } + err = service.Start() + if err != nil { + return err + } + } + return nil } -func (s Service) Stop() error { - //TODO implement me - panic("implement me") +func Stop() error { + for _, service := range applets { + err := service.Stop() + if err != nil { + return err + } + } + return nil } diff --git a/internal/services/system/controller/system_log_controller.go b/internal/services/system/controller/system_log_controller.go index 5adef52..285397d 100644 --- a/internal/services/system/controller/system_log_controller.go +++ b/internal/services/system/controller/system_log_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemLogController struct { - util.Controller[entities.SystemLog, request.SystemLogUpsertRequest] + app.Controller[entities.SystemLog, request.SystemLogUpsertRequest] } func (s *SystemLogController) InitRoutes(r *echo.Group) { diff --git a/internal/services/system/controller/system_menu_controller.go b/internal/services/system/controller/system_menu_controller.go index 2fce501..d0bb9dc 100644 --- a/internal/services/system/controller/system_menu_controller.go +++ b/internal/services/system/controller/system_menu_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemMenuController struct { - util.Controller[entities.SystemMenu, request.SystemMenuUpsertRequest] + app.Controller[entities.SystemMenu, request.SystemMenuUpsertRequest] } func (s *SystemMenuController) InitRoutes(r *echo.Group) { diff --git a/internal/services/system/controller/system_permission_controller.go b/internal/services/system/controller/system_permission_controller.go index e3c1124..f03f7d7 100644 --- a/internal/services/system/controller/system_permission_controller.go +++ b/internal/services/system/controller/system_permission_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemPermissionController struct { - util.Controller[entities.SystemPermission, request.SystemPermissionUpsertRequest] + app.Controller[entities.SystemPermission, request.SystemPermissionUpsertRequest] } func (s *SystemPermissionController) InitRoutes(r *echo.Group) { diff --git a/internal/services/system/controller/system_role_controller.go b/internal/services/system/controller/system_role_controller.go index df44a96..ff33d2d 100644 --- a/internal/services/system/controller/system_role_controller.go +++ b/internal/services/system/controller/system_role_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemRoleController struct { - util.Controller[entities.SystemRole, request.SystemRoleUpsertRequest] + app.Controller[entities.SystemRole, request.SystemRoleUpsertRequest] } func (s *SystemRoleController) InitRoutes(r *echo.Group) { diff --git a/internal/services/system/controller/system_role_power_controller.go b/internal/services/system/controller/system_role_power_controller.go index 7eb0951..6e51391 100644 --- a/internal/services/system/controller/system_role_power_controller.go +++ b/internal/services/system/controller/system_role_power_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemRolePowerController struct { - util.Controller[entities.SystemRolePower, request.SystemRolePowerUpsertRequest] + app.Controller[entities.SystemRolePower, request.SystemRolePowerUpsertRequest] } func (s *SystemRolePowerController) InitRoutes(r *echo.Group) { diff --git a/internal/services/system/controller/system_user_controller.go b/internal/services/system/controller/system_user_controller.go index 92218c5..962b98d 100644 --- a/internal/services/system/controller/system_user_controller.go +++ b/internal/services/system/controller/system_user_controller.go @@ -4,11 +4,11 @@ import ( "github.com/labstack/echo/v4" "sorbet/internal/entities" "sorbet/internal/services/system/request" - "sorbet/internal/util" + "sorbet/pkg/app" ) type SystemUserController struct { - util.Controller[entities.SystemUser, request.SystemUserUpsertRequest] + app.Controller[entities.SystemUser, request.SystemUserUpsertRequest] } func (s *SystemUserController) InitRoutes(r *echo.Group) { diff --git a/internal/util/echo_bind.go b/internal/util/echo_bind.go deleted file mode 100644 index f4829e8..0000000 --- a/internal/util/echo_bind.go +++ /dev/null @@ -1,43 +0,0 @@ -package util - -import ( - "github.com/labstack/echo/v4" - "sorbet/pkg/rsp" -) - -// RequestGuarder 参数守卫函数签名 -type RequestGuarder[T any] func(c echo.Context, req *T) error - -// Bind 将提交的参数绑定到泛型 T 的实例上 -func Bind[T any](c echo.Context, guards ...RequestGuarder[T]) (*T, error) { - var req T - if err := c.Bind(&req); err != nil { - return nil, err - } - if err := c.Validate(&req); err != nil { - return nil, err - } - for _, guard := range guards { - if err := guard(c, &req); err != nil { - return nil, err - } - } - return &req, nil -} - -func BindId(c echo.Context, must bool) (uint, error) { - request, err := Bind[struct { - ID uint `json:"id" xml:"id" path:"id"` - }](c) - if err != nil { - return 0, err - } - if must { - if request.ID <= 0 { - return 0, rsp.ErrBadParams - } - } else if request.ID < 0 { - return 0, rsp.ErrBadParams - } - return request.ID, err -} diff --git a/pkg/app/service.go b/pkg/app/applet.go similarity index 90% rename from pkg/app/service.go rename to pkg/app/applet.go index ce6690e..3ed1d9a 100644 --- a/pkg/app/service.go +++ b/pkg/app/applet.go @@ -2,7 +2,7 @@ package app import "github.com/labstack/echo/v4" -type Service interface { +type Applet interface { // Init 初始化服务 Init(ctx *Context) error // Start 启动服务 diff --git a/pkg/app/context.go b/pkg/app/context.go index 5ebbbfe..60a85f8 100644 --- a/pkg/app/context.go +++ b/pkg/app/context.go @@ -8,20 +8,18 @@ import ( type Context struct { context.Context - prefix string store map[any]any router *echo.Group mu sync.RWMutex } -// Prefix 设置路由前缀 -func (c *Context) Prefix(prefix string) { - c.mu.Lock() - defer c.mu.Unlock() - if c.prefix != "" { - panic("already prefixed") +func NewContext(ctx context.Context, router *echo.Group) *Context { + return &Context{ + Context: ctx, + store: make(map[any]any), + router: router, + mu: sync.RWMutex{}, } - c.prefix = prefix } // Routes 注册路由 diff --git a/internal/util/controller.go b/pkg/app/controller.go similarity index 60% rename from internal/util/controller.go rename to pkg/app/controller.go index 2f917e1..8e5b19c 100644 --- a/internal/util/controller.go +++ b/pkg/app/controller.go @@ -1,148 +1,35 @@ -package util +package app import ( "github.com/labstack/echo/v4" "gorm.io/gorm" - "net/url" "reflect" "sorbet/pkg/db" "sorbet/pkg/ioc" "sorbet/pkg/rsp" - "strconv" - "strings" ) -type GetID interface { +// Upsertable 数据创建和更新需要接口 +// 用于控制器 Create、Update 方法 +type Upsertable interface { + // GetID 用于 Controller.Update 方法, + // 定位被更新的数据编号 GetID() any -} - -type ToMap interface { + // ToMap 用于 Controller.Update 方法, + // 返回被更新的数据,支持零值 ToMap() map[string]any -} - -type ToEntity interface { + // ToEntity 用于 Controller.Create 方法, + // 返回被创建的实体的数据 ToEntity() any } -type ControllerRequest interface { - GetID - ToMap - ToEntity -} - -func ParseQuery[T any](query url.Values, qb *db.QueryBuilder[T]) (page, limit int, err error) { - var paginating bool - for key, values := range query { - switch key { - case "sortby": - for _, s := range values { - if s[0] == '+' { - qb.AscentBy(s[1:]) - } else if s[0] == '-' { - qb.DescentBy(s[1:]) - } else { - qb.AscentBy(s) - } - } - case "limit", "page": - var v int - if values[0] != "" { - v, err = strconv.Atoi(values[0]) - if err != nil { - return - } - } - if v <= 0 { - return 0, 0, rsp.ErrInternal - } - if key == "limit" { - qb.Limit(v) - limit = v - } else { - paginating = true - page = max(v, 1) - } - default: - v := values[0] - i := strings.IndexByte(key, '#') - if i == -1 { - qb.Eq(key, v) - continue - } - switch k, op := key[:i], key[i+1:]; op { - case "=": - qb.Eq(k, v) - case "!=": - qb.Neq(k, v) - case "<": - qb.Lt(k, v) - case "<=": - qb.Lte(k, v) - case ">": - qb.Gt(k, v) - case ">=": - qb.Gte(k, v) - case "<>", "><": - var less, more any - switch len(values) { - case 2: - less, more = values[0], values[1] - case 1: - vs := strings.Split(v, ",") - if len(vs) != 2 || vs[0] == "" || vs[1] == "" { - return 0, 0, rsp.ErrBadParams - } - less, more = vs[0], vs[1] - } - if op == "<>" { - qb.Between(k, less, more) - } else { - qb.NotBetween(k, key, more) - } - case "nil": - qb.IsNull(k) - case "!nil": - qb.NotNull(k) - case "~": - qb.Like(k, v) - case "!~": - qb.NotLike(k, v) - case "in", "!in": - if len(values) == 1 { - values = strings.Split(v, ",") - } - vs := make([]any, len(values)) - for i, value := range values { - vs[i] = value - } - if op == "in" { - qb.In(k, vs...) - } else { - qb.NotIn(k, vs...) - } - default: - qb.Eq(key, v) - } - } - } - if paginating { - return - } - if limit == 0 { - limit = 30 - qb.Limit(limit) - } - qb.Offset((page - 1) * limit) - return -} - func getID(req any) (val any) { defer func() { if recover() != nil { val = nil } }() - val = req.(GetID).GetID() + val = req.(Upsertable).GetID() rv := reflect.ValueOf(val) if rv.IsZero() || rv.IsNil() { val = nil @@ -151,19 +38,18 @@ func getID(req any) (val any) { } func getValues(req any) map[string]any { - if v, ok := req.(ToMap); ok { + if v, ok := req.(Upsertable); ok { return v.ToMap() } return nil } func getEntity[T any](request any) *T { - v, ok := request.(ToEntity) + v, ok := request.(Upsertable) if !ok { return nil } - ent, ok := v.ToEntity().(*T) - if ok { + if ent, ok := v.ToEntity().(*T); ok { return ent } return nil @@ -175,6 +61,7 @@ func getEntity[T any](request any) *T { // 泛型 [Upsert] 表示创建或更新时需要的数据。 type Controller[Entity any, Upsert any] struct{} +// RegisterRoutes 注册路由 func (ctr *Controller[Entity, Upsert]) RegisterRoutes(path string, r *echo.Group) { r.PUT(path, ctr.Create) r.DELETE(path+"/:id", ctr.Delete) @@ -183,6 +70,7 @@ func (ctr *Controller[Entity, Upsert]) RegisterRoutes(path string, r *echo.Group r.GET(path, ctr.List) } +// ORM 获取 gorm.DB 实例 func (ctr *Controller[Entity, Upsert]) ORM(c echo.Context) (*gorm.DB, error) { orm, err := ioc.Get[gorm.DB]() if err != nil { @@ -191,6 +79,7 @@ func (ctr *Controller[Entity, Upsert]) ORM(c echo.Context) (*gorm.DB, error) { return orm.WithContext(c.Request().Context()), nil } +// Repository 获取 Repository 实例 func (ctr *Controller[Entity, Upsert]) Repository() (*db.Repository[Entity], error) { orm, err := ioc.Get[gorm.DB]() if err != nil { @@ -249,7 +138,7 @@ func (ctr *Controller[Entity, Upsert]) upsert(c echo.Context, isCreate bool) err // Delete 通过ID删除数据 func (ctr *Controller[Entity, Upsert]) Delete(c echo.Context) error { - id, err := BindId(c, true) + id, err := BindID(c) if err != nil { return err } @@ -266,7 +155,7 @@ func (ctr *Controller[Entity, Upsert]) Delete(c echo.Context) error { // Get 通过ID获取数据 func (ctr *Controller[Entity, Upsert]) Get(c echo.Context) error { - id, err := BindId(c, true) + id, err := BindID(c) if err != nil { return err } @@ -288,7 +177,7 @@ func (ctr *Controller[Entity, Upsert]) List(c echo.Context) error { return err } qb := repo.NewQueryBuilder(c.Request().Context()) - _, _, err = ParseQuery[Entity](c.QueryParams(), qb) + _, _, err = BindQuery[Entity](c, qb) if err != nil { return err } diff --git a/pkg/app/echo.go b/pkg/app/echo.go deleted file mode 100644 index f8dcfd5..0000000 --- a/pkg/app/echo.go +++ /dev/null @@ -1,15 +0,0 @@ -package app - -import "github.com/labstack/echo/v4" - -type echoContext struct { - echo.Context - ctx *Context -} - -func (e *echoContext) Get(key string) any { - if val, ok := e.ctx.Get(key); ok && val != nil { - return val - } - return e.Context.Get(key) -} diff --git a/pkg/app/echo_utils.go b/pkg/app/echo_utils.go new file mode 100644 index 0000000..0194ccc --- /dev/null +++ b/pkg/app/echo_utils.go @@ -0,0 +1,239 @@ +package app + +import ( + "github.com/labstack/echo/v4" + "net/http" + "reflect" + "sorbet/pkg/db" + "sorbet/pkg/rsp" + "strconv" + "strings" +) + +type ( + // RequestGuarder 参数守卫函数签名 + RequestGuarder func(c echo.Context, req any) error + + // BodyBinder 将请求体绑定到结构体上 + BodyBinder interface { + BindBody(c echo.Context, i any) (err error) + } +) + +// 使用内置数据绑定函数 +// 默认使用 echo.DefaultBinder +var binder BodyBinder + +// Validate 验证数据 +func Validate(c echo.Context, t any, guards ...RequestGuarder) error { + err := c.Validate(t) + if err != nil { + return err + } + for _, guard := range guards { + err = guard(c, t) + if err != nil { + return err + } + } + return nil +} + +// Bind 将提交的参数绑定到泛型 T 的实例上 +func Bind[T any](c echo.Context, guards ...RequestGuarder) (*T, error) { + var req T + if err := c.Bind(&req); err != nil { + return nil, err + } + if err := Validate(c, &req, guards...); err != nil { + return nil, err + } + return &req, nil +} + +// BindBody 将提交请求体绑定到泛型 T 的实例上 +func BindBody[T any](c echo.Context, guards ...RequestGuarder) (*T, error) { + b, ok := c.Echo().Binder.(BodyBinder) + if !ok || b == nil { + if binder == nil { + binder = &echo.DefaultBinder{} + } + b = binder + } + var req T + if err := b.BindBody(c, &req); err != nil { + return nil, err + } + if err := Validate(c, &req, guards...); err != nil { + return nil, err + } + return &req, nil +} + +type identifier struct { + ID any `json:"id" xml:"id"` +} + +// BindID 自请求中获取需要的数据编号 +// 查找的是名称为 `id` 的数据,查找顺序如下 +// 1. 查看路由中是否定义,比如:`/configs/:id` +// 2. 查看查询字符串中是否存在,比如:`?id=123` +// 3. 查看请求体中是否存在,支持 json 和 xml 两种格式。 +func BindID(c echo.Context) (id any, err error) { + defer func() { + if recovered := recover(); recovered != nil { + c.Logger().Debugf("%#v", recovered) + id = nil + err = rsp.ErrBadParams + } + }() + for i, name := range c.ParamNames() { + if name == "id" { + id = c.ParamValues()[i] + if id != "" { + return + } + break + } + } + switch c.Request().Method { + case http.MethodGet, http.MethodDelete, http.MethodHead: + for key, values := range c.QueryParams() { + if key == "id" { + id = values[0] + if id != "" { + return + } + break + } + } + } + if c.Request().ContentLength == 0 { + err = rsp.ErrBadParams + return + } + var data *identifier + data, err = BindBody[identifier](c) + if err != nil { + return nil, err + } + if data.ID == nil { + return nil, rsp.ErrBadParams + } + rv := reflect.ValueOf(data.ID) + if rv.IsZero() || rv.IsNil() { + return nil, rsp.ErrBadParams + } + return data.ID, nil +} + +func BindQuery[T any](c echo.Context, qb *db.QueryBuilder[T]) (page, limit int, err error) { + query := c.QueryParams() + var paginating bool + for key, values := range query { + switch key { + case "sortby": + for _, s := range values { + if s[0] == '+' { + qb.AscentBy(s[1:]) + } else if s[0] == '-' { + qb.DescentBy(s[1:]) + } else { + qb.AscentBy(s) + } + } + case "limit", "page": + var v int + if values[0] != "" { + v, err = strconv.Atoi(values[0]) + if err != nil { + return + } + } + if v <= 0 { + err = rsp.ErrInternal + return + } + if key == "limit" { + qb.Limit(v) + limit = v + } else { + paginating = true + page = max(v, 1) + } + default: + v := values[0] + i := strings.IndexByte(key, '#') + if i == -1 { + qb.Eq(key, v) + continue + } + switch k, op := key[:i], key[i+1:]; op { + case "=": + qb.Eq(k, v) + case "!=": + qb.Neq(k, v) + case "<": + qb.Lt(k, v) + case "<=": + qb.Lte(k, v) + case ">": + qb.Gt(k, v) + case ">=": + qb.Gte(k, v) + case "<>", "><": + var less, more any + switch len(values) { + case 2: + less, more = values[0], values[1] + case 1: + vs := strings.Split(v, ",") + if len(vs) != 2 || vs[0] == "" || vs[1] == "" { + err = rsp.ErrBadParams + return + } + less, more = vs[0], vs[1] + default: + err = rsp.ErrBadParams + return + } + if op == "<>" { + qb.Between(k, less, more) + } else { + qb.NotBetween(k, key, more) + } + case "nil": + qb.IsNull(k) + case "!nil": + qb.NotNull(k) + case "~": + qb.Like(k, v) + case "!~": + qb.NotLike(k, v) + case "in", "!in": + if len(values) == 1 { + values = strings.Split(v, ",") + } + vs := make([]any, len(values)) + for i, value := range values { + vs[i] = value + } + if op == "in" { + qb.In(k, vs...) + } else { + qb.NotIn(k, vs...) + } + default: + qb.Eq(key, v) + } + } + } + if paginating { + if limit == 0 { + limit = 30 + qb.Limit(limit) + } + qb.Offset((page - 1) * limit) + } + return +}