diff --git a/src/apps/adminapp/api_server.go b/src/apps/adminapp/api_server.go index 72f402b33bf8ed5cbd9c96c95a58c6cdee00b571..e1a2da971d90e15028ea793a592f0b1d5a2672b8 100644 --- a/src/apps/adminapp/api_server.go +++ b/src/apps/adminapp/api_server.go @@ -57,6 +57,7 @@ func (s *ApiServer) router(g gin.IRouter) { //登陆相关 g.POST("/mag/auth/login", captcha.VerifyHandler(), ctr.AuthLogin, s.RecordLog("登录")) //不需要授权 + authGroup.POST("/mag/auth/check", ctr.AuthCheck) authGroup.POST("/mag/auth/info", ctr.AuthInfo) authGroup.POST("/mag/auth/logout", ctr.AuthLogout, s.RecordLog("登出")) authGroup.POST("/mag/auth/fresh_token", ctr.AuthFreshToken) @@ -169,8 +170,8 @@ func NewApiServer(di *dig.Scope, conf *configstc.AdminAppConfig, logger v1log.IL ApiServer: apps.NewApiServer(gin.Default(), conf.ApiServer), } - //开启跨域设置 - s.ApiServer.WithCors() + s.WithName(conf.Name) + s.WithCors() //开启跨域设置 s.AuthOption.AuthHandler = gin_http.AuthHandler(jwtauth.NewJwtTokenHandler(conf.AuthConfig.SecretKey)) diff --git a/src/apps/adminapp/controllers/http/ctr_auth.go b/src/apps/adminapp/controllers/http/ctr_auth.go index 603927ceae37be0b359bd3a2576e51057f667c46..69446294abbd85df758ce4c6eb073f7c17c374d2 100644 --- a/src/apps/adminapp/controllers/http/ctr_auth.go +++ b/src/apps/adminapp/controllers/http/ctr_auth.go @@ -178,6 +178,18 @@ func (ctr *AuthController) AuthInfo(ctx *gin.Context) { ctr.Response(ctx, respdata.CSuccess.MData(utils.CopyTo(&auth, new(respdata.AuthResp)))) } +// AuthCheck godoc +// @Summary Auth-登录校验 +// @Description 登录校验ok正常返回,不通过返回99 +// @Tags user.auth +// @Security ApiKeyAuth +// @Produce json +// @success 200 {object} respdata.ResponseData{} "返回结果" +// @Router /mag/auth/check [post] +func (ctr *AuthController) AuthCheck(ctx *gin.Context) { + ctr.Response(ctx, respdata.CSuccess) +} + // AuthFreshToken godoc // @Summary Auth-刷新授权 // @Description 刷新授权token diff --git a/src/apps/adminapp/i18n/en.go b/src/apps/adminapp/i18n/en.go new file mode 100644 index 0000000000000000000000000000000000000000..c32b716ec411f9201ace621ff01347ad3593dcf8 --- /dev/null +++ b/src/apps/adminapp/i18n/en.go @@ -0,0 +1,23 @@ +package i18n + +// En 英文 +const En = ` +SUCCESS="success" +ERROR="error" +PERMISSION_DENY="deny" +TRY_AGAIN="please try again" +SYSTEM_BUSY="system busy" +PARAMS_INVALID="invalid parameter" +OPERATE_FAIL="operate fail" + +Hello="Halo" +OperateFail="operate fail" +AccountExist="account has exist" +AccountDisable="account has disable" +PasswordWrong="password wrong" + +[user] +Exists="has exists" + +AccountWrong="account {{.Name}} wrong" +` diff --git a/src/apps/adminapp/i18n/zh.go b/src/apps/adminapp/i18n/zh.go new file mode 100644 index 0000000000000000000000000000000000000000..8195b0de48a6be8ec14a33a1ebd01b4d12d28fa4 --- /dev/null +++ b/src/apps/adminapp/i18n/zh.go @@ -0,0 +1,23 @@ +package i18n + +// Zh 中文 +const Zh = ` +SUCCESS="成功" +ERROR="错误" +PERMISSION_DENY="无权限" +TRY_AGAIN="请重试" +SYSTEM_BUSY="系统繁忙" +PARAMS_INVALID="无效参数" +OPERATE_FAIL="操作失败" + +Hello="你好" +OperateFail="操作失败" +AccountExist = "账户已存在" +AccountDisable="账户已禁用" +PasswordWrong="密码错误" + +[user] +Exists = "已存在" + +AccountWrong = "账户{{.Name}}错误" +` diff --git a/src/apps/app.go b/src/apps/app.go index 0af61be20e67b7bb4d1a21dc11220e6e96e64613..9660a64a6b13191a88dd3c55a42968e7486508dc 100644 --- a/src/apps/app.go +++ b/src/apps/app.go @@ -41,7 +41,7 @@ func (app *App) Start() error { err := server.Start() if err != nil { - logs.Out.Error("====== App[%s] Start FAIL %s", app.Name(), err) + logs.Out.Error("====== App[%s] START FAIL %s", app.Name(), err) return } }() diff --git a/src/apps/rssapp/api_server.go b/src/apps/rssapp/api_server.go new file mode 100644 index 0000000000000000000000000000000000000000..8f2bf2c0940fe2fae26bd83d8608306372d54558 --- /dev/null +++ b/src/apps/rssapp/api_server.go @@ -0,0 +1,118 @@ +package rssapp + +import ( + "gitee.com/captials-team/ubdframe/src/apps" + "gitee.com/captials-team/ubdframe/src/apps/rssapp/docs" + "gitee.com/captials-team/ubdframe/src/common" + "gitee.com/captials-team/ubdframe/src/common/utils" + "gitee.com/captials-team/ubdframe/src/domain/configstc" + "gitee.com/captials-team/ubdframe/src/domain/interfaces" + "gitee.com/captials-team/ubdframe/src/pkg/gin_http" + "gitee.com/captials-team/ubdframe/src/pkg/jwtauth" + v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" + "github.com/gin-gonic/gin" + "go.uber.org/dig" +) + +type ApiServer struct { + *apps.ApiServer + + di *dig.Scope + conf *configstc.RssAppConfig + + gin_http.AuthOption //认证相关选项配置 + gin_http.SwaggerOption //swagger相关选项配置 + gin_http.PProfOption //pprof选项配置 + gin_http.OperateLogOption //操作日志 + gin_http.AuthExtendInfoOption //认证扩展信息选项配置 + gin_http.AccreditOption //授权选项 + + AdminAuth gin_http.AuthOption //管理站认证相关选项配置 +} + +func (s *ApiServer) Name() string { + return "rss_api" +} + +func (s *ApiServer) InitRouter() { + s.Engine().GET("ping", gin_http.PingHandler) + s.InitRouterForGin(s.Engine()) +} + +func (s *ApiServer) router(g gin.IRouter) { + g.Use( + gin_http.PanicHandler, + gin_http.QPSLimiterHandler(10, 10), + ) + + //用于客户端 + common.ErrPanic(s.di.Invoke(func(ctr interfaces.ItfRssController) { + g.POST("/rss/items", ctr.RssCacheItems) + })) + + authGroup := g.Group("", s.OptAuthHandler(), s.OptAccreditHandler()) + //管理站 + common.ErrPanic(s.di.Invoke(func(ctr interfaces.ItfRssManageController) { + //rss原数据 + authGroup.POST("/mag/rss/sources", ctr.RssSourceList) + authGroup.POST("/mag/rss/source/items", ctr.RssSourceItems) + })) +} + +func (s *ApiServer) InitRouterForGin(engine *gin.Engine) { + var g = engine.Group("") + if len(s.conf.RoutePrefix) > 0 { + g = engine.Group(s.conf.RoutePrefix) + } + + //注册swagger + s.SwaggerRouter(g) + + //注册pprof + s.PProfRouter(engine) + + s.router(g) + + return +} + +func (s *ApiServer) Start() error { + if !s.Module() { + s.InitRouter() + } + return s.ApiServer.Start() +} + +func (s *ApiServer) Stop() error { + return s.ApiServer.Stop() +} + +func NewApiServer(di *dig.Scope, conf *configstc.RssAppConfig, logger v1log.ILog) *ApiServer { + + //swagger配置,取值后取指针是为了实现复用(多App) + swaggerDocs := *docs.SwaggerInforssservice + swaggerDocs.Host = conf.ApiServer.HostAddr() + utils.KeepHasPrefix(conf.RoutePrefix, "/") + + s := &ApiServer{ + di: di, + conf: conf, + SwaggerOption: gin_http.SwaggerOption{ + Enable: conf.DocsEnable, + Name: swaggerDocs.InstanceName(), + Swagger: &swaggerDocs, + }, + PProfOption: gin_http.PProfOption{ + Enable: conf.PProfEnable, + }, + ApiServer: apps.NewApiServer(gin.Default(), conf.ApiServer), + } + + //开启跨域设置 + s.ApiServer.WithCors() + + if conf.AuthConfig.Enable { + s.AuthOption.AuthHandler = gin_http.AuthHandler(jwtauth.NewJwtTokenHandler(conf.AuthConfig.SecretKey)) + } + + return s +} diff --git a/src/apps/rssapp/app.go b/src/apps/rssapp/app.go new file mode 100644 index 0000000000000000000000000000000000000000..3cec70a0725668578bf569f12943712ba1bc28c7 --- /dev/null +++ b/src/apps/rssapp/app.go @@ -0,0 +1,110 @@ +package rssapp + +import ( + "gitee.com/captials-team/ubdframe/src/apps" + httpController "gitee.com/captials-team/ubdframe/src/apps/rssapp/controllers/http" + "gitee.com/captials-team/ubdframe/src/common" + "gitee.com/captials-team/ubdframe/src/domain/configstc" + "gitee.com/captials-team/ubdframe/src/domain/interfaces" + "gitee.com/captials-team/ubdframe/src/domain/services" + "gitee.com/captials-team/ubdframe/src/domain/vo" + "gitee.com/captials-team/ubdframe/src/infrastructure/caches" + v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" + "gitee.com/captials-team/ubdframe/src/pkg/rsshelp" + "gitee.com/captials-team/ubdframe/src/pkg/uber_help" + "github.com/robfig/cron/v3" + "go.uber.org/dig" + "time" +) + +type App struct { + *apps.App + + l v1log.ILog + di *dig.Scope + ApiServer *ApiServer + RssFetcher *rsshelp.RssFetcher +} + +func (app *App) initCaches(conf *configstc.RssAppConfig) { + common.ErrPanic(app.di.Provide(func() caches.ItfCache { + return caches.NewMemoryStore(time.Minute * 10) + }), uber_help.ErrAlreadyProvided) + + common.ErrPanic(app.di.Provide(func(cache caches.ItfCache) services.RssCache { + return caches.NewCacheFacade[string, *vo.RssBody](cache) + }), uber_help.ErrAlreadyProvided) + + app.l.Info("provide cache success") +} + +func (app *App) startCron(f *rsshelp.RssFetcher, conf *configstc.RssAppConfig) { + //异步初始化 + if conf.AsyncInit { + go f.Fetch() + } else { + f.Fetch() + } + + //每30分钟获取一次 + c := cron.New() + c.AddFunc("@every 30m", f.Fetch) + c.Start() +} + +func (app *App) initRss(f *rsshelp.RssFetcher, ca services.RssCache, logger v1log.ILog, conf *configstc.RssAppConfig) { + app.RssFetcher = f + + for _, v := range conf.RssConfig { + if v.Enable { + f.AddRss(v.Name, v.Rss) + } + } + + f.AddHandler(func(meta *vo.RssMeta, body *vo.RssBody) { + err := ca.SetDefault(meta.Name, body) + logger.Ctl(err != nil).Error("%s", err) + }) +} + +func (app *App) initController() { + common.ErrPanic(app.di.Provide(httpController.NewRssController, dig.As(new(interfaces.ItfRssController))), uber_help.ErrAlreadyProvided) + common.ErrPanic(app.di.Provide(httpController.NewRssManagerController, dig.As(new(interfaces.ItfRssManageController))), uber_help.ErrAlreadyProvided) +} + +func NewApp(di *dig.Container, conf *configstc.RssAppConfig, logger v1log.ILog) *App { + scope := di.Scope("admin") + + common.ErrPanic(scope.Provide(func() *dig.Scope { + return scope + }), uber_help.ErrAlreadyProvided) + common.ErrPanic(scope.Provide(func() *configstc.RssAppConfig { + return conf + }), uber_help.ErrAlreadyProvided) + + app := &App{ + di: scope, + l: logger, + App: apps.NewApp(di, "rss_app"), + } + + app.WithModule(conf.ModuleMode) + + //db初始化 + common.ErrPanic(scope.Provide(rsshelp.NewRssFetcher)) + + common.ErrPanic(scope.Invoke(app.initCaches)) + common.ErrPanic(scope.Invoke(app.initRss)) + common.ErrPanic(scope.Invoke(app.initController)) + common.ErrPanic(scope.Invoke(app.startCron)) + + if conf.ApiServer.Enable { + common.ErrPanic(scope.Provide(NewApiServer)) + common.ErrPanic(scope.Invoke(func(api *ApiServer) { + app.WithApiServer(api) + app.ApiServer = api + })) + } + + return app +} diff --git a/src/apps/rssapp/app_test.go b/src/apps/rssapp/app_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a21fcf39366338083da41404c9f8b8ffc5f4da8a --- /dev/null +++ b/src/apps/rssapp/app_test.go @@ -0,0 +1,54 @@ +package rssapp + +import ( + "gitee.com/captials-team/ubdframe/src/common" + "gitee.com/captials-team/ubdframe/src/domain/configstc" + "gitee.com/captials-team/ubdframe/src/tests" + "go.uber.org/dig" + "testing" + "time" +) + +func TestAppStart(t *testing.T) { + di := testDi(t) + common.ErrPanic(di.Provide(NewApp)) + + common.ErrPanic(di.Invoke(func(app *App) error { + return app.Start() + })) + + time.Sleep(time.Hour) +} + +func testDi(t *testing.T) *dig.Container { + di := tests.NewDi(t) + + common.ErrPanic(di.Provide(func() *configstc.RssAppConfig { + return &configstc.RssAppConfig{ + ApiServer: configstc.ServerConfig{ + Enable: true, + ServerBindAddr: "", + Port: 10000, + }, + DBConfig: configstc.DBConfig{ + DbHost: "192.168.149.128", + DbPort: "3306", + DbUser: "root", + DbPassword: "root", + DbName: "db_ubd_frame", + TablePrefix: "test_", + }, + DocsEnable: true, + RssConfig: []configstc.RssMetaConfig{ + { + Enable: true, + Name: "百度焦点", + Rss: "https://news.baidu.com/n?cmd=1&class=civilnews&tn=rss&sub=0", + }, + }, + AsyncInit: false, + } + })) + + return di +} diff --git a/src/apps/rssapp/config.go b/src/apps/rssapp/config.go new file mode 100644 index 0000000000000000000000000000000000000000..a677e9a1474071f9fca1bf7cee9119976327854d --- /dev/null +++ b/src/apps/rssapp/config.go @@ -0,0 +1,14 @@ +package rssapp + +import "gitee.com/captials-team/ubdframe/src/domain/configstc" + +// NewConfig godoc +// @Summary CONFIG-配置 +// @Description CONFIG-配置内容 +// @Tags admin +// @Produce json +// @Success 200 {array} configstc.AdminAppConfig +// @Router /-admin-config [post] +func NewConfig() *configstc.RssAppConfig { + return configstc.LoadConfig(new(configstc.RssAppConfig)) +} diff --git a/src/apps/rssapp/controllers/http/ctr_rss.go b/src/apps/rssapp/controllers/http/ctr_rss.go new file mode 100644 index 0000000000000000000000000000000000000000..c337c85b7e0f382a90773c0441c21fe8bb4b98b4 --- /dev/null +++ b/src/apps/rssapp/controllers/http/ctr_rss.go @@ -0,0 +1,56 @@ +package http + +import ( + "gitee.com/captials-team/ubdframe/src/domain/configstc" + "gitee.com/captials-team/ubdframe/src/domain/dto/respdata" + "gitee.com/captials-team/ubdframe/src/domain/services" + "gitee.com/captials-team/ubdframe/src/domain/vo" + "gitee.com/captials-team/ubdframe/src/pkg/gin_http" + v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" + "github.com/gin-gonic/gin" +) + +type RssController struct { + l v1log.ILog + cache services.RssCache + conf *configstc.RssAppConfig + + gin_http.ResponseController +} + +func NewRssController(l v1log.ILog, conf *configstc.RssAppConfig, cache services.RssCache) *RssController { + ctr := &RssController{ + l: l, + cache: cache, + conf: conf, + } + + return ctr +} + +// RssCacheItems godoc +// @Summary RSS文章列表(缓存) +// @Description rss内容列表(缓存) +// @Tags rss +// @Produce json +// @success 200 {object} respdata.ResponseData{data=[]vo.RssItems} "授权成功" +// @success 500 {object} respdata.ResponseData{} "授权失败" +// @Router /rss/items [post] +func (ctr *RssController) RssCacheItems(ctx *gin.Context) { + var list []*vo.RssItems + + for _, v := range ctr.conf.RssConfig { + body, err := ctr.cache.Get(v.Name) + if err != nil { + ctr.l.Error("cache get err %s,%s", err, v.Name) + ctr.Response(ctx, respdata.CError) + return + } + for _, item := range body.Channel.Items { + item.Author = v.Name //修改作者 + list = append(list, item) + } + } + + ctr.Response(ctx, respdata.CSuccess.MData(list)) +} diff --git a/src/apps/rssapp/controllers/http/ctr_rss_mag.go b/src/apps/rssapp/controllers/http/ctr_rss_mag.go new file mode 100644 index 0000000000000000000000000000000000000000..4385d56e01ebcce3ae215751f680835fa84eb2a8 --- /dev/null +++ b/src/apps/rssapp/controllers/http/ctr_rss_mag.go @@ -0,0 +1,74 @@ +package http + +import ( + "gitee.com/captials-team/ubdframe/src/domain/configstc" + "gitee.com/captials-team/ubdframe/src/domain/dto" + "gitee.com/captials-team/ubdframe/src/domain/dto/respdata" + "gitee.com/captials-team/ubdframe/src/domain/vo" + "gitee.com/captials-team/ubdframe/src/pkg/gin_http" + v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" + "gitee.com/captials-team/ubdframe/src/pkg/rsshelp" + "github.com/gin-gonic/gin" +) + +type RssManagerController struct { + l v1log.ILog + fetcher *rsshelp.RssFetcher + conf *configstc.RssAppConfig + + gin_http.ResponseController +} + +func NewRssManagerController(l v1log.ILog, conf *configstc.RssAppConfig, fetcher *rsshelp.RssFetcher) *RssManagerController { + ctr := &RssManagerController{ + l: l, + fetcher: fetcher, + conf: conf, + } + + return ctr +} + +// RssSourceList godoc +// @Summary RSS源列表 +// @Description rss源列表 +// @Tags rss-manager +// @Produce json +// @Security AdminKeyAuth +// @success 200 {object} respdata.ResponseData{data=configstc.RssMetaConfig} "授权成功" +// @success 500 {object} respdata.ResponseData{} "授权失败" +// @Router /mag/rss/sources [post] +func (ctr *RssManagerController) RssSourceList(ctx *gin.Context) { + ctr.Response(ctx, respdata.CSuccess.MData(ctr.conf.RssConfig)) +} + +// RssSourceItems godoc +// @Summary RSS源文章列表 +// @Description rss源内容列表 +// @Tags rss-manager +// @Produce json +// @Security AdminKeyAuth +// @Param param body dto.SearchRssContentReq true "验证信息" +// @success 200 {object} respdata.ResponseData{data=vo.RssItems} "授权成功" +// @success 500 {object} respdata.ResponseData{} "授权失败" +// @Router /mag/rss/source/items [post] +func (ctr *RssManagerController) RssSourceItems(ctx *gin.Context) { + var req dto.SearchRssContentReq + ctx.ShouldBindJSON(&req) + + var fetcher = ctr.fetcher + if req.Rss != "" { + //指定rss + fetcher = rsshelp.NewRssFetcher(ctr.l) + fetcher.AddRss("tmp", req.Rss) + } + + var list []*vo.RssItems + fetcher.FetchHandler(func(meta *vo.RssMeta, body *vo.RssBody) { + for _, item := range body.Channel.Items { + item.Author = meta.Name //修改作者 + list = append(list, item) + } + }) + ctr.Response(ctx, respdata.CSuccess.MData(list)) +} diff --git a/src/apps/rssapp/docs/rssservice_docs.go b/src/apps/rssapp/docs/rssservice_docs.go new file mode 100644 index 0000000000000000000000000000000000000000..567d77445c3ddcdac26032a00262a69f1801006f --- /dev/null +++ b/src/apps/rssapp/docs/rssservice_docs.go @@ -0,0 +1,472 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplaterssservice = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/-admin-config": { + "post": { + "description": "CONFIG-配置内容", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "CONFIG-配置", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/configstc.AdminAppConfig" + } + } + } + } + } + }, + "/mag/rss/source/items": { + "post": { + "security": [ + { + "AdminKeyAuth": [] + } + ], + "description": "rss源内容列表", + "produces": [ + "application/json" + ], + "tags": [ + "rss-manager" + ], + "summary": "RSS源文章列表", + "parameters": [ + { + "description": "验证信息", + "name": "param", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.SearchRssContentReq" + } + } + ], + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/vo.RssItems" + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + }, + "/mag/rss/sources": { + "post": { + "security": [ + { + "AdminKeyAuth": [] + } + ], + "description": "rss源列表", + "produces": [ + "application/json" + ], + "tags": [ + "rss-manager" + ], + "summary": "RSS源列表", + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/configstc.RssMetaConfig" + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + }, + "/rss/items": { + "post": { + "description": "rss内容列表(缓存)", + "produces": [ + "application/json" + ], + "tags": [ + "rss" + ], + "summary": "RSS文章列表(缓存)", + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/vo.RssItems" + } + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + } + }, + "definitions": { + "configstc.AdminAppConfig": { + "type": "object", + "properties": { + "ApiServer": { + "$ref": "#/definitions/configstc.ServerConfig" + }, + "AuthConfig": { + "$ref": "#/definitions/configstc.AuthConfig" + }, + "AutoCreateAdmin": { + "description": "是否自动创建admin账户", + "type": "boolean" + }, + "DBConfig": { + "$ref": "#/definitions/configstc.DBConfig" + }, + "Debug": { + "description": "debug开关", + "type": "boolean" + }, + "DocsEnable": { + "description": "是否启用文档", + "type": "boolean" + }, + "I18nFiles": { + "description": "多语言配置:key=language,value=path", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "LimitingApi": { + "description": "api限流", + "allOf": [ + { + "$ref": "#/definitions/configstc.LimitingConfig" + } + ] + }, + "LogLevel": { + "type": "string" + }, + "LogPath": { + "description": "日志路径", + "type": "string" + }, + "ModuleMode": { + "description": "模块模式,作为module使用", + "type": "boolean" + }, + "Name": { + "type": "string" + }, + "PProfEnable": { + "description": "是否启用pprof", + "type": "boolean" + }, + "PasswordSalt": { + "description": "密码salt", + "type": "string" + }, + "PermissionFile": { + "description": "权限文件json", + "type": "string" + }, + "RoutePrefix": { + "description": "路由前缀", + "type": "string" + }, + "UserPasswordSalt": { + "description": "用户-密码salt", + "type": "string" + } + } + }, + "configstc.AuthConfig": { + "type": "object", + "properties": { + "AuthExpired": { + "description": "授权失效时间,单位:秒", + "type": "integer" + }, + "Enable": { + "description": "是否启用", + "type": "boolean" + }, + "SecretKey": { + "description": "加密秘钥,如JWT", + "type": "string" + } + } + }, + "configstc.DBConfig": { + "type": "object", + "properties": { + "ConnMaxIdleTime": { + "description": "每个链接最大空闲时间", + "type": "integer", + "default": 0 + }, + "ConnMaxLifeTime": { + "description": "每个链接最大生存时间", + "type": "integer", + "default": 0 + }, + "DbConn": { + "description": "数据库类型,如mysql", + "type": "string" + }, + "DbDsn": { + "description": "数据库dsn", + "type": "string" + }, + "DbHost": { + "description": "数据库地址", + "type": "string" + }, + "DbName": { + "description": "数据库", + "type": "string" + }, + "DbPassword": { + "description": "密码", + "type": "string" + }, + "DbPort": { + "description": "端口", + "type": "string" + }, + "DbUser": { + "description": "用户名", + "type": "string" + }, + "MaxConcatLen": { + "type": "string" + }, + "MaxIdleConn": { + "description": "预留并发链接数", + "type": "integer", + "default": 0 + }, + "MaxOpenConn": { + "description": "最大支持链接", + "type": "integer", + "default": 0 + }, + "TablePrefix": { + "description": "表前缀", + "type": "string" + }, + "TimeZone": { + "description": "时区设置", + "type": "string" + } + } + }, + "configstc.LimitingConfig": { + "type": "object", + "properties": { + "MaxRate": { + "description": "允许的最大速率", + "type": "integer" + }, + "PerRate": { + "description": "每次(秒)速率", + "type": "integer" + } + } + }, + "configstc.RssMetaConfig": { + "type": "object", + "properties": { + "Enable": { + "type": "boolean" + }, + "Name": { + "type": "string" + }, + "Rss": { + "type": "string" + } + } + }, + "configstc.ServerConfig": { + "type": "object", + "properties": { + "AllowOrigins": { + "description": "指定跨域允许访问源,空则为不限制访问", + "type": "array", + "items": { + "type": "string" + } + }, + "Enable": { + "description": "是否启用", + "type": "boolean" + }, + "EndpointAddr": { + "description": "对外访问地址", + "type": "string" + }, + "EndpointPort": { + "description": "对外访问端口", + "type": "integer" + }, + "Host": { + "description": "Host is the hostname or IP address of the service.", + "type": "string" + }, + "Port": { + "description": "端口", + "type": "integer" + }, + "ServerBindAddr": { + "description": "ListenAndServe to bind to, such as 0.0.0.0", + "type": "string" + }, + "Timeout": { + "description": "Timeout specifies a timeout (in milliseconds)", + "type": "integer" + } + } + }, + "dto.SearchRssContentReq": { + "type": "object", + "properties": { + "rss": { + "description": "指定rss地址", + "type": "string", + "x-nullable": true, + "example": "" + } + } + }, + "respdata.ResponseData": { + "type": "object", + "properties": { + "code": { + "description": "返回码,0:成功,\u003e0为对应错误码", + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "vo.RssItems": { + "type": "object", + "properties": { + "author": { + "description": "作者", + "type": "string" + }, + "description": { + "description": "描述信息", + "type": "string" + }, + "link": { + "description": "连接", + "type": "string" + }, + "pubDate": { + "description": "发布日期/时间", + "type": "string" + }, + "title": { + "description": "新闻标题", + "type": "string" + } + } + } + } +}` + +// SwaggerInforssservice holds exported Swagger Info so clients can modify it +var SwaggerInforssservice = &swag.Spec{ + Version: "", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "", + Description: "", + InfoInstanceName: "rssservice", + SwaggerTemplate: docTemplaterssservice, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInforssservice.InstanceName(), SwaggerInforssservice) +} diff --git a/src/apps/rssapp/docs/rssservice_swagger.json b/src/apps/rssapp/docs/rssservice_swagger.json new file mode 100644 index 0000000000000000000000000000000000000000..525809b5b3f80a45529152a34474db5e4f649ab6 --- /dev/null +++ b/src/apps/rssapp/docs/rssservice_swagger.json @@ -0,0 +1,443 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": { + "/-admin-config": { + "post": { + "description": "CONFIG-配置内容", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "CONFIG-配置", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/configstc.AdminAppConfig" + } + } + } + } + } + }, + "/mag/rss/source/items": { + "post": { + "security": [ + { + "AdminKeyAuth": [] + } + ], + "description": "rss源内容列表", + "produces": [ + "application/json" + ], + "tags": [ + "rss-manager" + ], + "summary": "RSS源文章列表", + "parameters": [ + { + "description": "验证信息", + "name": "param", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.SearchRssContentReq" + } + } + ], + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/vo.RssItems" + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + }, + "/mag/rss/sources": { + "post": { + "security": [ + { + "AdminKeyAuth": [] + } + ], + "description": "rss源列表", + "produces": [ + "application/json" + ], + "tags": [ + "rss-manager" + ], + "summary": "RSS源列表", + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/configstc.RssMetaConfig" + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + }, + "/rss/items": { + "post": { + "description": "rss内容列表(缓存)", + "produces": [ + "application/json" + ], + "tags": [ + "rss" + ], + "summary": "RSS文章列表(缓存)", + "responses": { + "200": { + "description": "授权成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/respdata.ResponseData" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/vo.RssItems" + } + } + } + } + ] + } + }, + "500": { + "description": "授权失败", + "schema": { + "$ref": "#/definitions/respdata.ResponseData" + } + } + } + } + } + }, + "definitions": { + "configstc.AdminAppConfig": { + "type": "object", + "properties": { + "ApiServer": { + "$ref": "#/definitions/configstc.ServerConfig" + }, + "AuthConfig": { + "$ref": "#/definitions/configstc.AuthConfig" + }, + "AutoCreateAdmin": { + "description": "是否自动创建admin账户", + "type": "boolean" + }, + "DBConfig": { + "$ref": "#/definitions/configstc.DBConfig" + }, + "Debug": { + "description": "debug开关", + "type": "boolean" + }, + "DocsEnable": { + "description": "是否启用文档", + "type": "boolean" + }, + "I18nFiles": { + "description": "多语言配置:key=language,value=path", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "LimitingApi": { + "description": "api限流", + "allOf": [ + { + "$ref": "#/definitions/configstc.LimitingConfig" + } + ] + }, + "LogLevel": { + "type": "string" + }, + "LogPath": { + "description": "日志路径", + "type": "string" + }, + "ModuleMode": { + "description": "模块模式,作为module使用", + "type": "boolean" + }, + "Name": { + "type": "string" + }, + "PProfEnable": { + "description": "是否启用pprof", + "type": "boolean" + }, + "PasswordSalt": { + "description": "密码salt", + "type": "string" + }, + "PermissionFile": { + "description": "权限文件json", + "type": "string" + }, + "RoutePrefix": { + "description": "路由前缀", + "type": "string" + }, + "UserPasswordSalt": { + "description": "用户-密码salt", + "type": "string" + } + } + }, + "configstc.AuthConfig": { + "type": "object", + "properties": { + "AuthExpired": { + "description": "授权失效时间,单位:秒", + "type": "integer" + }, + "Enable": { + "description": "是否启用", + "type": "boolean" + }, + "SecretKey": { + "description": "加密秘钥,如JWT", + "type": "string" + } + } + }, + "configstc.DBConfig": { + "type": "object", + "properties": { + "ConnMaxIdleTime": { + "description": "每个链接最大空闲时间", + "type": "integer", + "default": 0 + }, + "ConnMaxLifeTime": { + "description": "每个链接最大生存时间", + "type": "integer", + "default": 0 + }, + "DbConn": { + "description": "数据库类型,如mysql", + "type": "string" + }, + "DbDsn": { + "description": "数据库dsn", + "type": "string" + }, + "DbHost": { + "description": "数据库地址", + "type": "string" + }, + "DbName": { + "description": "数据库", + "type": "string" + }, + "DbPassword": { + "description": "密码", + "type": "string" + }, + "DbPort": { + "description": "端口", + "type": "string" + }, + "DbUser": { + "description": "用户名", + "type": "string" + }, + "MaxConcatLen": { + "type": "string" + }, + "MaxIdleConn": { + "description": "预留并发链接数", + "type": "integer", + "default": 0 + }, + "MaxOpenConn": { + "description": "最大支持链接", + "type": "integer", + "default": 0 + }, + "TablePrefix": { + "description": "表前缀", + "type": "string" + }, + "TimeZone": { + "description": "时区设置", + "type": "string" + } + } + }, + "configstc.LimitingConfig": { + "type": "object", + "properties": { + "MaxRate": { + "description": "允许的最大速率", + "type": "integer" + }, + "PerRate": { + "description": "每次(秒)速率", + "type": "integer" + } + } + }, + "configstc.RssMetaConfig": { + "type": "object", + "properties": { + "Enable": { + "type": "boolean" + }, + "Name": { + "type": "string" + }, + "Rss": { + "type": "string" + } + } + }, + "configstc.ServerConfig": { + "type": "object", + "properties": { + "AllowOrigins": { + "description": "指定跨域允许访问源,空则为不限制访问", + "type": "array", + "items": { + "type": "string" + } + }, + "Enable": { + "description": "是否启用", + "type": "boolean" + }, + "EndpointAddr": { + "description": "对外访问地址", + "type": "string" + }, + "EndpointPort": { + "description": "对外访问端口", + "type": "integer" + }, + "Host": { + "description": "Host is the hostname or IP address of the service.", + "type": "string" + }, + "Port": { + "description": "端口", + "type": "integer" + }, + "ServerBindAddr": { + "description": "ListenAndServe to bind to, such as 0.0.0.0", + "type": "string" + }, + "Timeout": { + "description": "Timeout specifies a timeout (in milliseconds)", + "type": "integer" + } + } + }, + "dto.SearchRssContentReq": { + "type": "object", + "properties": { + "rss": { + "description": "指定rss地址", + "type": "string", + "x-nullable": true, + "example": "" + } + } + }, + "respdata.ResponseData": { + "type": "object", + "properties": { + "code": { + "description": "返回码,0:成功,\u003e0为对应错误码", + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "vo.RssItems": { + "type": "object", + "properties": { + "author": { + "description": "作者", + "type": "string" + }, + "description": { + "description": "描述信息", + "type": "string" + }, + "link": { + "description": "连接", + "type": "string" + }, + "pubDate": { + "description": "发布日期/时间", + "type": "string" + }, + "title": { + "description": "新闻标题", + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/src/apps/rssapp/docs/rssservice_swagger.yaml b/src/apps/rssapp/docs/rssservice_swagger.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0f68e0eb09caecbf619d7ed339390b840e26bd79 --- /dev/null +++ b/src/apps/rssapp/docs/rssservice_swagger.yaml @@ -0,0 +1,293 @@ +definitions: + configstc.AdminAppConfig: + properties: + ApiServer: + $ref: '#/definitions/configstc.ServerConfig' + AuthConfig: + $ref: '#/definitions/configstc.AuthConfig' + AutoCreateAdmin: + description: 是否自动创建admin账户 + type: boolean + DBConfig: + $ref: '#/definitions/configstc.DBConfig' + Debug: + description: debug开关 + type: boolean + DocsEnable: + description: 是否启用文档 + type: boolean + I18nFiles: + additionalProperties: + type: string + description: 多语言配置:key=language,value=path + type: object + LimitingApi: + allOf: + - $ref: '#/definitions/configstc.LimitingConfig' + description: api限流 + LogLevel: + type: string + LogPath: + description: 日志路径 + type: string + ModuleMode: + description: 模块模式,作为module使用 + type: boolean + Name: + type: string + PProfEnable: + description: 是否启用pprof + type: boolean + PasswordSalt: + description: 密码salt + type: string + PermissionFile: + description: 权限文件json + type: string + RoutePrefix: + description: 路由前缀 + type: string + UserPasswordSalt: + description: 用户-密码salt + type: string + type: object + configstc.AuthConfig: + properties: + AuthExpired: + description: 授权失效时间,单位:秒 + type: integer + Enable: + description: 是否启用 + type: boolean + SecretKey: + description: 加密秘钥,如JWT + type: string + type: object + configstc.DBConfig: + properties: + ConnMaxIdleTime: + default: 0 + description: 每个链接最大空闲时间 + type: integer + ConnMaxLifeTime: + default: 0 + description: 每个链接最大生存时间 + type: integer + DbConn: + description: 数据库类型,如mysql + type: string + DbDsn: + description: 数据库dsn + type: string + DbHost: + description: 数据库地址 + type: string + DbName: + description: 数据库 + type: string + DbPassword: + description: 密码 + type: string + DbPort: + description: 端口 + type: string + DbUser: + description: 用户名 + type: string + MaxConcatLen: + type: string + MaxIdleConn: + default: 0 + description: 预留并发链接数 + type: integer + MaxOpenConn: + default: 0 + description: 最大支持链接 + type: integer + TablePrefix: + description: 表前缀 + type: string + TimeZone: + description: 时区设置 + type: string + type: object + configstc.LimitingConfig: + properties: + MaxRate: + description: 允许的最大速率 + type: integer + PerRate: + description: 每次(秒)速率 + type: integer + type: object + configstc.RssMetaConfig: + properties: + Enable: + type: boolean + Name: + type: string + Rss: + type: string + type: object + configstc.ServerConfig: + properties: + AllowOrigins: + description: 指定跨域允许访问源,空则为不限制访问 + items: + type: string + type: array + Enable: + description: 是否启用 + type: boolean + EndpointAddr: + description: 对外访问地址 + type: string + EndpointPort: + description: 对外访问端口 + type: integer + Host: + description: Host is the hostname or IP address of the service. + type: string + Port: + description: 端口 + type: integer + ServerBindAddr: + description: ListenAndServe to bind to, such as 0.0.0.0 + type: string + Timeout: + description: Timeout specifies a timeout (in milliseconds) + type: integer + type: object + dto.SearchRssContentReq: + properties: + rss: + description: 指定rss地址 + example: "" + type: string + x-nullable: true + type: object + respdata.ResponseData: + properties: + code: + description: 返回码,0:成功,>0为对应错误码 + type: integer + data: {} + msg: + type: string + type: object + vo.RssItems: + properties: + author: + description: 作者 + type: string + description: + description: 描述信息 + type: string + link: + description: 连接 + type: string + pubDate: + description: 发布日期/时间 + type: string + title: + description: 新闻标题 + type: string + type: object +info: + contact: {} +paths: + /-admin-config: + post: + description: CONFIG-配置内容 + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/configstc.AdminAppConfig' + type: array + summary: CONFIG-配置 + tags: + - admin + /mag/rss/source/items: + post: + description: rss源内容列表 + parameters: + - description: 验证信息 + in: body + name: param + required: true + schema: + $ref: '#/definitions/dto.SearchRssContentReq' + produces: + - application/json + responses: + "200": + description: 授权成功 + schema: + allOf: + - $ref: '#/definitions/respdata.ResponseData' + - properties: + data: + $ref: '#/definitions/vo.RssItems' + type: object + "500": + description: 授权失败 + schema: + $ref: '#/definitions/respdata.ResponseData' + security: + - AdminKeyAuth: [] + summary: RSS源文章列表 + tags: + - rss-manager + /mag/rss/sources: + post: + description: rss源列表 + produces: + - application/json + responses: + "200": + description: 授权成功 + schema: + allOf: + - $ref: '#/definitions/respdata.ResponseData' + - properties: + data: + $ref: '#/definitions/configstc.RssMetaConfig' + type: object + "500": + description: 授权失败 + schema: + $ref: '#/definitions/respdata.ResponseData' + security: + - AdminKeyAuth: [] + summary: RSS源列表 + tags: + - rss-manager + /rss/items: + post: + description: rss内容列表(缓存) + produces: + - application/json + responses: + "200": + description: 授权成功 + schema: + allOf: + - $ref: '#/definitions/respdata.ResponseData' + - properties: + data: + items: + $ref: '#/definitions/vo.RssItems' + type: array + type: object + "500": + description: 授权失败 + schema: + $ref: '#/definitions/respdata.ResponseData' + summary: RSS文章列表(缓存) + tags: + - rss +swagger: "2.0" diff --git a/src/apps/rssapp/gen_docs.sh b/src/apps/rssapp/gen_docs.sh new file mode 100644 index 0000000000000000000000000000000000000000..86921a93520db8625e33463c9009af159e884d12 --- /dev/null +++ b/src/apps/rssapp/gen_docs.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +swag init -g app.go --parseDependency --parseInternal --instanceName rssservice + + + + + + + + diff --git a/src/apps/userapp/api_server.go b/src/apps/userapp/api_server.go index 8839296b76be4c4f42b0ca6501e87b26529c0f84..1ac38f883075267119d2539ede5d9e7e2f01d707 100644 --- a/src/apps/userapp/api_server.go +++ b/src/apps/userapp/api_server.go @@ -77,7 +77,8 @@ func (s *ApiServer) router(g gin.IRouter) { g.POST("/auth/login", s.captchaCtr.VerifyHandler(), s.authCtr.AuthLogin) g.POST("/auth/wechat_code", s.authCtr.AuthByWxCode) //微信code第三方登录 - authGroup.POST("/auth/info", s.authCtr.AuthInfo) //获取授权信息(通用授权) + authGroup.POST("/auth/check", s.authCtr.AuthCheck) //校验登录状态 + authGroup.POST("/auth/info", s.authCtr.AuthInfo) //获取授权信息(通用授权) authGroup.POST("/auth/logout", s.authCtr.AuthLogout) authGroup.POST("/auth/fresh_token", s.authCtr.AuthFreshToken) //授权刷新token } @@ -159,7 +160,7 @@ func NewApiServer(conf *configstc.UserAppConfig, logger v1log.ILog, } s.WithName(conf.Name) - s.WithCors() + s.WithCors() //开启跨域设置 //用户端auth s.AuthOption.AuthHandler = gin_http.AuthHandler(jwtauth.NewJwtTokenHandler(conf.AuthConfig.SecretKey)) diff --git a/src/apps/userapp/controllers/http/ctr_auth.go b/src/apps/userapp/controllers/http/ctr_auth.go index acbe3de77b4d8c47641a65a8bf039d31f8f1ef1e..b97c8679fcab532f2651b9261f7b889d78158b4a 100644 --- a/src/apps/userapp/controllers/http/ctr_auth.go +++ b/src/apps/userapp/controllers/http/ctr_auth.go @@ -301,6 +301,18 @@ func (ctr *AuthController) AuthInfo(ctx *gin.Context) { ctr.Response(ctx, respdata.CSuccess.MData(utils.CopyTo(&auth, new(respdata.AuthResp)))) } +// AuthCheck godoc +// @Summary Auth-登录校验 +// @Description 登录校验ok正常返回,不通过返回99 +// @Tags user.auth +// @Security ApiKeyAuth +// @Produce json +// @success 200 {object} respdata.ResponseData{} "返回结果" +// @Router /auth/check [post] +func (ctr *AuthController) AuthCheck(ctx *gin.Context) { + ctr.Response(ctx, respdata.CSuccess) +} + // DebugAuthByThird godoc // @Summary [DEBUG]第三方授权 // @Description 第三方登录-微信 diff --git a/src/apps/webapp/api_server.go b/src/apps/webapp/api_server.go index 4612d6244db1bf1d51e29d6debc83d9332e5b535..e893dcf7b187b5ff356e939e6c0e70cdfc884f54 100644 --- a/src/apps/webapp/api_server.go +++ b/src/apps/webapp/api_server.go @@ -169,7 +169,8 @@ func NewApiServer(di *dig.Container, conf *configstc.WebAppConfig, logger v1log. ApiServer: apps.NewApiServer(gin.Default(), conf.WebServer), } - s.ApiServer.WithCors() + s.WithName(conf.Name) + s.WithCors() //开启跨域设置 return s } diff --git a/src/apps/wssapp/app.go b/src/apps/wssapp/app.go index c435a4b5ebe9107a6c0bf8010c7cb066614d6077..eb78e70a7511a52eb3d52f7568210a787c72f73c 100644 --- a/src/apps/wssapp/app.go +++ b/src/apps/wssapp/app.go @@ -33,6 +33,8 @@ func NewApp(di *dig.Container, conf *configstc.WebsocketAppConfig) *App { common.ErrPanic(di.Provide(NewApiServer)) wsserver.SetMessageSize(2048 * 1000) + wsserver.SetLogger(logger.CallerSkip(-1)) + common.ErrPanic(di.Provide(wsserver.NewHub)) common.ErrPanic(di.Provide(workers.NewWorkerMgr)) @@ -41,7 +43,6 @@ func NewApp(di *dig.Container, conf *configstc.WebsocketAppConfig) *App { mgr.AddWorker("log", workers.NewLogWorker(di, logger)) hub.SetWorker(mgr) - wsserver.SetLogger(logger) })) common.ErrPanic(di.Invoke(func(api *ApiServer, hub *wsserver.Hub, mgr *workers.WorkerMgr) { diff --git a/src/apps/wssapp/workers/mgr.go b/src/apps/wssapp/workers/mgr.go index 8e99683ebc3efe27ecee1fbbccf76f803b01a68e..d86d0f966c88d70889fe14fd270e36cd0c215a4c 100644 --- a/src/apps/wssapp/workers/mgr.go +++ b/src/apps/wssapp/workers/mgr.go @@ -7,7 +7,6 @@ import ( v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" "gitee.com/captials-team/ubdframe/src/pkg/wsserver" "go.uber.org/dig" - "log" "runtime/debug" ) @@ -36,7 +35,7 @@ func (mgr *WorkerMgr) SetHub(h *wsserver.Hub) { } func (mgr *WorkerMgr) Receive(msg *wsserver.WebSocketMessage) { - mgr.l.Debug("receive msg %+v", msg) + mgr.l.Debug("receive msg %d,%s", msg.MessageType, string(msg.Message)) cmd := mgr.convertCmd(msg.Message) if cmd == nil { @@ -84,8 +83,8 @@ func (mgr *WorkerMgr) convertCmd(message []byte) *cmddata.ClientCmd { func (mgr *WorkerMgr) handleCmd(cmd *cmddata.ClientCmd) { defer func() { if err := recover(); err != nil { - log.Fatalf("recover %s", err) - log.Fatalf("recover stack %s", debug.Stack()) + v1log.Out.Error("recover err: %s", err) + v1log.Out.Error("recover stack %s", debug.Stack()) cmd.Client.SendMessage(cmddata.CmdErrFrame) } }() diff --git a/src/common/err.go b/src/common/err.go index 5aa8d08fd04ecded86fbd6da700697b5c109535c..ff2254f54ace39862fdf3b2197a73fcbde3afe89 100644 --- a/src/common/err.go +++ b/src/common/err.go @@ -27,7 +27,7 @@ func ErrPanic(err error, ignoreErrs ...error) { func CatchPanic(fs ...func(interface{})) { if err := recover(); err != nil { fmt.Printf("recover panic %s\n", err) - fmt.Printf("recover Stack: %s\n", debug.Stack()) + fmt.Printf("recover stack: %s\n", debug.Stack()) for _, f := range fs { f(err) } diff --git a/src/domain/configstc/app.go b/src/domain/configstc/app.go index 4a1e06b8e45e8ab9d855d819123cff64bf3972a3..e6df2dbdf7e9cfb4cd466e87ce6bc8044fecfbe6 100644 --- a/src/domain/configstc/app.go +++ b/src/domain/configstc/app.go @@ -145,6 +145,7 @@ type ProxyRule struct { Replace string `json:"Replace" yaml:"Replace"` //替换前缀 } +// WebsocketAppConfig ws应用app type WebsocketAppConfig struct { Name string `json:"Name" yaml:"Name"` ApiServer ServerConfig `json:"ApiServer" yaml:"ApiServer"` //web服务器配置 @@ -157,6 +158,7 @@ type WebsocketAppConfig struct { RoutePrefix string `json:"RoutePrefix" yaml:"RoutePrefix"` //路由前缀 } +// OssAppConfig oss存储app type OssAppConfig struct { Name string `json:"Name" yaml:"Name"` ApiServer ServerConfig `json:"ApiServer" yaml:"ApiServer"` //web服务器配置 @@ -281,6 +283,52 @@ type JobAppConfig struct { JobConfig } +type RssAppConfig struct { + Name string `json:"Name" yaml:"Name"` + ApiServer ServerConfig `json:"ApiServer" yaml:"ApiServer"` + + LogConfig LogConfig `json:"LogConfig" yaml:"LogConfig"` //日志配置 + + //debug开关 + Debug bool `json:"Debug" yaml:"Debug"` + + AuthConfig AuthConfig `json:"AuthConfig" yaml:"AuthConfig"` //授权配置 + + DBConfig DBConfig `json:"DBConfig" yaml:"DBConfig"` //数据库配置 + + LimitingApi LimitingConfig `json:"LimitingApi" yaml:"LimitingApi"` //api限流 + + //路由前缀 + RoutePrefix string `json:"RoutePrefix" yaml:"RoutePrefix"` //路由前缀 + + //是否启用文档 + DocsEnable bool `json:"DocsEnable" yaml:"DocsEnable"` + + //是否启用pprof + PProfEnable bool `json:"PProfEnable" yaml:"PProfEnable"` + + //rss配置,示例: + //RssConfig: + // - Name: "人民网" + // Rss: "http://www.people.com.cn/rss/politics.xml" + // Enable: true + // - Name: "搜狐中国" + // Rss: "http://rss.news.sohu.com/rss/pfocus.xml" + // Enable: false + + RssConfig []RssMetaConfig `json:"RssConfig" yaml:"RssConfig"` + + AsyncInit bool `json:"AsyncInit" yaml:"AsyncInit"` //是否异步初始化运行,包含定时任务等 + + ModuleMode bool `json:"ModuleMode" yaml:"ModuleMode"` //模块模式,作为module使用 +} + +type RssMetaConfig struct { + Enable bool `json:"Enable" yaml:"Enable"` + Name string `json:"Name" yaml:"Name"` + Rss string `json:"Rss" yaml:"Rss"` +} + const ( ProxyModePrefix = "prefix" //前缀替换,匹配并替换指定前缀 ProxyModeFix = "fixed" //固定地址,完全匹配路由进行转发 diff --git a/src/domain/dto/rss.go b/src/domain/dto/rss.go new file mode 100644 index 0000000000000000000000000000000000000000..c667d88c1664e900847b63cc210db62b2af34209 --- /dev/null +++ b/src/domain/dto/rss.go @@ -0,0 +1,5 @@ +package dto + +type SearchRssContentReq struct { + Rss string `json:"rss" extensions:"x-nullable" example:""` //指定rss地址 +} diff --git a/src/domain/interfaces/controller_gin.go b/src/domain/interfaces/controller_gin.go index 47a612fe6f65d49f3c6ff2f4cbd8cc4d49b5d87a..3ccd7b231f68761f8a35cd4d414ddbf00e1f55a9 100644 --- a/src/domain/interfaces/controller_gin.go +++ b/src/domain/interfaces/controller_gin.go @@ -66,6 +66,7 @@ type ItfAuthController interface { AuthLogin(ctx *gin.Context) //登录 AuthLogout(ctx *gin.Context) //登出 + AuthCheck(ctx *gin.Context) //校验登录状态 AuthInfo(ctx *gin.Context) //获取认证信息 AuthFreshToken(ctx *gin.Context) //token刷新 } @@ -168,3 +169,12 @@ type ItfOrganizeManageController interface { DisableOrganize(ctx *gin.Context) //启用/禁用组织 DeleteOrganize(ctx *gin.Context) //删除组织 } + +type ItfRssController interface { + RssCacheItems(ctx *gin.Context) +} + +type ItfRssManageController interface { + RssSourceList(ctx *gin.Context) //rss源列表 + RssSourceItems(ctx *gin.Context) //rss内容列表 +} diff --git a/src/domain/models/user_third.go b/src/domain/models/user_third.go index 77cfd9c1300b2ea0a2b007a013798c398565148a..8911e6cf798b5b0794b283f0149c90a1eb92ffda 100644 --- a/src/domain/models/user_third.go +++ b/src/domain/models/user_third.go @@ -1,6 +1,7 @@ package models import ( + "fmt" "gitee.com/captials-team/ubdframe/src/common/utils" "gorm.io/gorm/schema" ) @@ -44,6 +45,9 @@ func (r *UserThird) TableName(namer schema.Namer) string { // ConvertUser 第三方转换为User func (r *UserThird) ConvertUser() *User { + if r.Nickname == "" { + r.Nickname = fmt.Sprintf("用户-%s", utils.RandLetterFigureCode(8)) + } return &User{ Id: r.UId, SqlTimeFields: r.SqlTimeFields, diff --git a/src/domain/services/block.go b/src/domain/services/block.go index a9330eaf8c7642872b01adb5aed63c3f3ab965b0..5e4bb31fb287884b0169462c980c260bf98d6d6d 100644 --- a/src/domain/services/block.go +++ b/src/domain/services/block.go @@ -14,35 +14,38 @@ import ( type BlockService struct { l v1log.ILog dao interfaces.ItfBlockContentDao - cacheById *caches.CacheFacade[int64, models.BlockContent] - cacheByCode *caches.CacheFacade[string, models.BlockContent] + cacheById *caches.CacheFacade[int64, *models.BlockContent] + cacheByCode *caches.CacheFacade[string, *models.BlockContent] } func NewBlockService(di *dig.Container, l v1log.ILog, blockDao interfaces.ItfBlockContentDao, cache caches.ItfCache) *BlockService { - ctr := &BlockService{ + svc := &BlockService{ l: l, dao: blockDao, - cacheById: caches.NewCacheFacade[int64, models.BlockContent](cache), - cacheByCode: caches.NewCacheFacade[string, models.BlockContent](cache), + cacheById: caches.NewCacheFacade[int64, *models.BlockContent](cache), + cacheByCode: caches.NewCacheFacade[string, *models.BlockContent](cache), } - ctr.cacheById.SetFunc(ctr.CacheKeyForId, func(k int64) *models.BlockContent { - s, err := blockDao.Query(k) - if err != nil { - return nil - } - return s - }) - - ctr.cacheByCode.SetFunc(ctr.CacheKeyForCode, func(k string) *models.BlockContent { - s, err := blockDao.QueryByCode(k) - if err != nil { - return nil - } - return s - }) - - return ctr + svc.cacheById.WithValueFunc(svc.blockCache).WithKeyPrefix("block_") + svc.cacheByCode.WithValueFunc(svc.blockCacheByCode).WithKeyPrefix("block_code_") + + return svc +} + +func (s *BlockService) blockCache(k int64) *models.BlockContent { + find, err := s.dao.Query(k) + if err != nil { + return nil + } + return find +} + +func (s *BlockService) blockCacheByCode(k string) *models.BlockContent { + find, err := s.dao.QueryByCode(k) + if err != nil { + return nil + } + return find } func (s *BlockService) Query(id int64, code string) (*models.BlockContent, error) { diff --git a/src/domain/services/organize.go b/src/domain/services/organize.go index 56c88f8088bfda5608904f46319ca51b84eb7f7e..4501159777f624c32326f4c63a20dc1e2096f0ed 100644 --- a/src/domain/services/organize.go +++ b/src/domain/services/organize.go @@ -20,30 +20,31 @@ type OrganizeService struct { organizeDao interfaces.ItfOrganize relateDao interfaces.ItfOrganizeRelate - cacheById *caches.CacheFacade[int64, models.Organize] + cacheById *caches.CacheFacade[int64, *models.Organize] } func NewOrganizeService(di *dig.Container, l v1log.ILog, organizeDao interfaces.ItfOrganize, relateDao interfaces.ItfOrganizeRelate, cache caches.ItfCache) *OrganizeService { - ctr := &OrganizeService{ + svc := &OrganizeService{ l: l, organizeDao: organizeDao, relateDao: relateDao, - cacheById: caches.NewCacheFacade[int64, models.Organize](cache), + cacheById: caches.NewCacheFacade[int64, *models.Organize](cache), } - ctr.cacheById.SetFunc(func(id int64) string { - return fmt.Sprintf("org_%d", id) - }, func(id int64) *models.Organize { - s, err := organizeDao.Query(id) - if err != nil { - l.Error("query organize-%d fail %s", id, err) - return nil - } - l.Info("org=%d %+v", id, s) - return s - }) + svc.cacheById.WithValueFunc(svc.orgCacheValue).WithKeyPrefix("org_") - return ctr + return svc +} + +// orgCacheValue 缓存org值获取方法 +func (s *OrganizeService) orgCacheValue(id int64) *models.Organize { + find, err := s.organizeDao.Query(id) + if err != nil { + s.l.Error("query organize-%d fail %s", id, err) + return nil + } + s.l.Info("org=%d %+v", id, s) + return find } func (s *OrganizeService) Search(search dto.SearchOrganizeParams, pa *paginate.Pager) ([]*respdata.OrganizeListItem, *paginate.Pager, error) { diff --git a/src/domain/services/rss.go b/src/domain/services/rss.go new file mode 100644 index 0000000000000000000000000000000000000000..632fe6d1e15faffc84f692142de12488a0d27306 --- /dev/null +++ b/src/domain/services/rss.go @@ -0,0 +1,8 @@ +package services + +import ( + "gitee.com/captials-team/ubdframe/src/domain/vo" + "gitee.com/captials-team/ubdframe/src/infrastructure/caches" +) + +type RssCache = *caches.CacheFacade[string, *vo.RssBody] diff --git a/src/domain/services/setting.go b/src/domain/services/setting.go index c0beb55179100fe4ee22961749c017d13a570ace..675a1f4a6197a01c84238b682e33d6b58bee4f35 100644 --- a/src/domain/services/setting.go +++ b/src/domain/services/setting.go @@ -20,15 +20,13 @@ func NewSettingService(di *dig.Container, l v1log.ILog, settingDao interfaces.It cacheByName: caches.NewCacheFacade[string, string](cache), } - ctr.cacheByName.SetFunc(func(k string) string { - return "settings_" + k - }, func(k string) *string { + ctr.cacheByName.WithValueFunc(func(k string) string { s, err := settingDao.Get(k) if err != nil { - return nil + return "" } - return &s - }) + return s + }).WithKeyPrefix("settings_") return ctr } @@ -55,10 +53,7 @@ func (s *SettingService) GetFromCache(name string) (string, error) { if err != nil { return "", err } - if v == nil { - return "", nil - } - return *v, err + return v, err } func (s *SettingService) GetMultiFromCache(names ...string) (map[string]string, error) { diff --git a/src/domain/vo/rss.go b/src/domain/vo/rss.go index c130e55a06c0b9c7a7674e4a20b5e02864a4f1d6..b254f7e10faefae9aba7e3f5b44f64f38cf936ae 100644 --- a/src/domain/vo/rss.go +++ b/src/domain/vo/rss.go @@ -33,11 +33,11 @@ type RssChannel struct { } type RssItems struct { - Title string `json:"title" xml:"title"` - Author string `json:"author" xml:"author"` - Link string `json:"link" xml:"link"` - PubDate string `json:"pubDate" xml:"pubDate"` - Description string `json:"description" xml:"description"` + Title string `json:"title" xml:"title"` //新闻标题 + Author string `json:"author" xml:"author"` //作者 + Link string `json:"link" xml:"link"` //连接 + PubDate string `json:"pubDate" xml:"pubDate"` //发布日期/时间 + Description string `json:"description" xml:"description"` //描述信息 } func (rss *RssBody) Parse(r io.Reader) error { diff --git a/src/infrastructure/caches/cache.go b/src/infrastructure/caches/cache.go index 184d451dbbc31a7b496020e4c0143ca571c487bb..4c8804dfa530063263178de0d7683fe46625cd9d 100644 --- a/src/infrastructure/caches/cache.go +++ b/src/infrastructure/caches/cache.go @@ -1,9 +1,6 @@ package caches import ( - "encoding/json" - "fmt" - "strings" "time" ) @@ -21,64 +18,24 @@ import ( //} type ItfCache interface { - Get(key string) (string, bool, error) - Set(key string, value string) error - SetExpire(key string, value string, expired time.Duration) error - TTL(key string) (time.Duration, error) //指定key的剩余过期时间 - - Inc(k string, n int64) error - Dec(k string, n int64) error - Exist(k string) (bool, error) - Delete(key string) error + Get(key string) (string, bool, error) //获取缓存值 + Set(key string, value string) error //设置缓存值 + SetExpire(key string, value string, expired time.Duration) error //设置缓存值且指定缓存时间 + TTL(key string) (time.Duration, error) //指定key的剩余过期时间 + + Inc(k string, n int64) error //对指定key的缓存值加 + Dec(k string, n int64) error //对指定key的缓存值减 + Exist(k string) (bool, error) //判断缓存key是否存在 + Delete(key string) error //删除指定缓存key GetSet(key string, value string, expired time.Duration) (string, error) GetSetFunc(key string, f func() string, expired time.Duration) (string, error) + + Keys() ([]string, error) //返回所有缓存key + + SetKeyPrefix(prefix string) } type ItemKey struct { Key string `json:"key"` //key值 Expire time.Duration `json:"expire"` //有效时间 } - -const CombineSep = "::" - -func GenericKey[T int | int64 | string | []string | interface{}](k T) string { - switch any(k).(type) { - case int: - d := any(k).(int) - return fmt.Sprintf("id_%d", d) - case int64: - d := any(k).(int64) - return fmt.Sprintf("id_%d", d) - case string: - d := any(k).(string) - return fmt.Sprintf("key_%s", d) - case []string: - d := any(k).([]string) - return fmt.Sprintf("key_%s", strings.Join(d, ".")) - } - //默认进行json处理后生成可以不同的key - s, _ := json.Marshal(k) - return fmt.Sprintf("key_%s", string(s)) -} - -// CombineKeys 多个key合并为一个key -func CombineKeys(keys ...string) string { - return strings.Join(keys, CombineSep) -} - -// SeparateKeys 分隔一个key为多个key -func SeparateKeys(key string) []string { - return strings.Split(key, CombineSep) -} - -// SeparateKeysFix 分隔一个key为多个key(保证返回的固定size的数组) -func SeparateKeysFix(key string, l int) []string { - arr := make([]string, l, l) - arr = strings.Split(key, CombineSep) - if len(arr) < l { - for i := len(arr); i < l; i++ { - arr = append(arr, "") - } - } - return arr -} diff --git a/src/infrastructure/caches/cache_mag.go b/src/infrastructure/caches/cache_mag.go index 84fe256042b94a17b9d7a452dd85feddca19e2d6..12c61777b8978ad37e975f7c3926995e0f362ef2 100644 --- a/src/infrastructure/caches/cache_mag.go +++ b/src/infrastructure/caches/cache_mag.go @@ -1,6 +1,7 @@ package caches // CacheMag 缓存管理器 +// 已废弃,请使用 CacheFacade type CacheMag struct { m map[string]ItfCache } diff --git a/src/infrastructure/caches/facade.go b/src/infrastructure/caches/facade.go index 280427e4c4ec6233c665c86426a7c45c46d7a4db..87b0fd6d04df0df6cfea42b74e4a9abd6db76bc7 100644 --- a/src/infrastructure/caches/facade.go +++ b/src/infrastructure/caches/facade.go @@ -1,76 +1,100 @@ package caches import ( + "gitee.com/captials-team/ubdframe/src/common/utils" "gitee.com/captials-team/ubdframe/src/domain/interfaces" "gitee.com/captials-team/ubdframe/src/pkg/codec" + "gitee.com/captials-team/ubdframe/src/pkg/logs" + "reflect" "time" ) -func NewCacheFacade[KeyT int | int64 | string | interface{}, ValueT interface{}](cache ItfCache) *CacheFacade[KeyT, ValueT] { +func NewCacheFacade[KeyT int | int64 | string | []string | interface{}, ValueT interface{}](cache ItfCache) *CacheFacade[KeyT, ValueT] { return &CacheFacade[KeyT, ValueT]{ cache: cache, cdc: new(codec.JsonCodec), } } +// CacheFacade cache门面 +// valueT返回使用指针的原因:若缓存值不存在则会返回nil type CacheFacade[KeyT int | int64 | string | interface{}, ValueT interface{}] struct { - cache ItfCache - cdc interfaces.Codec - keyFunc func(KeyT) string - valueFunc func(KeyT) *ValueT + cache ItfCache + cdc interfaces.Codec //编码器 + + //目前暂不支持,支持需要parseKeyFunc的 + keyFunc func(KeyT) string //自定义key转换的方法 + + valueFunc func(KeyT) ValueT //value获取的方法 + keyPrefix string //Key前缀 } // Get 直接获取缓存 -func (ca *CacheFacade[KeyT, T]) Get(k KeyT) (*T, error) { +func (ca *CacheFacade[KeyT, T]) Get(k KeyT) (T, error) { + var v T relKey := ca.key(k) s, exist, err := ca.cache.Get(relKey) if err != nil { - return nil, err + return v, err } if !exist { - return nil, nil + return v, nil } if string(s) == "" { - return nil, nil + return v, nil } - var d T - err = ca.cdc.Unmarshal([]byte(s), &d) - return &d, err + return ca.parseValue(s) } // GetOrLoad 获取缓存,当不存在时进行加载 -func (ca *CacheFacade[KeyT, T]) GetOrLoad(k KeyT) (*T, error) { +func (ca *CacheFacade[KeyT, T]) GetOrLoad(k KeyT) (T, error) { relKey := ca.key(k) + var v T s, exist, err := ca.cache.Get(relKey) if err != nil { - return nil, err + return v, err } if !exist { return ca.Load(k) } if string(s) == "" { - return nil, nil + return v, nil } + return ca.parseValue(s) +} + +// Load 重新加载入缓存 +func (ca *CacheFacade[KeyT, T]) parseValue(s string) (T, error) { var d T - err = ca.cdc.Unmarshal([]byte(s), &d) - return &d, err + var err error + + tyRef := reflect.TypeOf(d) + //针对指针进行特殊处理 + if tyRef.Kind() == reflect.Pointer { + d = reflect.New(tyRef.Elem()).Interface().(T) + err = ca.cdc.Unmarshal([]byte(s), d) + } else { + err = ca.cdc.Unmarshal([]byte(s), &d) + } + + return d, err } // Load 重新加载入缓存 -func (ca *CacheFacade[KeyT, T]) Load(k KeyT) (*T, error) { +func (ca *CacheFacade[KeyT, T]) Load(k KeyT) (T, error) { value := ca.value(k) err := ca.SetDefault(k, value) return value, err } // Set 手动设置值 -func (ca *CacheFacade[KeyT, T]) Set(k KeyT, v *T, expiration time.Duration) error { +func (ca *CacheFacade[KeyT, T]) Set(k KeyT, v T, expiration time.Duration) error { relKey := ca.key(k) - if v == nil { + if utils.IsNil(v) { return ca.cache.SetExpire(relKey, "", expiration) } value, err := ca.cdc.Marshal(v) @@ -80,10 +104,12 @@ func (ca *CacheFacade[KeyT, T]) Set(k KeyT, v *T, expiration time.Duration) erro return ca.cache.SetExpire(relKey, string(value), expiration) } -func (ca *CacheFacade[KeyT, T]) SetDefault(k KeyT, v *T) error { +func (ca *CacheFacade[KeyT, T]) SetDefault(k KeyT, v T) error { relKey := ca.key(k) - if v == nil { + logs.Out.Info("RelKey= %s", relKey) + + if utils.IsNil(v) { return ca.cache.Set(relKey, "") } value, err := ca.cdc.Marshal(v) @@ -95,10 +121,23 @@ func (ca *CacheFacade[KeyT, T]) SetDefault(k KeyT, v *T) error { func (ca *CacheFacade[KeyT, T]) Delete(k KeyT) error { relKey := ca.key(k) - return ca.cache.Delete(relKey) } +func (ca *CacheFacade[KeyT, T]) Keys() ([]KeyT, error) { + keys, err := ca.cache.Keys() + if err != nil { + return nil, err + } + + var ts []KeyT + for _, key := range keys { + ts = append(ts, ParseGenericKey[KeyT](key)) + } + + return ts, nil +} + func (ca *CacheFacade[KeyT, T]) key(key KeyT) string { if ca.keyFunc == nil { return GenericKey(key) @@ -106,22 +145,30 @@ func (ca *CacheFacade[KeyT, T]) key(key KeyT) string { return ca.keyFunc(key) } -func (ca *CacheFacade[KeyT, T]) value(key KeyT) *T { +func (ca *CacheFacade[KeyT, T]) value(key KeyT) T { + var v T if ca.valueFunc == nil { - return nil + return v } return ca.valueFunc(key) } -func (ca *CacheFacade[KeyT, T]) SetFunc(keyFunc func(KeyT) string, valueFunc func(KeyT) *T) { +func (ca *CacheFacade[KeyT, T]) WithFunc(keyFunc func(KeyT) string, valueFunc func(KeyT) T) { ca.keyFunc = keyFunc ca.valueFunc = valueFunc } -func (ca *CacheFacade[KeyT, T]) SetKeyFunc(keyFunc func(KeyT) string) { - ca.keyFunc = keyFunc +func (ca *CacheFacade[KeyT, T]) WithKeyPrefix(pre string) *CacheFacade[KeyT, T] { + ca.cache.SetKeyPrefix(pre) + return ca } -func (ca *CacheFacade[KeyT, T]) SetValueFunc(valueFunc func(KeyT) *T) { +func (ca *CacheFacade[KeyT, T]) WithValueFunc(valueFunc func(KeyT) T) *CacheFacade[KeyT, T] { ca.valueFunc = valueFunc + return ca +} + +func (ca *CacheFacade[KeyT, T]) WithCodec(cdc interfaces.Codec) *CacheFacade[KeyT, T] { + ca.cdc = cdc + return ca } diff --git a/src/infrastructure/caches/facade_opt.go b/src/infrastructure/caches/facade_opt.go new file mode 100644 index 0000000000000000000000000000000000000000..2c7012f7e1c086dd30d84367ff0e8a6bb315f90f --- /dev/null +++ b/src/infrastructure/caches/facade_opt.go @@ -0,0 +1,17 @@ +package caches + +import "time" + +type facadeConfig struct { + expiration time.Duration + + keyPrefix string //key前缀 +} + +type CacheOpt func(*facadeConfig) + +func OptionCacheExpireTime(t time.Duration) CacheOpt { + return func(config *facadeConfig) { + config.expiration = t + } +} diff --git a/src/infrastructure/caches/facade_test.go b/src/infrastructure/caches/facade_test.go index 218b193bc532e3b617f5a8864c26379a579d8eca..d60cac62f83d6071e932e51e6fb50d4e7553b1e4 100644 --- a/src/infrastructure/caches/facade_test.go +++ b/src/infrastructure/caches/facade_test.go @@ -1,28 +1,30 @@ package caches import ( + "fmt" + "gitee.com/captials-team/ubdframe/src/common" "gitee.com/captials-team/ubdframe/src/common/utils" + "gitee.com/captials-team/ubdframe/src/domain/configstc" + redisClients "gitee.com/captials-team/ubdframe/src/infrastructure/clients/redis" "github.com/stretchr/testify/assert" "testing" "time" ) func TestNewCacheFacade(t *testing.T) { - - fa := NewCacheFacade(NewMemoryStore(time.Second*3), nil, func(id int64) *A { + fa := NewCacheFacade[int64, *A](getStore()).WithValueFunc(func(id int64) *A { return &A{Id: id} }) - + var a *A d, err := fa.Get(12) assert.Equal(t, err, nil) - assert.Equal(t, d, nil) + assert.Equal(t, d, a) - t.Logf("ID= %d", d) + t.Logf("ID= %+v", d) } func TestNewCacheFacade_Get(t *testing.T) { - - fa := NewCacheFacade(NewMemoryStore(time.Second*3), nil, func(id int64) *A { + fa := NewCacheFacade[int64, *A](getStore()).WithValueFunc(func(id int64) *A { return &A{Id: id} }) @@ -38,14 +40,14 @@ func TestNewCacheFacade_Set(t *testing.T) { Id int64 `json:"id"` } - fa := NewCacheFacade(NewMemoryStore(time.Second*3), nil, func(id int64) *A { + fa := NewCacheFacade[int64, *A](getStore()).WithValueFunc(func(id int64) *A { return &A{Id: id} - }) + }).WithKeyPrefix("testing_set_") var id = int64(1) var value = int64(12) - err := fa.Set(1, &A{Id: value}, time.Second*1) + err := fa.Set(1, &A{Id: value}, time.Second*5) assert.Equal(t, err, nil) d, err := fa.Get(id) @@ -55,13 +57,64 @@ func TestNewCacheFacade_Set(t *testing.T) { t.Logf("ID= %d", d.Id) - time.Sleep(time.Second * 2) + time.Sleep(time.Second * 8) d, err = fa.Get(id) assert.Equal(t, err, nil) assert.Equal(t, utils.IsNil(d), true) } +func TestNewCacheFacade_map(t *testing.T) { + fa := NewCacheFacade[int64, map[string]string](getStore()). + WithValueFunc(func(id int64) map[string]string { + s := fmt.Sprint(id) + return map[string]string{ + s: fmt.Sprint(id + 1), + } + }) + + d, err := fa.GetOrLoad(12) + assert.Equal(t, err, nil) + assert.Equal(t, d["12"], "13") + + t.Logf("ID= %v", d) +} + +func TestNewCacheFacade_Keys(t *testing.T) { + fa := NewCacheFacade[int64, map[string]string](getStore()).WithKeyPrefix("test_keys_") + + common.ErrPanic(fa.SetDefault(1, map[string]string{ + "A": "111", + "V": "222", + })) + common.ErrPanic(fa.SetDefault(2, map[string]string{ + "A": "ccc", + "V": "bbb", + })) + + keys, err := fa.Keys() + common.ErrPanic(err) + + t.Logf("Len= %d", len(keys)) + for _, k := range keys { + v, err := fa.Get(k) + common.ErrPanic(err) + t.Logf("Key= %+v,Value= %+v", k, v) + } + +} + type A struct { Id int64 `json:"id"` } + +func getStore() ItfCache { + rd := redisClients.NewRedisClient(configstc.DBConfig{ + DbHost: "192.168.149.128", + DbPort: "6379", + }) + rs := NewRedisStore(rd, time.Minute*10) + return rs + + return NewMemoryStore(time.Second * 3) +} diff --git a/src/infrastructure/caches/key.go b/src/infrastructure/caches/key.go new file mode 100644 index 0000000000000000000000000000000000000000..b9720f7d313561ae01bc88faeb240bbc0a4d1b72 --- /dev/null +++ b/src/infrastructure/caches/key.go @@ -0,0 +1,89 @@ +package caches + +import ( + "encoding/json" + "fmt" + "github.com/spf13/cast" + "reflect" + "strings" +) + +const CombineSep = "::" +const CacheKeyPrefix = "" + +// GenericKey 通用key生成(泛型) +func GenericKey[T int | int64 | string | []string | interface{}](k T) string { + switch any(k).(type) { + case int: + d := any(k).(int) + return fmt.Sprintf("%d", d) + case int64: + d := any(k).(int64) + return fmt.Sprintf("%d", d) + case string: + d := any(k).(string) + return fmt.Sprintf("%s", d) + case []string: + d := any(k).([]string) + return fmt.Sprintf("%s", strings.Join(d, ".")) + } + //默认进行json处理后生成可以不同的key + s, _ := json.Marshal(k) + return fmt.Sprintf("%s", string(s)) +} + +// ParseGenericKey 解析缓存key(泛型) +func ParseGenericKey[T int | int64 | string | []string | interface{}](k string) T { + var v T + d := k + + var res interface{} + + switch any(v).(type) { + case int: + res = cast.ToInt(d) + return res.(T) + case int64: + res = cast.ToInt64(d) + return res.(T) + case string: + res = d + return res.(T) + case []string: + res = strings.Split(d, ".") + return res.(T) + } + //默认进行json处理后生成可以不同的key + + tyRef := reflect.TypeOf(v) + //针对指针进行特殊处理 + if tyRef.Kind() == reflect.Pointer { + v = reflect.New(tyRef.Elem()).Interface().(T) + json.Unmarshal([]byte(d), v) + } else { + json.Unmarshal([]byte(d), &v) + } + return v +} + +// CombineKeys 多个key合并为一个key +func CombineKeys(keys ...string) string { + return strings.Join(keys, CombineSep) +} + +// SeparateKeys 分隔一个key为多个key +func SeparateKeys(key string) []string { + return strings.Split(key, CombineSep) +} + +// SeparateKeysFix 分隔一个key为多个key(保证返回的固定size的数组) +func SeparateKeysFix(key string, l int) []string { + arr := make([]string, l, l) + arr = strings.Split(key, CombineSep) + if len(arr) < l { + for i := len(arr); i < l; i++ { + arr = append(arr, "") + } + } + return arr +} diff --git a/src/infrastructure/caches/store_memory.go b/src/infrastructure/caches/store_memory.go index efd1ef208cb40b1a2dbb9a94fa84d6c2e2b26a8b..3fe2b01daf988f495b65685360575793bc12a832 100644 --- a/src/infrastructure/caches/store_memory.go +++ b/src/infrastructure/caches/store_memory.go @@ -80,3 +80,17 @@ func (store *MemoryStore) SetExpire(key string, value string, expired time.Durat store.cache.Set(key, value, expired) return nil } + +// Keys 返回所有缓存key +func (store *MemoryStore) Keys() ([]string, error) { + d := store.cache.Items() + var keys []string + for k, _ := range d { + keys = append(keys, k) + } + return keys, nil +} + +func (store *MemoryStore) SetKeyPrefix(prefix string) { + return +} diff --git a/src/infrastructure/caches/store_memory_test.go b/src/infrastructure/caches/store_memory_test.go index 1116c25c61a7fab5e94bf5f2e3d1df4a985b696b..b0867a3618ab100541c85dbd212610bdf3421134 100644 --- a/src/infrastructure/caches/store_memory_test.go +++ b/src/infrastructure/caches/store_memory_test.go @@ -1,6 +1,7 @@ package caches import ( + "gitee.com/captials-team/ubdframe/src/common" "github.com/stretchr/testify/assert" "testing" "time" @@ -38,3 +39,26 @@ func TestMemoryStore_Set(t *testing.T) { assert.Equal(t, exist, true) assert.Equal(t, d, value) } + +func TestMemoryStore_Keys(t *testing.T) { + var c ItfCache + c = NewMemoryStore(time.Second * 5) + + mockData(t, c) + + keys, err := c.Keys() + common.ErrPanic(err) + + assert.Greater(t, len(keys), 0) + t.Logf("keys len= %d", len(keys)) + t.Logf("keys= %+v", keys) +} + +func mockData(t *testing.T, c ItfCache) { + common.ErrPanic(c.Set("xxx_1", time.Now().String())) + common.ErrPanic(c.Set("xxx_2", "hello")) + common.ErrPanic(c.Set("xxx_3", "world")) + common.ErrPanic(c.Set("aaa_1", time.Now().String())) + common.ErrPanic(c.Set("bbb_1", time.Now().String())) + common.ErrPanic(c.Set("vvv_1", time.Now().String())) +} diff --git a/src/infrastructure/caches/store_redis.go b/src/infrastructure/caches/store_redis.go index 281fd014123951160c78285e49099fac90e6b5ee..a4fd928e15b18068ebb7c8b8c74c539e0ccef295 100644 --- a/src/infrastructure/caches/store_redis.go +++ b/src/infrastructure/caches/store_redis.go @@ -5,22 +5,27 @@ import ( "errors" "gitee.com/captials-team/ubdframe/src/domain/interfaces" "github.com/go-redis/redis/v8" + "strings" "time" ) type RedisStore struct { - rd *redis.Client - - expired time.Duration + rd *redis.Client + expired time.Duration + keyPrefix string Codec interfaces.Codec } func (store *RedisStore) Get(key string) (string, bool, error) { + key = store.key(key) tmp, err := store.rd.Get(context.Background(), key).Result() if errors.Is(err, redis.Nil) { return tmp, false, nil } + if err != nil { + return tmp, false, err + } return tmp, true, nil } @@ -29,11 +34,13 @@ func (store *RedisStore) Set(key string, value string) error { } func (store *RedisStore) SetExpire(key string, value string, expired time.Duration) error { + key = store.key(key) _, err := store.rd.Set(context.Background(), key, value, expired).Result() return err } func (store *RedisStore) TTL(key string) (time.Duration, error) { + key = store.key(key) t, err := store.rd.TTL(context.Background(), key).Result() if errors.Is(err, redis.Nil) { return -1, nil @@ -42,10 +49,12 @@ func (store *RedisStore) TTL(key string) (time.Duration, error) { } func (store *RedisStore) Inc(key string, n int64) error { + key = store.key(key) return store.rd.IncrBy(context.Background(), key, n).Err() } func (store *RedisStore) Dec(key string, n int64) error { + key = store.key(key) return store.rd.DecrBy(context.Background(), key, n).Err() } @@ -56,25 +65,57 @@ func (store *RedisStore) GetSet(key string, value string, expired time.Duration) } func (store *RedisStore) GetSetFunc(key string, f func() string, expired time.Duration) (string, error) { - tmp, err := store.rd.Get(context.Background(), key).Result() - if errors.Is(err, redis.Nil) { + tmp, exists, err := store.Get(key) + if err != nil { + return "", err + } + if !exists { d := f() - err := store.SetExpire(key, d, expired) - return d, err + err1 := store.SetExpire(key, d, expired) + return d, err1 } return tmp, nil } func (store *RedisStore) Delete(key string) error { + key = store.key(key) _, err := store.rd.Del(context.Background(), key).Result() return err } func (store *RedisStore) Exist(key string) (bool, error) { + key = store.key(key) d, err := store.rd.Exists(context.Background(), key).Result() return d > 0, err } +func (store *RedisStore) Keys() ([]string, error) { + key := store.key("*") + keys, err := store.rd.Keys(context.Background(), key).Result() + if err != nil { + return nil, err + } + + if store.keyPrefix == "" { + return keys, nil + } + + var retKeys []string + for _, k := range keys { + oriK, _ := strings.CutPrefix(k, store.keyPrefix) + retKeys = append(retKeys, oriK) + } + return retKeys, nil +} + +func (store *RedisStore) key(key string) string { + return store.keyPrefix + key +} + +func (store *RedisStore) SetKeyPrefix(prefix string) { + store.keyPrefix = prefix +} + func NewRedisStore(rd *redis.Client, expired time.Duration) *RedisStore { return &RedisStore{ rd: rd, diff --git a/src/pkg/logs/consts.go b/src/pkg/logs/consts.go index 8f8e26acdcc718fc9d692c63de6ac1c2f4662e51..7fc51e3606598c65b6ca9d9f6a9482a4612b2928 100644 --- a/src/pkg/logs/consts.go +++ b/src/pkg/logs/consts.go @@ -7,7 +7,7 @@ import ( // 全局logger var ( - Out ILog = NewWriterLog(os.Stdout, DebugLog) + Out ILog = NewWriterLog(os.Stdout, DebugLog).CallerSkip(01) ) func SetLogger(log ILog) { diff --git a/src/pkg/logs/invokelog.go b/src/pkg/logs/invokelog.go new file mode 100644 index 0000000000000000000000000000000000000000..a58e654811476fd44530a5bee1b68f8ad264c031 --- /dev/null +++ b/src/pkg/logs/invokelog.go @@ -0,0 +1,131 @@ +package logs + +import "fmt" + +// InvokeLog fast use log in struct +type InvokeLog struct { + logger ILog +} + +func (ink *InvokeLog) AddLogger(logger ILog) { + ink.logger = logger +} + +func (ink *InvokeLog) GetLogger() ILog { + return ink.logger +} + +func (ink *InvokeLog) Log(level LogLevel, msg string) { + if ink.logger == nil { + return + } + switch level { + case DebugLog: + ink.logger.Debug(msg) + case WarnLog: + ink.logger.Warn(msg) + case InfoLog: + ink.logger.Info(msg) + case ErrorLog: + ink.logger.Error(msg) + case FatalLog: + ink.logger.Fatal(msg) + case PanicLog: + ink.logger.Panic(msg) + } +} + +func (ink *InvokeLog) Debug(log string, params ...interface{}) { + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Debug(log) + return + } + + ink.logger.Debug(fmt.Sprintf(log, params...)) + return +} + +func (ink *InvokeLog) Info(log string, params ...interface{}) { + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Info(log) + return + } + + ink.logger.Info(fmt.Sprintf(log, params...)) + return +} + +func (ink *InvokeLog) Warn(log string, params ...interface{}) { + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Warn(log) + return + } + + ink.logger.Warn(fmt.Sprintf(log, params...)) + return +} +func (ink *InvokeLog) Error(log string, params ...interface{}) { + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Error(log) + return + } + + ink.logger.Error(fmt.Sprintf(log, params...)) + return +} +func (ink *InvokeLog) Panic(log string, params ...interface{}) { + + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Panic(log) + return + } + + ink.logger.Panic(fmt.Sprintf(log, params...)) + return +} +func (ink *InvokeLog) Fatal(log string, params ...interface{}) { + if ink.logger == nil { + return + } + if len(params) <= 0 { + ink.logger.Fatal(log) + return + } + + ink.logger.Fatal(fmt.Sprintf(log, params...)) + return +} +func (ink *InvokeLog) Write(p []byte) (n int, err error) { + return ink.logger.Write(p) +} +func (ink *InvokeLog) Ctl(t bool) ILog { + if !t { + return _nullLog + } + return ink +} + +func (ink *InvokeLog) Caller(skip int) ILog { + return &InvokeLog{ + logger: ink.logger.CallerSkip(skip), + } +} + +func (ink *InvokeLog) CallerSkip(skip int) ILog { + return ink.Caller(skip) +} diff --git a/src/pkg/logs/invokelog_test.go b/src/pkg/logs/invokelog_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a8316acd6d970060282ffd6beb9ec5c3734bc415 --- /dev/null +++ b/src/pkg/logs/invokelog_test.go @@ -0,0 +1,21 @@ +package logs + +import "testing" + +func TestInvokeLog_AddLogger(t *testing.T) { + + l := NewZapLog("xx", DefaultLogPath) + + a := &A{} + a.AddLogger(l) + + a.Do() +} + +type A struct { + InvokeLog +} + +func (a *A) Do() { + a.Debug("THIS DEBUG LOG") +} diff --git a/src/pkg/logs/log.go b/src/pkg/logs/log.go index 36f3009575ddbcbd90ccea5146cc368a2140df99..eaa55765ca1442c94372961da1d2f0ab36ac145e 100644 --- a/src/pkg/logs/log.go +++ b/src/pkg/logs/log.go @@ -32,134 +32,6 @@ type ILogCallerSkip interface { CallerSkip(offset int) ILog } -// InvokeLog fast use log in struct -type InvokeLog struct { - logger ILog -} - -func (ink *InvokeLog) AddLogger(logger ILog) { - ink.logger = logger -} - -func (ink *InvokeLog) GetLogger() ILog { - return ink.logger -} - -func (ink *InvokeLog) Log(level LogLevel, msg string) { - if ink.logger == nil { - return - } - switch level { - case DebugLog: - ink.logger.Debug(msg) - case WarnLog: - ink.logger.Warn(msg) - case InfoLog: - ink.logger.Info(msg) - case ErrorLog: - ink.logger.Error(msg) - case FatalLog: - ink.logger.Fatal(msg) - case PanicLog: - ink.logger.Panic(msg) - } -} - -func (ink *InvokeLog) Debug(log string, params ...interface{}) { - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Debug(log) - return - } - - ink.logger.Debug(fmt.Sprintf(log, params...)) - return -} - -func (ink *InvokeLog) Info(log string, params ...interface{}) { - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Info(log) - return - } - - ink.logger.Info(fmt.Sprintf(log, params...)) - return -} - -func (ink *InvokeLog) Warn(log string, params ...interface{}) { - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Warn(log) - return - } - - ink.logger.Warn(fmt.Sprintf(log, params...)) - return -} -func (ink *InvokeLog) Error(log string, params ...interface{}) { - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Error(log) - return - } - - ink.logger.Error(fmt.Sprintf(log, params...)) - return -} -func (ink *InvokeLog) Panic(log string, params ...interface{}) { - - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Panic(log) - return - } - - ink.logger.Panic(fmt.Sprintf(log, params...)) - return -} -func (ink *InvokeLog) Fatal(log string, params ...interface{}) { - if ink.logger == nil { - return - } - if len(params) <= 0 { - ink.logger.Fatal(log) - return - } - - ink.logger.Fatal(fmt.Sprintf(log, params...)) - return -} -func (ink *InvokeLog) Write(p []byte) (n int, err error) { - return ink.logger.Write(p) -} -func (ink *InvokeLog) Ctl(t bool) ILog { - if !t { - return _nullLog - } - return ink -} - -func (ink *InvokeLog) Caller(skip int) ILog { - return &InvokeLog{ - logger: ink.logger.CallerSkip(skip), - } -} - -func (ink *InvokeLog) CallerSkip(skip int) ILog { - return ink.Caller(skip) -} - type logHelper struct { } diff --git a/src/pkg/ossadaptor/minio_oss.go b/src/pkg/ossadaptor/minio_oss.go index cabf551589956e0aad702d67745e9519204b10dc..db5672b8a2f47596ae52e0a97fdd41dbf6b2ee0e 100644 --- a/src/pkg/ossadaptor/minio_oss.go +++ b/src/pkg/ossadaptor/minio_oss.go @@ -3,6 +3,7 @@ package ossadaptor import ( "encoding/json" "fmt" + "gitee.com/captials-team/ubdframe/src/pkg/logs" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "golang.org/x/net/context" @@ -18,11 +19,11 @@ type MinioOss struct { } type MinioOssOps struct { - AccessKey string - SecretKey string - Endpoint string - Bucket string - SubPath string + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` + Endpoint string `json:"endpoint"` // minio服务器地址端口,如:192.168.1.129:9000 + Bucket string `json:"bucket"` //bucket名称 + SubPath string `json:"subPath"` //子路径,如: download/images } func NewMinioOss(bindDomain string, ops *MinioOssOps) (*MinioOss, error) { @@ -65,9 +66,11 @@ func (oss *MinioOss) Parse(s string) error { } func (oss *MinioOss) url(info minio.UploadInfo) string { + logs.Out.Info("Upload= %+v", info) return fmt.Sprintf("%s/%s/%s", oss.bindDomain, info.Bucket, info.Key) } +// Put 上传文件, file: 本地文件路径,objName: minio存储的对象名 func (oss *MinioOss) Put(file string, objName string) (*UploadRet, error) { ret := &UploadRet{} bucketName := oss.ops.Bucket @@ -80,7 +83,7 @@ func (oss *MinioOss) Put(file string, objName string) (*UploadRet, error) { ret.Url = oss.url(info) - println(oss.client.EndpointURL()) + //println(oss.client.EndpointURL()) return ret, nil } diff --git a/src/pkg/ossadaptor/minio_oss_test.go b/src/pkg/ossadaptor/minio_oss_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4dd222be6724745690ea817f8fab2dbb674ed6ca --- /dev/null +++ b/src/pkg/ossadaptor/minio_oss_test.go @@ -0,0 +1,33 @@ +package ossadaptor + +import ( + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +func TestMinioOss_Put(t *testing.T) { + //export OSS_BIND_DOMAIN="http://192.168.1.128:9000" + //export OSS_ACCESS_KEY="xx" + //export OSS_SECRET_KEY="xx" + //export OSS_ENDPOINT="192.168.1.128:9000" + //export OSS_BUCKET="demo" + //export OSS_SUB_PATH="t" + + //最终生成的访问url: http://192.168.1.128:9000/demo/t/ossgotest + oss, err := NewMinioOss(os.Getenv("OSS_BIND_DOMAIN"), &MinioOssOps{ + AccessKey: os.Getenv("OSS_ACCESS_KEY"), + SecretKey: os.Getenv("OSS_SECRET_KEY"), + Endpoint: os.Getenv("OSS_ENDPOINT"), + Bucket: os.Getenv("OSS_BUCKET"), + SubPath: os.Getenv("OSS_SUB_PATH"), + }) + assert.Equal(t, err, nil) + + t.Logf("inited") + + ret, err := oss.Put("./oss.go", "ossgotest") + assert.Equal(t, err, nil) + + t.Logf("%s", ret.Url) +} diff --git a/src/pkg/rss/rss.go b/src/pkg/rsshelp/rss.go similarity index 86% rename from src/pkg/rss/rss.go rename to src/pkg/rsshelp/rss.go index 189a66d397e19efab4e9a22ca6173b061453d299..dbb3257081e55c6a71b0a10be47d2f36a6b44ae3 100644 --- a/src/pkg/rss/rss.go +++ b/src/pkg/rsshelp/rss.go @@ -1,11 +1,9 @@ -package rss +package rsshelp import ( "fmt" - "gitee.com/captials-team/ubdframe/src/common" "gitee.com/captials-team/ubdframe/src/domain/vo" v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" - "go.uber.org/dig" "io" "net/http" "os" @@ -21,17 +19,14 @@ type RssFetcher struct { type RssHandler func(meta *vo.RssMeta, body *vo.RssBody) -func NewRssFetcher(di *dig.Container) *RssFetcher { - var logger v1log.ILog = v1log.NewWriterLog(os.Stdout, v1log.DebugLog) - if di != nil { - common.ErrPanic(di.Invoke(func(l v1log.ILog) { - logger = l - })) +func NewRssFetcher(l v1log.ILog) *RssFetcher { + if l == nil { + l = v1log.NewWriterLog(os.Stdout, v1log.DebugLog) } return &RssFetcher{ client: &http.Client{Timeout: time.Second * 5}, rssMap: map[string]*vo.RssMeta{}, - l: logger, + l: l, } } diff --git a/src/pkg/rss/rss_test.go b/src/pkg/rsshelp/rss_test.go similarity index 75% rename from src/pkg/rss/rss_test.go rename to src/pkg/rsshelp/rss_test.go index 338a762d2dfe40e8b6f6a83e1719404a9c09d79a..d7cbf5ec71fd94e6f00f666cd4148f24b4eb2d7f 100644 --- a/src/pkg/rss/rss_test.go +++ b/src/pkg/rsshelp/rss_test.go @@ -1,20 +1,15 @@ -package rss +package rsshelp import ( - "gitee.com/captials-team/ubdframe/src/common" "gitee.com/captials-team/ubdframe/src/domain/vo" v1log "gitee.com/captials-team/ubdframe/src/pkg/logs" - "go.uber.org/dig" "testing" ) func TestRssFetchWorker_Run(t *testing.T) { - di := dig.New() - common.ErrPanic(di.Provide(func() v1log.ILog { - return v1log.NewTestingLog(t, v1log.DebugLog) - }, dig.As(new(v1log.ILog)))) + l := v1log.NewTestingLog(t, v1log.DebugLog) - rss := NewRssFetcher(di) + rss := NewRssFetcher(l) rss.AddRssMeta(&vo.RssMeta{ Name: "people_com_china", RssUrl: "http://www.people.com.cn/rss/politics.xml",