From a60608bdbc18e87309489b8ef1c5fbd2ca041e93 Mon Sep 17 00:00:00 2001 From: ivamp Date: Fri, 8 Nov 2024 02:25:15 +0800 Subject: [PATCH] update --- cmd/wire_gen.go | 12 +- cmd/worker.go | 93 +++ configs/config.yaml | 5 +- docs/docs.go | 591 +++++++++++++++++- docs/swagger.json | 591 +++++++++++++++++- docs/swagger.yaml | 367 ++++++++++- go.mod | 48 +- go.sum | 67 ++ hack/gorm-gen/gorm.go | 1 + internal/base/conf/conf.go | 1 + internal/dao/categories.gen.go | 466 ++++++++++++++ internal/dao/gen.go | 8 + internal/dao/post_tags.gen.go | 37 ++ internal/dao/posts.gen.go | 192 +++++- internal/dao/tag_mappings.gen.go | 104 ++- internal/dao/tags.gen.go | 90 ++- internal/dao/user_tag_scores.gen.go | 9 + internal/entity/Category.go | 14 + internal/entity/Post.go | 12 +- internal/entity/Tag.go | 6 +- internal/entity/TagMapping.go | 10 +- .../{content.go => applications.go} | 4 +- .../controller/application_v1/categories.go | 180 ++++++ .../http/controller/application_v1/posts.go | 195 ++++++ .../http/controller/v1/applications.go | 32 +- internal/handler/http/provider.go | 16 +- internal/handler/http/request/applications.go | 11 + internal/handler/http/request/categories.go | 11 + internal/handler/http/request/posts.go | 14 + .../{schema => handler/http/response}/http.go | 2 +- internal/handler/http/response/response.go | 7 +- internal/migrations/1_setup.sql | 8 +- internal/router/api.go | 9 + internal/schema/applications.go | 9 - internal/schema/stream.go | 22 + internal/service/category/categories.go | 42 ++ internal/service/category/provider.go | 11 + internal/service/post/posts.go | 53 ++ internal/service/post/provider.go | 21 + internal/service/post/tags.go | 69 ++ internal/service/provider.go | 15 + internal/service/stream/consumer.go | 68 ++ internal/service/stream/kafka.go | 13 + internal/service/stream/producer.go | 119 ++++ internal/service/stream/provider.go | 15 + 45 files changed, 3533 insertions(+), 137 deletions(-) create mode 100644 cmd/worker.go create mode 100644 internal/dao/categories.gen.go create mode 100644 internal/entity/Category.go rename internal/handler/http/controller/application_v1/{content.go => applications.go} (88%) create mode 100644 internal/handler/http/controller/application_v1/categories.go create mode 100644 internal/handler/http/controller/application_v1/posts.go create mode 100644 internal/handler/http/request/applications.go create mode 100644 internal/handler/http/request/categories.go create mode 100644 internal/handler/http/request/posts.go rename internal/{schema => handler/http/response}/http.go (91%) delete mode 100644 internal/schema/applications.go create mode 100644 internal/schema/stream.go create mode 100644 internal/service/category/categories.go create mode 100644 internal/service/category/provider.go create mode 100644 internal/service/post/posts.go create mode 100644 internal/service/post/provider.go create mode 100644 internal/service/post/tags.go create mode 100644 internal/service/stream/consumer.go create mode 100644 internal/service/stream/kafka.go create mode 100644 internal/service/stream/producer.go create mode 100644 internal/service/stream/provider.go diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 2d8ef8f..0c7ade9 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -29,7 +29,10 @@ import ( "leafdev.top/Ecosystem/recommender/internal/service" "leafdev.top/Ecosystem/recommender/internal/service/application" "leafdev.top/Ecosystem/recommender/internal/service/auth" + "leafdev.top/Ecosystem/recommender/internal/service/category" "leafdev.top/Ecosystem/recommender/internal/service/jwks" + "leafdev.top/Ecosystem/recommender/internal/service/post" + "leafdev.top/Ecosystem/recommender/internal/service/stream" ) // Injectors from wire.go: @@ -44,7 +47,12 @@ func CreateApp() (*base.Application, error) { applicationService := application.NewService(query) applicationController := v1.NewApplicationController(authService, applicationService) application_v1ApplicationController := application_v1.NewApplicationController(authService, applicationService) - handlers := http.NewHandler(applicationController, application_v1ApplicationController) + streamService := stream.NewService(config) + postService := post.NewService(query, config, streamService) + categoryService := category.NewService(query) + postController := application_v1.NewPostController(authService, applicationService, postService, categoryService) + categoryController := application_v1.NewCategoryController(authService, applicationService, postService, categoryService) + handlers := http.NewHandler(applicationController, application_v1ApplicationController, postController, categoryController) api := router.NewApiRoute(handlers) swaggerRouter := router.NewSwaggerRoute() ginLoggerMiddleware := middleware.NewGinLoggerMiddleware(loggerLogger) @@ -59,7 +67,7 @@ func CreateApp() (*base.Application, error) { grpcInterceptor := grpc.NewInterceptor(interceptorAuth, interceptorLogger) grpcHandlers := grpc.NewHandler(documentService, grpcInterceptor) handlerHandler := handler.NewHandler(grpcHandlers, handlers) - serviceService := service.NewService(loggerLogger, jwksJWKS, authService, applicationService) + serviceService := service.NewService(loggerLogger, jwksJWKS, streamService, authService, applicationService, postService, categoryService) redisRedis := redis.NewRedis(config) batchBatch := batch.NewBatch(loggerLogger) s3S3 := s3.NewS3(config) diff --git a/cmd/worker.go b/cmd/worker.go new file mode 100644 index 0000000..1880510 --- /dev/null +++ b/cmd/worker.go @@ -0,0 +1,93 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/bytedance/sonic" + "leafdev.top/Ecosystem/recommender/internal/base" + "leafdev.top/Ecosystem/recommender/internal/schema" + "strconv" + "strings" + + "github.com/spf13/cobra" +) + +func init() { + RootCmd.AddCommand(workerCmd) +} + +var workerCmd = &cobra.Command{ + Use: "worker", + Short: "Receive chunk data", + Long: `Receive chunk data`, + Run: func(cmd *cobra.Command, args []string) { + app, err := CreateApp() + if err != nil { + panic(err) + } + + runWorker(app) + }, +} + +func runWorker(app *base.Application) { + app.Logger.Sugar.Info("start worker") + var r = app.Service.Stream.Consumer(app.Config.Kafka.Topic, app.Config.Kafka.GroupId) + + for { + fmt.Println("Loop!") + var ctx = context.Background() + + msg, err := r.ReadMessage(ctx) + if err != nil { + app.Logger.Sugar.Error(err) + continue + } + + //fmt.Println(fmt.Sprintf("topic=%s,partition=%d,offset=%d,key=%s,value=%s", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value)) + + var data = &schema.ProcessPostResult{} + + if err := sonic.Unmarshal(msg.Value, data); err != nil { + app.Logger.Sugar.Error(err) + continue + } + + postId, err := strconv.ParseUint(data.PostId, 10, 64) + + postEntity, err := app.Service.Post.GetPostById(ctx, schema.EntityId(postId)) + if err != nil { + app.Logger.Sugar.Error(err) + continue + } + + app.Logger.Sugar.Infof("receive post %s keywords", postEntity.Title) + + var processError error + + for _, k := range data.Keywords { + // 清除 k 的空格 + if strings.Trim(k, " ") == "" { + continue + } + + app.Logger.Sugar.Infof("bind post %s with tag %s", postEntity.Title, k) + err = app.Service.Post.BindTag(ctx, postEntity, k) + if err != nil { + processError = err + app.Logger.Sugar.Error(err) + continue + } + } + + if processError == nil { + err = app.Service.Post.MarkAsProcessed(ctx, postEntity) + if err != nil { + app.Logger.Sugar.Error(err) + } + } else { + app.Logger.Sugar.Error(processError) + } + + } +} diff --git a/configs/config.yaml b/configs/config.yaml index 7617bed..09a9386 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -54,8 +54,9 @@ milvus: kafka: bootstrap_servers: - 127.0.0.1:9092 - topic: "amber" - group_id: "" + topic: "recommender-main" + worker_topic: "recommender-worker" + group_id: "recommender-main" # Plain username: "" password: "" diff --git a/docs/docs.go b/docs/docs.go index 4100c80..2b87ba2 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -46,7 +46,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -65,7 +65,7 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -99,7 +99,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/schema.ApplicationCreateRequest" + "$ref": "#/definitions/request.ApplicationCreateRequest" } } ], @@ -109,7 +109,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -125,7 +125,7 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -155,7 +155,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -174,7 +174,7 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -203,7 +203,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/schema.ApplicationCreateRequest" + "$ref": "#/definitions/request.ApplicationCreateRequest" } } ], @@ -213,7 +213,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -232,7 +232,214 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/categories": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "列出分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "列出分类", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/page.PagedResult-entity_Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "新建分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "新建分类", + "parameters": [ + { + "description": "body", + "name": "CategorySaveRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CategorySaveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Application" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/categories/{category_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "获取 Post", + "parameters": [ + { + "type": "integer", + "name": "category_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "删除分类", + "parameters": [ + { + "type": "integer", + "name": "category_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" } } } @@ -262,7 +469,7 @@ const docTemplate = `{ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -278,7 +485,214 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/post/{post_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "获取 Post", + "parameters": [ + { + "type": "integer", + "name": "post_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "删除 Post", + "parameters": [ + { + "type": "integer", + "name": "post_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/posts": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "列出 Posts", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "列出 Posts", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/page.PagedResult-entity_Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "新建资源", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "新建资源", + "parameters": [ + { + "description": "body", + "name": "PostSaveRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PostSaveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" } } } @@ -329,7 +743,122 @@ const docTemplate = `{ } } }, - "schema.ApplicationCreateRequest": { + "entity.Category": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/entity.Application" + }, + "application_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "entity.Post": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/entity.Application" + }, + "application_id": { + "type": "integer" + }, + "category": { + "$ref": "#/definitions/entity.Category" + }, + "category_id": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "processed": { + "type": "boolean" + }, + "target_id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "page.PagedResult-entity_Category": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Category" + } + }, + "page": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页大小", + "type": "integer" + }, + "total_count": { + "description": "数据总条数", + "type": "integer" + }, + "total_pages": { + "description": "总页数", + "type": "integer" + } + } + }, + "page.PagedResult-entity_Post": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Post" + } + }, + "page": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页大小", + "type": "integer" + }, + "total_count": { + "description": "数据总条数", + "type": "integer" + }, + "total_pages": { + "description": "总页数", + "type": "integer" + } + } + }, + "request.ApplicationCreateRequest": { "type": "object", "properties": { "name": { @@ -337,7 +866,41 @@ const docTemplate = `{ } } }, - "schema.ResponseBody": { + "request.CategorySaveRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "request.PostSaveRequest": { + "type": "object", + "required": [ + "category_id", + "content", + "target_id", + "title" + ], + "properties": { + "category_id": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "target_id": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "response.ResponseBody": { "type": "object", "properties": { "data": {}, diff --git a/docs/swagger.json b/docs/swagger.json index a0667bb..6d3fe2d 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -37,7 +37,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -56,7 +56,7 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -90,7 +90,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/schema.ApplicationCreateRequest" + "$ref": "#/definitions/request.ApplicationCreateRequest" } } ], @@ -100,7 +100,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -116,7 +116,7 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -146,7 +146,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -165,7 +165,7 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" } } } @@ -194,7 +194,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/schema.ApplicationCreateRequest" + "$ref": "#/definitions/request.ApplicationCreateRequest" } } ], @@ -204,7 +204,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -223,7 +223,214 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/categories": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "列出分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "列出分类", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/page.PagedResult-entity_Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "新建分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "新建分类", + "parameters": [ + { + "description": "body", + "name": "CategorySaveRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CategorySaveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Application" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/categories/{category_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "获取 Post", + "parameters": [ + { + "type": "integer", + "name": "category_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除分类", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "删除分类", + "parameters": [ + { + "type": "integer", + "name": "category_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Category" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" } } } @@ -253,7 +460,7 @@ "schema": { "allOf": [ { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" }, { "type": "object", @@ -269,7 +476,214 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/schema.ResponseBody" + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/post/{post_id}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "获取 Post", + "parameters": [ + { + "type": "integer", + "name": "post_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除 Post", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "删除 Post", + "parameters": [ + { + "type": "integer", + "name": "post_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + } + }, + "/applications/v1/posts": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "列出 Posts", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "列出 Posts", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/page.PagedResult-entity_Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "新建资源", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application_api" + ], + "summary": "新建资源", + "parameters": [ + { + "description": "body", + "name": "PostSaveRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PostSaveRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.ResponseBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/entity.Post" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.ResponseBody" } } } @@ -320,7 +734,122 @@ } } }, - "schema.ApplicationCreateRequest": { + "entity.Category": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/entity.Application" + }, + "application_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, + "entity.Post": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/entity.Application" + }, + "application_id": { + "type": "integer" + }, + "category": { + "$ref": "#/definitions/entity.Category" + }, + "category_id": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "processed": { + "type": "boolean" + }, + "target_id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "page.PagedResult-entity_Category": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Category" + } + }, + "page": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页大小", + "type": "integer" + }, + "total_count": { + "description": "数据总条数", + "type": "integer" + }, + "total_pages": { + "description": "总页数", + "type": "integer" + } + } + }, + "page.PagedResult-entity_Post": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Post" + } + }, + "page": { + "description": "当前页码", + "type": "integer" + }, + "page_size": { + "description": "每页大小", + "type": "integer" + }, + "total_count": { + "description": "数据总条数", + "type": "integer" + }, + "total_pages": { + "description": "总页数", + "type": "integer" + } + } + }, + "request.ApplicationCreateRequest": { "type": "object", "properties": { "name": { @@ -328,7 +857,41 @@ } } }, - "schema.ResponseBody": { + "request.CategorySaveRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "request.PostSaveRequest": { + "type": "object", + "required": [ + "category_id", + "content", + "target_id", + "title" + ], + "properties": { + "category_id": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "target_id": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "response.ResponseBody": { "type": "object", "properties": { "data": {}, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6ac965a..3e938a5 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -27,12 +27,113 @@ definitions: updated_at: type: string type: object - schema.ApplicationCreateRequest: + entity.Category: + properties: + application: + $ref: '#/definitions/entity.Application' + application_id: + type: integer + id: + type: integer + name: + type: string + type: object + entity.Post: + properties: + application: + $ref: '#/definitions/entity.Application' + application_id: + type: integer + category: + $ref: '#/definitions/entity.Category' + category_id: + type: integer + content: + type: string + created_at: + type: string + id: + type: integer + processed: + type: boolean + target_id: + type: string + title: + type: string + updated_at: + type: string + type: object + page.PagedResult-entity_Category: + properties: + count: + type: integer + data: + items: + $ref: '#/definitions/entity.Category' + type: array + page: + description: 当前页码 + type: integer + page_size: + description: 每页大小 + type: integer + total_count: + description: 数据总条数 + type: integer + total_pages: + description: 总页数 + type: integer + type: object + page.PagedResult-entity_Post: + properties: + count: + type: integer + data: + items: + $ref: '#/definitions/entity.Post' + type: array + page: + description: 当前页码 + type: integer + page_size: + description: 每页大小 + type: integer + total_count: + description: 数据总条数 + type: integer + total_pages: + description: 总页数 + type: integer + type: object + request.ApplicationCreateRequest: properties: name: type: string type: object - schema.ResponseBody: + request.CategorySaveRequest: + properties: + name: + type: string + required: + - name + type: object + request.PostSaveRequest: + properties: + category_id: + type: integer + content: + type: string + target_id: + type: string + title: + type: string + required: + - category_id + - content + - target_id + - title + type: object + response.ResponseBody: properties: data: {} error: @@ -63,7 +164,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' + - $ref: '#/definitions/response.ResponseBody' - properties: data: items: @@ -73,7 +174,7 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.ResponseBody' security: - ApiKeyAuth: [] summary: 获取应用程序的 Token 列表 @@ -92,7 +193,7 @@ paths: name: ApplicationCreateRequest required: true schema: - $ref: '#/definitions/schema.ApplicationCreateRequest' + $ref: '#/definitions/request.ApplicationCreateRequest' produces: - application/json responses: @@ -100,7 +201,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' + - $ref: '#/definitions/response.ResponseBody' - properties: data: $ref: '#/definitions/entity.ApplicationToken' @@ -108,7 +209,7 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.ResponseBody' security: - ApiKeyAuth: [] summary: 获取应用程序的 Token 列表 @@ -126,7 +227,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' + - $ref: '#/definitions/response.ResponseBody' - properties: data: items: @@ -136,7 +237,7 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.ResponseBody' security: - ApiKeyAuth: [] summary: List Applications @@ -152,7 +253,7 @@ paths: name: ApplicationCreateRequest required: true schema: - $ref: '#/definitions/schema.ApplicationCreateRequest' + $ref: '#/definitions/request.ApplicationCreateRequest' produces: - application/json responses: @@ -160,7 +261,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' + - $ref: '#/definitions/response.ResponseBody' - properties: data: items: @@ -170,12 +271,131 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.ResponseBody' security: - ApiKeyAuth: [] summary: 创建并保存一个应用程序 tags: - applications + /applications/v1/categories: + get: + consumes: + - application/json + description: 列出分类 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/page.PagedResult-entity_Category' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 列出分类 + tags: + - application_api + post: + consumes: + - application/json + description: 新建分类 + parameters: + - description: body + in: body + name: CategorySaveRequest + required: true + schema: + $ref: '#/definitions/request.CategorySaveRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Application' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 新建分类 + tags: + - application_api + /applications/v1/categories/{category_id}: + delete: + consumes: + - application/json + description: 删除分类 + parameters: + - in: path + name: category_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Category' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 删除分类 + tags: + - application_api + get: + consumes: + - application/json + description: 获取 Post + parameters: + - in: path + name: category_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Category' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 获取 Post + tags: + - application_api /applications/v1/info: get: consumes: @@ -188,7 +408,7 @@ paths: description: OK schema: allOf: - - $ref: '#/definitions/schema.ResponseBody' + - $ref: '#/definitions/response.ResponseBody' - properties: data: $ref: '#/definitions/entity.Application' @@ -196,12 +416,131 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/schema.ResponseBody' + $ref: '#/definitions/response.ResponseBody' security: - ApiKeyAuth: [] summary: Current Application Info tags: - application_api + /applications/v1/post/{post_id}: + delete: + consumes: + - application/json + description: 删除 Post + parameters: + - in: path + name: post_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Post' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 删除 Post + tags: + - application_api + get: + consumes: + - application/json + description: 获取 Post + parameters: + - in: path + name: post_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Post' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 获取 Post + tags: + - application_api + /applications/v1/posts: + get: + consumes: + - application/json + description: 列出 Posts + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/page.PagedResult-entity_Post' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 列出 Posts + tags: + - application_api + post: + consumes: + - application/json + description: 新建资源 + parameters: + - description: body + in: body + name: PostSaveRequest + required: true + schema: + $ref: '#/definitions/request.PostSaveRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.ResponseBody' + - properties: + data: + $ref: '#/definitions/entity.Post' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.ResponseBody' + security: + - ApiKeyAuth: [] + summary: 新建资源 + tags: + - application_api securityDefinitions: ApiKeyAuth: in: header diff --git a/go.mod b/go.mod index cd37d93..d381e9d 100644 --- a/go.mod +++ b/go.mod @@ -10,22 +10,23 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/wire v0.6.0 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 github.com/iVampireSP/pkg v0.0.8-0.20241015163922-65faf24f73ac github.com/milvus-io/milvus-sdk-go/v2 v2.4.2 - github.com/minio/minio-go/v7 v7.0.78 + github.com/minio/minio-go/v7 v7.0.80 github.com/mitchellh/mapstructure v1.5.0 github.com/pressly/goose/v3 v3.22.1 github.com/prometheus/client_golang v1.20.5 - github.com/redis/go-redis/v9 v9.6.2 + github.com/redis/go-redis/v9 v9.7.0 + github.com/segmentio/kafka-go v0.4.47 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 - github.com/swaggo/swag v1.16.3 + github.com/swaggo/swag v1.16.4 github.com/tmc/langchaingo v0.1.12 go.uber.org/zap v1.27.0 - google.golang.org/grpc v1.64.1 + google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 gorm.io/driver/mysql v1.5.7 gorm.io/gen v0.3.26 @@ -40,20 +41,20 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/MicahParks/jwkset v0.5.20 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.12.3 // indirect + github.com/bytedance/sonic v1.12.4 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/cockroachdb/errors v1.9.1 // indirect - github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect - github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.10.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gabriel-vasile/mimetype v1.4.6 // indirect - github.com/getsentry/sentry-go v0.12.0 // indirect + github.com/getsentry/sentry-go v0.29.1 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -68,7 +69,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect @@ -76,7 +77,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -84,18 +85,19 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a // indirect + github.com/milvus-io/milvus-proto/go-api/v2 v2.4.15 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pkoukk/tiktoken-go v0.1.6 // indirect + github.com/pkoukk/tiktoken-go v0.1.7 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -105,9 +107,9 @@ require ( github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -121,10 +123,10 @@ require ( golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.26.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/datatypes v1.2.3 // indirect + gorm.io/datatypes v1.2.4 // indirect gorm.io/hints v1.1.2 // indirect ) diff --git a/go.sum b/go.sum index a9c2282..7df920d 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.113.0 h1:g3C70mn3lWfckKBiCVsAshabrDg01pQ0pnX1MNtnMkA= cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= @@ -31,6 +32,8 @@ github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw= github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk= github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k= +github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= @@ -47,10 +50,16 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -68,6 +77,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -85,11 +96,15 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA= +github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0= github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw= github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= @@ -108,7 +123,9 @@ github.com/go-faker/faker/v4 v4.1.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0 github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= @@ -187,10 +204,14 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -240,6 +261,7 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -247,6 +269,8 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -292,12 +316,16 @@ github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a h1:0B/8Fo66D8Aa23Il0yrQvg1KKz92tE/BJ5BvkUxxAAk= github.com/milvus-io/milvus-proto/go-api/v2 v2.4.10-0.20240819025435-512e3b98866a/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= +github.com/milvus-io/milvus-proto/go-api/v2 v2.4.15 h1:1y+hkeGh7zaD5ZasWjfKNZYWdH8VlLKcjoeyFiSh/I8= +github.com/milvus-io/milvus-proto/go-api/v2 v2.4.15/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= github.com/milvus-io/milvus-sdk-go/v2 v2.4.2 h1:Xqf+S7iicElwYoS2Zly8Nf/zKHuZsNy1xQajfdtygVY= github.com/milvus-io/milvus-sdk-go/v2 v2.4.2/go.mod h1:ulO1YUXKH0PGg50q27grw048GDY9ayB4FPmh7D+FFTA= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.78 h1:LqW2zy52fxnI4gg8C2oZviTaKHcBV36scS+RzJnxUFs= github.com/minio/minio-go/v7 v7.0.78/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk= +github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -325,6 +353,9 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -333,6 +364,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +github.com/pkoukk/tiktoken-go v0.1.7 h1:qOBHXX4PHtvIvmOtyg1EeKlwFRiMKAcoMp4Q+bLQDmw= +github.com/pkoukk/tiktoken-go v0.1.7/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -345,10 +378,14 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk= github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -356,6 +393,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -366,6 +405,8 @@ github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0= +github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= @@ -414,12 +455,18 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= +github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/langchaingo v0.1.12 h1:yXwSu54f3b1IKw0jJ5/DWu+qFVH1NBblwC0xddBzGJE= github.com/tmc/langchaingo v0.1.12/go.mod h1:cd62xD6h+ouk8k/QQFhOsjRYBSA1JJ5UVKXSIgm7Ni4= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -436,6 +483,9 @@ github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBn github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -450,6 +500,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -459,6 +510,7 @@ go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95a go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= @@ -473,6 +525,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= @@ -516,6 +569,7 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= @@ -556,6 +610,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -563,6 +618,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -571,6 +627,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -578,6 +635,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= @@ -595,6 +653,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -621,8 +680,12 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -632,6 +695,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f h1:rqzndB2lIQGivcXdTuY3Y9NBvr70X+y77woofSRluec= google.golang.org/grpc/examples v0.0.0-20220617181431-3e7b97febc7f/go.mod h1:gxndsbNG1n4TZcHGgsYEfVGnTxqfEdfiDv6/DADXX9o= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -673,6 +738,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/datatypes v1.2.3 h1:95ucr9ip9dZMPhB3Tc9zbcoAi62hxYAgHicu7SLjK4g= gorm.io/datatypes v1.2.3/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= +gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= +gorm.io/datatypes v1.2.4/go.mod h1:f4BsLcFAX67szSv8svwLRjklArSHAvHLeE3pXAS5DZI= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= diff --git a/hack/gorm-gen/gorm.go b/hack/gorm-gen/gorm.go index c2ecf90..a96dff3 100644 --- a/hack/gorm-gen/gorm.go +++ b/hack/gorm-gen/gorm.go @@ -33,6 +33,7 @@ func main() { entity.Application{}, entity.ApplicationToken{}, entity.UserTagScore{}, + entity.Category{}, ) // Generate Type Safe API with Dynamic SQL defined on Querier interface for `model.User` and `model.Company` diff --git a/internal/base/conf/conf.go b/internal/base/conf/conf.go index 5e83586..0e8b1db 100644 --- a/internal/base/conf/conf.go +++ b/internal/base/conf/conf.go @@ -79,6 +79,7 @@ type S3 struct { type Kafka struct { BootstrapServers []string `yaml:"bootstrap_servers" mapstructure:"bootstrap_servers"` Topic string `yaml:"topic" mapstructure:"topic"` + WorkerTopic string `yaml:"worker_topic" mapstructure:"worker_topic"` GroupId string `yaml:"group_id" mapstructure:"group_id"` Username string `yaml:"username" mapstructure:"username"` Password string `yaml:"password" mapstructure:"password"` diff --git a/internal/dao/categories.gen.go b/internal/dao/categories.gen.go new file mode 100644 index 0000000..9c1d7a6 --- /dev/null +++ b/internal/dao/categories.gen.go @@ -0,0 +1,466 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package dao + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "leafdev.top/Ecosystem/recommender/internal/entity" +) + +func newCategory(db *gorm.DB, opts ...gen.DOOption) category { + _category := category{} + + _category.categoryDo.UseDB(db, opts...) + _category.categoryDo.UseModel(&entity.Category{}) + + tableName := _category.categoryDo.TableName() + _category.ALL = field.NewAsterisk(tableName) + _category.Id = field.NewUint(tableName, "id") + _category.Name = field.NewString(tableName, "name") + _category.ApplicationId = field.NewUint(tableName, "application_id") + _category.Application = categoryBelongsToApplication{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Application", "entity.Application"), + } + + _category.fillFieldMap() + + return _category +} + +type category struct { + categoryDo + + ALL field.Asterisk + Id field.Uint + Name field.String + ApplicationId field.Uint + Application categoryBelongsToApplication + + fieldMap map[string]field.Expr +} + +func (c category) Table(newTableName string) *category { + c.categoryDo.UseTable(newTableName) + return c.updateTableName(newTableName) +} + +func (c category) As(alias string) *category { + c.categoryDo.DO = *(c.categoryDo.As(alias).(*gen.DO)) + return c.updateTableName(alias) +} + +func (c *category) updateTableName(table string) *category { + c.ALL = field.NewAsterisk(table) + c.Id = field.NewUint(table, "id") + c.Name = field.NewString(table, "name") + c.ApplicationId = field.NewUint(table, "application_id") + + c.fillFieldMap() + + return c +} + +func (c *category) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := c.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (c *category) fillFieldMap() { + c.fieldMap = make(map[string]field.Expr, 4) + c.fieldMap["id"] = c.Id + c.fieldMap["name"] = c.Name + c.fieldMap["application_id"] = c.ApplicationId + +} + +func (c category) clone(db *gorm.DB) category { + c.categoryDo.ReplaceConnPool(db.Statement.ConnPool) + return c +} + +func (c category) replaceDB(db *gorm.DB) category { + c.categoryDo.ReplaceDB(db) + return c +} + +type categoryBelongsToApplication struct { + db *gorm.DB + + field.RelationField +} + +func (a categoryBelongsToApplication) Where(conds ...field.Expr) *categoryBelongsToApplication { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a categoryBelongsToApplication) WithContext(ctx context.Context) *categoryBelongsToApplication { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a categoryBelongsToApplication) Session(session *gorm.Session) *categoryBelongsToApplication { + a.db = a.db.Session(session) + return &a +} + +func (a categoryBelongsToApplication) Model(m *entity.Category) *categoryBelongsToApplicationTx { + return &categoryBelongsToApplicationTx{a.db.Model(m).Association(a.Name())} +} + +type categoryBelongsToApplicationTx struct{ tx *gorm.Association } + +func (a categoryBelongsToApplicationTx) Find() (result *entity.Application, err error) { + return result, a.tx.Find(&result) +} + +func (a categoryBelongsToApplicationTx) Append(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a categoryBelongsToApplicationTx) Replace(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a categoryBelongsToApplicationTx) Delete(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a categoryBelongsToApplicationTx) Clear() error { + return a.tx.Clear() +} + +func (a categoryBelongsToApplicationTx) Count() int64 { + return a.tx.Count() +} + +type categoryDo struct{ gen.DO } + +type ICategoryDo interface { + gen.SubQuery + Debug() ICategoryDo + WithContext(ctx context.Context) ICategoryDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() ICategoryDo + WriteDB() ICategoryDo + As(alias string) gen.Dao + Session(config *gorm.Session) ICategoryDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) ICategoryDo + Not(conds ...gen.Condition) ICategoryDo + Or(conds ...gen.Condition) ICategoryDo + Select(conds ...field.Expr) ICategoryDo + Where(conds ...gen.Condition) ICategoryDo + Order(conds ...field.Expr) ICategoryDo + Distinct(cols ...field.Expr) ICategoryDo + Omit(cols ...field.Expr) ICategoryDo + Join(table schema.Tabler, on ...field.Expr) ICategoryDo + LeftJoin(table schema.Tabler, on ...field.Expr) ICategoryDo + RightJoin(table schema.Tabler, on ...field.Expr) ICategoryDo + Group(cols ...field.Expr) ICategoryDo + Having(conds ...gen.Condition) ICategoryDo + Limit(limit int) ICategoryDo + Offset(offset int) ICategoryDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) ICategoryDo + Unscoped() ICategoryDo + Create(values ...*entity.Category) error + CreateInBatches(values []*entity.Category, batchSize int) error + Save(values ...*entity.Category) error + First() (*entity.Category, error) + Take() (*entity.Category, error) + Last() (*entity.Category, error) + Find() ([]*entity.Category, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*entity.Category, err error) + FindInBatches(result *[]*entity.Category, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*entity.Category) (info gen.ResultInfo, err error) + Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + Updates(value interface{}) (info gen.ResultInfo, err error) + UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + UpdateColumns(value interface{}) (info gen.ResultInfo, err error) + UpdateFrom(q gen.SubQuery) gen.Dao + Attrs(attrs ...field.AssignExpr) ICategoryDo + Assign(attrs ...field.AssignExpr) ICategoryDo + Joins(fields ...field.RelationField) ICategoryDo + Preload(fields ...field.RelationField) ICategoryDo + FirstOrInit() (*entity.Category, error) + FirstOrCreate() (*entity.Category, error) + FindByPage(offset int, limit int) (result []*entity.Category, count int64, err error) + ScanByPage(result interface{}, offset int, limit int) (count int64, err error) + Scan(result interface{}) (err error) + Returning(value interface{}, columns ...string) ICategoryDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (c categoryDo) Debug() ICategoryDo { + return c.withDO(c.DO.Debug()) +} + +func (c categoryDo) WithContext(ctx context.Context) ICategoryDo { + return c.withDO(c.DO.WithContext(ctx)) +} + +func (c categoryDo) ReadDB() ICategoryDo { + return c.Clauses(dbresolver.Read) +} + +func (c categoryDo) WriteDB() ICategoryDo { + return c.Clauses(dbresolver.Write) +} + +func (c categoryDo) Session(config *gorm.Session) ICategoryDo { + return c.withDO(c.DO.Session(config)) +} + +func (c categoryDo) Clauses(conds ...clause.Expression) ICategoryDo { + return c.withDO(c.DO.Clauses(conds...)) +} + +func (c categoryDo) Returning(value interface{}, columns ...string) ICategoryDo { + return c.withDO(c.DO.Returning(value, columns...)) +} + +func (c categoryDo) Not(conds ...gen.Condition) ICategoryDo { + return c.withDO(c.DO.Not(conds...)) +} + +func (c categoryDo) Or(conds ...gen.Condition) ICategoryDo { + return c.withDO(c.DO.Or(conds...)) +} + +func (c categoryDo) Select(conds ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Select(conds...)) +} + +func (c categoryDo) Where(conds ...gen.Condition) ICategoryDo { + return c.withDO(c.DO.Where(conds...)) +} + +func (c categoryDo) Order(conds ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Order(conds...)) +} + +func (c categoryDo) Distinct(cols ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Distinct(cols...)) +} + +func (c categoryDo) Omit(cols ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Omit(cols...)) +} + +func (c categoryDo) Join(table schema.Tabler, on ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Join(table, on...)) +} + +func (c categoryDo) LeftJoin(table schema.Tabler, on ...field.Expr) ICategoryDo { + return c.withDO(c.DO.LeftJoin(table, on...)) +} + +func (c categoryDo) RightJoin(table schema.Tabler, on ...field.Expr) ICategoryDo { + return c.withDO(c.DO.RightJoin(table, on...)) +} + +func (c categoryDo) Group(cols ...field.Expr) ICategoryDo { + return c.withDO(c.DO.Group(cols...)) +} + +func (c categoryDo) Having(conds ...gen.Condition) ICategoryDo { + return c.withDO(c.DO.Having(conds...)) +} + +func (c categoryDo) Limit(limit int) ICategoryDo { + return c.withDO(c.DO.Limit(limit)) +} + +func (c categoryDo) Offset(offset int) ICategoryDo { + return c.withDO(c.DO.Offset(offset)) +} + +func (c categoryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ICategoryDo { + return c.withDO(c.DO.Scopes(funcs...)) +} + +func (c categoryDo) Unscoped() ICategoryDo { + return c.withDO(c.DO.Unscoped()) +} + +func (c categoryDo) Create(values ...*entity.Category) error { + if len(values) == 0 { + return nil + } + return c.DO.Create(values) +} + +func (c categoryDo) CreateInBatches(values []*entity.Category, batchSize int) error { + return c.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (c categoryDo) Save(values ...*entity.Category) error { + if len(values) == 0 { + return nil + } + return c.DO.Save(values) +} + +func (c categoryDo) First() (*entity.Category, error) { + if result, err := c.DO.First(); err != nil { + return nil, err + } else { + return result.(*entity.Category), nil + } +} + +func (c categoryDo) Take() (*entity.Category, error) { + if result, err := c.DO.Take(); err != nil { + return nil, err + } else { + return result.(*entity.Category), nil + } +} + +func (c categoryDo) Last() (*entity.Category, error) { + if result, err := c.DO.Last(); err != nil { + return nil, err + } else { + return result.(*entity.Category), nil + } +} + +func (c categoryDo) Find() ([]*entity.Category, error) { + result, err := c.DO.Find() + return result.([]*entity.Category), err +} + +func (c categoryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*entity.Category, err error) { + buf := make([]*entity.Category, 0, batchSize) + err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (c categoryDo) FindInBatches(result *[]*entity.Category, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return c.DO.FindInBatches(result, batchSize, fc) +} + +func (c categoryDo) Attrs(attrs ...field.AssignExpr) ICategoryDo { + return c.withDO(c.DO.Attrs(attrs...)) +} + +func (c categoryDo) Assign(attrs ...field.AssignExpr) ICategoryDo { + return c.withDO(c.DO.Assign(attrs...)) +} + +func (c categoryDo) Joins(fields ...field.RelationField) ICategoryDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Joins(_f)) + } + return &c +} + +func (c categoryDo) Preload(fields ...field.RelationField) ICategoryDo { + for _, _f := range fields { + c = *c.withDO(c.DO.Preload(_f)) + } + return &c +} + +func (c categoryDo) FirstOrInit() (*entity.Category, error) { + if result, err := c.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*entity.Category), nil + } +} + +func (c categoryDo) FirstOrCreate() (*entity.Category, error) { + if result, err := c.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*entity.Category), nil + } +} + +func (c categoryDo) FindByPage(offset int, limit int) (result []*entity.Category, count int64, err error) { + result, err = c.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = c.Offset(-1).Limit(-1).Count() + return +} + +func (c categoryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = c.Count() + if err != nil { + return + } + + err = c.Offset(offset).Limit(limit).Scan(result) + return +} + +func (c categoryDo) Scan(result interface{}) (err error) { + return c.DO.Scan(result) +} + +func (c categoryDo) Delete(models ...*entity.Category) (result gen.ResultInfo, err error) { + return c.DO.Delete(models) +} + +func (c *categoryDo) withDO(do gen.Dao) *categoryDo { + c.DO = *do.(*gen.DO) + return c +} diff --git a/internal/dao/gen.go b/internal/dao/gen.go index 485f943..7e2d280 100644 --- a/internal/dao/gen.go +++ b/internal/dao/gen.go @@ -19,6 +19,7 @@ var ( Q = new(Query) Application *application ApplicationToken *applicationToken + Category *category Post *post PostTag *postTag Tag *tag @@ -31,6 +32,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { *Q = *Use(db, opts...) Application = &Q.Application ApplicationToken = &Q.ApplicationToken + Category = &Q.Category Post = &Q.Post PostTag = &Q.PostTag Tag = &Q.Tag @@ -44,6 +46,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { db: db, Application: newApplication(db, opts...), ApplicationToken: newApplicationToken(db, opts...), + Category: newCategory(db, opts...), Post: newPost(db, opts...), PostTag: newPostTag(db, opts...), Tag: newTag(db, opts...), @@ -58,6 +61,7 @@ type Query struct { Application application ApplicationToken applicationToken + Category category Post post PostTag postTag Tag tag @@ -73,6 +77,7 @@ func (q *Query) clone(db *gorm.DB) *Query { db: db, Application: q.Application.clone(db), ApplicationToken: q.ApplicationToken.clone(db), + Category: q.Category.clone(db), Post: q.Post.clone(db), PostTag: q.PostTag.clone(db), Tag: q.Tag.clone(db), @@ -95,6 +100,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { db: db, Application: q.Application.replaceDB(db), ApplicationToken: q.ApplicationToken.replaceDB(db), + Category: q.Category.replaceDB(db), Post: q.Post.replaceDB(db), PostTag: q.PostTag.replaceDB(db), Tag: q.Tag.replaceDB(db), @@ -107,6 +113,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { type queryCtx struct { Application IApplicationDo ApplicationToken IApplicationTokenDo + Category ICategoryDo Post IPostDo PostTag IPostTagDo Tag ITagDo @@ -119,6 +126,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ Application: q.Application.WithContext(ctx), ApplicationToken: q.ApplicationToken.WithContext(ctx), + Category: q.Category.WithContext(ctx), Post: q.Post.WithContext(ctx), PostTag: q.PostTag.WithContext(ctx), Tag: q.Tag.WithContext(ctx), diff --git a/internal/dao/post_tags.gen.go b/internal/dao/post_tags.gen.go index d128f94..ca917c5 100644 --- a/internal/dao/post_tags.gen.go +++ b/internal/dao/post_tags.gen.go @@ -34,12 +34,35 @@ func newPostTag(db *gorm.DB, opts ...gen.DOOption) postTag { db: db.Session(&gorm.Session{}), RelationField: field.NewRelation("Post", "entity.Post"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Post.Application", "entity.Application"), + }, + Category: struct { + field.RelationField + Application struct { + field.RelationField + } + }{ + RelationField: field.NewRelation("Post.Category", "entity.Category"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Post.Category.Application", "entity.Application"), + }, + }, } _postTag.Tag = postTagBelongsToTag{ db: db.Session(&gorm.Session{}), RelationField: field.NewRelation("Tag", "entity.Tag"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Tag.Application", "entity.Application"), + }, } _postTag.fillFieldMap() @@ -113,6 +136,16 @@ type postTagBelongsToPost struct { db *gorm.DB field.RelationField + + Application struct { + field.RelationField + } + Category struct { + field.RelationField + Application struct { + field.RelationField + } + } } func (a postTagBelongsToPost) Where(conds ...field.Expr) *postTagBelongsToPost { @@ -184,6 +217,10 @@ type postTagBelongsToTag struct { db *gorm.DB field.RelationField + + Application struct { + field.RelationField + } } func (a postTagBelongsToTag) Where(conds ...field.Expr) *postTagBelongsToTag { diff --git a/internal/dao/posts.gen.go b/internal/dao/posts.gen.go index 23fcffb..72120d2 100644 --- a/internal/dao/posts.gen.go +++ b/internal/dao/posts.gen.go @@ -33,7 +33,25 @@ func newPost(db *gorm.DB, opts ...gen.DOOption) post { _post.Title = field.NewString(tableName, "title") _post.Content = field.NewString(tableName, "content") _post.TargetId = field.NewString(tableName, "target_id") + _post.ApplicationId = field.NewUint(tableName, "application_id") + _post.CategoryId = field.NewUint(tableName, "category_id") _post.Processed = field.NewBool(tableName, "processed") + _post.Application = postBelongsToApplication{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Application", "entity.Application"), + } + + _post.Category = postBelongsToCategory{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Category", "entity.Category"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Category.Application", "entity.Application"), + }, + } _post.fillFieldMap() @@ -43,14 +61,19 @@ func newPost(db *gorm.DB, opts ...gen.DOOption) post { type post struct { postDo - ALL field.Asterisk - Id field.Uint - CreatedAt field.Time - UpdatedAt field.Time - Title field.String - Content field.String - TargetId field.String - Processed field.Bool + ALL field.Asterisk + Id field.Uint + CreatedAt field.Time + UpdatedAt field.Time + Title field.String + Content field.String + TargetId field.String + ApplicationId field.Uint + CategoryId field.Uint + Processed field.Bool + Application postBelongsToApplication + + Category postBelongsToCategory fieldMap map[string]field.Expr } @@ -73,6 +96,8 @@ func (p *post) updateTableName(table string) *post { p.Title = field.NewString(table, "title") p.Content = field.NewString(table, "content") p.TargetId = field.NewString(table, "target_id") + p.ApplicationId = field.NewUint(table, "application_id") + p.CategoryId = field.NewUint(table, "category_id") p.Processed = field.NewBool(table, "processed") p.fillFieldMap() @@ -90,14 +115,17 @@ func (p *post) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (p *post) fillFieldMap() { - p.fieldMap = make(map[string]field.Expr, 7) + p.fieldMap = make(map[string]field.Expr, 11) p.fieldMap["id"] = p.Id p.fieldMap["created_at"] = p.CreatedAt p.fieldMap["updated_at"] = p.UpdatedAt p.fieldMap["title"] = p.Title p.fieldMap["content"] = p.Content p.fieldMap["target_id"] = p.TargetId + p.fieldMap["application_id"] = p.ApplicationId + p.fieldMap["category_id"] = p.CategoryId p.fieldMap["processed"] = p.Processed + } func (p post) clone(db *gorm.DB) post { @@ -110,6 +138,152 @@ func (p post) replaceDB(db *gorm.DB) post { return p } +type postBelongsToApplication struct { + db *gorm.DB + + field.RelationField +} + +func (a postBelongsToApplication) Where(conds ...field.Expr) *postBelongsToApplication { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a postBelongsToApplication) WithContext(ctx context.Context) *postBelongsToApplication { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a postBelongsToApplication) Session(session *gorm.Session) *postBelongsToApplication { + a.db = a.db.Session(session) + return &a +} + +func (a postBelongsToApplication) Model(m *entity.Post) *postBelongsToApplicationTx { + return &postBelongsToApplicationTx{a.db.Model(m).Association(a.Name())} +} + +type postBelongsToApplicationTx struct{ tx *gorm.Association } + +func (a postBelongsToApplicationTx) Find() (result *entity.Application, err error) { + return result, a.tx.Find(&result) +} + +func (a postBelongsToApplicationTx) Append(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a postBelongsToApplicationTx) Replace(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a postBelongsToApplicationTx) Delete(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a postBelongsToApplicationTx) Clear() error { + return a.tx.Clear() +} + +func (a postBelongsToApplicationTx) Count() int64 { + return a.tx.Count() +} + +type postBelongsToCategory struct { + db *gorm.DB + + field.RelationField + + Application struct { + field.RelationField + } +} + +func (a postBelongsToCategory) Where(conds ...field.Expr) *postBelongsToCategory { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a postBelongsToCategory) WithContext(ctx context.Context) *postBelongsToCategory { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a postBelongsToCategory) Session(session *gorm.Session) *postBelongsToCategory { + a.db = a.db.Session(session) + return &a +} + +func (a postBelongsToCategory) Model(m *entity.Post) *postBelongsToCategoryTx { + return &postBelongsToCategoryTx{a.db.Model(m).Association(a.Name())} +} + +type postBelongsToCategoryTx struct{ tx *gorm.Association } + +func (a postBelongsToCategoryTx) Find() (result *entity.Category, err error) { + return result, a.tx.Find(&result) +} + +func (a postBelongsToCategoryTx) Append(values ...*entity.Category) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a postBelongsToCategoryTx) Replace(values ...*entity.Category) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a postBelongsToCategoryTx) Delete(values ...*entity.Category) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a postBelongsToCategoryTx) Clear() error { + return a.tx.Clear() +} + +func (a postBelongsToCategoryTx) Count() int64 { + return a.tx.Count() +} + type postDo struct{ gen.DO } type IPostDo interface { diff --git a/internal/dao/tag_mappings.gen.go b/internal/dao/tag_mappings.gen.go index ee66940..b0c8a1b 100644 --- a/internal/dao/tag_mappings.gen.go +++ b/internal/dao/tag_mappings.gen.go @@ -30,10 +30,22 @@ func newTagMapping(db *gorm.DB, opts ...gen.DOOption) tagMapping { _tagMapping.Id = field.NewUint(tableName, "id") _tagMapping.TagId = field.NewUint(tableName, "tag_id") _tagMapping.Name = field.NewString(tableName, "name") + _tagMapping.ApplicationId = field.NewUint(tableName, "application_id") _tagMapping.Tag = tagMappingBelongsToTag{ db: db.Session(&gorm.Session{}), RelationField: field.NewRelation("Tag", "entity.Tag"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Tag.Application", "entity.Application"), + }, + } + + _tagMapping.Application = tagMappingBelongsToApplication{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Application", "entity.Application"), } _tagMapping.fillFieldMap() @@ -44,11 +56,14 @@ func newTagMapping(db *gorm.DB, opts ...gen.DOOption) tagMapping { type tagMapping struct { tagMappingDo - ALL field.Asterisk - Id field.Uint - TagId field.Uint - Name field.String - Tag tagMappingBelongsToTag + ALL field.Asterisk + Id field.Uint + TagId field.Uint + Name field.String + ApplicationId field.Uint + Tag tagMappingBelongsToTag + + Application tagMappingBelongsToApplication fieldMap map[string]field.Expr } @@ -68,6 +83,7 @@ func (t *tagMapping) updateTableName(table string) *tagMapping { t.Id = field.NewUint(table, "id") t.TagId = field.NewUint(table, "tag_id") t.Name = field.NewString(table, "name") + t.ApplicationId = field.NewUint(table, "application_id") t.fillFieldMap() @@ -84,10 +100,11 @@ func (t *tagMapping) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (t *tagMapping) fillFieldMap() { - t.fieldMap = make(map[string]field.Expr, 4) + t.fieldMap = make(map[string]field.Expr, 6) t.fieldMap["id"] = t.Id t.fieldMap["tag_id"] = t.TagId t.fieldMap["name"] = t.Name + t.fieldMap["application_id"] = t.ApplicationId } @@ -105,6 +122,10 @@ type tagMappingBelongsToTag struct { db *gorm.DB field.RelationField + + Application struct { + field.RelationField + } } func (a tagMappingBelongsToTag) Where(conds ...field.Expr) *tagMappingBelongsToTag { @@ -172,6 +193,77 @@ func (a tagMappingBelongsToTagTx) Count() int64 { return a.tx.Count() } +type tagMappingBelongsToApplication struct { + db *gorm.DB + + field.RelationField +} + +func (a tagMappingBelongsToApplication) Where(conds ...field.Expr) *tagMappingBelongsToApplication { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a tagMappingBelongsToApplication) WithContext(ctx context.Context) *tagMappingBelongsToApplication { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a tagMappingBelongsToApplication) Session(session *gorm.Session) *tagMappingBelongsToApplication { + a.db = a.db.Session(session) + return &a +} + +func (a tagMappingBelongsToApplication) Model(m *entity.TagMapping) *tagMappingBelongsToApplicationTx { + return &tagMappingBelongsToApplicationTx{a.db.Model(m).Association(a.Name())} +} + +type tagMappingBelongsToApplicationTx struct{ tx *gorm.Association } + +func (a tagMappingBelongsToApplicationTx) Find() (result *entity.Application, err error) { + return result, a.tx.Find(&result) +} + +func (a tagMappingBelongsToApplicationTx) Append(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a tagMappingBelongsToApplicationTx) Replace(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a tagMappingBelongsToApplicationTx) Delete(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a tagMappingBelongsToApplicationTx) Clear() error { + return a.tx.Clear() +} + +func (a tagMappingBelongsToApplicationTx) Count() int64 { + return a.tx.Count() +} + type tagMappingDo struct{ gen.DO } type ITagMappingDo interface { diff --git a/internal/dao/tags.gen.go b/internal/dao/tags.gen.go index 3aaf8de..855f8e1 100644 --- a/internal/dao/tags.gen.go +++ b/internal/dao/tags.gen.go @@ -29,6 +29,12 @@ func newTag(db *gorm.DB, opts ...gen.DOOption) tag { _tag.ALL = field.NewAsterisk(tableName) _tag.Id = field.NewUint(tableName, "id") _tag.Name = field.NewString(tableName, "name") + _tag.ApplicationId = field.NewUint(tableName, "application_id") + _tag.Application = tagBelongsToApplication{ + db: db.Session(&gorm.Session{}), + + RelationField: field.NewRelation("Application", "entity.Application"), + } _tag.fillFieldMap() @@ -38,9 +44,11 @@ func newTag(db *gorm.DB, opts ...gen.DOOption) tag { type tag struct { tagDo - ALL field.Asterisk - Id field.Uint - Name field.String + ALL field.Asterisk + Id field.Uint + Name field.String + ApplicationId field.Uint + Application tagBelongsToApplication fieldMap map[string]field.Expr } @@ -59,6 +67,7 @@ func (t *tag) updateTableName(table string) *tag { t.ALL = field.NewAsterisk(table) t.Id = field.NewUint(table, "id") t.Name = field.NewString(table, "name") + t.ApplicationId = field.NewUint(table, "application_id") t.fillFieldMap() @@ -75,9 +84,11 @@ func (t *tag) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (t *tag) fillFieldMap() { - t.fieldMap = make(map[string]field.Expr, 2) + t.fieldMap = make(map[string]field.Expr, 4) t.fieldMap["id"] = t.Id t.fieldMap["name"] = t.Name + t.fieldMap["application_id"] = t.ApplicationId + } func (t tag) clone(db *gorm.DB) tag { @@ -90,6 +101,77 @@ func (t tag) replaceDB(db *gorm.DB) tag { return t } +type tagBelongsToApplication struct { + db *gorm.DB + + field.RelationField +} + +func (a tagBelongsToApplication) Where(conds ...field.Expr) *tagBelongsToApplication { + if len(conds) == 0 { + return &a + } + + exprs := make([]clause.Expression, 0, len(conds)) + for _, cond := range conds { + exprs = append(exprs, cond.BeCond().(clause.Expression)) + } + a.db = a.db.Clauses(clause.Where{Exprs: exprs}) + return &a +} + +func (a tagBelongsToApplication) WithContext(ctx context.Context) *tagBelongsToApplication { + a.db = a.db.WithContext(ctx) + return &a +} + +func (a tagBelongsToApplication) Session(session *gorm.Session) *tagBelongsToApplication { + a.db = a.db.Session(session) + return &a +} + +func (a tagBelongsToApplication) Model(m *entity.Tag) *tagBelongsToApplicationTx { + return &tagBelongsToApplicationTx{a.db.Model(m).Association(a.Name())} +} + +type tagBelongsToApplicationTx struct{ tx *gorm.Association } + +func (a tagBelongsToApplicationTx) Find() (result *entity.Application, err error) { + return result, a.tx.Find(&result) +} + +func (a tagBelongsToApplicationTx) Append(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Append(targetValues...) +} + +func (a tagBelongsToApplicationTx) Replace(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Replace(targetValues...) +} + +func (a tagBelongsToApplicationTx) Delete(values ...*entity.Application) (err error) { + targetValues := make([]interface{}, len(values)) + for i, v := range values { + targetValues[i] = v + } + return a.tx.Delete(targetValues...) +} + +func (a tagBelongsToApplicationTx) Clear() error { + return a.tx.Clear() +} + +func (a tagBelongsToApplicationTx) Count() int64 { + return a.tx.Count() +} + type tagDo struct{ gen.DO } type ITagDo interface { diff --git a/internal/dao/user_tag_scores.gen.go b/internal/dao/user_tag_scores.gen.go index ff1dee3..352a377 100644 --- a/internal/dao/user_tag_scores.gen.go +++ b/internal/dao/user_tag_scores.gen.go @@ -34,6 +34,11 @@ func newUserTagScore(db *gorm.DB, opts ...gen.DOOption) userTagScore { db: db.Session(&gorm.Session{}), RelationField: field.NewRelation("Tag", "entity.Tag"), + Application: struct { + field.RelationField + }{ + RelationField: field.NewRelation("Tag.Application", "entity.Application"), + }, } _userTagScore.fillFieldMap() @@ -105,6 +110,10 @@ type userTagScoreBelongsToTag struct { db *gorm.DB field.RelationField + + Application struct { + field.RelationField + } } func (a userTagScoreBelongsToTag) Where(conds ...field.Expr) *userTagScoreBelongsToTag { diff --git a/internal/entity/Category.go b/internal/entity/Category.go new file mode 100644 index 0000000..2105477 --- /dev/null +++ b/internal/entity/Category.go @@ -0,0 +1,14 @@ +package entity + +import "leafdev.top/Ecosystem/recommender/internal/schema" + +type Category struct { + Id schema.EntityId `gorm:"primarykey" json:"id"` + Name string `json:"name"` + Application *Application + ApplicationId schema.EntityId `json:"application_id"` +} + +func (c *Category) TableName() string { + return "categories" +} diff --git a/internal/entity/Post.go b/internal/entity/Post.go index 25e2389..28f6433 100644 --- a/internal/entity/Post.go +++ b/internal/entity/Post.go @@ -5,10 +5,14 @@ import "leafdev.top/Ecosystem/recommender/internal/schema" type Post struct { Model - Title string `json:"title"` - Content string `json:"content"` - TargetId string `json:"target_id"` - Processed bool `json:"processed"` + Title string `json:"title"` + Content string `json:"content"` + TargetId string `json:"target_id"` + ApplicationId schema.EntityId `json:"application_id"` + Application *Application `json:"application"` + Category *Category `json:"category"` + CategoryId *schema.EntityId `json:"category_id"` + Processed bool `json:"processed"` } func (u *Post) TableName() string { diff --git a/internal/entity/Tag.go b/internal/entity/Tag.go index acc63d9..b6dcfeb 100644 --- a/internal/entity/Tag.go +++ b/internal/entity/Tag.go @@ -3,8 +3,10 @@ package entity import "leafdev.top/Ecosystem/recommender/internal/schema" type Tag struct { - Id schema.EntityId `gorm:"primarykey" json:"id"` - Name string `json:"name"` + Id schema.EntityId `gorm:"primarykey" json:"id"` + Name string `json:"name"` + Application *Application + ApplicationId schema.EntityId `json:"application_id"` } func (u *Tag) TableName() string { diff --git a/internal/entity/TagMapping.go b/internal/entity/TagMapping.go index 96e7ef8..5bc39f9 100644 --- a/internal/entity/TagMapping.go +++ b/internal/entity/TagMapping.go @@ -3,10 +3,12 @@ package entity import "leafdev.top/Ecosystem/recommender/internal/schema" type TagMapping struct { - Id schema.EntityId `gorm:"primarykey" json:"id"` - TagId *schema.EntityId `gorm:"primarykey" json:"tag_id"` - Tag *Tag `json:"tag"` - Name string `json:"name"` + Id schema.EntityId `gorm:"primarykey" json:"id"` + TagId *schema.EntityId `gorm:"primarykey" json:"tag_id"` + Tag *Tag `json:"tag"` + Name string `json:"name"` + Application *Application + ApplicationId schema.EntityId `json:"application_id"` } func (u *TagMapping) TableName() string { diff --git a/internal/handler/http/controller/application_v1/content.go b/internal/handler/http/controller/application_v1/applications.go similarity index 88% rename from internal/handler/http/controller/application_v1/content.go rename to internal/handler/http/controller/application_v1/applications.go index 9d7cd63..d5a9784 100644 --- a/internal/handler/http/controller/application_v1/content.go +++ b/internal/handler/http/controller/application_v1/applications.go @@ -27,8 +27,8 @@ func NewApplicationController(authService *auth.Service, applicationService *app // @Accept json // @Produce json // @Security ApiKeyAuth -// @Success 200 {object} schema.ResponseBody{data=entity.Application} -// @Failure 400 {object} schema.ResponseBody +// @Success 200 {object} response.ResponseBody{data=entity.Application} +// @Failure 400 {object} response.ResponseBody // @Router /applications/v1/info [get] func (ac *ApplicationController) Info(c *gin.Context) { app, err := ac.authService.GetApplication(c) diff --git a/internal/handler/http/controller/application_v1/categories.go b/internal/handler/http/controller/application_v1/categories.go new file mode 100644 index 0000000..b501430 --- /dev/null +++ b/internal/handler/http/controller/application_v1/categories.go @@ -0,0 +1,180 @@ +package application_v1 + +import ( + "github.com/gin-gonic/gin" + "github.com/iVampireSP/pkg/page" + "leafdev.top/Ecosystem/recommender/internal/entity" + "leafdev.top/Ecosystem/recommender/internal/handler/http/request" + "leafdev.top/Ecosystem/recommender/internal/handler/http/response" + "leafdev.top/Ecosystem/recommender/internal/service/application" + "leafdev.top/Ecosystem/recommender/internal/service/auth" + "leafdev.top/Ecosystem/recommender/internal/service/category" + "leafdev.top/Ecosystem/recommender/internal/service/post" + "net/http" +) + +type CategoryController struct { + authService *auth.Service + applicationService *application.Service + postService *post.Service + categoryService *category.Service +} + +func NewCategoryController( + authService *auth.Service, + applicationService *application.Service, + postService *post.Service, + categoryService *category.Service, +) *CategoryController { + return &CategoryController{ + authService, + applicationService, + postService, + categoryService, + } +} + +// List godoc +// @Summary 列出分类 +// @Description 列出分类 +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Success 200 {object} response.ResponseBody{data=page.PagedResult[entity.Category]} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/categories [get] +func (cc *CategoryController) List(c *gin.Context) { + app, err := cc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var pagedResult = page.NewPagedResult[*entity.Category]() + + err = cc.categoryService.ListCategories(c, pagedResult, app) + + response.Ctx(c).Data(pagedResult.Output()).Send() + return +} + +// Save godoc +// @Summary 新建分类 +// @Description 新建分类 +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param CategorySaveRequest body request.CategorySaveRequest true "body" +// @Success 200 {object} response.ResponseBody{data=entity.Application} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/categories [post] +func (cc *CategoryController) Save(c *gin.Context) { + app, err := cc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var categorySaveRequest = &request.CategorySaveRequest{} + if err := c.ShouldBindJSON(categorySaveRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var categoryEntity = &entity.Category{ + Name: categorySaveRequest.Name, + ApplicationId: app.Id, + } + + err = cc.categoryService.CreateCategory(c, categoryEntity, app) + + response.Ctx(c).Error(err).Data(categoryEntity).Send() + return +} + +// Delete godoc +// @Summary 删除分类 +// @Description 删除分类 +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param CategoryIdRequest path request.CategoryIdRequest true "CategoryIdRequest" +// @Success 200 {object} response.ResponseBody{data=entity.Category} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/categories/{category_id} [delete] +func (cc *CategoryController) Delete(c *gin.Context) { + app, err := cc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var categoryIdRequest = &request.CategoryIdRequest{} + if err := c.ShouldBindUri(categoryIdRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + categoryEntity, err := cc.categoryService.GetCategoryById(c, categoryIdRequest.CategoryId) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + if categoryEntity.ApplicationId != app.Id { + response.Ctx(c).Status(http.StatusForbidden).Send() + return + } + + err = cc.categoryService.DeleteCategory(c, categoryEntity) + if err != nil { + response.Ctx(c).Error(err).Send() + } + + response.Ctx(c).Status(http.StatusNoContent).Send() + + return +} + +// Get godoc +// @Summary 获取 Post +// @Description 获取 Post +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param CategoryIdRequest path request.CategoryIdRequest true "CategoryIdRequest" +// @Success 200 {object} response.ResponseBody{data=entity.Category} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/categories/{category_id} [get] +func (cc *CategoryController) Get(c *gin.Context) { + app, err := cc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var categoryIdRequest = &request.CategoryIdRequest{} + if err := c.ShouldBindUri(categoryIdRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + categoryEntity, err := cc.categoryService.GetCategoryById(c, categoryIdRequest.CategoryId) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + if categoryEntity.ApplicationId != app.Id { + response.Ctx(c).Status(http.StatusForbidden).Send() + return + } + + response.Ctx(c).Data(categoryEntity).Send() + + return +} diff --git a/internal/handler/http/controller/application_v1/posts.go b/internal/handler/http/controller/application_v1/posts.go new file mode 100644 index 0000000..1aa4590 --- /dev/null +++ b/internal/handler/http/controller/application_v1/posts.go @@ -0,0 +1,195 @@ +package application_v1 + +import ( + "github.com/gin-gonic/gin" + "github.com/iVampireSP/pkg/page" + "leafdev.top/Ecosystem/recommender/internal/entity" + "leafdev.top/Ecosystem/recommender/internal/handler/http/request" + "leafdev.top/Ecosystem/recommender/internal/handler/http/response" + "leafdev.top/Ecosystem/recommender/internal/service/application" + "leafdev.top/Ecosystem/recommender/internal/service/auth" + "leafdev.top/Ecosystem/recommender/internal/service/category" + "leafdev.top/Ecosystem/recommender/internal/service/post" + "net/http" +) + +type PostController struct { + authService *auth.Service + applicationService *application.Service + postService *post.Service + categoryService *category.Service +} + +func NewPostController( + authService *auth.Service, + applicationService *application.Service, + postService *post.Service, + categoryService *category.Service, +) *PostController { + return &PostController{ + authService, + applicationService, + postService, + categoryService, + } +} + +// List godoc +// @Summary 列出 Posts +// @Description 列出 Posts +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Success 200 {object} response.ResponseBody{data=page.PagedResult[entity.Post]} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/posts [get] +func (pc *PostController) List(c *gin.Context) { + app, err := pc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var pagedResult = page.NewPagedResult[*entity.Post]() + + err = pc.postService.ListPosts(c, pagedResult, nil, app) + + response.Ctx(c).Data(pagedResult.Output()).Send() + return +} + +// Save godoc +// @Summary 新建资源 +// @Description 新建资源 +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param PostSaveRequest body request.PostSaveRequest true "body" +// @Success 200 {object} response.ResponseBody{data=entity.Post} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/posts [post] +func (pc *PostController) Save(c *gin.Context) { + app, err := pc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var postSaveRequest = &request.PostSaveRequest{} + if err := c.ShouldBindJSON(postSaveRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + categoryEntity, err := pc.categoryService.GetCategoryById(c, postSaveRequest.CategoryId) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + if categoryEntity.ApplicationId != app.Id { + response.Ctx(c).Status(http.StatusNotFound).Send() + return + } + + var postEntity = &entity.Post{ + Title: postSaveRequest.Title, + Content: postSaveRequest.Content, + TargetId: postSaveRequest.TargetId, + ApplicationId: app.Id, + CategoryId: &categoryEntity.Id, + Processed: false, + } + + err = pc.postService.CreatePost(c, postEntity) + + response.Ctx(c).Error(err).Data(postEntity).Send() + return +} + +// Delete godoc +// @Summary 删除 Post +// @Description 删除 Post +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param postIdRequest path request.PostIdRequest true "post_id" +// @Success 200 {object} response.ResponseBody{data=entity.Post} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/post/{post_id} [delete] +func (pc *PostController) Delete(c *gin.Context) { + app, err := pc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var postIdRequest = &request.PostIdRequest{} + if err := c.ShouldBindUri(postIdRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + postEntity, err := pc.postService.GetPostById(c, postIdRequest.PostId) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + if postEntity.ApplicationId != app.Id { + response.Ctx(c).Status(http.StatusForbidden).Send() + return + } + + err = pc.postService.DeletePost(c, postEntity) + if err != nil { + response.Ctx(c).Error(err).Send() + } + + response.Ctx(c).Status(http.StatusNoContent).Send() + + return +} + +// Get godoc +// @Summary 获取 Post +// @Description 获取 Post +// @Tags application_api +// @Accept json +// @Produce json +// @Security ApiKeyAuth +// @Param postIdRequest path request.PostIdRequest true "post_id" +// @Success 200 {object} response.ResponseBody{data=entity.Post} +// @Failure 400 {object} response.ResponseBody +// @Router /applications/v1/post/{post_id} [get] +func (pc *PostController) Get(c *gin.Context) { + app, err := pc.authService.GetApplication(c) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + var postIdRequest = &request.PostIdRequest{} + if err := c.ShouldBindUri(postIdRequest); err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + postEntity, err := pc.postService.GetPostById(c, postIdRequest.PostId) + if err != nil { + response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() + return + } + + if postEntity.ApplicationId != app.Id { + response.Ctx(c).Status(http.StatusForbidden).Send() + return + } + + response.Ctx(c).Data(postEntity).Send() + + return +} diff --git a/internal/handler/http/controller/v1/applications.go b/internal/handler/http/controller/v1/applications.go index ebfca10..e998016 100644 --- a/internal/handler/http/controller/v1/applications.go +++ b/internal/handler/http/controller/v1/applications.go @@ -2,8 +2,8 @@ package v1 import ( "github.com/gin-gonic/gin" + "leafdev.top/Ecosystem/recommender/internal/handler/http/request" "leafdev.top/Ecosystem/recommender/internal/handler/http/response" - "leafdev.top/Ecosystem/recommender/internal/schema" "leafdev.top/Ecosystem/recommender/internal/service/application" "leafdev.top/Ecosystem/recommender/internal/service/auth" "net/http" @@ -28,8 +28,8 @@ func NewApplicationController(authService *auth.Service, applicationService *app // @Accept json // @Produce json // @Security ApiKeyAuth -// @Success 200 {object} schema.ResponseBody{data=[]entity.Application} -// @Failure 400 {object} schema.ResponseBody +// @Success 200 {object} response.ResponseBody{data=[]entity.Application} +// @Failure 400 {object} response.ResponseBody // @Router /api/v1/applications [get] func (u *ApplicationController) List(c *gin.Context) { user := u.authService.GetUser(c) @@ -49,14 +49,14 @@ func (u *ApplicationController) List(c *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param ApplicationCreateRequest body schema.ApplicationCreateRequest true "创建应用程序的请求" -// @Success 200 {object} schema.ResponseBody{data=[]entity.Application} -// @Failure 400 {object} schema.ResponseBody +// @Param ApplicationCreateRequest body request.ApplicationCreateRequest true "创建应用程序的请求" +// @Success 200 {object} response.ResponseBody{data=[]entity.Application} +// @Failure 400 {object} response.ResponseBody // @Router /api/v1/applications [post] func (u *ApplicationController) Save(c *gin.Context) { user := u.authService.GetUser(c) - var applicationCreateRequest = &schema.ApplicationCreateRequest{} + var applicationCreateRequest = &request.ApplicationCreateRequest{} if err := c.ShouldBindJSON(applicationCreateRequest); err != nil { response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() return @@ -78,14 +78,14 @@ func (u *ApplicationController) Save(c *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param application_id path schema.ApplicationId true "应用程序 ID" -// @Success 200 {object} schema.ResponseBody{data=[]entity.ApplicationToken} -// @Failure 400 {object} schema.ResponseBody +// @Param application_id path request.ApplicationId true "应用程序 ID" +// @Success 200 {object} response.ResponseBody{data=[]entity.ApplicationToken} +// @Failure 400 {object} response.ResponseBody // @Router /api/v1/application/{application_id}/tokens [get] func (u *ApplicationController) ListToken(c *gin.Context) { user := u.authService.GetUser(c) - var applicationRequest = &schema.ApplicationId{} + var applicationRequest = &request.ApplicationId{} if err := c.ShouldBindUri(applicationRequest); err != nil { response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() return @@ -118,15 +118,15 @@ func (u *ApplicationController) ListToken(c *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param application_id path schema.ApplicationId true "应用程序 ID" -// @Param ApplicationCreateRequest body schema.ApplicationCreateRequest true "创建应用程序的请求" -// @Success 200 {object} schema.ResponseBody{data=entity.ApplicationToken} -// @Failure 400 {object} schema.ResponseBody +// @Param application_id path request.ApplicationId true "应用程序 ID" +// @Param ApplicationCreateRequest body request.ApplicationCreateRequest true "创建应用程序的请求" +// @Success 200 {object} response.ResponseBody{data=entity.ApplicationToken} +// @Failure 400 {object} response.ResponseBody // @Router /api/v1/application/{application_id}/tokens [post] func (u *ApplicationController) SaveToken(c *gin.Context) { user := u.authService.GetUser(c) - var applicationRequest = &schema.ApplicationId{} + var applicationRequest = &request.ApplicationId{} if err := c.ShouldBindUri(applicationRequest); err != nil { response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send() return diff --git a/internal/handler/http/provider.go b/internal/handler/http/provider.go index 011cb54..b7b9541 100644 --- a/internal/handler/http/provider.go +++ b/internal/handler/http/provider.go @@ -15,6 +15,8 @@ var ProviderSet = wire.NewSet( NewMiddleware, v1.NewApplicationController, application_v1.NewApplicationController, + application_v1.NewPostController, + application_v1.NewCategoryController, NewHandler, ) @@ -40,16 +42,22 @@ func NewMiddleware( } type Handlers struct { - ApplicationController *v1.ApplicationController - ApplicationApi *application_v1.ApplicationController + ApplicationController *v1.ApplicationController + ApplicationApi *application_v1.ApplicationController + ApplicationPostApi *application_v1.PostController + ApplicationCategoryApi *application_v1.CategoryController } func NewHandler( applicationController *v1.ApplicationController, applicationApiController *application_v1.ApplicationController, + applicationPostApi *application_v1.PostController, + applicationCategoryApi *application_v1.CategoryController, ) *Handlers { return &Handlers{ - ApplicationController: applicationController, - ApplicationApi: applicationApiController, + ApplicationController: applicationController, + ApplicationApi: applicationApiController, + ApplicationPostApi: applicationPostApi, + ApplicationCategoryApi: applicationCategoryApi, } } diff --git a/internal/handler/http/request/applications.go b/internal/handler/http/request/applications.go new file mode 100644 index 0000000..a9791c6 --- /dev/null +++ b/internal/handler/http/request/applications.go @@ -0,0 +1,11 @@ +package request + +import "leafdev.top/Ecosystem/recommender/internal/schema" + +type ApplicationId struct { + ApplicationId schema.EntityId `json:"application_id" uri:"application_id"` +} + +type ApplicationCreateRequest struct { + Name string `json:"name"` +} diff --git a/internal/handler/http/request/categories.go b/internal/handler/http/request/categories.go new file mode 100644 index 0000000..766731b --- /dev/null +++ b/internal/handler/http/request/categories.go @@ -0,0 +1,11 @@ +package request + +import "leafdev.top/Ecosystem/recommender/internal/schema" + +type CategorySaveRequest struct { + Name string `json:"name" binding:"required"` +} + +type CategoryIdRequest struct { + CategoryId schema.EntityId `json:"category_id" uri:"category_id" form:"category_id" binding:"required"` +} diff --git a/internal/handler/http/request/posts.go b/internal/handler/http/request/posts.go new file mode 100644 index 0000000..592e2a9 --- /dev/null +++ b/internal/handler/http/request/posts.go @@ -0,0 +1,14 @@ +package request + +import "leafdev.top/Ecosystem/recommender/internal/schema" + +type PostSaveRequest struct { + Title string `json:"title" binding:"required"` + Content string `json:"content" binding:"required"` + CategoryId schema.EntityId `json:"category_id" binding:"required"` + TargetId string `json:"target_id" binding:"required"` +} + +type PostIdRequest struct { + PostId schema.EntityId `json:"post_id" uri:"post_id" form:"post_id" binding:"required"` +} diff --git a/internal/schema/http.go b/internal/handler/http/response/http.go similarity index 91% rename from internal/schema/http.go rename to internal/handler/http/response/http.go index 9c60d09..e9fbc6d 100644 --- a/internal/schema/http.go +++ b/internal/handler/http/response/http.go @@ -1,4 +1,4 @@ -package schema +package response type ResponseBody struct { Message string `json:"message"` diff --git a/internal/handler/http/response/response.go b/internal/handler/http/response/response.go index 227978f..46ee400 100644 --- a/internal/handler/http/response/response.go +++ b/internal/handler/http/response/response.go @@ -2,19 +2,18 @@ package response import ( "github.com/gin-gonic/gin" - "leafdev.top/Ecosystem/recommender/internal/schema" "net/http" ) type HttpResponse struct { - body *schema.ResponseBody + body *ResponseBody httpStatus int ctx *gin.Context } func NewResponse() *HttpResponse { return &HttpResponse{ - body: &schema.ResponseBody{ + body: &ResponseBody{ Wrap: true, }, httpStatus: 0, @@ -125,7 +124,7 @@ func (r *HttpResponse) Abort() { func Ctx(c *gin.Context) *HttpResponse { return &HttpResponse{ - body: &schema.ResponseBody{ + body: &ResponseBody{ Wrap: true, }, ctx: c, diff --git a/internal/migrations/1_setup.sql b/internal/migrations/1_setup.sql index 0b61a4a..3783b59 100644 --- a/internal/migrations/1_setup.sql +++ b/internal/migrations/1_setup.sql @@ -42,7 +42,7 @@ CREATE TABLE `tags` foreign key (application_id) references applications (id) on delete cascade ); -CREATE TABLE `tag_mapping` +CREATE TABLE `tag_mappings` ( id serial NOT NULL, tag_id bigint unsigned NOT NULL, @@ -63,11 +63,13 @@ CREATE TABLE `posts` content LONGTEXT NOT NULL, application_id bigint unsigned NOT NULL, processed BOOLEAN DEFAULT FALSE, + category_id bigint unsigned, created_at timestamp DEFAULT CURRENT_TIMESTAMP, updated_at timestamp DEFAULT CURRENT_TIMESTAMP, index (target_id, processed, application_id), primary key (id), - foreign key (application_id) references applications (id) on delete cascade + foreign key (application_id) references applications (id) on delete cascade, + foreign key (category_id) references categories (id) ); CREATE TABLE `post_tags` @@ -111,7 +113,7 @@ CREATE TABLE `user_likes` -- +goose Down -DROP TABLE IF EXISTS `tag_mapping`; +DROP TABLE IF EXISTS `tag_mappings`; DROP TABLE IF EXISTS `user_tag_scores`; DROP TABLE IF EXISTS `post_tags`; DROP TABLE IF EXISTS `user_likes`; diff --git a/internal/router/api.go b/internal/router/api.go index e11fd86..bd94401 100644 --- a/internal/router/api.go +++ b/internal/router/api.go @@ -36,4 +36,13 @@ func (a *Api) InitNoAuthApiRouter(r *gin.RouterGroup) { func (a *Api) InitApplicationApi(r *gin.RouterGroup) { r.GET("/info", a.h.ApplicationApi.Info) + r.GET("/posts", a.h.ApplicationPostApi.List) + r.POST("/posts", a.h.ApplicationPostApi.Save) + r.DELETE("/post/:post_id", a.h.ApplicationPostApi.Delete) + r.GET("/post/:post_id", a.h.ApplicationPostApi.Get) + + r.GET("/categories", a.h.ApplicationCategoryApi.List) + r.POST("/categories", a.h.ApplicationCategoryApi.Save) + r.GET("/categories/:category_id", a.h.ApplicationCategoryApi.Get) + r.DELETE("/categories/:category_id", a.h.ApplicationCategoryApi.Delete) } diff --git a/internal/schema/applications.go b/internal/schema/applications.go deleted file mode 100644 index 388ecc9..0000000 --- a/internal/schema/applications.go +++ /dev/null @@ -1,9 +0,0 @@ -package schema - -type ApplicationId struct { - ApplicationId EntityId `json:"application_id" uri:"application_id"` -} - -type ApplicationCreateRequest struct { - Name string `json:"name"` -} diff --git a/internal/schema/stream.go b/internal/schema/stream.go new file mode 100644 index 0000000..d554ff2 --- /dev/null +++ b/internal/schema/stream.go @@ -0,0 +1,22 @@ +package schema + +import "encoding/json" + +type EventMessage interface { + JSON() ([]byte, error) +} + +type ProcessPostRequest struct { + EventMessage + PostId string `json:"post_id"` + Content string `json:"content"` +} + +func (p *ProcessPostRequest) JSON() ([]byte, error) { + return json.Marshal(p) +} + +type ProcessPostResult struct { + PostId string `json:"post_id"` + Keywords []string `json:"keywords"` +} diff --git a/internal/service/category/categories.go b/internal/service/category/categories.go new file mode 100644 index 0000000..5dfa49d --- /dev/null +++ b/internal/service/category/categories.go @@ -0,0 +1,42 @@ +package category + +import ( + "context" + "github.com/iVampireSP/pkg/page" + "leafdev.top/Ecosystem/recommender/internal/entity" + "leafdev.top/Ecosystem/recommender/internal/schema" +) + +func (s *Service) ListCategories(c context.Context, pagedResult *page.PagedResult[*entity.Category], application *entity.Application) error { + var q = s.dao.WithContext(c).Category + + if application != nil { + q = q.Preload(s.dao.Category.Application).Where(s.dao.Category.ApplicationId.Eq(application.Id.Uint())) + } + + posts, count, err := q.FindByPage(pagedResult.Offset(), pagedResult.PageSize) + if err != nil { + return err + } + + pagedResult.Data = posts + pagedResult.TotalCount = count + + return nil +} + +func (s *Service) CreateCategory(c context.Context, category *entity.Category, applicationEntity *entity.Application) error { + category.ApplicationId = applicationEntity.Id + + return s.dao.WithContext(c).Category.Create(category) +} + +func (s *Service) DeleteCategory(c context.Context, category *entity.Category) error { + _, err := s.dao.WithContext(c).Category.Delete(category) + + return err +} + +func (s *Service) GetCategoryById(c context.Context, categoryId schema.EntityId) (*entity.Category, error) { + return s.dao.WithContext(c).Category.Where(s.dao.Category.Id.Eq(categoryId.Uint())).First() +} diff --git a/internal/service/category/provider.go b/internal/service/category/provider.go new file mode 100644 index 0000000..3865a69 --- /dev/null +++ b/internal/service/category/provider.go @@ -0,0 +1,11 @@ +package category + +import "leafdev.top/Ecosystem/recommender/internal/dao" + +type Service struct { + dao *dao.Query +} + +func NewService(dao *dao.Query) *Service { + return &Service{dao: dao} +} diff --git a/internal/service/post/posts.go b/internal/service/post/posts.go new file mode 100644 index 0000000..2086e25 --- /dev/null +++ b/internal/service/post/posts.go @@ -0,0 +1,53 @@ +package post + +import ( + "context" + "github.com/iVampireSP/pkg/page" + "leafdev.top/Ecosystem/recommender/internal/entity" + "leafdev.top/Ecosystem/recommender/internal/schema" +) + +func (s *Service) ListPosts(c context.Context, pagedResult *page.PagedResult[*entity.Post], category *entity.Category, application *entity.Application) error { + var q = s.dao.WithContext(c).Post + + if application != nil { + q = q.Preload(s.dao.Post.Application).Where(s.dao.Post.ApplicationId.Eq(application.Id.Uint())) + } + + if category != nil { + q = q.Preload(s.dao.Post.Category).Where(s.dao.Post.CategoryId.Eq(category.Id.Uint())) + } + + posts, count, err := q.FindByPage(pagedResult.Offset(), pagedResult.PageSize) + if err != nil { + return err + } + + pagedResult.Data = posts + pagedResult.TotalCount = count + + return nil +} + +func (s *Service) CreatePost(c context.Context, post *entity.Post) error { + err := s.dao.WithContext(c).Post.Create(post) + + if err != nil { + return err + } + + return s.stream.SendEvent(c, s.config.Kafka.WorkerTopic, &schema.ProcessPostRequest{ + PostId: post.Id.String(), + Content: post.Content, + }) +} + +func (s *Service) DeletePost(c context.Context, post *entity.Post) error { + _, err := s.dao.WithContext(c).Post.Delete(post) + + return err +} + +func (s *Service) GetPostById(c context.Context, postId schema.EntityId) (*entity.Post, error) { + return s.dao.WithContext(c).Post.Preload(s.dao.Post.Application).Where(s.dao.Post.Id.Eq(postId.Uint())).First() +} diff --git a/internal/service/post/provider.go b/internal/service/post/provider.go new file mode 100644 index 0000000..423bc85 --- /dev/null +++ b/internal/service/post/provider.go @@ -0,0 +1,21 @@ +package post + +import ( + "leafdev.top/Ecosystem/recommender/internal/base/conf" + "leafdev.top/Ecosystem/recommender/internal/dao" + "leafdev.top/Ecosystem/recommender/internal/service/stream" +) + +type Service struct { + dao *dao.Query + config *conf.Config + stream *stream.Service +} + +func NewService(dao *dao.Query, config *conf.Config, stream *stream.Service) *Service { + return &Service{ + dao: dao, + config: config, + stream: stream, + } +} diff --git a/internal/service/post/tags.go b/internal/service/post/tags.go new file mode 100644 index 0000000..2fabfc0 --- /dev/null +++ b/internal/service/post/tags.go @@ -0,0 +1,69 @@ +package post + +import ( + "context" + "leafdev.top/Ecosystem/recommender/internal/entity" +) + +func (s *Service) GetTag(c context.Context, name string, applicationEntity *entity.Application) (*entity.Tag, error) { + var tmq = s.dao.WithContext(c).TagMapping.Where(s.dao.TagMapping.Name.Eq(name)). + Where(s.dao.TagMapping.ApplicationId.Eq(applicationEntity.Id.Uint())) + tmqCount, err := tmq.Count() + if err != nil { + return nil, err + } + + if tmqCount > 0 { + r, err := tmq.First() + + if err != nil { + return nil, err + } + + return r.Tag, nil + } + + return s.dao.WithContext(c).Tag.Where(s.dao.Tag.Name.Eq(name)). + Where(s.dao.Tag.ApplicationId.Eq(applicationEntity.Id.Uint())). + FirstOrCreate() +} + +// Has Bind +func (s *Service) HasBindTag(c context.Context, post *entity.Post, tagName string) (bool, error) { + tag, err := s.GetTag(c, tagName, post.Application) + if err != nil { + return false, err + } + + count, err := s.dao.WithContext(c).PostTag.Where(s.dao.PostTag.PostId.Eq(post.Id.Uint())). + Where(s.dao.PostTag.TagId.Eq(tag.Id.Uint())).Count() + if err != nil { + return false, err + } + + return count > 0, nil +} + +func (s *Service) BindTag(c context.Context, post *entity.Post, tagName string) error { + tag, err := s.GetTag(c, tagName, post.Application) + + if err != nil { + return err + } + + bind, err := s.HasBindTag(c, post, tag.Name) + if !bind { + err = s.dao.WithContext(c).PostTag.Create(&entity.PostTag{ + PostId: &post.Id, + TagId: &tag.Id, + }) + } + + return err +} + +func (s *Service) MarkAsProcessed(c context.Context, post *entity.Post) error { + _, err := s.dao.WithContext(c).Post.Where(s.dao.Post.Id.Eq(post.Id.Uint())).Update(s.dao.Post.Processed, true) + + return err +} diff --git a/internal/service/provider.go b/internal/service/provider.go index 7c85785..b3b3bc1 100644 --- a/internal/service/provider.go +++ b/internal/service/provider.go @@ -4,7 +4,10 @@ import ( "leafdev.top/Ecosystem/recommender/internal/base/logger" "leafdev.top/Ecosystem/recommender/internal/service/application" "leafdev.top/Ecosystem/recommender/internal/service/auth" + "leafdev.top/Ecosystem/recommender/internal/service/category" "leafdev.top/Ecosystem/recommender/internal/service/jwks" + "leafdev.top/Ecosystem/recommender/internal/service/post" + "leafdev.top/Ecosystem/recommender/internal/service/stream" "github.com/google/wire" ) @@ -12,27 +15,39 @@ import ( type Service struct { logger *logger.Logger Jwks *jwks.JWKS + Stream *stream.Service Auth *auth.Service Application *application.Service + Post *post.Service + Category *category.Service } var Provider = wire.NewSet( jwks.NewJWKS, + stream.NewService, auth.NewAuthService, application.NewService, + post.NewService, + category.NewService, NewService, ) func NewService( logger *logger.Logger, jwks *jwks.JWKS, + stream *stream.Service, auth *auth.Service, application *application.Service, + post *post.Service, + category *category.Service, ) *Service { return &Service{ logger, jwks, + stream, auth, application, + post, + category, } } diff --git a/internal/service/stream/consumer.go b/internal/service/stream/consumer.go new file mode 100644 index 0000000..f70838a --- /dev/null +++ b/internal/service/stream/consumer.go @@ -0,0 +1,68 @@ +package stream + +import ( + "context" + "fmt" + "github.com/segmentio/kafka-go" + "time" +) + +//func (s *Service) Listen(topic string, handler HandlerFunc) error { +// conn, err := s.dial(topic) +// if err != nil { +// return err +// } +// +// batch := conn.ReadBatch(10e3, 1e6) // fetch 10KB min, 1MB max +// +// b := make([]byte, 10e3) // 10KB max per message +// for { +// n, err := batch.Read(b) +// if err != nil { +// return err +// } +// handler(b[:n]) +// } +//} + +func (s *Service) Consumer(topic string, groupId string) *kafka.Reader { + var r = kafka.ReaderConfig{ + Brokers: s.config.Kafka.BootstrapServers, + GroupID: groupId, + GroupTopics: nil, + Topic: topic, + CommitInterval: time.Second, + //StartOffset: kafka., // 仅对新创建的消费者组生效,从头开始消费,工作中可能更常用从最新的开始消费kafka.LastOffset + Logger: nil, + ErrorLogger: nil, + IsolationLevel: 0, + MaxAttempts: 0, + OffsetOutOfRangeError: false, + MaxBytes: 10e6, // 10MB + } + + if s.config.Kafka.Username != "" && s.config.Kafka.Password != "" { + r.Dialer = &kafka.Dialer{ + Timeout: 10 * time.Second, + DualStack: true, + SASLMechanism: s.auth(), + } + } + + return kafka.NewReader(r) + +} + +type HandlerFunc func([]byte) + +// ReadMessage 消费消息 +func (s *Service) ReadMessage(ctx context.Context, topic string, groupId string) { + for { + if msg, err := s.Consumer(topic, groupId).ReadMessage(ctx); err != nil { + fmt.Println(fmt.Sprintf("读kafka失败,err:%v", err)) + continue + } else { + fmt.Println(fmt.Sprintf("topic=%s,partition=%d,offset=%d,key=%s,value=%s", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value)) + } + } +} diff --git a/internal/service/stream/kafka.go b/internal/service/stream/kafka.go new file mode 100644 index 0000000..60149ee --- /dev/null +++ b/internal/service/stream/kafka.go @@ -0,0 +1,13 @@ +package stream + +import ( + "github.com/segmentio/kafka-go/sasl/plain" +) + +func (s *Service) auth() plain.Mechanism { + mechanism := plain.Mechanism{ + Username: s.config.Kafka.Username, + Password: s.config.Kafka.Password, + } + return mechanism +} diff --git a/internal/service/stream/producer.go b/internal/service/stream/producer.go new file mode 100644 index 0000000..9812231 --- /dev/null +++ b/internal/service/stream/producer.go @@ -0,0 +1,119 @@ +package stream + +import ( + "context" + "github.com/segmentio/kafka-go" + "leafdev.top/Ecosystem/recommender/internal/schema" + "time" +) + +//var connections = map[string]*kafka.Conn{} + +//func (s *Service) dial(topic string) (*kafka.Conn, error) { +// +// // 如果topic 存在于 connections 则直接返回 +// if conn, ok := connections[topic]; ok { +// return conn, nil +// } +// +// ctx := context.Background() +// +// conn, err := kafka.DialLeader(ctx, "tcp", s.config.Kafka.BootstrapServers[0], s.topic(topic), 0) +// if err != nil { +// return nil, err +// } +// //err = conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) +// //if err != nil { +// // return conn, err +// //} +// // +// //// set read deadline +// //err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) +// //if err != nil { +// // return conn, err +// //} +// +// connections[topic] = conn +// +// return conn, nil +//} +// +//func (s *Service) Publish(topic string, message ...[]byte) error { +// conn, err := s.dial(topic) +// if err != nil { +// return err +// } +// +// msg := make([]kafka.Message, len(message)) +// for i, v := range message { +// msg[i] = kafka.Message{Value: v} +// } +// +// _, err = conn.WriteMessages(msg...) +// +// return err +//} + +func (s *Service) Producer(topic string) *kafka.Writer { + var w = &kafka.Writer{ + Addr: kafka.TCP(s.config.Kafka.BootstrapServers...), + Topic: topic, + Balancer: &kafka.Hash{}, // 用于对key进行hash,决定消息发送到哪个分区 + MaxAttempts: 0, + WriteBackoffMin: 0, + WriteBackoffMax: 0, + BatchSize: 0, + BatchBytes: 0, + BatchTimeout: 0, + ReadTimeout: 0, + //WriteTimeout: time.Second, // kafka有时候可能负载很高,写不进去,那么超时后可以放弃写入,用于可以丢消息的场景 + RequiredAcks: kafka.RequireAll, // 不需要任何节点确认就返回 + Async: true, + Completion: nil, + Compression: 0, + Logger: nil, + ErrorLogger: nil, + Transport: nil, + AllowAutoTopicCreation: false, // 第一次发消息的时候,如果topic不存在,就自动创建topic,工作中禁止使用 + } + + if s.config.Kafka.Username != "" && s.config.Kafka.Password != "" { + w.Transport = &kafka.Transport{ + SASL: s.auth(), + } + } + + return w +} + +func (s *Service) SendMessage(ctx context.Context, topic string, data []byte) error { + msg := kafka.Message{ + Partition: 0, + Offset: 0, + HighWaterMark: 0, + //Key: key, + Value: data, + Time: time.Time{}, + } + + err := s.Producer(topic).WriteMessages(ctx, msg) + return err +} + +func (s *Service) SendEvent(ctx context.Context, topic string, data schema.EventMessage) error { + j, err := data.JSON() + if err != nil { + return err + } + + msg := kafka.Message{ + Partition: 0, + Offset: 0, + HighWaterMark: 0, + Value: j, + Time: time.Time{}, + } + + err = s.Producer(topic).WriteMessages(ctx, msg) + return err +} diff --git a/internal/service/stream/provider.go b/internal/service/stream/provider.go new file mode 100644 index 0000000..8e024a7 --- /dev/null +++ b/internal/service/stream/provider.go @@ -0,0 +1,15 @@ +package stream + +import ( + "leafdev.top/Ecosystem/recommender/internal/base/conf" +) + +type Service struct { + config *conf.Config +} + +func NewService(config *conf.Config) *Service { + return &Service{ + config, + } +}