From 2934edf8a9cda9af96929751c65c5aa9c31d8649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Fri, 3 Apr 2026 11:30:13 +0800 Subject: [PATCH 01/17] =?UTF-8?q?golang=E7=89=88=E6=9C=AC=E5=8D=87?= =?UTF-8?q?=E7=BA=A71.26.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 515b2a6..b2ed5f5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module gitea.com/red-future/common -go 1.25.5 +go 1.26.0 require ( github.com/alibaba/sentinel-golang v1.0.4 From d7d039a423a9bff6c03516ab58764744ca8d8924 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Fri, 3 Apr 2026 15:21:01 +0800 Subject: [PATCH 02/17] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20eino=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81=E5=8F=8A=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 94 +----- go.mod | 58 +--- go.sum | 568 +--------------------------------- rag/eino/base_task.go | 49 --- rag/eino/consts.go | 8 - rag/eino/document_loader.go | 51 --- rag/eino/document_semantic.go | 64 ---- rag/eino/embedding.go | 69 ----- rag/eino/embedding_batch.go | 47 --- rag/eino/embedding_qwen.go | 273 ---------------- rag/eino/priority_enum.go | 11 - rag/eino/status_enum.go | 12 - rag/eino/task_type.go | 14 - rag/gse/utils.go | 114 ------- 14 files changed, 18 insertions(+), 1414 deletions(-) delete mode 100644 rag/eino/base_task.go delete mode 100644 rag/eino/consts.go delete mode 100644 rag/eino/document_loader.go delete mode 100644 rag/eino/document_semantic.go delete mode 100644 rag/eino/embedding.go delete mode 100644 rag/eino/embedding_batch.go delete mode 100644 rag/eino/embedding_qwen.go delete mode 100644 rag/eino/priority_enum.go delete mode 100644 rag/eino/status_enum.go delete mode 100644 rag/eino/task_type.go delete mode 100644 rag/gse/utils.go diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index 3c1c754..c938dac 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -53,7 +53,7 @@ func getFromCache(ctx context.Context, key string) ([]byte, bool) { if err == nil && !result.IsEmpty() { data := result.Bytes() // 写入本地缓存 - err = getLocalCache().Set(ctx, key, data, time.Duration(g.Cfg().MustGet(ctx, "cache.localTTL").Int64())*time.Second) + err = getLocalCache().Set(ctx, key, data, time.Duration(g.Cfg().MustGet(ctx, "cache.localTTL", 60).Int64())*time.Second) if err != nil { return nil, false } @@ -71,7 +71,7 @@ func setToCache(ctx context.Context, key string, data []byte) (err error) { } // 1. 写入本地缓存 - if err = getLocalCache().Set(ctx, key, data, time.Duration(g.Cfg().MustGet(ctx, "cache.localTTL").Int64())*time.Second); err != nil { + if err = getLocalCache().Set(ctx, key, data, time.Duration(g.Cfg().MustGet(ctx, "cache.localTTL", 60).Int64())*time.Second); err != nil { return } @@ -79,7 +79,7 @@ func setToCache(ctx context.Context, key string, data []byte) (err error) { if g.Redis() != nil { _, err = g.Redis().Set(ctx, key, data, gredis.SetOption{ TTLOption: gredis.TTLOption{ - EX: gconv.PtrInt64(g.Cfg().MustGet(ctx, "cache.redisTTL")), + EX: gconv.PtrInt64(g.Cfg().MustGet(ctx, "cache.redisTTL", 300)), }, }) if err != nil { @@ -405,7 +405,7 @@ func getSelectTypeString(selectType gdb.SelectType) string { } // ==================== 调用方法 ==================== - +var TablePrefix string var ( schemaPrefix = "tenant-" cacheKeyPrefix = "cache-" @@ -452,51 +452,26 @@ func checkSchemaConfig(ctx context.Context) (uint64, bool) { return user.TenantId, false } -func DB(ctx context.Context) Gfdb { - tenantId, config := checkSchemaConfig(ctx) - - var schema = fmt.Sprintf("%s%v", schemaPrefix, tenantId) - - var dbName []string - if config { - dbName = append(dbName, schema) +func DB(ctx context.Context, name ...string) Gfdb { + var groupName = gdb.DefaultGroupName + if len(name) > 0 && name[0] != "" { + groupName = name[0] } else { - dbName = append(dbName, "default") - // 配置文件中 default 是数组格式,需要通过索引 0 访问 - defaultConfig := g.Cfg().MustGet(ctx, "database.default") - if defaultConfig.IsSlice() { - schema = g.Cfg().MustGet(ctx, "database.default.0.name").String() - } else { - schema = g.Cfg().MustGet(ctx, "database.default.name").String() + tenantId, config := checkSchemaConfig(ctx) + if config { + groupName = fmt.Sprintf("%s%v", schemaPrefix, tenantId) } } - + db := g.DB(groupName) + TablePrefix = db.GetConfig().Prefix return &dataBase{ - DB: g.DB(dbName...).Schema(schema), + DB: db, } } func (d *dataBase) Model(ctx context.Context, tableNameOrStruct ...any) *model { - - m := d.DB.Model(tableNameOrStruct...).Ctx(ctx) - - tenantId, config := checkSchemaConfig(ctx) - - if config { - // 创建按地区分库的配置 - shardingConfig := gdb.ShardingConfig{ - Schema: gdb.ShardingSchemaConfig{ - Enable: true, // 启用分库 - Prefix: schemaPrefix, // 分库前缀 - Rule: &RegionShardingRule{RegionMapping: tenantId}, // 自定义分库规则 - }, - } - m.Sharding(shardingConfig).ShardingValue(tenantId) - } - - m.OmitNil().Hook(catchSQLHook()) return &model{ - Model: m, + Model: d.DB.Model(tableNameOrStruct...).Ctx(ctx).OmitNil().Hook(catchSQLHook()), } } @@ -542,42 +517,3 @@ func getTraceID(ctx context.Context, prefix string) string { } return "" } - -type RegionShardingRule struct { - RegionMapping uint64 -} - -func (r *RegionShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) { - region, ok := value.(uint64) - if !ok { - return "", fmt.Errorf("sharding value must be string for RegionShardingRule") - } - - if r.RegionMapping == region { - return config.Prefix + gconv.String(region), nil - } - - return "default", nil -} - -// TableName 实现分表规则接口 -func (r *RegionShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) { - // 这里不实现分表,返回空字符串 - return "", nil -} - -func GetTablePrefix(ctx context.Context) (prefix string, err error) { - tenantId, config := checkSchemaConfig(ctx) - if config { - sprintf := fmt.Sprintf("database.%s%v.0.prefix", schemaPrefix, tenantId) - prefix = g.Cfg().MustGet(ctx, sprintf).String() - return - } - defaultConfig := g.Cfg().MustGet(ctx, "database.default") - if defaultConfig.IsSlice() { - prefix = g.Cfg().MustGet(ctx, "database.default.0.prefix").String() - } else { - prefix = g.Cfg().MustGet(ctx, "database.default.prefix").String() - } - return -} diff --git a/go.mod b/go.mod index 515b2a6..38c2379 100644 --- a/go.mod +++ b/go.mod @@ -5,24 +5,9 @@ go 1.25.5 require ( github.com/alibaba/sentinel-golang v1.0.4 github.com/bwmarrin/snowflake v0.3.0 - github.com/cloudwego/eino v0.7.26 - github.com/cloudwego/eino-ext/components/document/loader/url v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/document/parser/docx v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/document/parser/pdf v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/document/parser/xlsx v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/document/transformer/splitter/recursive v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/document/transformer/splitter/semantic v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/embedding/ark v0.1.1 - github.com/cloudwego/eino-ext/components/embedding/dashscope v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/embedding/openai v0.0.0-20260323112355-f061db7e8419 - github.com/cloudwego/eino-ext/components/indexer/es8 v0.0.0-20260331071634-4f359694d2d9 - github.com/cloudwego/eino-ext/components/retriever/es8 v0.0.0-20260331071634-4f359694d2d9 - github.com/elastic/go-elasticsearch/v8 v8.16.0 - github.com/go-ego/gse v1.0.2 github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 github.com/gogf/gf/v2 v2.9.5 - github.com/golang/glog v1.2.5 github.com/google/uuid v1.6.0 github.com/meilisearch/meilisearch-go v0.36.1 github.com/minio/minio-go/v7 v7.0.97 @@ -39,42 +24,25 @@ require ( require ( github.com/BurntSushi/toml v1.5.0 // indirect - github.com/PuerkitoBio/goquery v1.8.1 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/akutz/memconn v0.1.0 // indirect github.com/alitto/pond v1.9.2 // indirect github.com/andybalholm/brotli v1.1.1 // indirect - github.com/andybalholm/cascadia v1.3.1 // indirect github.com/apache/thrift v0.21.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aymerick/douceur v0.2.0 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.1 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cenk/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/cloudwego/eino-ext/components/document/parser/html v0.0.0-20241224063832-9fbcc0e56c28 // indirect - github.com/cloudwego/eino-ext/libs/acl/openai v0.1.14 // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dslipak/pdf v0.0.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edwingeng/doublejump v1.0.1 // indirect - github.com/eino-contrib/docx2md v0.0.1 // indirect - github.com/eino-contrib/jsonschema v1.0.3 // indirect - github.com/elastic/elastic-transport-go/v8 v8.8.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/evanphx/json-patch v0.5.2 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -88,13 +56,12 @@ require ( github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/glog v1.2.5 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf // indirect - github.com/goph/emperror v0.17.2 // indirect - github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grandcat/zeroconf v1.0.0 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect @@ -108,9 +75,7 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/juju/ratelimit v1.0.2 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/kavu/go_reuseport v1.5.0 // indirect @@ -124,24 +89,17 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/meguminnnnnnnnn/go-openai v0.1.1 // indirect - github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/miekg/dns v1.1.63 // indirect github.com/minio/crc64nvme v1.1.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // indirect - github.com/nikolalohinski/gonja v1.5.3 // indirect github.com/olekukonko/errors v1.1.0 // indirect github.com/olekukonko/ll v0.0.9 // indirect github.com/olekukonko/tablewriter v1.1.0 // indirect github.com/onsi/ginkgo/v2 v2.22.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect @@ -151,16 +109,12 @@ require ( github.com/quic-go/quic-go v0.49.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/redis/go-redis/v9 v9.12.1 // indirect - github.com/richardlehane/mscfb v1.0.4 // indirect - github.com/richardlehane/msoleps v1.0.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rpcxio/libkv v0.5.1 // indirect github.com/rs/cors v1.11.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/rubyist/circuitbreaker v2.2.1+incompatible // indirect github.com/shirou/gopsutil/v3 v3.21.6 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect github.com/smallnest/quick v0.2.0 // indirect github.com/smallnest/rsocket v0.0.0-20241130031020-4a72eb6ff62a // indirect github.com/soheilhy/cmux v0.1.5 // indirect @@ -170,22 +124,13 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/valyala/fastrand v1.1.0 // indirect - github.com/vcaesar/cedar v0.30.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/volcengine/volc-sdk-golang v1.0.199 // indirect - github.com/volcengine/volcengine-go-sdk v1.0.181 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xtaci/kcp-go v5.4.20+incompatible // indirect - github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect - github.com/xuri/excelize/v2 v2.9.0 // indirect - github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect - github.com/yargevad/filepathx v1.0.0 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -195,7 +140,6 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/mock v0.5.0 // indirect - golang.org/x/arch v0.15.0 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/mod v0.26.0 // indirect diff --git a/go.sum b/go.sum index 0f09cce..27b7bff 100644 --- a/go.sum +++ b/go.sum @@ -1,58 +1,16 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -66,9 +24,6 @@ github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs= github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= @@ -76,121 +31,50 @@ github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/mockey v1.3.0 h1:ONLRdvhqmCfr9rTasUB8ZKCfvbdD2tohOg4u+4Q/ed0= -github.com/bytedance/mockey v1.3.0/go.mod h1:1BPHF9sol5R1ud/+0VEHGQq/+i2lN+GTsr3O2Q9IENY= -github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= -github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenk/backoff v2.2.1+incompatible h1:djdFT7f4gF2ttuzRKPbMOWgZajgesItGLwG5FTQKmmE= github.com/cenk/backoff v2.2.1+incompatible/go.mod h1:7FtoeaSnHoZnmZzz47cM35Y9nSW7tNyaidugnHTaFDE= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/cloudwego/eino v0.7.26 h1:FD8xnhd8WyV495eJs0Rka8aHnknqj/ljq3ZbwGH/SiQ= -github.com/cloudwego/eino v0.7.26/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ= -github.com/cloudwego/eino-ext/components/document/loader/url v0.0.0-20260323112355-f061db7e8419 h1:dMr31rw5pjZMKMPEvNvpy+1RI3HnqVUWmk2abNkb+yM= -github.com/cloudwego/eino-ext/components/document/loader/url v0.0.0-20260323112355-f061db7e8419/go.mod h1:/IeSk52Hhym5AUjCs3ESTF5Nb0RLYFWW8llSpWuc/JA= -github.com/cloudwego/eino-ext/components/document/parser/docx v0.0.0-20260323112355-f061db7e8419 h1:NkEyzkMvYj1imCNclbL0OeesosinmbU10uW+ZlC0vtA= -github.com/cloudwego/eino-ext/components/document/parser/docx v0.0.0-20260323112355-f061db7e8419/go.mod h1:7Nfu+tf9OkdI8Fy5VRi9Xap13OGVV8SXlpj+GHbaWQ8= -github.com/cloudwego/eino-ext/components/document/parser/html v0.0.0-20241224063832-9fbcc0e56c28 h1:Z1cWrlqxdc5IuPV1UcqoW2BGlFr7IQJHGwn7I3Tax0A= -github.com/cloudwego/eino-ext/components/document/parser/html v0.0.0-20241224063832-9fbcc0e56c28/go.mod h1:e+Hf9OyKXFxAoCTF3thTm2Sz8KDfJ/iiEOHOmADpxRI= -github.com/cloudwego/eino-ext/components/document/parser/pdf v0.0.0-20260323112355-f061db7e8419 h1:kw67tDHJ8vBI4eaB2Gb11kcLFrnQuCX8yFF9onU7CEQ= -github.com/cloudwego/eino-ext/components/document/parser/pdf v0.0.0-20260323112355-f061db7e8419/go.mod h1:kHC3xkGM/gv3IHpOk33p75BfBaEIYATOs2XmYFKffcs= -github.com/cloudwego/eino-ext/components/document/parser/xlsx v0.0.0-20260323112355-f061db7e8419 h1:iCibCaUZNr7BFuqSI2U+fH1MUZCYS5lFjIb/Fytj3V8= -github.com/cloudwego/eino-ext/components/document/parser/xlsx v0.0.0-20260323112355-f061db7e8419/go.mod h1:7aF6WcOvowbYaXUnDY+awRZJ29LHP+4oWhS5ogH36bE= -github.com/cloudwego/eino-ext/components/document/transformer/splitter/recursive v0.0.0-20260323112355-f061db7e8419 h1:y8XB6u0Z/AyNww1BHFZqBCzLmxnBUFtQmqaWT/YH4q8= -github.com/cloudwego/eino-ext/components/document/transformer/splitter/recursive v0.0.0-20260323112355-f061db7e8419/go.mod h1:9R0RQrQSpg1JaNnRtw7+RfRAAv0HgdE348YnrlZ6coo= -github.com/cloudwego/eino-ext/components/document/transformer/splitter/semantic v0.0.0-20260323112355-f061db7e8419 h1:XsvQmwMKMD/w/YIPK0Y9pfBv0UH6rJ5ZiedUuZiA9Vo= -github.com/cloudwego/eino-ext/components/document/transformer/splitter/semantic v0.0.0-20260323112355-f061db7e8419/go.mod h1:Ov33JMUewdOoUgJbYNJt3qL7KQDVHYpoVBCjJXsz8sw= -github.com/cloudwego/eino-ext/components/embedding/ark v0.1.1 h1:PM/+XAvJtrBqFlBY15ws0pb0+92XKHQv0ei3M7PIJcQ= -github.com/cloudwego/eino-ext/components/embedding/ark v0.1.1/go.mod h1:6O6x0fHfM3uCLr3lX1DnB/my7fC3WRUA5hpkCkrkZrg= -github.com/cloudwego/eino-ext/components/embedding/dashscope v0.0.0-20260323112355-f061db7e8419 h1:gGnohcgEaHqp5V826Ay0H3fi4TpK8ReWlUPePAnzvA4= -github.com/cloudwego/eino-ext/components/embedding/dashscope v0.0.0-20260323112355-f061db7e8419/go.mod h1:ekJmA+GLD9vJyZNeODZDBFMiJ92Suy6nF0OY42X3sao= -github.com/cloudwego/eino-ext/components/embedding/openai v0.0.0-20260323112355-f061db7e8419 h1:eM29lyMShtFZNoAhE5g96+zHg9PBLckRyd2HtVeeY4E= -github.com/cloudwego/eino-ext/components/embedding/openai v0.0.0-20260323112355-f061db7e8419/go.mod h1:SajSFFRIXJXIbxadAAlSUIS5KTY8R/jzJg9RNSOXCCI= -github.com/cloudwego/eino-ext/components/indexer/es8 v0.0.0-20260331071634-4f359694d2d9 h1:vZ3dL8xwo2sy73aBVKs4AJiO5OCHRxMOJUwIYkp0CWs= -github.com/cloudwego/eino-ext/components/indexer/es8 v0.0.0-20260331071634-4f359694d2d9/go.mod h1:+oI0sr0rA0OHCxaQJ0rzMYld3LAODHhPKzBx5JYCya0= -github.com/cloudwego/eino-ext/components/retriever/es8 v0.0.0-20260331071634-4f359694d2d9 h1:Sl6giB1SJlA+ZlO0gzPH05IsUORtdYYPN6GiyH1B9MA= -github.com/cloudwego/eino-ext/components/retriever/es8 v0.0.0-20260331071634-4f359694d2d9/go.mod h1:H4kNmiTe2irnvipVNIP4q8yqXf2fZ6v24krvQYBtYb8= -github.com/cloudwego/eino-ext/libs/acl/openai v0.1.14 h1:yOZII6VYaL00CVZYba+HUixFygsW0Xz/1QjQ5htj1Ls= -github.com/cloudwego/eino-ext/libs/acl/openai v0.1.14/go.mod h1:1xMQZ8eE11pkEoTAEy8UlaAY817qGVMvjpDPGSIO3Ns= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -207,80 +91,48 @@ github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8 github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dslipak/pdf v0.0.2 h1:djAvcM5neg9Ush+zR6QXB+VMJzR6TdnX766HPIg1JmI= -github.com/dslipak/pdf v0.0.2/go.mod h1:2L3SnkI9cQwnAS9gfPz2iUoLC0rUZwbucpbKi5R1mUo= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edwingeng/doublejump v1.0.1 h1:wJ6QgNyyF23Of9vw+ThbwJ/obe9KdxaWEg/Brpv5S1o= github.com/edwingeng/doublejump v1.0.1/go.mod h1:ykMWX8JWePtMtk2OGjNE9kwtgpI+SF2FNIyXV4gS36k= -github.com/eino-contrib/docx2md v0.0.1 h1:Clz0sF8jiQRYAIZAUTuTAjh0vF/1KqHQqsMha1ZX4q4= -github.com/eino-contrib/docx2md v0.0.1/go.mod h1:b1dupA9cF5yExHjVMCcP6feyE6mwZjsY7Cc9ESO5Y14= -github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0= -github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4= -github.com/elastic/elastic-transport-go/v8 v8.8.0 h1:7k1Ua+qluFr6p1jfJjGDl97ssJS/P7cHNInzfxgBQAo= -github.com/elastic/elastic-transport-go/v8 v8.8.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= -github.com/elastic/go-elasticsearch/v8 v8.16.0 h1:f7bR+iBz8GTAVhwyFO3hm4ixsz2eMaEy0QroYnXV3jE= -github.com/elastic/go-elasticsearch/v8 v8.16.0/go.mod h1:lGMlgKIbYoRvay3xWBeKahAiJOgmFDsjZC39nmO3H64= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4= -github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 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.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -293,14 +145,10 @@ github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3G github.com/go-redis/redis/v8 v8.8.2/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godzie44/go-uring v0.0.0-20220926161041-69611e8b13d5 h1:5zELAgnSz0gqmr4Q5DWCoOzNHoeBAxVUXB7LS1eG+sw= github.com/godzie44/go-uring v0.0.0-20220926161041-69611e8b13d5/go.mod h1:ermjEDUoT/fS+3Ona5Vd6t6mZkw1eHp99ILO5jGRBkM= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.1 h1:egobo4YfQX3C4NtrEFunBqMX3jsddagklgut9u91+BM= github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.1/go.mod h1:YQ+u5Cs5N2ETCeQaLbv29z/UWYuxw27mJpUkOLj0kJ8= github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 h1:eUqwJ/qNH8lJ6yssiqskazgp1ACQuNU6zXlLOZVuXTQ= @@ -315,32 +163,21 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -349,13 +186,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -368,27 +201,12 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf h1:BvBLUD2hkvLI3dJTJMiopAq8/wp43AAZKTP7qdpptbU= github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -396,24 +214,12 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18= -github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= -github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= -github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -424,17 +230,14 @@ github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWI github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM= github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU= github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -446,7 +249,6 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -470,7 +272,6 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -496,23 +297,9 @@ github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= @@ -522,28 +309,17 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kavu/go_reuseport v1.5.0 h1:UNuiY2OblcqAtVDE8Gsg1kZz8zbBWg907sP1ceBV+bk= github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -558,7 +334,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -595,25 +370,16 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/meguminnnnnnnnn/go-openai v0.1.1 h1:u/IMMgrj/d617Dh/8BKAwlcstD74ynOJzCtVl+y8xAs= -github.com/meguminnnnnnnnn/go-openai v0.1.1/go.mod h1:qs96ysDmxhE4BZoU45I43zcyfnaYxU3X+aRzLko/htY= github.com/meilisearch/meilisearch-go v0.36.1 h1:mJTCJE5g7tRvaqKco6DfqOuJEjX+rRltDEnkEC02Y0M= github.com/meilisearch/meilisearch-go v0.36.1/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= -github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q= github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= 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.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ= @@ -629,43 +395,27 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U= github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c= -github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= @@ -679,18 +429,14 @@ github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5Bn github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= @@ -698,28 +444,22 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -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/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1 h1:5Dl+ADmsGerAqHwWzyLqkNaUBQ+48DQwfDCaW1gHAQM= github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -737,7 +477,6 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -754,8 +493,6 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -764,8 +501,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= @@ -777,20 +512,13 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= -github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= -github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= -github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= +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/rpcxio/libkv v0.5.1 h1:M0/QqwTcdXz7us0NB+2i8Kq5+wikTm7zZ4Hyb/jNgME= github.com/rpcxio/libkv v0.5.1/go.mod h1:zHGgtLr3cFhGtbalum0BrMPOjhFZFJXCKiws/25ewls= github.com/rpcxio/rpcx-consul v0.1.1 h1:z/IHpIytgChEuHndWlpo4BY0V0mVBSg/XsKsc54f0iU= @@ -813,52 +541,34 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f h1:Z2cODYsUxQPofhpYRMQVwWz4yUVpHF+vPi+eUdruUYI= -github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f/go.mod h1:JqzWyvTuI2X4+9wOHmKSQCYxybB/8j6Ko43qVmXDuZg= github.com/smallnest/quick v0.2.0 h1:AEvm7ZovZ6Utv+asFDBh866G4ufMNhRNMKbZHVMFYPE= github.com/smallnest/quick v0.2.0/go.mod h1:ODNivpfZTaMgYrNb/fhDtqoEe2TTPxSRo8JaIT/QThI= github.com/smallnest/rpcx v1.9.1 h1:fGw+qMcDRPm7Ei9fdEfqteYY6qQqgVsoyxoUw6hEXy0= github.com/smallnest/rpcx v1.9.1/go.mod h1:owr4mDCReTn+dy9m5ilof0mBivFBeK0XrkYfZYdDGb4= github.com/smallnest/rsocket v0.0.0-20241130031020-4a72eb6ff62a h1:GI6kCNC5AVFbKA6ZKbVd4r+fk+Z7XZCRQm9LURZY4t4= github.com/smallnest/rsocket v0.0.0-20241130031020-4a72eb6ff62a/go.mod h1:VJeIKKrDEzT4ZNVe87JN9uRLw1XLp/ZnnE9PfsyJ1jY= -github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY= -github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= -github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= @@ -877,36 +587,18 @@ github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefld github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= -github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y= -github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik= -github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= -github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= -github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi+pjq8ILWjLYUc= -github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= -github.com/volcengine/volcengine-go-sdk v1.0.181 h1:/3PB4M1N4fjMqiSKTJwX43EZ5Nn1HUOtQrSCk+22+wI= -github.com/volcengine/volcengine-go-sdk v1.0.181/go.mod h1:gfEDc1s7SYaGoY+WH2dRrS3qiuDJMkwqyfXWCa7+7oA= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -914,39 +606,20 @@ github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZ github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= -github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY= -github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= -github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE= -github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE= -github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= -github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= -github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.mongodb.org/mongo-driver/v2 v2.4.0 h1:Oq6BmUAAFTzMeh6AonuDlgZMuAuEiUxoAD1koK5MuFo= go.mongodb.org/mongo-driver/v2 v2.4.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -969,15 +642,11 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689Cbtr go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/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= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= @@ -985,77 +654,34 @@ go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= -golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1070,69 +696,36 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.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.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1143,233 +736,102 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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-20220503163025-988cb79eb6c6/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= @@ -1379,23 +841,14 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1406,33 +859,25 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1445,16 +890,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/rag/eino/base_task.go b/rag/eino/base_task.go deleted file mode 100644 index b883341..0000000 --- a/rag/eino/base_task.go +++ /dev/null @@ -1,49 +0,0 @@ -package eino - -import ( - "time" - - "gitea.com/red-future/common/beans" -) - -// BaseTask 任务基类 - MongoDB版本 -type BaseTask struct { - beans.MongoBaseDO `bson:",inline"` - // 任务信息 - TaskType TaskType `bson:"taskType" json:"taskType"` - Status TaskStatus `bson:"status" json:"status"` - Priority TaskPriority `bson:"priority,omitempty" json:"priority,omitempty"` - // 进度 - TotalItems int64 `bson:"totalItems" json:"totalItems"` - ProcessedItems int64 `bson:"processedItems" json:"processedItems"` - Progress float64 `bson:"progress" json:"progress"` - // 结果 - StartTime *time.Time `bson:"startTime" json:"startTime"` - EndTime *time.Time `bson:"endTime,omitempty" json:"endTime,omitempty"` - Duration int64 `bson:"duration,omitempty" json:"duration,omitempty"` - SuccessCount int64 `bson:"successCount" json:"successCount"` - FailCount int64 `bson:"failCount" json:"failCount"` - // 其他 - Executor string `bson:"executor,omitempty" json:"executor,omitempty"` -} - -// SQLBaseTask 任务基类 - SQL版本 -type SQLBaseTask struct { - beans.SQLBaseDO - // 任务信息 - TaskType TaskType `json:"taskType"` - Status TaskStatus `json:"status"` - Priority TaskPriority `json:"priority,omitempty"` - // 进度 - TotalItems int64 `json:"totalItems"` - ProcessedItems int64 `json:"processedItems"` - Progress float64 `json:"progress"` - // 结果 - StartTime *time.Time `json:"startTime"` - EndTime *time.Time `json:"endTime,omitempty"` - Duration int64 `json:"duration,omitempty"` - SuccessCount int64 `json:"successCount"` - FailCount int64 `json:"failCount"` - // 其他 - Executor string `json:"executor,omitempty"` -} diff --git a/rag/eino/consts.go b/rag/eino/consts.go deleted file mode 100644 index e766233..0000000 --- a/rag/eino/consts.go +++ /dev/null @@ -1,8 +0,0 @@ -package eino - -const ( - providerArk = "ark" - providerOpenai = "openai" - providerQianfan = "qianfan" - providerDashscope = "dashscope" -) diff --git a/rag/eino/document_loader.go b/rag/eino/document_loader.go deleted file mode 100644 index c5b7f65..0000000 --- a/rag/eino/document_loader.go +++ /dev/null @@ -1,51 +0,0 @@ -package eino - -import ( - "context" - "fmt" - - "gitea.com/red-future/common/utils" - "github.com/cloudwego/eino-ext/components/document/loader/url" - "github.com/cloudwego/eino-ext/components/document/parser/docx" - "github.com/cloudwego/eino-ext/components/document/parser/pdf" - "github.com/cloudwego/eino-ext/components/document/parser/xlsx" - "github.com/cloudwego/eino/components/document" - "github.com/cloudwego/eino/components/document/parser" - "github.com/cloudwego/eino/schema" -) - -// LoadDocument 业务函数:加载文件 -func LoadDocument(ctx context.Context, filePath, fileFormat string) (docs []*schema.Document, err error) { - p, err := docsParser(ctx, fileFormat) - if err != nil { - return - } - loader, err := url.NewLoader(ctx, &url.LoaderConfig{ - Parser: p, - }) - imageUrl, err := utils.GetFileAddressPrefix(ctx) - if err != nil { - return - } - docs, err = loader.Load(context.Background(), document.Source{ - URI: fmt.Sprintf("%s%s", imageUrl, filePath), - }) - return -} - -func docsParser(ctx context.Context, fileFormat string) (p parser.Parser, err error) { - switch fileFormat { - case "docx": - p, err = docx.NewDocxParser(ctx, &docx.Config{ - ToSections: true, - IncludeHeaders: true, - IncludeFooters: true, - IncludeTables: true, - }) - case "pdf": - p, err = pdf.NewPDFParser(ctx, &pdf.Config{}) - case "xlsx": - p, err = xlsx.NewXlsxParser(ctx, &xlsx.Config{}) - } - return -} diff --git a/rag/eino/document_semantic.go b/rag/eino/document_semantic.go deleted file mode 100644 index 2f39a1a..0000000 --- a/rag/eino/document_semantic.go +++ /dev/null @@ -1,64 +0,0 @@ -package eino - -import ( - "context" - - "github.com/cloudwego/eino-ext/components/document/transformer/splitter/recursive" - "github.com/cloudwego/eino-ext/components/document/transformer/splitter/semantic" - "github.com/cloudwego/eino/schema" - "github.com/gogf/gf/v2/frame/g" -) - -// SemanticSplitDocument 语义分割文档 -func SemanticSplitDocument(ctx context.Context, docs []*schema.Document) (res []*schema.Document, err error) { - // 默认分隔符(支持中英文) - separators := []string{"\n\n", "\n", "。", "!", "?", ";", ".", "!", "?", ";"} - // 读取配置,使用合理的默认值 - bufferSize := g.Cfg().MustGet(ctx, "eino.splitter.bufferSize").Int() - minChunkSize := g.Cfg().MustGet(ctx, "eino.splitter.minChunkSize").Int() - percentile := g.Cfg().MustGet(ctx, "eino.splitter.percentile").Float64() - batchSize := g.Cfg().MustGet(ctx, "eino.splitter.batchSize").Int() - if batchSize <= 0 { - batchSize = 10 // doubao-embedding-vision 限制每批最多 10 个 - } - - // 使用批量包装器 - var batchEmbedder *BatchEmbedder - provider := g.Cfg().MustGet(ctx, "eino.embedding.provider").String() - switch provider { - case providerArk: - batchEmbedder = NewBatchEmbedder(EmbedderArk, batchSize) - case providerOpenai: - batchEmbedder = NewBatchEmbedder(EmbedderOpenAI, batchSize) - case providerDashscope: - batchEmbedder = NewBatchEmbedder(EmbedderDashscope, batchSize) - } - - splitter, err := semantic.NewSplitter(ctx, &semantic.Config{ - Embedding: batchEmbedder, - BufferSize: bufferSize, - MinChunkSize: minChunkSize, - Percentile: percentile, - Separators: separators, - }) - if err != nil { - return - } - return splitter.Transform(ctx, docs) -} - -// RecursiveSplitDocument 递归分割文档 -func RecursiveSplitDocument(ctx context.Context, docs []*schema.Document) (res []*schema.Document, err error) { - // 默认分隔符(支持中英文) - separators := []string{"\n\n", "\n", "。", "!", "?", ";", ".", "!", "?", ";"} - splitter, err := recursive.NewSplitter(ctx, &recursive.Config{ - ChunkSize: 512, - OverlapSize: 100, - KeepType: recursive.KeepTypeNone, - Separators: separators, - }) - if err != nil { - return - } - return splitter.Transform(ctx, docs) -} diff --git a/rag/eino/embedding.go b/rag/eino/embedding.go deleted file mode 100644 index 7af67e3..0000000 --- a/rag/eino/embedding.go +++ /dev/null @@ -1,69 +0,0 @@ -package eino - -import ( - "context" - "fmt" - - "github.com/cloudwego/eino-ext/components/embedding/ark" - "github.com/cloudwego/eino-ext/components/embedding/dashscope" - "github.com/cloudwego/eino-ext/components/embedding/openai" - "github.com/gogf/gf/v2/frame/g" - "github.com/golang/glog" -) - -// 全局只初始化一次 -var ( - EmbedderArk *ark.Embedder - EmbedderDashscope *dashscope.Embedder - EmbedderOpenAI *openai.Embedder -) - -func init() { - ctx := context.Background() - if !g.Cfg().MustGet(ctx, "eino.embedding").IsEmpty() { - var err error - provider := g.Cfg().MustGet(ctx, "eino.embedding.provider").String() - switch provider { - case providerArk: - cfg := &ark.EmbeddingConfig{ - APIKey: g.Cfg().MustGet(ctx, "eino.embedding.apiKey").String(), - Model: g.Cfg().MustGet(ctx, "eino.embedding.model").String(), - } - if apiType := g.Cfg().MustGet(ctx, "eino.embedding.apiType").String(); apiType != "" { - apiTypeVal := ark.APIType(apiType) - cfg.APIType = &apiTypeVal - } - EmbedderArk, err = ark.NewEmbedder(ctx, cfg) - case providerOpenai: - chatModelConfig := &openai.EmbeddingConfig{ - APIKey: g.Cfg().MustGet(ctx, "eino.embedding.apiKey").String(), - Model: g.Cfg().MustGet(ctx, "eino.embedding.model").String(), - } - EmbedderOpenAI, err = openai.NewEmbedder(ctx, chatModelConfig) - case providerDashscope: - cfg := &dashscope.EmbeddingConfig{ - APIKey: g.Cfg().MustGet(ctx, "eino.embedding.apiKey").String(), - Model: g.Cfg().MustGet(ctx, "eino.embedding.model").String(), - } - EmbedderDashscope, err = dashscope.NewEmbedder(ctx, cfg) - } - if err != nil { - glog.Fatalf("NewEmbedder of %v error: %v", provider, err) - } - } - - return -} - -func EmbedStrings(ctx context.Context, texts []string) (embeddings [][]float64, err error) { - provider := g.Cfg().MustGet(ctx, "eino.embedding.provider").String() - switch provider { - case providerArk: - return EmbedderArk.EmbedStrings(ctx, texts) - case providerOpenai: - return EmbedderOpenAI.EmbedStrings(ctx, texts) - case providerDashscope: - return EmbedderDashscope.EmbedStrings(ctx, texts) - } - return nil, fmt.Errorf("unsupported provider: %v", provider) -} diff --git a/rag/eino/embedding_batch.go b/rag/eino/embedding_batch.go deleted file mode 100644 index 83e0606..0000000 --- a/rag/eino/embedding_batch.go +++ /dev/null @@ -1,47 +0,0 @@ -package eino - -import ( - "context" - - "github.com/cloudwego/eino/components/embedding" -) - -// BatchEmbedder 包装器,支持批量限制 -type BatchEmbedder struct { - embedder embedding.Embedder - batchSize int -} - -// NewBatchEmbedder 创建支持批量限制的 embedding 包装器 -func NewBatchEmbedder(embedder embedding.Embedder, batchSize int) *BatchEmbedder { - if batchSize <= 0 { - batchSize = 10 // 默认每批 10 个 - } - return &BatchEmbedder{ - embedder: embedder, - batchSize: batchSize, - } -} - -// EmbedStrings 分批调用 embedding -func (b *BatchEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...embedding.Option) ([][]float64, error) { - if len(texts) <= b.batchSize { - return b.embedder.EmbedStrings(ctx, texts, opts...) - } - - var allEmbeddings [][]float64 - for i := 0; i < len(texts); i += b.batchSize { - end := i + b.batchSize - if end > len(texts) { - end = len(texts) - } - - batch := texts[i:end] - embeddings, err := b.embedder.EmbedStrings(ctx, batch, opts...) - if err != nil { - return nil, err - } - allEmbeddings = append(allEmbeddings, embeddings...) - } - return allEmbeddings, nil -} diff --git a/rag/eino/embedding_qwen.go b/rag/eino/embedding_qwen.go deleted file mode 100644 index 9496874..0000000 --- a/rag/eino/embedding_qwen.go +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2024 Red Future Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eino - -import ( - "context" - "fmt" - "net/http" - "time" - - "github.com/cloudwego/eino/callbacks" - "github.com/cloudwego/eino/components" - "github.com/cloudwego/eino/components/embedding" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/gclient" - "github.com/gogf/gf/v2/util/gconv" -) - -var ( - // 千问API默认配置 - defaultBaseURL = "https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding" - defaultTimeout = 10 * time.Minute - defaultRetryTimes = 2 -) - -type QwenEmbeddingConfig struct { - // Timeout specifies the maximum duration to wait for API responses - // Optional. Default: 10 minutes - Timeout *time.Duration `json:"timeout"` - - // HTTPClient specifies the client to send HTTP requests. - // Optional. Default &http.Client{Timeout: Timeout} - HTTPClient *http.Client `json:"http_client"` - - // RetryTimes specifies the number of retry attempts for failed API calls - // Optional. Default: 2 - RetryTimes *int `json:"retry_times"` - - // BaseURL specifies the base URL for Qwen DashScope service - // Optional. Default: "https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding" - BaseURL string `json:"base_url"` - - // APIKey specifies the API Key for authentication - // Required - APIKey string `json:"api_key"` - - // Model specifies the model name for Qwen embedding - // Required. Examples: "text-embedding-v2", "text-embedding-v3" - Model string `json:"model"` - - // TextType specifies the type of text: "document" or "query" - // Optional. Default: "document" - TextType string `json:"text_type"` - - // MaxConcurrentRequests specifies the maximum number of concurrent requests allowed - // Optional. Default: 5 - MaxConcurrentRequests *int `json:"max_concurrent_requests"` -} - -type QwenEmbedder struct { - client *gclient.Client - conf *QwenEmbeddingConfig -} - -// EmbeddingRequest 千问embedding请求结构 -type EmbeddingRequest struct { - Model string `json:"model"` - Input struct { - Texts []string `json:"texts"` - } `json:"input"` - Parameters struct { - TextType string `json:"text_type,omitempty"` - } `json:"parameters,omitempty"` -} - -// EmbeddingResponse 千问embedding响应结构 -type EmbeddingResponse struct { - Output struct { - Embeddings []struct { - TextIndex int `json:"text_index"` - Embedding []float64 `json:"embedding"` - } `json:"embeddings"` - } `json:"output"` - Usage struct { - TotalTokens int `json:"total_tokens"` - } `json:"usage"` - RequestID string `json:"request_id"` -} - -type APIError struct { - Code string `json:"code"` - Message string `json:"message"` - RequestID string `json:"request_id"` -} - -func (e *APIError) Error() string { - return fmt.Sprintf("API Error: %s - %s (RequestID: %s)", e.Code, e.Message, e.RequestID) -} - -func buildQwenClient(config *QwenEmbeddingConfig) *gclient.Client { - if len(config.BaseURL) == 0 { - config.BaseURL = defaultBaseURL - } - if config.Timeout == nil { - config.Timeout = &defaultTimeout - } - if config.RetryTimes == nil { - defaultRetryTimes := 2 - config.RetryTimes = &defaultRetryTimes - } - if len(config.TextType) == 0 { - config.TextType = "document" - } - if config.MaxConcurrentRequests == nil { - defaultMaxConcurrentRequests := 5 - config.MaxConcurrentRequests = &defaultMaxConcurrentRequests - } - - client := g.Client() - client.SetTimeout(*config.Timeout) - - return client -} - -func NewQwenEmbedder(ctx context.Context, config *QwenEmbeddingConfig) (*QwenEmbedder, error) { - if len(config.APIKey) == 0 { - return nil, fmt.Errorf("[Qwen] APIKey is required") - } - if len(config.Model) == 0 { - return nil, fmt.Errorf("[Qwen] Model is required") - } - - client := buildQwenClient(config) - - return &QwenEmbedder{ - client: client, - conf: config, - }, nil -} - -func (e *QwenEmbedder) EmbedStrings(ctx context.Context, texts []string, opts ...embedding.Option) ( - [][]float64, error) { - - if len(texts) == 0 { - return nil, fmt.Errorf("[Qwen] texts cannot be empty") - } - - options := embedding.GetCommonOptions(&embedding.Options{ - Model: &e.conf.Model, - }, opts...) - - conf := &embedding.Config{ - Model: dereferenceOrZero(options.Model), - } - - ctx = callbacks.EnsureRunInfo(ctx, e.GetType(), components.ComponentOfEmbedding) - ctx = callbacks.OnStart(ctx, &embedding.CallbackInput{ - Texts: texts, - Config: conf, - }) - defer func() { - if err := recover(); err != nil { - callbacks.OnError(ctx, fmt.Errorf("[Qwen] panic: %v", err)) - } - }() - - var usage *embedding.TokenUsage - var embeddings [][]float64 - var err error - - // 调用千问API获取embedding - embeddings, usage, err = e.callEmbeddingAPI(ctx, texts) - if err != nil { - callbacks.OnError(ctx, err) - return nil, err - } - - callbacks.OnEnd(ctx, &embedding.CallbackOutput{ - Embeddings: embeddings, - Config: conf, - TokenUsage: usage, - }) - - return embeddings, nil -} - -func (e *QwenEmbedder) callEmbeddingAPI(ctx context.Context, texts []string) ([][]float64, *embedding.TokenUsage, error) { - // 构建请求 - var req EmbeddingRequest - req.Model = e.conf.Model - req.Input.Texts = texts - req.Parameters.TextType = e.conf.TextType - - // 调用API - client := e.client.Clone() - client.SetHeader("Authorization", "Bearer "+e.conf.APIKey) - client.SetHeader("Content-Type", "application/json") - client.SetTimeout(*e.conf.Timeout) - - resp, err := client.Post(ctx, e.conf.BaseURL, req) - if err != nil { - return nil, nil, fmt.Errorf("[Qwen] HTTP request error: %w", err) - } - - defer resp.Close() - - // 检查状态码 - if resp.StatusCode != http.StatusOK { - var errResp APIError - result := resp.ReadAll() - if err = gconv.Struct(result, &errResp); err == nil && errResp.Code != "" { - return nil, nil, &errResp - } - return nil, nil, fmt.Errorf("[Qwen] HTTP status error: %d", resp.StatusCode) - } - - // 解析响应 - var apiResp EmbeddingResponse - result := resp.ReadAll() - if err = gconv.Struct(result, &apiResp); err != nil { - return nil, nil, fmt.Errorf("[Qwen] parse response error: %w", err) - } - - // 解析响应结果 - embeddings := make([][]float64, len(texts)) - for _, emb := range apiResp.Output.Embeddings { - if emb.TextIndex >= 0 && emb.TextIndex < len(embeddings) { - embeddings[emb.TextIndex] = emb.Embedding - } - } - - usage := &embedding.TokenUsage{ - TotalTokens: apiResp.Usage.TotalTokens, - } - - g.Log().Debugf(ctx, "[Qwen] Embedding success: request_id=%s, total_tokens=%d", apiResp.RequestID, usage.TotalTokens) - - return embeddings, usage, nil -} - -func (e *QwenEmbedder) GetType() string { - return getType() -} - -func (e *QwenEmbedder) IsCallbacksEnabled() bool { - return true -} - -func getType() string { - return "Qwen" -} - -func dereferenceOrZero[T any](v *T) T { - if v == nil { - var t T - return t - } - return *v -} diff --git a/rag/eino/priority_enum.go b/rag/eino/priority_enum.go deleted file mode 100644 index 371706b..0000000 --- a/rag/eino/priority_enum.go +++ /dev/null @@ -1,11 +0,0 @@ -package eino - -// TaskPriority 任务优先级 -type TaskPriority string - -const ( - TaskPriorityLow TaskPriority = "low" // 低优先级 - TaskPriorityMedium TaskPriority = "medium" // 中优先级 - TaskPriorityHigh TaskPriority = "high" // 高优先级 - TaskPriorityUrgent TaskPriority = "urgent" // 紧急 -) diff --git a/rag/eino/status_enum.go b/rag/eino/status_enum.go deleted file mode 100644 index 6e12daf..0000000 --- a/rag/eino/status_enum.go +++ /dev/null @@ -1,12 +0,0 @@ -package eino - -// TaskStatus 任务状态 -type TaskStatus string - -const ( - TaskStatusPending TaskStatus = "pending" // 待处理 - TaskStatusRunning TaskStatus = "running" // 运行中 - TaskStatusCompleted TaskStatus = "completed" // 已完成 - TaskStatusFailed TaskStatus = "failed" // 失败 - TaskStatusCancelled TaskStatus = "cancelled" // 已取消 -) diff --git a/rag/eino/task_type.go b/rag/eino/task_type.go deleted file mode 100644 index 0ba5a64..0000000 --- a/rag/eino/task_type.go +++ /dev/null @@ -1,14 +0,0 @@ -package eino - -// TaskType 任务类型 -type TaskType string - -const ( - TaskTypeDocumentIngestion TaskType = "document_ingestion" // 文档摄入任务 - TaskTypeVectorIngestion TaskType = "vector_ingestion" // 向量摄入任务 - TaskTypeIndexCreation TaskType = "index_creation" // 索引创建任务 - TaskTypeQAProcessing TaskType = "qa_processing" // 问答处理任务 - TaskTypeKnowledgeConstruction TaskType = "knowledge_construction" // 知识库构建任务 - TaskTypeGraphBuilding TaskType = "graph_building" // 图谱构建任务 - TaskTypeKnowledgeSync TaskType = "knowledge_sync" // 知识同步任务 -) diff --git a/rag/gse/utils.go b/rag/gse/utils.go deleted file mode 100644 index aea4b38..0000000 --- a/rag/gse/utils.go +++ /dev/null @@ -1,114 +0,0 @@ -package gse - -import ( - "context" - "sort" - - "github.com/go-ego/gse" - "github.com/go-ego/gse/hmm/extracker" - "github.com/go-ego/gse/hmm/segment" - "github.com/gogf/gf/v2/os/glog" -) - -var GseTool *gseTool - -// 初始化函数:程序启动时执行一次 -func init() { - var err error - GseTool, err = newGseTool() - if err != nil { - glog.Error(context.Background(), err) - } -} - -// gseTool 关键词提取工具(gse v1.0.2 标准) -type gseTool struct { - seg gse.Segmenter - tfidf *extracker.TagExtracter - tr *extracker.TextRanker -} - -// newGseTool 初始化工具(内置词典 + 停用词) -func newGseTool() (tool *gseTool, err error) { - // 1. 初始化分词器 - var seg gse.Segmenter - // 内置词典(无外部文件) - err = seg.LoadDictEmbed() - if err != nil { - return - } - // 内置停用词(v1.0.2 标准) - err = seg.LoadStopEmbed() - if err != nil { - return - } - - // 2. 初始化 TF-IDF 提取器 - tfidf := &extracker.TagExtracter{} - tfidf.WithGse(seg) - err = tfidf.LoadIdf() - if err != nil { - return - } - - // 3. 初始化 TextRank 提取器 - tr := &extracker.TextRanker{} - tr.WithGse(seg) - - tool = &gseTool{ - seg: seg, - tfidf: tfidf, - tr: tr, - } - return -} - -// Cut 分词(关键词提取唯一正确模式:精确模式 + HMM) -func (k *gseTool) Cut(text string) []string { - return k.seg.Cut(text, true) -} - -// Keyword 最终输出:关键词 + 权重 -type Keyword struct { - Word string `json:"word"` - Score float64 `json:"score"` -} - -func (k *gseTool) Extract(text string, topN int) []Keyword { - // 1. 提取 TF-IDF - tfTags := k.extractTFIDF(text, topN) - - // 2. 提取 TextRank - trTags := k.extractTextRank(text, topN) - - // 3. 合并成最终关键词(业务最常用) - scoreMap := make(map[string]float64) - for _, tag := range tfTags { - scoreMap[tag.Text] = tag.Weight - } - for _, tag := range trTags { - scoreMap[tag.Text] = tag.Weight - } - - // 转成切片并排序(高分在前) - res := make([]Keyword, 0, len(scoreMap)) - for word, score := range scoreMap { - res = append(res, Keyword{Word: word, Score: score}) - } - - sort.Slice(res, func(i, j int) bool { - return res[i].Score > res[j].Score - }) - - return res -} - -// ExtractTFIDF TF-IDF 关键词(带权重)90% 业务:文章标签、搜索、关键词 -func (k *gseTool) extractTFIDF(text string, topN int) segment.Segments { - return k.tfidf.ExtractTags(text, topN) -} - -// ExtractTextRank TextRank 关键词(带权重)长文本、摘要、语义理解 -func (k *gseTool) extractTextRank(text string, topN int) segment.Segments { - return k.tr.TextRank(text, topN) -} From 7acee87023640da064bbd0cb34fa6cbb499603f2 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Fri, 3 Apr 2026 17:59:04 +0800 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E4=B8=8EPGVector?= =?UTF-8?q?=E6=A3=80=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 1 - middleware/rate_limiter.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index c938dac..1d990dc 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -441,7 +441,6 @@ type modelCache struct { func checkSchemaConfig(ctx context.Context) (uint64, bool) { user, err := utils.GetUserInfo(ctx) if err != nil { - glog.Errorf(ctx, "[DB] GetUserInfo error: %v", err) return 0, false } var schema = fmt.Sprintf("%s%v", schemaPrefix, user.TenantId) diff --git a/middleware/rate_limiter.go b/middleware/rate_limiter.go index 49576e1..4306c5a 100644 --- a/middleware/rate_limiter.go +++ b/middleware/rate_limiter.go @@ -62,7 +62,7 @@ func IPLimiter(r *ghttp.Request) { // UserLimiter 用户维度限流中间件(防止单用户滥用) func UserLimiter(r *ghttp.Request) { - if r.RequestURI == "/swagger" || r.RequestURI == "/admin-go/api/v1/pub/captcha/get" || r.RequestURI == "/admin-go/api/v1/system/login" { + if strings.Contains(r.RequestURI, "/swagger") || strings.Contains(r.RequestURI, "/pub/captcha/get") || strings.Contains(r.RequestURI, "/login") || strings.Contains(r.RequestURI, "/web/socket/") { r.Middleware.Next() return } From 5022e9115d68daf26781e223e857d655b1be32a5 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Wed, 8 Apr 2026 14:21:12 +0800 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=88=86?= =?UTF-8?q?=E8=AF=8D=E5=B7=A5=E5=85=B7=E6=94=AF=E6=8C=81=E5=B9=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=AD=97=E6=AE=B5=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 1 + full-text-search/meilisearch/meilisearch.go | 2 +- go.mod | 2 + go.sum | 6 + utils/gse.go | 122 ++++++++++++++++++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 utils/gse.go diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index 1d990dc..9706a1e 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -414,6 +414,7 @@ var ( type Gfdb interface { Exec(ctx context.Context, sql string, args ...any) (sql.Result, error) + GetAll(ctx context.Context, sql string, args ...any) (gdb.Result, error) Model(ctx context.Context, tableNameOrStruct ...any) *model Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error } diff --git a/full-text-search/meilisearch/meilisearch.go b/full-text-search/meilisearch/meilisearch.go index 708f8a0..2bb81bf 100644 --- a/full-text-search/meilisearch/meilisearch.go +++ b/full-text-search/meilisearch/meilisearch.go @@ -118,7 +118,7 @@ func (m *meilisearchDB) ensureIndexExists(client ms.ServiceManager, indexName st return err } - requiredFilterable := []string{"tenantId", "isDeleted", "datasetId", "creator", "updater"} + requiredFilterable := []string{"tenantId", "isDeleted", "dataset_id", "creator", "updater"} needUpdate := false // 检查是否缺少必要的 filterable attributes diff --git a/go.mod b/go.mod index 49d50b6..b86c7e3 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.26.0 require ( github.com/alibaba/sentinel-golang v1.0.4 github.com/bwmarrin/snowflake v0.3.0 + github.com/go-ego/gse v1.0.2 github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 github.com/gogf/gf/v2 v2.9.5 @@ -125,6 +126,7 @@ require ( github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/valyala/fastrand v1.1.0 // indirect + github.com/vcaesar/cedar v0.30.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect diff --git a/go.sum b/go.sum index 27b7bff..7a8b7ff 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4= +github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc= 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.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -591,6 +593,10 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y= +github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik= +github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= +github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= diff --git a/utils/gse.go b/utils/gse.go new file mode 100644 index 0000000..64539a6 --- /dev/null +++ b/utils/gse.go @@ -0,0 +1,122 @@ +package utils + +import ( + "context" + "sort" + "sync" + + "github.com/go-ego/gse" + "github.com/go-ego/gse/hmm/extracker" + "github.com/go-ego/gse/hmm/segment" + "github.com/gogf/gf/v2/os/glog" +) + +// 全局工具实例(不再自动初始化) +var ( + GseTool *gseTool + once sync.Once // 保证只初始化一次,线程安全 +) + +func InitGseTool(ctx context.Context) error { + var err error + once.Do(func() { + // 只执行一次初始化 + GseTool, err = newGseTool() + }) + if err != nil { + glog.Error(ctx, "gse 分词工具初始化失败:", err) + } + return err +} + +// gseTool 关键词提取工具(gse v1.0.2 标准) +type gseTool struct { + seg gse.Segmenter + tfidf *extracker.TagExtracter + tr *extracker.TextRanker +} + +// newGseTool 初始化工具(内置词典 + 停用词) +func newGseTool() (tool *gseTool, err error) { + // 1. 初始化分词器 + var seg gse.Segmenter + // 内置词典(无外部文件) + err = seg.LoadDictEmbed() + if err != nil { + return + } + // 内置停用词(v1.0.2 标准) + err = seg.LoadStopEmbed() + if err != nil { + return + } + + // 2. 初始化 TF-IDF 提取器 + tfidf := &extracker.TagExtracter{} + tfidf.WithGse(seg) + err = tfidf.LoadIdf() + if err != nil { + return + } + + // 3. 初始化 TextRank 提取器 + tr := &extracker.TextRanker{} + tr.WithGse(seg) + + tool = &gseTool{ + seg: seg, + tfidf: tfidf, + tr: tr, + } + return +} + +// Cut 分词(关键词提取唯一正确模式:精确模式 + HMM) +func (k *gseTool) Cut(text string) []string { + return k.seg.Cut(text, true) +} + +// Keyword 最终输出:关键词 + 权重 +type Keyword struct { + Word string `json:"word"` + Score float64 `json:"score"` +} + +func (k *gseTool) Extract(text string, topN int) []Keyword { + // 1. 提取 TF-IDF + tfTags := k.extractTFIDF(text, topN) + + // 2. 提取 TextRank + trTags := k.extractTextRank(text, topN) + + // 3. 合并成最终关键词(业务最常用) + scoreMap := make(map[string]float64) + for _, tag := range tfTags { + scoreMap[tag.Text] = tag.Weight + } + for _, tag := range trTags { + scoreMap[tag.Text] = tag.Weight + } + + // 转成切片并排序(高分在前) + res := make([]Keyword, 0, len(scoreMap)) + for word, score := range scoreMap { + res = append(res, Keyword{Word: word, Score: score}) + } + + sort.Slice(res, func(i, j int) bool { + return res[i].Score > res[j].Score + }) + + return res +} + +// ExtractTFIDF TF-IDF 关键词(带权重)90% 业务:文章标签、搜索、关键词 +func (k *gseTool) extractTFIDF(text string, topN int) segment.Segments { + return k.tfidf.ExtractTags(text, topN) +} + +// ExtractTextRank TextRank 关键词(带权重)长文本、摘要、语义理解 +func (k *gseTool) extractTextRank(text string, topN int) segment.Segments { + return k.tr.TextRank(text, topN) +} From 8eb992e658bb625f6a008086d675ab4be3ecdc3e Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Thu, 9 Apr 2026 13:58:36 +0800 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Meilisearch?= =?UTF-8?q?=20=E6=8E=92=E5=90=8D=E5=88=86=E6=95=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- full-text-search/meilisearch/meilisearch.go | 2 ++ full-text-search/meilisearch/types.go | 1 + 2 files changed, 3 insertions(+) diff --git a/full-text-search/meilisearch/meilisearch.go b/full-text-search/meilisearch/meilisearch.go index 2bb81bf..53d363c 100644 --- a/full-text-search/meilisearch/meilisearch.go +++ b/full-text-search/meilisearch/meilisearch.go @@ -226,6 +226,8 @@ func (m *meilisearchDB) buildSearchRequest(ctx context.Context, searchParams *Se req.AttributesToRetrieve = searchParams.AttributesToRetrieve } + req.ShowRankingScore = searchParams.ShowRankingScore + return req, nil } diff --git a/full-text-search/meilisearch/types.go b/full-text-search/meilisearch/types.go index f5e9e65..f91b469 100644 --- a/full-text-search/meilisearch/types.go +++ b/full-text-search/meilisearch/types.go @@ -15,6 +15,7 @@ type SearchParams struct { AttributesToRetrieve []string // 返回字段 Facets []string // 聚合字段 HitsPerPage int // 每页命中数 + ShowRankingScore bool // 是否显示排名分数 } // SearchResult 搜索结果 From 45e6bf653fcd6db641f1682f3b735adedc9bea7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Thu, 9 Apr 2026 16:10:11 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E5=A2=9E=E5=8A=A0dockerfile=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 config/config.go diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..67bf1f0 --- /dev/null +++ b/config/config.go @@ -0,0 +1,16 @@ +package config + +import ( + "fmt" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/gogf/gf/v2/os/genv" +) + +func init() { + env := genv.Get("APP_ENV", "").String() + if env != "" { + g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetFileName(fmt.Sprintf("config-%s.yml", env)) + } +} From 0c80c5fd8be611f5e69e2cda52627d0245692dca Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Fri, 10 Apr 2026 15:42:48 +0800 Subject: [PATCH 07/17] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8IP=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E5=93=88=E5=B8=8C=E7=94=9F=E6=88=90=E9=9B=AA=E8=8A=B1?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E8=8A=82=E7=82=B9ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 30 +++++++++++++++++++++---- utils/utils.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index 9706a1e..8c22c1a 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "fmt" + "hash/fnv" "regexp" "strings" "time" @@ -157,16 +158,38 @@ func catchSQLHook() gdb.HookHandler { } } +func getNodeIdFromIPPort(ctx context.Context) int64 { + // 获取本地IP + ip, err := utils.GetLocalIP() + if err != nil { + return 0 + } + // 获取端口 + port := g.Cfg().MustGet(ctx, "server.address").String() + // 拼接字符串 + addr := fmt.Sprintf("%s%s", ip, port) + + // 计算哈希(保证唯一且稳定) + h := fnv.New64a() + h.Write([]byte(addr)) + hashVal := h.Sum64() + + // 取模 1024 → 得到 0~1023 的合法 node + return int64(hashVal % 1024) +} + // ==================== Insert钩子 ==================== func insertHook(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) { - userInfo, err := utils.GetUserInfo(ctx) if err != nil { return nil, err } - - node, err := snowflake.NewNode(g.Cfg().MustGet(ctx, "server.workerId").Int64()) + nodeId := getNodeIdFromIPPort(ctx) + if nodeId == 0 { + return nil, fmt.Errorf("nodeId cannot be empty") + } + node, err := snowflake.NewNode(nodeId) if err != nil { return nil, err } @@ -414,7 +437,6 @@ var ( type Gfdb interface { Exec(ctx context.Context, sql string, args ...any) (sql.Result, error) - GetAll(ctx context.Context, sql string, args ...any) (gdb.Result, error) Model(ctx context.Context, tableNameOrStruct ...any) *model Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error } diff --git a/utils/utils.go b/utils/utils.go index f09e32c..d7e934a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -443,3 +443,62 @@ LOOP: time.Sleep(time.Second) goto LOOP } + +// GetLocalIP 获取本地IP ✅ 阿里云 ECS✅ 腾讯云 CVM✅ 华为云✅ 物理机✅ Docker 容器✅ K8s Pod✅ 虚拟机 +func GetLocalIP() (string, error) { + // 先获取所有网卡 + ifaces, err := net.Interfaces() + if err != nil { + return "", err + } + + // 遍历网卡,找符合条件的 + for _, iface := range ifaces { + // 跳过 禁用、回环、虚拟网卡 + if iface.Flags&net.FlagUp == 0 || // 网卡未启用 + iface.Flags&net.FlagLoopback != 0 || // 回环地址 + strings.Contains(iface.Name, "docker") || // docker 网卡 + strings.Contains(iface.Name, "veth") || // 容器虚拟网卡 + strings.Contains(iface.Name, "bridge") || // 网桥 + strings.Contains(iface.Name, "lo") { // 本地回环 + continue + } + + // 获取网卡地址 + addrs, err := iface.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + ipNet, ok := addr.(*net.IPNet) + if !ok || ipNet.IP.IsLoopback() { + continue + } + + ip := ipNet.IP + if ip.To4() != nil && isPrivateIP(ip) { // 只取内网 IPv4 + return ip.String(), nil + } + } + } + + return "", errors.New("cannot find valid local private IP") +} + +// 判断是否内网IP(生产必须) +func isPrivateIP(ip net.IP) bool { + privateIPBlocks := []string{ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + } + + for _, block := range privateIPBlocks { + _, ipNet, err := net.ParseCIDR(block) + if err == nil && ipNet.Contains(ip) { + return true + } + } + return false +} From f58002f8f2e7531f5d733a34089ce772df8e0a4a Mon Sep 17 00:00:00 2001 From: lmk <1095689763@qq.com> Date: Fri, 10 Apr 2026 15:54:39 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BB=8Econsul=E7=9A=84=E9=85=8D=E7=BD=AE=E4=B8=AD=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=EF=BC=8C=E7=9B=91=E5=90=AC=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=94=B9=E5=8F=98=E7=9A=84=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 16 ----- consul/consul.go | 162 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 8 +++ 4 files changed, 175 insertions(+), 17 deletions(-) delete mode 100644 config/config.go diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 67bf1f0..0000000 --- a/config/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package config - -import ( - "fmt" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcfg" - "github.com/gogf/gf/v2/os/genv" -) - -func init() { - env := genv.Get("APP_ENV", "").String() - if env != "" { - g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetFileName(fmt.Sprintf("config-%s.yml", env)) - } -} diff --git a/consul/consul.go b/consul/consul.go index c0a4ee0..42c1dfa 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -13,7 +13,11 @@ import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/gsel" "github.com/gogf/gf/v2/net/gsvc" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/gogf/gf/v2/os/genv" "github.com/gogf/gf/v2/util/grand" + "github.com/hashicorp/consul/api" + "github.com/r3labs/diff/v2" ) var ( @@ -135,7 +139,165 @@ func init() { // 连接成功后启动健康检查和自动重连 go startHealthCheckAndReconnect() } + + loadConfigFromConsul() } + +func loadConfigFromConsul() { + ctx := context.Background() + + serviceName := g.Cfg().MustGet(ctx, "server.name", "admin-go").String() + env := genv.Get("APP_ENV", "").String() + fmt.Printf("服务名称: %s, 环境变量 APP_ENV: %s\n", serviceName, env) + + if env == "" { + fmt.Printf("未设置 APP_ENV 环境变量,跳过 Consul 配置加载\n") + return + } + + consulData, lastIndex, err := loadEnvFromConsul(env, serviceName) + if err == nil && len(consulData) > 0 { + adapter, err := gcfg.NewAdapterContent() + if err != nil { + fmt.Printf("创建配置适配器失败: %v\n", err) + return + } + adapter.SetContent(string(consulData)) + g.Cfg().SetAdapter(adapter) + + fmt.Printf("已从 Consul 成功加载初始配置\n") + + go watchConsulConfig(env, serviceName, lastIndex) + } else { + fmt.Printf("从 Consul 获取配置失败,使用本地配置文件\n") + } +} + +func loadEnvFromConsul(env string, serviceName string) ([]byte, uint64, error) { + ctx := context.Background() + + consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() + fmt.Printf("Consul 地址: %s\n", consulAddress) + + config := api.DefaultConfig() + config.Address = consulAddress + + client, err := api.NewClient(config) + if err != nil { + fmt.Printf("创建 Consul 客户端失败: %v\n", err) + return nil, 0, err + } + consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) + fmt.Printf("从 Consul 读取配置键: %s\n", consulKey) + + kv := client.KV() + + opts := &api.QueryOptions{ + WaitIndex: 0, + WaitTime: 60 * time.Second, + } + + pair, meta, err := kv.Get(consulKey, opts) + if err != nil { + fmt.Printf("从 Consul 读取配置失败: %v\n", err) + return nil, 0, err + } + + if pair == nil { + fmt.Printf("Consul 中未找到配置键: %s\n", consulKey) + return nil, 0, nil + } + + fmt.Printf("已从 Consul 加载配置, Index: %d\n", meta.LastIndex) + return pair.Value, meta.LastIndex, nil +} + +func watchConsulConfig(env string, serviceName string, lastIndex uint64) { + ctx := context.Background() + + consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() + config := api.DefaultConfig() + config.Address = consulAddress + + client, err := api.NewClient(config) + if err != nil { + fmt.Printf("创建 Consul 监听客户端失败: %v\n", err) + return + } + + consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) + + for { + opts := &api.QueryOptions{ + WaitIndex: lastIndex, + WaitTime: 60 * time.Second, + } + + pair, meta, err := client.KV().Get(consulKey, opts) + if err != nil { + fmt.Printf("Consul 监听出错: %v, 5秒后重试...\n", err) + time.Sleep(5 * time.Second) + continue + } + + if meta.LastIndex == lastIndex { + continue + } + + if pair == nil { + fmt.Printf("Consul 配置被删除: %s\n", consulKey) + lastIndex = meta.LastIndex + continue + } + + fmt.Printf("检测到 Consul 配置变更: %s, New Index: %d\n", consulKey, meta.LastIndex) + + updateLocalConfig(pair.Value) + + lastIndex = meta.LastIndex + } +} + +func updateLocalConfig(content []byte) { + ctx := context.Background() + + oldConfig, _ := g.Cfg().Data(ctx) + + adapter, err := gcfg.NewAdapterContent() + if err != nil { + fmt.Printf("创建新配置适配器失败: %v\n", err) + return + } + adapter.SetContent(string(content)) + g.Cfg().SetAdapter(adapter) + + newConfig, _ := g.Cfg().Data(ctx) + + changelog, err := diff.Diff(oldConfig, newConfig) + if err != nil { + fmt.Printf("配置对比失败: %v\n", err) + return + } + + if len(changelog) == 0 { + fmt.Printf("配置已热更新成功(内容无实质变化)\n") + return + } + + fmt.Printf("=== 检测到配置变更 (%d 项) ===\n", len(changelog)) + for _, change := range changelog { + switch change.Type { + case "create": + fmt.Printf("[+] 新增: %s = %v\n", change.Path, change.To) + case "update": + fmt.Printf("[~] 修改: %s: %v -> %v\n", change.Path, change.From, change.To) + case "delete": + fmt.Printf("[-] 删除: %s (原值: %v)\n", change.Path, change.From) + } + } + fmt.Printf("=================================\n") +} + func getLocalIP() (string, error) { // 获取本机所有网络接口 addrs, err := net.InterfaceAddrs() diff --git a/go.mod b/go.mod index b86c7e3..81d2c40 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,14 @@ require ( github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 github.com/gogf/gf/v2 v2.9.5 github.com/google/uuid v1.6.0 + github.com/hashicorp/consul/api v1.26.1 github.com/meilisearch/meilisearch-go v0.36.1 github.com/minio/minio-go/v7 v7.0.97 github.com/nats-io/nats.go v1.48.0 github.com/olivere/elastic/v7 v7.0.32 + github.com/r3labs/diff/v2 v2.15.1 github.com/rabbitmq/amqp091-go v1.10.0 + github.com/rogpeppe/go-internal v1.13.1 github.com/rpcxio/rpcx-consul v0.1.1 github.com/smallnest/rpcx v1.9.1 github.com/tiger1103/gfast-token v1.0.10 @@ -67,7 +70,6 @@ require ( github.com/grandcat/zeroconf v1.0.0 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect - github.com/hashicorp/consul/api v1.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect @@ -127,6 +129,7 @@ require ( github.com/tklauser/numcpus v0.2.2 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/vcaesar/cedar v0.30.0 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -150,6 +153,7 @@ require ( golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect golang.org/x/tools v0.35.0 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect diff --git a/go.sum b/go.sum index 7a8b7ff..7920f68 100644 --- a/go.sum +++ b/go.sum @@ -507,6 +507,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= +github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= +github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -568,6 +570,7 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -597,6 +600,8 @@ github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y= github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik= github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -832,6 +837,9 @@ google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMt google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= From 0c9bced44e5929613f78d9a74f0c3e9904e9f662 Mon Sep 17 00:00:00 2001 From: lmk <1095689763@qq.com> Date: Fri, 10 Apr 2026 16:06:21 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E6=9C=AA=E9=85=8D=E7=BD=AE=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=EF=BC=8C=E7=9B=B4=E6=8E=A5=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consul/consul.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/consul/consul.go b/consul/consul.go index 42c1dfa..0e68817 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -151,8 +151,7 @@ func loadConfigFromConsul() { fmt.Printf("服务名称: %s, 环境变量 APP_ENV: %s\n", serviceName, env) if env == "" { - fmt.Printf("未设置 APP_ENV 环境变量,跳过 Consul 配置加载\n") - return + panic("未设置 APP_ENV 环境变量,无法从 Consul 加载配置") } consulData, lastIndex, err := loadEnvFromConsul(env, serviceName) From 8183fc89f1596a80abdfca7aa587cf3ec9e44a80 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Sat, 11 Apr 2026 18:25:18 +0800 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=E5=9C=A8=20Gfdb=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E4=B8=AD=E6=B7=BB=E5=8A=A0=20GetAll=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 1 + 1 file changed, 1 insertion(+) diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index 8c22c1a..0c06dce 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -436,6 +436,7 @@ var ( ) type Gfdb interface { + GetAll(ctx context.Context, sql string, args ...any) (gdb.Result, error) Exec(ctx context.Context, sql string, args ...any) (sql.Result, error) Model(ctx context.Context, tableNameOrStruct ...any) *model Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) error From 1b85b42e780d796ee5767ab05e5e95412d64ef9e Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Mon, 13 Apr 2026 14:50:25 +0800 Subject: [PATCH 11/17] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=E4=BB=A3=E6=9B=BFIP=E5=93=88?= =?UTF-8?q?=E5=B8=8C=E7=94=9F=E6=88=90=E8=8A=82=E7=82=B9ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/gfdb/gfdb.go | 31 +++++-------------------- utils/utils.go | 60 ++++++++----------------------------------------- 2 files changed, 15 insertions(+), 76 deletions(-) diff --git a/db/gfdb/gfdb.go b/db/gfdb/gfdb.go index 0c06dce..d44a9f2 100644 --- a/db/gfdb/gfdb.go +++ b/db/gfdb/gfdb.go @@ -5,7 +5,6 @@ import ( "database/sql" "encoding/json" "fmt" - "hash/fnv" "regexp" "strings" "time" @@ -18,6 +17,7 @@ import ( "github.com/gogf/gf/v2/database/gredis" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/os/genv" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" @@ -158,37 +158,18 @@ func catchSQLHook() gdb.HookHandler { } } -func getNodeIdFromIPPort(ctx context.Context) int64 { - // 获取本地IP - ip, err := utils.GetLocalIP() - if err != nil { - return 0 - } - // 获取端口 - port := g.Cfg().MustGet(ctx, "server.address").String() - // 拼接字符串 - addr := fmt.Sprintf("%s%s", ip, port) - - // 计算哈希(保证唯一且稳定) - h := fnv.New64a() - h.Write([]byte(addr)) - hashVal := h.Sum64() - - // 取模 1024 → 得到 0~1023 的合法 node - return int64(hashVal % 1024) -} - // ==================== Insert钩子 ==================== - func insertHook(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) { userInfo, err := utils.GetUserInfo(ctx) if err != nil { return nil, err } - nodeId := getNodeIdFromIPPort(ctx) - if nodeId == 0 { - return nil, fmt.Errorf("nodeId cannot be empty") + + nodeId := genv.Get("APP_NODE", "").Int64() + if g.IsEmpty(nodeId) { + nodeId = 1 } + node, err := snowflake.NewNode(nodeId) if err != nil { return nil, err diff --git a/utils/utils.go b/utils/utils.go index d7e934a..32a75ba 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -444,60 +444,18 @@ LOOP: goto LOOP } -// GetLocalIP 获取本地IP ✅ 阿里云 ECS✅ 腾讯云 CVM✅ 华为云✅ 物理机✅ Docker 容器✅ K8s Pod✅ 虚拟机 -func GetLocalIP() (string, error) { - // 先获取所有网卡 - ifaces, err := net.Interfaces() +// IsLocalIP 判断是否是本地IP +func IsLocalIP(ip string) bool { + addrs, err := net.InterfaceAddrs() if err != nil { - return "", err + return false } - - // 遍历网卡,找符合条件的 - for _, iface := range ifaces { - // 跳过 禁用、回环、虚拟网卡 - if iface.Flags&net.FlagUp == 0 || // 网卡未启用 - iface.Flags&net.FlagLoopback != 0 || // 回环地址 - strings.Contains(iface.Name, "docker") || // docker 网卡 - strings.Contains(iface.Name, "veth") || // 容器虚拟网卡 - strings.Contains(iface.Name, "bridge") || // 网桥 - strings.Contains(iface.Name, "lo") { // 本地回环 - continue - } - - // 获取网卡地址 - addrs, err := iface.Addrs() - if err != nil { - continue - } - - for _, addr := range addrs { - ipNet, ok := addr.(*net.IPNet) - if !ok || ipNet.IP.IsLoopback() { - continue + for _, addr := range addrs { + ipNet, ok := addr.(*net.IPNet) + if ok && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil { + if ipNet.IP.String() == ip { + return true } - - ip := ipNet.IP - if ip.To4() != nil && isPrivateIP(ip) { // 只取内网 IPv4 - return ip.String(), nil - } - } - } - - return "", errors.New("cannot find valid local private IP") -} - -// 判断是否内网IP(生产必须) -func isPrivateIP(ip net.IP) bool { - privateIPBlocks := []string{ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - } - - for _, block := range privateIPBlocks { - _, ipNet, err := net.ParseCIDR(block) - if err == nil && ipNet.Contains(ip) { - return true } } return false From f09cc8640d58f08230c793e47e7c7592a5e39ca4 Mon Sep 17 00:00:00 2001 From: lmk <1095689763@qq.com> Date: Mon, 13 Apr 2026 15:19:08 +0800 Subject: [PATCH 12/17] =?UTF-8?q?consul=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=E9=85=8D=E7=BD=AE=EF=BC=8C=E4=B8=8D=E8=B5=B0?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F,=E6=96=B0=E5=A2=9Ek3s?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consul/consul.go | 32 +++--- go.mod | 52 ++++++++-- go.sum | 146 +++++++++++++++++++++------ k3sconfig/k3sconfig.go | 222 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+), 58 deletions(-) create mode 100644 k3sconfig/k3sconfig.go diff --git a/consul/consul.go b/consul/consul.go index 0e68817..166956a 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -14,7 +14,6 @@ import ( "github.com/gogf/gf/v2/net/gsel" "github.com/gogf/gf/v2/net/gsvc" "github.com/gogf/gf/v2/os/gcfg" - "github.com/gogf/gf/v2/os/genv" "github.com/gogf/gf/v2/util/grand" "github.com/hashicorp/consul/api" "github.com/r3labs/diff/v2" @@ -127,16 +126,17 @@ func checkConsulHealth(ctx context.Context) bool { } func init() { - consulAddr = g.Cfg().MustGet(context.Background(), "consul.address").String() + ctx := context.Background() + + consulAddr = g.Cfg().MustGet(ctx, "consul.address").String() if consulAddr == "" { - g.Log().Warning(context.Background(), "⚠️ Consul 配置未找到,跳过初始化") + g.Log().Debug(ctx, "📄 [Consul] 配置文件中未设置 consul.address,跳过 Consul 初始化") return } - if err := connectConsul(context.Background()); err != nil { - g.Log().Errorf(context.Background(), "❌ Consul 初始化失败: %v", err) + if err := connectConsul(ctx); err != nil { + g.Log().Errorf(ctx, "❌ Consul 初始化失败: %v", err) } else { - // 连接成功后启动健康检查和自动重连 go startHealthCheckAndReconnect() } @@ -147,14 +147,12 @@ func loadConfigFromConsul() { ctx := context.Background() serviceName := g.Cfg().MustGet(ctx, "server.name", "admin-go").String() - env := genv.Get("APP_ENV", "").String() - fmt.Printf("服务名称: %s, 环境变量 APP_ENV: %s\n", serviceName, env) + fmt.Printf("服务名称: %s\n", serviceName) - if env == "" { - panic("未设置 APP_ENV 环境变量,无法从 Consul 加载配置") - } + consulKey := fmt.Sprintf("config/%s/%s", serviceName, serviceName) + fmt.Printf("从 Consul 读取配置键: %s\n", consulKey) - consulData, lastIndex, err := loadEnvFromConsul(env, serviceName) + consulData, lastIndex, err := loadFromConsul(consulKey) if err == nil && len(consulData) > 0 { adapter, err := gcfg.NewAdapterContent() if err != nil { @@ -166,13 +164,13 @@ func loadConfigFromConsul() { fmt.Printf("已从 Consul 成功加载初始配置\n") - go watchConsulConfig(env, serviceName, lastIndex) + go watchConsulConfig(consulKey, lastIndex) } else { fmt.Printf("从 Consul 获取配置失败,使用本地配置文件\n") } } -func loadEnvFromConsul(env string, serviceName string) ([]byte, uint64, error) { +func loadFromConsul(consulKey string) ([]byte, uint64, error) { ctx := context.Background() consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() @@ -186,8 +184,6 @@ func loadEnvFromConsul(env string, serviceName string) ([]byte, uint64, error) { fmt.Printf("创建 Consul 客户端失败: %v\n", err) return nil, 0, err } - consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) - fmt.Printf("从 Consul 读取配置键: %s\n", consulKey) kv := client.KV() @@ -211,7 +207,7 @@ func loadEnvFromConsul(env string, serviceName string) ([]byte, uint64, error) { return pair.Value, meta.LastIndex, nil } -func watchConsulConfig(env string, serviceName string, lastIndex uint64) { +func watchConsulConfig(consulKey string, lastIndex uint64) { ctx := context.Background() consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() @@ -224,8 +220,6 @@ func watchConsulConfig(env string, serviceName string, lastIndex uint64) { return } - consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) - for { opts := &api.QueryOptions{ WaitIndex: lastIndex, diff --git a/go.mod b/go.mod index 81d2c40..15e20fc 100644 --- a/go.mod +++ b/go.mod @@ -17,17 +17,19 @@ require ( github.com/olivere/elastic/v7 v7.0.32 github.com/r3labs/diff/v2 v2.15.1 github.com/rabbitmq/amqp091-go v1.10.0 - github.com/rogpeppe/go-internal v1.13.1 github.com/rpcxio/rpcx-consul v0.1.1 github.com/smallnest/rpcx v1.9.1 github.com/tiger1103/gfast-token v1.0.10 go.mongodb.org/mongo-driver/v2 v2.4.0 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/trace v1.38.0 + k8s.io/apimachinery v0.35.3 + k8s.io/client-go v0.35.3 ) require ( github.com/BurntSushi/toml v1.5.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/akutz/memconn v0.1.0 // indirect github.com/alitto/pond v1.9.2 // indirect @@ -40,20 +42,26 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/edwingeng/doublejump v1.0.1 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-ping/ping v1.2.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/godzie44/go-uring v0.0.0-20220926161041-69611e8b13d5 // indirect @@ -65,8 +73,9 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/flatbuffers v1.12.1 // indirect - github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf // indirect - github.com/gorilla/websocket v1.5.3 // indirect + github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grandcat/zeroconf v1.0.0 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -79,6 +88,7 @@ require ( github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/juju/ratelimit v1.0.2 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/kavu/go_reuseport v1.5.0 // indirect @@ -97,12 +107,15 @@ require ( github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/olekukonko/errors v1.1.0 // indirect github.com/olekukonko/ll v0.0.9 // indirect github.com/olekukonko/tablewriter v1.1.0 // indirect - github.com/onsi/ginkgo/v2 v2.22.2 // indirect + github.com/onsi/ginkgo/v2 v2.27.2 // indirect github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect @@ -121,6 +134,7 @@ require ( github.com/smallnest/quick v0.2.0 // indirect github.com/smallnest/rsocket v0.0.0-20241130031020-4a72eb6ff62a // indirect github.com/soheilhy/cmux v0.1.5 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect github.com/tinylib/msgp v1.3.0 // indirect @@ -132,6 +146,7 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect @@ -145,19 +160,34 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/mock v0.5.0 // indirect - golang.org/x/crypto v0.41.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.44.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/tools v0.35.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.38.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.35.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 7920f68..cf71633 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= @@ -75,6 +77,7 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -101,6 +104,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edwingeng/doublejump v1.0.1 h1:wJ6QgNyyF23Of9vw+ThbwJ/obe9KdxaWEg/Brpv5S1o= github.com/edwingeng/doublejump v1.0.1/go.mod h1:ykMWX8JWePtMtk2OGjNE9kwtgpI+SF2FNIyXV4gS36k= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= @@ -124,7 +129,15 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4= github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= @@ -142,6 +155,14 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +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= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-ping/ping v1.2.0 h1:vsJ8slZBZAXNCK4dPcI2PEE9eM9n9RbXbGouVQ/Y4yQ= github.com/go-ping/ping v1.2.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-redis/redis/v8 v8.8.2/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y= @@ -149,6 +170,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godzie44/go-uring v0.0.0-20220926161041-69611e8b13d5 h1:5zELAgnSz0gqmr4Q5DWCoOzNHoeBAxVUXB7LS1eG+sw= github.com/godzie44/go-uring v0.0.0-20220926161041-69611e8b13d5/go.mod h1:ermjEDUoT/fS+3Ona5Vd6t6mZkw1eHp99ILO5jGRBkM= github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.1 h1:egobo4YfQX3C4NtrEFunBqMX3jsddagklgut9u91+BM= @@ -195,10 +218,12 @@ github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -209,8 +234,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf h1:BvBLUD2hkvLI3dJTJMiopAq8/wp43AAZKTP7qdpptbU= -github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -223,8 +248,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE= github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs= github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= @@ -305,12 +330,16 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= @@ -336,6 +365,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -349,8 +379,11 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -374,6 +407,8 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/meilisearch/meilisearch-go v0.36.1 h1:mJTCJE5g7tRvaqKco6DfqOuJEjX+rRltDEnkEC02Y0M= github.com/meilisearch/meilisearch-go v0.36.1/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -400,9 +435,15 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= @@ -433,14 +474,14 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -521,8 +562,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rpcxio/libkv v0.5.1 h1:M0/QqwTcdXz7us0NB+2i8Kq5+wikTm7zZ4Hyb/jNgME= github.com/rpcxio/libkv v0.5.1/go.mod h1:zHGgtLr3cFhGtbalum0BrMPOjhFZFJXCKiws/25ewls= github.com/rpcxio/rpcx-consul v0.1.1 h1:z/IHpIytgChEuHndWlpo4BY0V0mVBSg/XsKsc54f0iU= @@ -559,12 +600,16 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -573,13 +618,24 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +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.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s= github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= @@ -606,6 +662,8 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= @@ -668,6 +726,10 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -678,8 +740,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= @@ -693,8 +755,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -724,10 +786,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -737,8 +801,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -789,22 +853,24 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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= golang.org/x/text v0.3.3/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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -825,8 +891,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -884,8 +950,12 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -906,5 +976,25 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= +k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4= +k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= +k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= +k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/k3sconfig/k3sconfig.go b/k3sconfig/k3sconfig.go new file mode 100644 index 0000000..1af9c3a --- /dev/null +++ b/k3sconfig/k3sconfig.go @@ -0,0 +1,222 @@ +package k3sconfig + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/r3labs/diff/v2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +type K8sConfig struct { + ApiServer string `json:"apiServer"` + Token string `json:"token"` + Kubeconfig string `json:"kubeconfig"` + Namespace string `json:"namespace"` +} + +func init() { + ctx := context.Background() + + consulAddr := g.Cfg().MustGet(ctx, "consul.address").String() + if consulAddr != "" { + g.Log().Debug(ctx, "📄 [K3sConfig] 检测到 consul.address 配置,使用 Consul,跳过 K3s 加载") + return + } + + loadFromK3sCluster(ctx) +} + +func loadFromK3sCluster(ctx context.Context) { + serviceName := g.Cfg().MustGet(ctx, "server.name", "").String() + + if serviceName == "" { + panic("❌ [K3sConfig] 配置文件中未设置 server.name") + } + + k8sConfig := getK8sConfig(ctx) + namespace := k8sConfig.Namespace + if namespace == "" { + namespace = "default" + } + + configMapName := fmt.Sprintf("%s-config", serviceName) + g.Log().Info(ctx, "🔗 [K3sConfig] 从 K3s 集群加载配置:", configMapName) + + var config *rest.Config + var err error + + if k8sConfig.ApiServer != "" && k8sConfig.Token != "" { + g.Log().Infof(ctx, "📍 [K3sConfig] 使用远程 K8s API Server: %s", k8sConfig.ApiServer) + config = &rest.Config{ + Host: k8sConfig.ApiServer, + BearerToken: k8sConfig.Token, + TLSClientConfig: rest.TLSClientConfig{ + Insecure: true, + }, + } + } else if k8sConfig.Kubeconfig != "" { + g.Log().Infof(ctx, "📍 [K3sConfig] 使用 Kubeconfig 文件: %s", k8sConfig.Kubeconfig) + config, err = clientcmd.BuildConfigFromFlags("", k8sConfig.Kubeconfig) + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 构建 K8s 配置失败: %v", err)) + } + } else { + kubeconfigEnv := os.Getenv("KUBECONFIG") + if kubeconfigEnv != "" { + g.Log().Infof(ctx, "📍 [K3sConfig] 使用环境变量 KUBECONFIG: %s", kubeconfigEnv) + config, err = clientcmd.BuildConfigFromFlags("", kubeconfigEnv) + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 构建 K8s 配置失败: %v", err)) + } + } else { + home, _ := os.UserHomeDir() + defaultKubeconfig := fmt.Sprintf("%s/.kube/config", home) + g.Log().Infof(ctx, "📍 [K3sConfig] 使用默认 Kubeconfig: %s", defaultKubeconfig) + config, err = clientcmd.BuildConfigFromFlags("", defaultKubeconfig) + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 构建 K8s 配置失败: %v", err)) + } + } + } + + g.Log().Infof(ctx, "🔌 [K3sConfig] K8s API Server: %s", config.Host) + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 创建 K8s 客户端失败: %v", err)) + } + + cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(ctx, configMapName, metav1.GetOptions{}) + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 获取 ConfigMap 失败: %v", err)) + } + + g.Log().Infof(ctx, "📦 [K3sConfig] ConfigMap 信息 - Name: %s, Namespace: %s, ResourceVersion: %s", + cm.Name, cm.Namespace, cm.ResourceVersion) + + configData, ok := cm.Data["config.yml"] + if !ok { + g.Log().Debugf(ctx, "📋 [K3sConfig] ConfigMap 可用键: %v", getMapKeys(cm.Data)) + panic("❌ [K3sConfig] ConfigMap 中未找到 config.yml 键") + } + + g.Log().Infof(ctx, "📄 [K3sConfig] 获取到的配置内容长度: %d 字节", len(configData)) + adapter, err := gcfg.NewAdapterContent() + if err != nil { + panic(fmt.Sprintf("❌ [K3sConfig] 创建配置适配器失败: %v", err)) + } + + adapter.SetContent(configData) + g.Cfg().SetAdapter(adapter) + g.Log().Infof(ctx, "✅ [K3sConfig] 成功从 K3s 加载配置: %s/%s", namespace, configMapName) + + go watchK3sConfig(ctx, clientset, namespace, configMapName, cm.ResourceVersion) +} + +func getK8sConfig(ctx context.Context) K8sConfig { + return K8sConfig{ + ApiServer: g.Cfg().MustGet(ctx, "k8s.apiServer", "").String(), + Token: g.Cfg().MustGet(ctx, "k8s.token", "").String(), + Kubeconfig: g.Cfg().MustGet(ctx, "k8s.kubeconfig", "").String(), + Namespace: g.Cfg().MustGet(ctx, "k8s.namespace", "default").String(), + } +} + +func watchK3sConfig(ctx context.Context, clientset *kubernetes.Clientset, namespace, configMapName string, lastResourceVersion string) { + g.Log().Info(ctx, "👀 [K3sConfig] 开始监听 ConfigMap 变更...") + + for { + time.Sleep(10 * time.Second) + + cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(ctx, configMapName, metav1.GetOptions{ + ResourceVersion: "0", + }) + if err != nil { + g.Log().Errorf(ctx, "❌ [K3sConfig] 监听 ConfigMap 失败: %v", err) + continue + } + + if cm.ResourceVersion == lastResourceVersion { + continue + } + + g.Log().Infof(ctx, "🔔 [K3sConfig] 检测到 ConfigMap 变更, Old Version: %s, New Version: %s", + lastResourceVersion, cm.ResourceVersion) + + configData, ok := cm.Data["config.yml"] + if !ok { + g.Log().Error(ctx, "❌ [K3sConfig] ConfigMap 中未找到 config.yml 键") + continue + } + + updateK3sConfig(configData) + lastResourceVersion = cm.ResourceVersion + } +} + +func updateK3sConfig(content string) { + ctx := context.Background() + + oldConfig, _ := g.Cfg().Data(ctx) + + adapter, err := gcfg.NewAdapterContent() + if err != nil { + g.Log().Errorf(ctx, "❌ [K3sConfig] 创建配置适配器失败: %v", err) + return + } + adapter.SetContent(content) + g.Cfg().SetAdapter(adapter) + + newConfig, _ := g.Cfg().Data(ctx) + + changelog, err := diff.Diff(oldConfig, newConfig) + if err != nil { + g.Log().Errorf(ctx, "❌ [K3sConfig] 配置对比失败: %v", err) + return + } + + if len(changelog) == 0 { + g.Log().Info(ctx, "✅ [K3sConfig] 配置已热更新成功(内容无实质变化)") + return + } + + g.Log().Infof(ctx, "=== [K3sConfig] 检测到配置变更 (%d 项) ===", len(changelog)) + for _, change := range changelog { + switch change.Type { + case "create": + g.Log().Infof(ctx, "[+] 新增: %s = %v", change.Path, change.To) + case "update": + g.Log().Infof(ctx, "[~] 修改: %s: %v -> %v", change.Path, change.From, change.To) + case "delete": + g.Log().Infof(ctx, "[-] 删除: %s (原值: %v)", change.Path, change.From) + } + } + g.Log().Info(ctx, "=================================") +} + +func getMapKeys(m map[string]string) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} + +func loadConfigFromFile(ctx context.Context, configPath string) { + adapter, err := gcfg.NewAdapterFile(configPath) + if err != nil { + g.Log().Errorf(ctx, "❌ [K3sConfig] 创建文件适配器失败: %v", err) + return + } + + g.Cfg().SetAdapter(adapter) + g.Log().Info(ctx, "✅ [K3sConfig] 已成功加载配置:", configPath) +} From bfdfe9d896cd1b29978078dfd9f6cbbc5dc86398 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Wed, 15 Apr 2026 16:55:15 +0800 Subject: [PATCH 13/17] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20Ragflow?= =?UTF-8?q?=20=E5=92=8C=20NATS=20=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/mongo/mongo.go | 58 +- .../elasticsearch}/client.go | 0 message/connection_nats.go | 167 ---- message/connection_rabbitmq.go | 164 ---- message/connection_redis.go | 198 ----- message/msg_interfaces.go | 32 - message/msg_plugin_manager.go | 114 --- message/nats_msg.go | 373 --------- message/nats_rpc.go | 770 ------------------ message/rabbitmq_msg.go | 311 ------- message/reconnect.go | 73 -- message/redis_msg.go | 279 ------- message/store.go | 125 --- message/stream.go | 212 ----- middleware/rate_limiter.go | 41 +- minio/minio.go | 129 --- rabbitmq/consumer.go | 216 ----- rabbitmq/consumer_manager.go | 175 ---- rabbitmq/delay.go | 95 --- rabbitmq/instance.go | 59 -- rabbitmq/publisher.go | 152 ---- rabbitmq/queue_setup.go | 111 --- rabbitmq/rabbitmq_client.go | 210 ----- rabbitmq/setup.go | 231 ------ ragflow/agent.go | 141 ---- ragflow/chat.go | 198 ----- ragflow/chunk.go | 180 ---- ragflow/client.go | 195 ----- ragflow/dataset.go | 190 ----- ragflow/document.go | 274 ------- ragflow/openai.go | 117 --- ragflow/session.go | 178 ---- ragflow/system.go | 39 - ragflow/worker_pool.go | 154 ---- redis/keys.go | 19 - redis/message.go | 13 - redis/redis.go | 743 ----------------- redis/types.go | 129 --- 38 files changed, 70 insertions(+), 6795 deletions(-) rename {elasticsearch => full-text-search/elasticsearch}/client.go (100%) delete mode 100644 message/connection_nats.go delete mode 100644 message/connection_rabbitmq.go delete mode 100644 message/connection_redis.go delete mode 100644 message/msg_interfaces.go delete mode 100644 message/msg_plugin_manager.go delete mode 100644 message/nats_msg.go delete mode 100644 message/nats_rpc.go delete mode 100644 message/rabbitmq_msg.go delete mode 100644 message/reconnect.go delete mode 100644 message/redis_msg.go delete mode 100644 message/store.go delete mode 100644 message/stream.go delete mode 100644 minio/minio.go delete mode 100644 rabbitmq/consumer.go delete mode 100644 rabbitmq/consumer_manager.go delete mode 100644 rabbitmq/delay.go delete mode 100644 rabbitmq/instance.go delete mode 100644 rabbitmq/publisher.go delete mode 100644 rabbitmq/queue_setup.go delete mode 100644 rabbitmq/rabbitmq_client.go delete mode 100644 rabbitmq/setup.go delete mode 100644 ragflow/agent.go delete mode 100644 ragflow/chat.go delete mode 100644 ragflow/chunk.go delete mode 100644 ragflow/client.go delete mode 100644 ragflow/dataset.go delete mode 100644 ragflow/document.go delete mode 100644 ragflow/openai.go delete mode 100644 ragflow/session.go delete mode 100644 ragflow/system.go delete mode 100644 ragflow/worker_pool.go delete mode 100644 redis/keys.go delete mode 100644 redis/message.go delete mode 100644 redis/redis.go delete mode 100644 redis/types.go diff --git a/db/mongo/mongo.go b/db/mongo/mongo.go index 0c3d360..b25e132 100644 --- a/db/mongo/mongo.go +++ b/db/mongo/mongo.go @@ -16,7 +16,6 @@ import ( "gitea.com/red-future/common/beans" "gitea.com/red-future/common/log/model/entity" - "gitea.com/red-future/common/redis" "gitea.com/red-future/common/utils" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/errors/gerror" @@ -30,6 +29,15 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo/options" ) +// Redis 数据缓存 Key 常量 +const ( + CleanList = "list:tenantId-%v:collection-%s:*" // 清理列表Key + CleanCount = "count:tenantId-%v:collection-%s:*" // 清理计数Key + List = "list:tenantId-%v:collection-%s:filter:%s:options:%s" // 列表查询Key + Count = "count:tenantId-%v:collection-%s:filter:%s" // 计数查询Key + One = "one:tenantId-%v:collection-%s:filter:%s" // 单条查询Key +) + // ============================================================================= // 向后兼容的MongoDB结构体 // ============================================================================= @@ -175,10 +183,10 @@ func (m *mongoDB) Count(ctx context.Context, filter bson.M, collection string) ( filter["isDeleted"] = false delete(filter, "tenantId") filterKey := fmt.Sprintf("%+v", filter) - redisKey := fmt.Sprintf(redis.Count, user.TenantId, collection, filterKey) + redisKey := fmt.Sprintf(Count, user.TenantId, collection, filterKey) if !m.noCache { var resultStr *gvar.Var - resultStr, err = redis.RedisClient().Get(ctx, redisKey) + resultStr, err = g.Redis().Get(ctx, redisKey) if err != nil { return } @@ -193,7 +201,7 @@ func (m *mongoDB) Count(ctx context.Context, filter bson.M, collection string) ( } count, err = db.Collection(collection).CountDocuments(ctx, filter) if !m.noCache { - err = redis.RedisClient().SetEX(ctx, redisKey, count, int64(time.Hour)) + err = g.Redis().SetEX(ctx, redisKey, count, int64(time.Hour)) if err != nil { return } @@ -221,10 +229,10 @@ func (m *mongoDB) Find(ctx context.Context, filter bson.M, result interface{}, c } filterKey := fmt.Sprintf("%+v", filter) optionsKey := fmt.Sprintf("%+v%+v", page, orderBy) - redisKey := fmt.Sprintf(redis.List, user.TenantId, collection, filterKey, optionsKey) + redisKey := fmt.Sprintf(List, user.TenantId, collection, filterKey, optionsKey) if !m.noCache { var resultStr *gvar.Var - resultStr, err = redis.RedisClient().Get(ctx, redisKey) + resultStr, err = g.Redis().Get(ctx, redisKey) if err != nil { return } @@ -284,7 +292,7 @@ func (m *mongoDB) Find(ctx context.Context, filter bson.M, result interface{}, c return } if !m.noCache { - err = redis.RedisClient().SetEX(ctx, redisKey, result, int64(time.Hour)) + err = g.Redis().SetEX(ctx, redisKey, result, int64(time.Hour)) if err != nil { return } @@ -313,10 +321,10 @@ func (m *mongoDB) FindOne(ctx context.Context, filter bson.M, result interface{} } filter["isDeleted"] = false filterKey := fmt.Sprintf("%+v", filter) - redisKey := fmt.Sprintf(redis.One, user.TenantId, collection, filterKey) + redisKey := fmt.Sprintf(One, user.TenantId, collection, filterKey) if !m.noCache { var resultStr *gvar.Var - resultStr, err = redis.RedisClient().Get(ctx, redisKey) + resultStr, err = g.Redis().Get(ctx, redisKey) if err != nil { return } @@ -338,7 +346,7 @@ func (m *mongoDB) FindOne(ctx context.Context, filter bson.M, result interface{} err = nil } if !m.noCache { - err = redis.RedisClient().SetEX(ctx, redisKey, result, int64(time.Hour)) + err = g.Redis().SetEX(ctx, redisKey, result, int64(time.Hour)) if err != nil { return err } @@ -358,24 +366,24 @@ func (m *mongoDB) getDeletedData(ctx context.Context, filter bson.M, collection } func (m *mongoDB) CleanRedis(ctx context.Context, filter bson.M, tenantId interface{}, collection string) (err error) { - listKeys := fmt.Sprintf(redis.CleanList, tenantId, collection) - keys, err := redis.RedisClient().Keys(ctx, listKeys) + listKeys := fmt.Sprintf(CleanList, tenantId, collection) + keys, err := g.Redis().Keys(ctx, listKeys) if err != nil { return } for _, key := range keys { - _, err = redis.RedisClient().Del(ctx, key) + _, err = g.Redis().Del(ctx, key) if err != nil { return } } - countKeys := fmt.Sprintf(redis.CleanCount, tenantId, collection) - keys, err = redis.RedisClient().Keys(ctx, countKeys) + countKeys := fmt.Sprintf(CleanCount, tenantId, collection) + keys, err = g.Redis().Keys(ctx, countKeys) if err != nil { return } for _, key := range keys { - _, err = redis.RedisClient().Del(ctx, key) + _, err = g.Redis().Del(ctx, key) if err != nil { return } @@ -383,8 +391,8 @@ func (m *mongoDB) CleanRedis(ctx context.Context, filter bson.M, tenantId interf filter["isDeleted"] = false delete(filter, "tenantId") filterKey := fmt.Sprintf("%+v", filter) - oneKey := fmt.Sprintf(redis.One, tenantId, collection, filterKey) - _, err = redis.RedisClient().Del(ctx, oneKey) + oneKey := fmt.Sprintf(One, tenantId, collection, filterKey) + _, err = g.Redis().Del(ctx, oneKey) if err != nil { return } @@ -422,10 +430,20 @@ func (m *mongoDB) log(ctx context.Context, ids []bson.ObjectID, filter bson.M, c log.CreatedAt = now log.UpdatedAt = now log.TenantId = tenantId - // 使用新的 context 进行 Redis 操作 - if _, err := redis.AddToStream(ctx, LogRedisKey, log); err != nil { + + // 将结构体转换为 map + values := gconv.Map(log) + // XADD streamKey * field1 value1 field2 value2 ... + args := make([]interface{}, 0, len(values)*2+2) + args = append(args, LogRedisKey, "*") // "*" 自动生成ID + for key, val := range values { + args = append(args, key, val) + } + _, err := g.Redis().Do(ctx, "XADD", args...) + if err != nil { glog.Error(ctx, "mongoLog-AddToStream err: %v", err) } + return } diff --git a/elasticsearch/client.go b/full-text-search/elasticsearch/client.go similarity index 100% rename from elasticsearch/client.go rename to full-text-search/elasticsearch/client.go diff --git a/message/connection_nats.go b/message/connection_nats.go deleted file mode 100644 index 5b33021..0000000 --- a/message/connection_nats.go +++ /dev/null @@ -1,167 +0,0 @@ -package message - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/gogf/gf/v2/frame/g" - "github.com/nats-io/nats.go" -) - -var ( - muNats sync.RWMutex - natsConns map[string]*nats.Conn // key: 数据源名称, value: NATS 连接 - natsJS map[string]nats.JetStreamContext // key: 数据源名称, value: JetStream 上下文 -) - -func init() { - natsConns = make(map[string]*nats.Conn) - natsJS = make(map[string]nats.JetStreamContext) -} - -// natsConnect 建立 NATS 连接 -func natsConnect(ctx context.Context, name string) error { - - if g.Cfg().MustGet(ctx, "nats").IsEmpty() { - g.Log().Errorf(ctx, "❌ NATS 配置不存在") - return fmt.Errorf("NATS Configuration does not exist") - } - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - g.Log().Infof(ctx, "🔔 NATS [%s] 开始创建连接", dsName) - muNats.Lock() - defer muNats.Unlock() - - // 安全地关闭旧连接(仅针对该数据源) - if oldConn, exists := natsConns[dsName]; exists && oldConn != nil && !oldConn.IsClosed() { - oldConn.Close() - delete(natsConns, dsName) - delete(natsJS, dsName) - } - - // 从配置文件读取 NATS 地址 - natsURL := g.Cfg().MustGet(ctx, fmt.Sprintf("nats.%s.url", dsName)).String() - if natsURL == "" { - // 默认使用本地地址 - natsURL = nats.DefaultURL - } - - // 连接选项配置 - opts := []nats.Option{ - nats.Name(fmt.Sprintf("goframe-nats-client-%s", dsName)), - nats.NoReconnect(), - nats.PingInterval(10 * time.Second), - nats.MaxPingsOutstanding(5), - nats.ClosedHandler(func(nc *nats.Conn) { - g.Log().Infof(ctx, "NATS [%s] 连接已关闭: %s", dsName, nc.ConnectedUrl()) - }), - nats.ErrorHandler(func(nc *nats.Conn, sub *nats.Subscription, err error) { - g.Log().Errorf(ctx, "❌ NATS [%s] 错误: %v", dsName, err) - }), - } - - newConn, err := nats.Connect(natsURL, opts...) - if err != nil { - g.Log().Errorf(ctx, "❌ NATS [%s] 连接失败: %v", dsName, err) - return err - } - - // 等待连接就绪 - if newConn.Status() != nats.CONNECTED { - select { - case <-time.After(5 * time.Second): - // 连接超时,清理资源 - newConn.Close() - g.Log().Errorf(ctx, "❌ NATS [%s] 连接超时", dsName) - return fmt.Errorf("NATS 连接超时") - case <-newConn.StatusChanged(nats.CONNECTED): - // 连接成功 - g.Log().Infof(ctx, "✅ NATS [%s] 连接成功: %s", dsName, newConn.ConnectedUrl()) - case <-ctx.Done(): - // 外部上下文被取消,清理资源 - newConn.Close() - g.Log().Errorf(ctx, "NATS [%s] 连接被取消: %v", dsName, ctx.Err()) - return fmt.Errorf("NATS 连接被取消: %w", ctx.Err()) - } - } - - // 创建 JetStream 实例 - newJS, err := newConn.JetStream(nats.MaxWait(10 * time.Second)) - if err != nil { - // 创建 JetStream 失败,清理连接 - newConn.Close() - g.Log().Errorf(ctx, "❌ NATS [%s] 创建 JetStream 失败: %v", dsName, err) - return err - } - - // 保存连接和 JetStream 上下文 - natsConns[dsName] = newConn - natsJS[dsName] = newJS - - return nil -} - -// natsPing 检测 NATS 连接状态 -func natsPing(ctx context.Context, name string) bool { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muNats.RLock() - defer muNats.RUnlock() - - nc, exists := natsConns[dsName] - if !exists || nc == nil || nc.IsClosed() || nc.Status() != nats.CONNECTED { - g.Log().Errorf(ctx, "❌ NATS [%s] 连接已关闭或不可用", dsName) - return false - } - g.Log().Infof(ctx, "📊 NATS [%s] 连接正常: %s", dsName, nc.ConnectedUrl()) - return true -} - -// natsClose 关闭 NATS 连接 -func natsClose(ctx context.Context, name string) error { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muNats.Lock() - defer muNats.Unlock() - - if nc, exists := natsConns[dsName]; exists && nc != nil && !nc.IsClosed() { - nc.Close() - } - delete(natsConns, dsName) - delete(natsJS, dsName) - - g.Log().Infof(ctx, "✅ NATS [%s] 连接已关闭", dsName) - return nil -} - -// getNatsConn 获取 NATS 连接(内部使用) -func getNatsConn(name string) *nats.Conn { - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - return natsConns[dsName] -} - -// getNatsJS 获取 JetStream 上下文(内部使用) -func getNatsJS(name string) nats.JetStreamContext { - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - return natsJS[dsName] -} diff --git a/message/connection_rabbitmq.go b/message/connection_rabbitmq.go deleted file mode 100644 index 8b8d47a..0000000 --- a/message/connection_rabbitmq.go +++ /dev/null @@ -1,164 +0,0 @@ -package message - -import ( - "context" - "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/gconv" - amqp "github.com/rabbitmq/amqp091-go" - "sync" -) - -var ( - muRabbitMQ sync.RWMutex - rabbitmqConns map[string]*amqp.Connection - rabbitmqChannels map[string]*amqp.Channel -) - -func init() { - rabbitmqConns = make(map[string]*amqp.Connection) - rabbitmqChannels = make(map[string]*amqp.Channel) -} - -// rabbitmqConnect 建立 RabbitMQ 连接 -func rabbitmqConnect(ctx context.Context, name string) error { - if g.Cfg().MustGet(ctx, "rabbitmq").IsEmpty() { - g.Log().Errorf(ctx, "❌ RabbitMQ 配置不存在") - return fmt.Errorf("RabbitMQ Configuration does not exist") - } - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - g.Log().Infof(ctx, "🔔 RabbitMQ [%s] 开始创建连接", dsName) - muRabbitMQ.Lock() - defer muRabbitMQ.Unlock() - - // 安全地关闭旧连接(仅针对该数据源) - if oldConn, exists := rabbitmqConns[dsName]; exists && oldConn != nil && !oldConn.IsClosed() { - oldConn.Close() - } - if oldChannel, exists := rabbitmqChannels[dsName]; exists && oldChannel != nil && !oldChannel.IsClosed() { - oldChannel.Close() - } - delete(rabbitmqConns, dsName) - delete(rabbitmqChannels, dsName) - - // 从配置文件读取 RabbitMQ 配置 - host := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.host", dsName)).String() - port := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.port", dsName)).Int() - username := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.username", dsName)).String() - password := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.password", dsName)).String() - vHost := g.Cfg().MustGet(ctx, fmt.Sprintf("rabbitmq.%s.vhost", dsName), "/").String() - if g.IsEmpty(host) { - return fmt.Errorf("❌ RabbitMQ 配置错误: host 不能为空 (数据源: %s)", dsName) - } - if g.IsEmpty(port) { - return fmt.Errorf("❌ RabbitMQ 配置错误: port 不能为空 (数据源: %s)", dsName) - } - if g.IsEmpty(username) { - return fmt.Errorf("❌ RabbitMQ 配置错误: username 不能为空 (数据源: %s)", dsName) - } - if g.IsEmpty(password) { - return fmt.Errorf("❌ RabbitMQ 配置错误: password 不能为空 (数据源: %s)", dsName) - } - // 构建连接 URL - url := "amqp://" + username + ":" + password + "@" + host + ":" + gconv.String(port) + "/" + vHost - - // 创建连接 - newConn, err := amqp.Dial(url) - if err != nil { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 连接失败: %v", dsName, err) - return err - } - - // 创建 Channel - newChannel, err := newConn.Channel() - if err != nil { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 创建 Channel 失败: %v", dsName, err) - newConn.Close() - return err - } - - // 保存连接和 Channel - rabbitmqConns[dsName] = newConn - rabbitmqChannels[dsName] = newChannel - - g.Log().Infof(ctx, "✅ RabbitMQ [%s] 连接成功", dsName) - return nil -} - -// rabbitmqPing 检测 RabbitMQ 连接状态 -func rabbitmqPing(ctx context.Context, name string) bool { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muRabbitMQ.RLock() - defer muRabbitMQ.RUnlock() - - conn, exists := rabbitmqConns[dsName] - channel, channelExists := rabbitmqChannels[dsName] - if !exists || conn == nil || conn.IsClosed() || !channelExists || channel == nil || channel.IsClosed() { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 连接已关闭或不可用", dsName) - return false - } - - g.Log().Infof(ctx, "📊 RabbitMQ [%s] 连接正常", dsName) - return true -} - -// rabbitmqClose 关闭 RabbitMQ 连接 -func rabbitmqClose(ctx context.Context, name string) error { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muRabbitMQ.Lock() - defer muRabbitMQ.Unlock() - - var lastErr error - - if channel, exists := rabbitmqChannels[dsName]; exists && channel != nil && !channel.IsClosed() { - if err := channel.Close(); err != nil { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 关闭 Channel 失败: %v", dsName, err) - lastErr = err - } - } - delete(rabbitmqChannels, dsName) - - if conn, exists := rabbitmqConns[dsName]; exists && conn != nil && !conn.IsClosed() { - if err := conn.Close(); err != nil { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] 关闭连接失败: %v", dsName, err) - lastErr = err - } - } - delete(rabbitmqConns, dsName) - - g.Log().Infof(ctx, "✅ RabbitMQ [%s] 连接已关闭", dsName) - return lastErr -} - -// getRabbitMQConn 获取 RabbitMQ 连接(内部使用) -func getRabbitMQConn(name string) *amqp.Connection { - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - return rabbitmqConns[dsName] -} - -// getRabbitMQChannel 获取 RabbitMQ Channel(内部使用) -func getRabbitMQChannel(name string) *amqp.Channel { - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - return rabbitmqChannels[dsName] -} diff --git a/message/connection_redis.go b/message/connection_redis.go deleted file mode 100644 index 528eca0..0000000 --- a/message/connection_redis.go +++ /dev/null @@ -1,198 +0,0 @@ -// ============================================================================= -// Redis 连接管理 -// 负责 Redis 的连接、重连、健康检查和优雅关闭 -// ============================================================================= - -package message - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/gogf/gf/v2/database/gredis" - "github.com/gogf/gf/v2/frame/g" -) - -var ( - muRedis sync.RWMutex - redisConns map[string]*gredis.Redis - redisConfigs map[string]*gredis.Config -) - -func init() { - redisConns = make(map[string]*gredis.Redis) - redisConfigs = make(map[string]*gredis.Config) -} - -// redisConnect 建立 Redis 连接 -// name: 数据源名称,如果为空则使用默认数据源 -func redisConnect(ctx context.Context, name string) error { - if g.Cfg().MustGet(ctx, "redis").IsEmpty() { - g.Log().Errorf(ctx, "❌ Redis 配置不存在") - return fmt.Errorf("redis Configuration does not exist") - } - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - g.Log().Infof(ctx, "🔔 Redis [%s] 开始创建连接", dsName) - muRedis.Lock() - defer muRedis.Unlock() - - // 安全地关闭旧连接(仅针对该数据源) - if oldRedis, exists := redisConns[dsName]; exists && oldRedis != nil { - oldRedis.Close(ctx) - delete(redisConns, dsName) - } - - // 从配置文件读取 Redis 配置 - redisAddr := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.address", dsName)).String() - if g.IsEmpty(redisAddr) { - g.Log().Errorf(ctx, "❌ Redis 配置错误: address 不能为空 (数据源: %s)", dsName) - return fmt.Errorf("❌ Redis 配置错误: address 不能为空 (数据源: %s)", dsName) - } - redisDB := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.db", dsName)).Int() - if redisDB < 0 || redisDB > 15 { - g.Log().Errorf(ctx, "❌ Redis 配置错误: db 必须在 0-15 之间 (当前值: %d)", redisDB) - return fmt.Errorf("❌ Redis 配置错误: db 必须在 0-15 之间 (当前值: %d)", redisDB) - } - idleTimeout := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.idleTimeout", dsName)).String() - redisIdleTimeout, err := time.ParseDuration(idleTimeout) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis idleTimeout 格式错误: %v", err) - return err - } - maxConnLifetime := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.maxConnLifetime", dsName)).String() - redisMaxConnLifetime, err := time.ParseDuration(maxConnLifetime) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis maxConnLifetime 格式错误: %v", err) - return err - } - waitTimeout := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.waitTimeout", dsName)).String() - redisWaitTimeout, err := time.ParseDuration(waitTimeout) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis waitTimeout 格式错误: %v", err) - return err - } - dialTimeout := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.dialTimeout", dsName)).String() - redisDialTimeout, err := time.ParseDuration(dialTimeout) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis dialTimeout 格式错误: %v", err) - return err - } - readTimeout := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.readTimeout", dsName)).String() - redisReadTimeout, err := time.ParseDuration(readTimeout) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis readTimeout 格式错误: %v", err) - return err - } - writeTimeout := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.writeTimeout", dsName)).String() - redisWriteTimeout, err := time.ParseDuration(writeTimeout) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis writeTimeout 格式错误: %v", err) - return err - } - maxActive := g.Cfg().MustGet(ctx, fmt.Sprintf("redis.%s.maxActive", dsName)).Int() - if g.IsEmpty(maxActive) { - g.Log().Errorf(ctx, "❌ Redis maxActive 配置错误: %v", maxActive) - return fmt.Errorf("❌ Redis maxActive 配置错误") - } - // 构建 GoFrame Redis 配置 - redisConfig := &gredis.Config{ - Address: redisAddr, - Db: redisDB, - IdleTimeout: redisIdleTimeout, - MaxConnLifetime: redisMaxConnLifetime, - WaitTimeout: redisWaitTimeout, - DialTimeout: redisDialTimeout, - ReadTimeout: redisReadTimeout, - WriteTimeout: redisWriteTimeout, - MaxActive: maxActive, - } - redisConfigs[dsName] = redisConfig - - // 使用 GoFrame 的 Redis 连接 - newRedis, err := gredis.New(redisConfig) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis [%s] 连接失败: %v", dsName, err) - return err - } - // 测试连接(直接调用避免死锁) - _, err = newRedis.Do(ctx, "PING") - if err != nil { - g.Log().Errorf(ctx, "❌ Redis [%s] 连接失败: ping 失败 - %v", dsName, err) - _ = newRedis.Close(ctx) - return err - } - - redisConns[dsName] = newRedis - g.Log().Infof(ctx, "✅ Redis [%s] 连接成功: %s (DB: %d)", dsName, redisAddr, redisDB) - return nil -} - -// redisPing 检测 Redis 连接状态(带超时保护) -func redisPing(ctx context.Context, name string) bool { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muRedis.RLock() - defer muRedis.RUnlock() - - rc, exists := redisConns[dsName] - if !exists || rc == nil { - g.Log().Errorf(ctx, "❌ Redis [%s] 连接未建立", dsName) - return false - } - - // 创建带超时的子上下文,避免死锁 - timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second) - defer cancel() - - _, err := rc.Do(timeoutCtx, "PING") - if err != nil { - g.Log().Errorf(ctx, "❌ Redis [%s] ping 失败: %v", dsName, err) - return false - } - - g.Log().Infof(ctx, "📊 Redis [%s] 连接正常", dsName) - return true -} - -// redisClose 关闭 Redis 连接 -func redisClose(ctx context.Context, name string) error { - // 确定数据源名称 - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - - muRedis.Lock() - defer muRedis.Unlock() - - if rc, exists := redisConns[dsName]; exists && rc != nil { - if err := rc.Close(ctx); err != nil { - g.Log().Errorf(ctx, "❌ Redis [%s] 关闭失败: %v", dsName, err) - return err - } - delete(redisConns, dsName) - } - - g.Log().Infof(ctx, "✅ Redis [%s] 连接已关闭", dsName) - return nil -} - -// getRedisConn 获取 Redis 连接(内部使用) -func getRedisConn(name string) *gredis.Redis { - dsName := "default" - if !g.IsEmpty(name) { - dsName = name - } - return redisConns[dsName] -} diff --git a/message/msg_interfaces.go b/message/msg_interfaces.go deleted file mode 100644 index db1c86d..0000000 --- a/message/msg_interfaces.go +++ /dev/null @@ -1,32 +0,0 @@ -package message - -import "context" - -type messagePublishConfig interface { - GetPublishMsgType() -} - -type messagePublishDelayConfig interface { - GetPublishDelayMsgType() -} - -type messageSubscribeConfig interface { - GetSubscribeMsgType() -} - -// messageUtil 消息队列公共配置接口 -// 只暴露核心的发布/订阅功能,配置访问器方法不需要在公共接口中 -type messageUtil interface { - // Publish 发布消息 - Publish(ctx context.Context, msg messagePublishConfig) error - // PublishDelay 发布延迟消息 - PublishDelay(ctx context.Context, msg messagePublishDelayConfig) error - // Subscribe 订阅消息 - Subscribe(ctx context.Context, msg messageSubscribeConfig) error - // Ping 检测连接状态 - Ping(ctx context.Context) bool - // Connect 连接 - Connect(ctx context.Context) error - // Close 关闭连接 - Close(ctx context.Context) error -} diff --git a/message/msg_plugin_manager.go b/message/msg_plugin_manager.go deleted file mode 100644 index 8340a8a..0000000 --- a/message/msg_plugin_manager.go +++ /dev/null @@ -1,114 +0,0 @@ -package message - -import ( - "context" - "fmt" - "time" - - "github.com/gogf/gf/v2/frame/g" - "sync" -) - -// MessageType 消息队列类型 -type messageType string - -const ( - // MessageRedis Redis 消息队列 - MessageRedis messageType = "redis" - // MessageRabbitMQ RabbitMQ 消息队列 - MessageRabbitMQ messageType = "rabbitmq" - // MessageNATS NATS 消息队列 - MessageNATS messageType = "nats" -) - -// configFactory 消息队列配置工厂函数类型 -type configFactory func() messageUtil - -// PluginManager 消息队列插件管理器 -type pluginManager struct { - mu sync.RWMutex - instances map[messageType]messageUtil // 已连接的插件实例 -} - -var ( - defaultPluginManager = newPluginManager() -) - -// newPluginManager 创建插件管理器 -func newPluginManager() *pluginManager { - return &pluginManager{ - instances: make(map[messageType]messageUtil), - } -} - -// register 注册插件(内部方法) -func (m *pluginManager) register(msgType messageType, instance messageUtil) error { - m.mu.Lock() - defer m.mu.Unlock() - m.instances[msgType] = instance - return nil -} - -// RegisterPlugin 注册消息队列插件 -// 所有插件必须通过此方法注册,自动进行连接检测 -// 只有连接成功的插件才会被注册,连接失败的插件不会被注册 -// 异步无限重连,只有连接成功了才注册 -// name: 数据源名称,用于标识不同的连接实例 -func RegisterPlugin(ctx context.Context, name string, msgType messageType, factory configFactory) error { - if factory == nil { - g.Log().Errorf(ctx, "❌ factory cannot be nil") - return fmt.Errorf("factory cannot be nil") - } - // 开启异步连接,无限重试直到成功 - go func() { - // 创建实例 - instance := factory() - // 创建通知 channel - pluginKey := fmt.Sprintf("%s-%s", msgType, name) - if !instance.Ping(ctx) { - // 使用统一的重连函数 - if err := commonConnect(ctx, msgType, name, func(ctx context.Context) error { - return instance.Connect(ctx) - }, func(ctx context.Context) error { - return instance.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", msgType, name, err) - return - } - } - // 连接成功,注册插件 - defaultPluginManager.mu.Lock() - defaultPluginManager.instances[messageType(pluginKey)] = instance - defaultPluginManager.mu.Unlock() - g.Log().Infof(ctx, "✅ [%s][%s] 插件注册成功", msgType, name) - }() - return nil -} - -// GetMsgPlugin 获取消息队列插件(默认数据源),如果未注册则等待 -func GetMsgPlugin(ctx context.Context, msgType messageType) (messageUtil, error) { - return GetMsgPluginWithName(ctx, msgType, "default") -} - -// GetMsgPluginWithName 获取指定数据源的消息队列插件,如果未注册则等待直到超时 -func GetMsgPluginWithName(ctx context.Context, msgType messageType, name string) (messageUtil, error) { - pluginKey := fmt.Sprintf("%s-%s", msgType, name) - - for { - defaultPluginManager.mu.RLock() - instance, ok := defaultPluginManager.instances[messageType(pluginKey)] - defaultPluginManager.mu.RUnlock() - - if ok { - return instance, nil - } - - // 未注册,等待一段时间后重试 - select { - case <-ctx.Done(): - return nil, fmt.Errorf("wait for plugin ready canceled: %s with datasource: %s", msgType, name) - default: - time.Sleep(3 * time.Second) - } - } -} diff --git a/message/nats_msg.go b/message/nats_msg.go deleted file mode 100644 index 365c171..0000000 --- a/message/nats_msg.go +++ /dev/null @@ -1,373 +0,0 @@ -package message - -import ( - "context" - "encoding/json" - "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/nats-io/nats.go" - "time" -) - -type NatsPublishMsgConfig struct { - QueueName string - Durable bool - Data any -} - -type NatsPublishDelayMsgConfig struct { - QueueName string - Durable bool - DelayTime int - Data any -} - -type NatsSubscribeMsgConfig struct { - QueueName string - ConsumerName string - Durable bool - DelayTime int - AutoAck bool - PrefetchCount int - HandleFunc func(ctx context.Context, message map[string]interface{}) error -} - -func (*NatsPublishMsgConfig) GetPublishMsgType() { - -} - -func (*NatsPublishDelayMsgConfig) GetPublishDelayMsgType() { - -} - -func (*NatsSubscribeMsgConfig) GetSubscribeMsgType() { - -} - -type natsMsg struct { - name string // 数据源名称 -} - -func init() { - // 注册 Nats 插件(默认数据源) - RegisterPlugin(context.Background(), "default", MessageNATS, func() messageUtil { - return &natsMsg{name: "default"} - }) -} - -// Connect 连接 NATS -func (c *natsMsg) Connect(ctx context.Context) error { - return natsConnect(ctx, c.name) -} - -// Ping 检测 NATS 连接状态 -func (c *natsMsg) Ping(ctx context.Context) bool { - return natsPing(ctx, c.name) -} - -// Close 关闭 NATS 连接 -func (c *natsMsg) Close(ctx context.Context) error { - return natsClose(ctx, c.name) -} - -// Publish 发布消息 -func (c *natsMsg) Publish(ctx context.Context, msgConfig messagePublishConfig) error { - cfg, ok := msgConfig.(*NatsPublishMsgConfig) - if !ok { - return fmt.Errorf("无效的 NATS 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("必须提供队列名称") - } - if g.IsEmpty(cfg.Data) { - return fmt.Errorf("必须提供数据") - } - return c.createPublish(ctx, cfg.QueueName, cfg.Durable, 0, cfg.Data) -} - -// PublishDelay 发布延迟消息 -func (c *natsMsg) PublishDelay(ctx context.Context, msgConfig messagePublishDelayConfig) error { - cfg, ok := msgConfig.(*NatsPublishDelayMsgConfig) - if !ok { - return fmt.Errorf("无效的 NATS 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("必须提供队列名称") - } - if g.IsEmpty(cfg.DelayTime) { - return fmt.Errorf("延迟时间必须大于 0") - } - if g.IsEmpty(cfg.Data) { - return fmt.Errorf("必须提供数据") - } - return c.createPublish(ctx, cfg.QueueName, cfg.Durable, cfg.DelayTime, cfg.Data) -} - -// Publish 发布消息 -func (c *natsMsg) createPublish(ctx context.Context, subject string, durable bool, delayTime int, data any) error { - delayMsg := delayTime > 0 - if err := c.createStream(ctx, subject, durable, delayMsg); err != nil { - return err - } - payload, err := json.Marshal(data) - if err != nil { - return fmt.Errorf("序列化数据失败: %w", err) - } - - m := nats.NewMsg(subject) - m.Data = payload // 所有消息都需要设置数据 - - if delayMsg { - // 使用 @at 指定具体延迟时间,而不是 @every 重复执行 - futureTime := time.Now().Add(time.Duration(delayTime) * time.Second).Format(time.RFC3339Nano) - m.Header.Set("Nats-Schedule", fmt.Sprintf("@at %s", futureTime)) - m.Subject = subject + ".schedule" - m.Header.Set("Nats-Schedule-Target", subject) - g.Log().Infof(ctx, "📅 NATS 延迟消息配置: DelayTime=%ds, Schedule=@at %s, Header=%s", delayTime, futureTime, m.Header) - } - - // 发布消息到 JetStream - js := getNatsJS(c.name) - if js == nil { - g.Log().Errorf(ctx, "❌ NATS [%s] JetStream 不存在", c.name) - return fmt.Errorf("NATS JetStream 不存在") - } - ack, err := js.PublishMsg(m) - if err != nil { - g.Log().Errorf(ctx, "❌ NATS 发布消息失败: err=%v, Subject=%s", err, m.Subject) - return err - } - g.Log().Infof(ctx, "✅ NATS 发布消息成功: Stream=%v, StreamSeq=%d", ack.Stream, ack.Sequence) - return nil -} - -// Subscribe 订阅消息 -func (c *natsMsg) Subscribe(ctx context.Context, msgConfig messageSubscribeConfig) error { - cfg, ok := msgConfig.(*NatsSubscribeMsgConfig) - if !ok { - return fmt.Errorf("无效的 NATS 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("必须提供队列名称") - } - if g.IsEmpty(cfg.ConsumerName) { - return fmt.Errorf("必须提供消费者名称") - } - if g.IsEmpty(cfg.HandleFunc) { - return fmt.Errorf("必须提供处理函数") - } - if g.IsEmpty(cfg.PrefetchCount) { - cfg.PrefetchCount = 1 - } - return c.createSubscribe(ctx, cfg.QueueName, cfg.ConsumerName, cfg.PrefetchCount, cfg.DelayTime, cfg.AutoAck, cfg.Durable, cfg.HandleFunc) -} - -// createSubscribe 内部订阅消息 -func (c *natsMsg) createSubscribe(ctx context.Context, subject, consumerName string, prefetchCount, delayTime int, autoAck, durable bool, handler func(ctx context.Context, message map[string]any) error) error { - g.Log().Infof(ctx, "🔔 NATS 开始订阅: QueueName=%s, ConsumerName=%s", subject, consumerName) - // 创建推送订阅的回调函数 - msgHandler := func(msg *nats.Msg) { - var data map[string]any - if err := json.Unmarshal(msg.Data, &data); err != nil { - g.Log().Errorf(ctx, "❌ 解析消息失败: %v", err) - return - } - g.Log().Infof(ctx, "📨 收到消息: Subject=%s, Data=%v", msg.Subject, data) - - // 处理业务逻辑 - if err := handler(ctx, data); err != nil { - g.Log().Errorf(ctx, "❌ 处理消息失败: %v", err) - if !autoAck { - if err := msg.Nak(); err != nil { - g.Log().Errorf(ctx, "❌ Nak 失败: %v", err) - return - } - return - } - } else { - g.Log().Infof(ctx, "✅ 处理消息成功") - } - if err := msg.Ack(); err != nil { - g.Log().Errorf(ctx, "❌ Ack 失败: %v", err) - } - } - delayMsg := delayTime > 0 - // 创建流 - if err := c.createStream(ctx, subject, durable, delayMsg); err != nil { - return err - } - // 获取 JetStream 上下文 - js := getNatsJS(c.name) - if js == nil { - g.Log().Errorf(ctx, "❌ NATS [%s] JetStream 不存在", c.name) - return fmt.Errorf("NATS JetStream 不存在") - } - // 创建推送订阅 - var sub *nats.Subscription - var err error - // 配置订阅选项 - 使用 DeliverSubject 创建 Push Consumer - subOpts := []nats.SubOpt{ - nats.Durable(consumerName), - nats.MaxAckPending(prefetchCount), - nats.DeliverSubject(consumerName), - } - if !autoAck { - subOpts = append(subOpts, nats.ManualAck()) - } - // 使用 Subscribe 创建推送订阅 - sub, err = js.Subscribe(subject, msgHandler, subOpts...) - if err != nil { - g.Log().Errorf(ctx, "创建推送订阅失败: %v", err) - return err - } - g.Log().Infof(ctx, "✅ NATS 推送订阅成功: Consumer=%s", consumerName) - // 启动后台 goroutine 监听上下文取消,用于清理订阅 - go func() { - <-ctx.Done() - g.Log().Infof(ctx, "订阅上下文取消,取消订阅") - if err := sub.Unsubscribe(); err != nil { - return - } - }() - - return nil -} - -// createStream 内部创建消费组 -func (c *natsMsg) createStream(ctx context.Context, subject string, durable, delayMsg bool) error { - streamName, storage := getStreamInfo(durable, delayMsg) - // 构建流配置 - // 如果是延迟消息,需要包含两个 subjects: - // 1. subject.schedule - 用于发送调度消息 - // 2. subject - 用于实际投递目标 - subjects := []string{subject} - if delayMsg { - subjects = []string{subject, subject + ".schedule"} - } - jsConfig := &StreamConfig{ - Name: streamName, - Subjects: subjects, - AllowMsgSchedules: delayMsg, // 延迟消息核心开关 - Storage: storage, - Discard: DiscardNew, // 达到上限删除旧消息 - } - nc := getNatsConn(c.name) - if !c.Ping(ctx) { - // 使用统一的重连函数 - if err := commonConnect(ctx, MessageNATS, c.name, func(ctx context.Context) error { - return c.Connect(ctx) - }, func(ctx context.Context) error { - return c.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", MessageNATS, c.name, err) - return err - } - } - if nc == nil { - g.Log().Errorf(ctx, "❌ NATS [%s] 连接不存在", c.name) - return fmt.Errorf("NATS 连接不存在") - } - err := jsStreamCreate(nc, jsConfig) - if err != nil { - g.Log().Errorf(ctx, "❌ 创建 Stream 失败: err=%v", err) - return err - } - g.Log().Infof(ctx, "✅ 创建 Stream 成功: stream=%s, subjects=%v, allowSchedules=%v", streamName, subjects, delayMsg) - return nil -} - -func getStreamInfo(durable, delayMsg bool) (string, StorageType) { - // Stream 不存在,创建新的 - streamName := "ordinary_msg_memory" - storage := MemoryStorage - - // 延迟消息必须使用 FileStorage(NATS 官方要求) - if delayMsg { - if durable { - streamName = "delay_msg_file" - storage = FileStorage - } else { - streamName = "delay_msg_memory" - storage = MemoryStorage - } - } else { - if durable { - streamName = "ordinary_msg_file" - storage = FileStorage - } - } - return streamName, storage -} - -const ( - // JSApiStreamCreateT is the endpoint to create new streams. - // Will return JSON response. - JSApiStreamCreateT = "$JS.API.STREAM.CREATE.%s" - - // JSApiStreamUpdateT is the endpoint to update existing streams. - // Will return JSON response. - JSApiStreamUpdateT = "$JS.API.STREAM.UPDATE.%s" -) - -// jsStreamCreate is for sending a stream create for fields that nats.go does not know about yet. -func jsStreamCreate(nc *nats.Conn, cfg *StreamConfig) error { - j, err := json.Marshal(cfg) - if err != nil { - return err - } - - msg, err := nc.Request(fmt.Sprintf(JSApiStreamCreateT, cfg.Name), j, time.Second*3) - if err != nil { - return err - } - - // 检查 API 响应中的错误 - var resp struct { - Error *struct { - Code int `json:"code"` - ErrCode int `json:"err_code"` - Description string `json:"description"` - } `json:"error,omitempty"` - } - if err := json.Unmarshal(msg.Data, &resp); err != nil { - return err - } - if resp.Error != nil { - // 如果 Stream 已存在,尝试更新 - if resp.Error.ErrCode == 10058 { // JSStreamNameExistErr - return jsStreamUpdate(nc, cfg) - } - return fmt.Errorf("JS API error: %s", resp.Error.Description) - } - - return nil -} - -// jsStreamUpdate is for sending a stream create for fields that nats.go does not know about yet. -func jsStreamUpdate(nc *nats.Conn, cfg *StreamConfig) error { - j, err := json.Marshal(cfg) - if err != nil { - return err - } - msg, err := nc.Request(fmt.Sprintf(JSApiStreamUpdateT, cfg.Name), j, time.Second*3) - if err != nil { - return err - } - - // 检查 API 响应中的错误 - var resp struct { - Error *struct { - Code int `json:"code"` - ErrCode int `json:"err_code"` - Description string `json:"description"` - } `json:"error,omitempty"` - } - if err := json.Unmarshal(msg.Data, &resp); err != nil { - return err - } - if resp.Error != nil { - return fmt.Errorf("JS API error: %s", resp.Error.Description) - } - - return nil -} diff --git a/message/nats_rpc.go b/message/nats_rpc.go deleted file mode 100644 index 8e09938..0000000 --- a/message/nats_rpc.go +++ /dev/null @@ -1,770 +0,0 @@ -package message - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/nats-io/nats.go" - "go.opentelemetry.io/otel/trace" - "reflect" - "sync" -) - -// ============ RPC 服务封装 ============ -// 以下方法提供了完全抽象的 RPC 调用接口 -// 调用方和响应方完全不需要知道底层使用的是 NATS 的发布订阅模式 - -// RPC 服务注册表 -var ( - rpcServices map[string]rpcHandler - rpcSubs map[string]*nats.Subscription // 服务名 -> 订阅 - rpcServicesMu sync.RWMutex - queueRPCServices map[string]map[string]rpcHandler // queueName -> subject -> handler - queueRPCSubs map[string]map[string]*nats.Subscription // queueName -> serviceName -> 订阅 - queueRPCMu sync.RWMutex - - // ============ TraceID 主动取消支持 ============ - // 全局映射表:TraceID -> CancelFunc,并发安全 - traceCancelMap map[string]context.CancelFunc - traceCancelMu sync.RWMutex - // 取消主题前缀 - cancelSubjectPrefix = "ctx.cancel.otel." - - // RPC 使用的默认数据源名称 - rpcDefaultDatasource = "default" -) - -// rpcHandler RPC 处理函数类型 -// 实现方只需要关注请求参数和返回值,无需了解底层 NATS 实现 -// 返回值可以是任意类型,会被自动序列化为 JSON -type rpcHandler func(ctx context.Context, req []byte) (any, error) - -// registerRPCService 注册 RPC 服务(单实例) -// serviceName: 服务名称,调用方通过此名称调用服务 -// handler: 服务处理函数,接收请求并返回响应 -func registerRPCService(serviceName string, handler rpcHandler) (err error) { - if !natsPing(context.Background(), rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接") - } - - rpcServicesMu.Lock() - if rpcServices == nil { - rpcServices = make(map[string]rpcHandler) - } - if rpcSubs == nil { - rpcSubs = make(map[string]*nats.Subscription) - } - - // 如果已存在该服务,先取消之前的订阅 - if oldSub, exists := rpcSubs[serviceName]; exists { - oldSub.Unsubscribe() - } - - rpcServices[serviceName] = handler - rpcServicesMu.Unlock() - - // 订阅服务主题 - nc := getNatsConn(rpcDefaultDatasource) - if nc == nil { - return fmt.Errorf("NATS 连接不存在") - } - - subject := fmt.Sprintf("rpc.%s", serviceName) - sub, err := nc.Subscribe(subject, func(msg *nats.Msg) { - // 执行处理函数 - executeHandler(handler, msg) - }) - - if err != nil { - return fmt.Errorf("注册 RPC 服务失败: %w", err) - } - - rpcSubs[serviceName] = sub - g.Log().Infof(context.Background(), "✅ RPC 服务已注册: %s", serviceName) - return nil -} - -// registerQueueRPCService 注册 RPC 服务(集群模式) -// 多个服务实例注册同一服务时,请求会自动负载均衡 -// serviceName: 服务名称 -// queueName: 队列组名,同一队列组的实例共享请求 -// handler: 服务处理函数 -func registerQueueRPCService(serviceName, queueName string, handler rpcHandler) (err error) { - if !natsPing(context.Background(), rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接") - } - - queueRPCMu.Lock() - if queueRPCServices == nil { - queueRPCServices = make(map[string]map[string]rpcHandler) - } - if queueRPCSubs == nil { - queueRPCSubs = make(map[string]map[string]*nats.Subscription) - } - if queueRPCServices[queueName] == nil { - queueRPCServices[queueName] = make(map[string]rpcHandler) - } - if queueRPCSubs[queueName] == nil { - queueRPCSubs[queueName] = make(map[string]*nats.Subscription) - } - - // 如果已存在该服务,先取消之前的订阅 - if oldSub, exists := queueRPCSubs[queueName][serviceName]; exists { - oldSub.Unsubscribe() - } - - queueRPCServices[queueName][serviceName] = handler - queueRPCMu.Unlock() - - // 订阅服务主题(队列模式) - nc := getNatsConn(rpcDefaultDatasource) - if nc == nil { - return fmt.Errorf("NATS 连接不存在") - } - - subject := fmt.Sprintf("rpc.%s", serviceName) - sub, err := nc.QueueSubscribe(subject, queueName, func(msg *nats.Msg) { - // 执行处理函数 - executeHandler(handler, msg) - }) - - if err != nil { - return fmt.Errorf("注册队列 RPC 服务失败: %w", err) - } - - queueRPCMu.Lock() - queueRPCSubs[queueName][serviceName] = sub - queueRPCMu.Unlock() - - g.Log().Infof(context.Background(), "✅ 队列 RPC 服务已注册: %s (队列组: %s)", serviceName, queueName) - return nil -} - -// executeHandler 执行 RPC 处理函数 -func executeHandler(handler rpcHandler, msg *nats.Msg) { - // 响应 - var respData []byte - // 从消息头重建上下文 - ctx := headersToContext(context.Background(), msg.Header) - // 提取 TraceID,创建可取消的 context - ctx = createCancelContext(ctx, msg.Header.Get(traceIDKey)) - // 检查 context 是否已取消(在调用 handler 之前) - select { - case <-ctx.Done(): - // context 已取消,返回取消错误 - g.Log().Infof(ctx, "RPC 请求已取消,traceID: %s", msg.Header.Get(traceIDKey)) - // 仍然需要发送响应以避免客户端超时 - respData = []byte(`{"_err":"请求已取消"}`) - // 清理取消映射表 - cleanupTraceCancel(msg.Header.Get(traceIDKey)) - return - default: - } - - // 执行业务处理 - response, err := handler(ctx, msg.Data) - - if err != nil { - // 错误时返回 {"_err": "错误信息"} - if respData, err = json.Marshal(map[string]any{"_err": err.Error()}); err != nil { - g.Log().Errorf(ctx, "RPC 错误响应序列化失败: %v", err) - respData = []byte(`{"_err":"错误响应序列化失败"}`) - } - } else if response == nil { - // 空响应时返回空对象(或 {"_err": ""}) - respData = []byte(`{}`) - } else { - // 成功时返回业务数据 - if respData, err = json.Marshal(response); err != nil { - g.Log().Errorf(ctx, "RPC 响应序列化失败: %v", err) - respData = []byte(`{"_err":"响应序列化失败"}`) - } - } - // 发送响应(必须执行) 如果客户端用 nc.Request(...) 发送消息 → 双向模式,服务端必须 msg.Respond - if err = msg.Respond(respData); err != nil { - g.Log().Errorf(ctx, "RPC 响应失败: %v", err) - } - // 请求结束,清理取消映射表 - cleanupTraceCancel(msg.Header.Get(traceIDKey)) -} - -// createCancelContext 创建可取消的 context 并注册到取消映射表 -// 返回可取消的 context(如果 traceID 为空则返回原 context) -func createCancelContext(ctx context.Context, traceID string) context.Context { - if g.IsEmpty(traceID) { - return ctx - } - // 创建带取消功能的 context - taskCtx, cancel := context.WithCancel(ctx) - // 注册到取消映射表 - traceCancelMu.Lock() - if traceCancelMap == nil { - traceCancelMap = make(map[string]context.CancelFunc) - } - // 如果同一 TraceID 已有 CancelFunc,先调用它 - if oldCancel, exists := traceCancelMap[traceID]; exists { - oldCancel() - } - traceCancelMap[traceID] = cancel - traceCancelMu.Unlock() - - return taskCtx -} - -// ============ TraceID 主动取消功能 ============ -// 以下函数实现了基于 OpenTelemetry TraceID 的跨进程任务取消机制 - -// SetupCancelListener 设置取消监听器 -// 订阅取消主题,监听取消指令 -// 使用示例: -// -// sub, err := nats.SetupCancelListener(ctx) -func setupCancelListener(ctx context.Context) (*nats.Subscription, error) { - if !natsPing(ctx, rpcDefaultDatasource) { - return nil, fmt.Errorf("NATS 未连接") - } - - if traceCancelMap == nil { - traceCancelMap = make(map[string]context.CancelFunc) - } - - // 修复问题3:订阅取消主题,格式: ctx.cancel.otel.* - // 使用 * 通配符而不是 >,因为 TraceID 是最后一部分 - nc := getNatsConn(rpcDefaultDatasource) - if nc == nil { - return nil, fmt.Errorf("NATS 连接不存在") - } - - cancelSubject := cancelSubjectPrefix + "*" - sub, err := nc.Subscribe(cancelSubject, func(msg *nats.Msg) { - // 从主题中解析 TraceID (去除前缀) - prefixLen := len(cancelSubjectPrefix) - if len(msg.Subject) <= prefixLen { - g.Log().Warningf(ctx, "取消消息主题格式错误: %s", msg.Subject) - return - } - traceID := msg.Subject[prefixLen:] - - if traceID == "" { - g.Log().Warning(ctx, "取消消息主题缺少 TraceID") - return - } - - // 从映射表获取 CancelFunc 并执行取消 - traceCancelMu.RLock() - cancel, ok := traceCancelMap[traceID] - traceCancelMu.RUnlock() - - if ok { - cancel() - g.Log().Infof(ctx, "📢 取消信号已发送,traceID: %s", traceID) - } else { - g.Log().Infof(ctx, "⚠️ 未找到对应的可取消任务,traceID: %s", traceID) - } - }) - - if err != nil { - return nil, fmt.Errorf("设置取消监听器失败: %w", err) - } - - g.Log().Infof(ctx, "✅ 取消监听器已设置: %s", cancelSubject) - return sub, nil -} - -// publishCancel 发布取消指令 -// 向指定 TraceID 发送取消信号 -// 使用示例: -// -// err := nats.publishCancel(ctx, traceID) -func publishCancel(ctx context.Context, traceID string) error { - if !natsPing(ctx, rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接") - } - - if traceID == "" { - return fmt.Errorf("TraceID 不能为空") - } - - nc := getNatsConn(rpcDefaultDatasource) - if nc == nil { - return fmt.Errorf("NATS 连接不存在") - } - - cancelSubject := cancelSubjectPrefix + traceID - err := nc.Publish(cancelSubject, nil) - if err != nil { - return fmt.Errorf("发布取消信号失败: %w", err) - } - - g.Log().Infof(ctx, "📤 已发送取消信号,traceID: %s,主题: %s", traceID, cancelSubject) - return nil -} - -// cleanupTraceCancel 清理取消映射表中的条目 -// 任务取消/正常结束后必须调用此函数,避免内存泄漏 -// 使用示例: -// -// defer nats.cleanupTraceCancel(traceID) -func cleanupTraceCancel(traceID string) { - if traceID == "" { - return - } - - traceCancelMu.Lock() - defer traceCancelMu.Unlock() - - if _, ok := traceCancelMap[traceID]; ok { - delete(traceCancelMap, traceID) - g.Log().Infof(context.Background(), "✅ 已清理取消映射表,traceID: %s", traceID) - } -} - -// CallRPC 调用 RPC 服务 -// serviceName: 服务名称 -// req: 请求数据 -// 返回: 响应数据(任意类型)和错误 -func CallRPC(ctx context.Context, serviceName string, req any, resp any) (err error) { - if !natsPing(ctx, rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接") - } - - // 验证 resp 必须是指针类型 - respValue := reflect.ValueOf(resp) - if respValue.Kind() != reflect.Ptr { - return fmt.Errorf("resp 参数必须是指针类型(当前类型: %T)", resp) - } - - // 构建请求体 - var reqBody []byte - if !g.IsEmpty(req) { - reqValue := reflect.ValueOf(req) - if !(reqValue.Kind() == reflect.Ptr && reqValue.IsNil()) && !reqValue.IsZero() { - reqData, err := json.Marshal(req) - if err != nil { - return fmt.Errorf("序列化请求参数失败: %w", err) - } - reqBody = reqData - } - } - - // 检查本地是否有注册的单实例服务,如果有则直接调用(优化性能) - rpcServicesMu.RLock() - if localHandler, exists := rpcServices[serviceName]; exists { - rpcServicesMu.RUnlock() - - // 修复问题1:本地调用也需要处理取消机制 - var traceID string - if traceID, err = getTraceID(ctx); err != nil { - return err - } - // 提取 TraceID,创建可取消的 context - cancelCtx := createCancelContext(ctx, traceID) - // 执行本地调用 - var response interface{} - if response, err = localHandler(cancelCtx, reqBody); err != nil { - return fmt.Errorf("本地调用 RPC 服务失败 [%s]: %w", serviceName, err) - } - - // 请求结束,清理取消映射表 - cleanupTraceCancel(traceID) - - // 检查是否为错误消息:尝试解析为 map,看是否包含 "_err" 字段 - var respMap map[string]any - if json.Unmarshal(response.([]byte), &respMap) == nil { - if errMsg, ok := respMap["_err"]; ok { - return fmt.Errorf("%v", errMsg) - } - } - // 正常数据直接返回 - // responseMsg.Data 已经是 []byte 类型(来自 msg.Data),直接反序列化 - if err = json.Unmarshal(response.([]byte), resp); err != nil { - return fmt.Errorf("解析响应失败: %w (响应内容: %s)", err, response) - } - - return - } - rpcServicesMu.RUnlock() - - subject := fmt.Sprintf("rpc.%s", serviceName) - - // 创建消息并将上下文元数据写入消息头 - msg := nats.NewMsg(subject) - msg.Data = reqBody - headers, err := contextToHeaders(ctx) - if err != nil { - return fmt.Errorf("上下文转换失败: %w", err) - } - msg.Header = headers - - // 修复问题5:优化 go 协程避免资源泄漏 - // 使用 done channel 来确保 goroutine 能正确退出 - done := make(chan struct{}) - var closeDoneOnce sync.Once - closeDone := func() { - closeDoneOnce.Do(func() { - close(done) - }) - } - - if msg.Header.Get(traceIDKey) != "" { - go func() { - defer closeDone() - select { - case <-ctx.Done(): - // context 被取消时,发送取消信号给服务端 - if errors.Is(ctx.Err(), context.Canceled) { - if err := publishCancel(context.Background(), msg.Header.Get(traceIDKey)); err != nil { - g.Log().Errorf(ctx, "发送 RPC 取消信号失败: %v", err) - } else { - g.Log().Infof(ctx, "RPC 调用已取消,traceID: %s", msg.Header.Get(traceIDKey)) - } - } - case <-done: - // 请求已完成,无需发送取消信号 - return - } - }() - } - - // 发送请求 - nc := getNatsConn(rpcDefaultDatasource) - if nc == nil { - return fmt.Errorf("NATS 连接不存在") - } - - responseMsg, err := nc.RequestMsgWithContext(ctx, msg) - - // 关闭 done channel,通知 goroutine 退出 - closeDone() - - if err != nil { - return fmt.Errorf("调用 RPC 服务失败 [%s]: %w", serviceName, err) - } - - if responseMsg == nil { - return fmt.Errorf("RPC 响应为空 [%s]", serviceName) - } - - // 解析响应 - if len(responseMsg.Data) > 0 { - // 检查是否为错误消息:尝试解析为 map,看是否包含 "_err" 字段 - var respMap map[string]any - if json.Unmarshal(responseMsg.Data, &respMap) == nil { - if errMsg, ok := respMap["_err"]; ok { - return fmt.Errorf("%v", errMsg) - } - } - // 正常数据直接返回 - // responseMsg.Data 已经是 []byte 类型(来自 msg.Data),直接反序列化 - if err = json.Unmarshal(responseMsg.Data, resp); err != nil { - return fmt.Errorf("解析响应失败: %w (响应内容: %s)", err, responseMsg.Data) - } - } - - return -} - -// RegisterServiceOption 注册选项类型 -type registerServiceOption func(*registerServiceConfig) - -type registerServiceConfig struct { - queueName string // 队列组名(用于集群模式) - excludeMethods []string -} - -// WithQueueGroup 设置队列组名(集群模式) -func WithQueueGroup(queueName string) registerServiceOption { - return func(cfg *registerServiceConfig) { - cfg.queueName = queueName - } -} - -// WithExcludeMethods 排除不需要注册的方法 -func WithExcludeMethods(methods ...string) registerServiceOption { - return func(cfg *registerServiceConfig) { - cfg.excludeMethods = append(cfg.excludeMethods, methods...) - } -} - -// AutoRegisterServices 自动注册多个服务的所有公开方法 -// serviceInstances: map[包名]service实例,如 map[string]interface{}{"user": userService, "order": orderService} -// options: 注册选项(可选) -// 示例: -// -// AutoRegisterServices(map[string]interface{}{ -// "user": userService, -// "order": orderService, -// }) -// 或 -// AutoRegisterServices(map[string]interface{}{ -// "order": orderService, -// }, WithQueueGroup("order-group")) -func AutoRegisterServices(ctx context.Context, serviceInstances map[string]interface{}, options ...registerServiceOption) error { - // 先注册 RPC 服务(如果 NATS 不可用则记录警告但不阻塞启动) - if !natsPing(ctx, rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接,RPC 服务未注册") - } - - if len(serviceInstances) == 0 { - return fmt.Errorf("service 实例列表不能为空") - } - - totalRegistered := 0 - // 遍历每个 service 实例 - for pkgName, serviceInstance := range serviceInstances { - // 注册服务 - err := registerService(serviceInstance, pkgName, options...) - if err != nil { - g.Log().Errorf(ctx, "注册 %s 服务失败: %v", pkgName, err) - continue - } - totalRegistered++ - g.Log().Infof(ctx, "✅ %s 服务已自动注册", pkgName) - } - - if totalRegistered == 0 { - return fmt.Errorf("未能注册任何服务") - } - // 设置取消监听器(监听基于 TraceID 的取消请求) - if _, err := setupCancelListener(ctx); err != nil { - g.Log().Errorf(ctx, "设置取消监听器失败: %v", err) - } else { - g.Log().Infof(ctx, "✅ 取消监听器已自动设置") - } - g.Log().Infof(ctx, "✅ 共自动注册了 %d 个服务", totalRegistered) - - return nil -} - -// registerService 注册单个服务的所有公开方法(内部函数) -func registerService(service interface{}, serviceNamePrefix string, options ...registerServiceOption) (err error) { - if !natsPing(context.Background(), rpcDefaultDatasource) { - return fmt.Errorf("NATS 未连接") - } - - // 应用选项 - cfg := ®isterServiceConfig{} - for _, opt := range options { - opt(cfg) - } - - // 创建排除方法集合 - excludeSet := make(map[string]struct{}) - for _, method := range cfg.excludeMethods { - excludeSet[method] = struct{}{} - } - - // 获取 service 的类型 - serviceType := reflect.TypeOf(service) - - // 遍历所有方法 - registeredCount := 0 - for i := 0; i < serviceType.NumMethod(); i++ { - method := serviceType.Method(i) - - // 只注册导出方法(首字母大写) - if !method.IsExported() { - continue - } - - // 排除指定的方法 - if _, exists := excludeSet[method.Name]; exists { - continue - } - - // 检查方法签名:必须是 func(ctx context.Context, request) (response, error) - // 注意:method.Type.NumIn() 包含接收者,所以实际参数数量需要减去 1 - // 要求:接收者 + context.Context + request,总共3个参数 - if method.Type.NumIn() != 3 { - g.Log().Warningf(context.Background(), "方法 %s 必须有2个参数(context.Context 和请求参数),跳过注册", method.Name) - continue - } - - // 第一个参数(接收者之后的第一个参数)必须是 context.Context - // method.Type.In(0) 是接收者,method.Type.In(1) 才是第一个参数 - if !method.Type.In(1).Implements(reflect.TypeOf((*context.Context)(nil)).Elem()) { - g.Log().Warningf(context.Background(), "方法 %s 的第一个参数必须是 context.Context,跳过注册", method.Name) - continue - } - - // 第二个参数必须是结构体指针或数组 - reqType := method.Type.In(2) - if reqType.Kind() != reflect.Ptr && reqType.Kind() != reflect.Slice && reqType.Kind() != reflect.Array { - g.Log().Warningf(context.Background(), "方法 %s 的第二个参数必须是结构体指针或数组,跳过注册", method.Name) - continue - } - - // 返回值必须是 (result, error),即2个返回值 - if method.Type.NumOut() != 2 { - g.Log().Warningf(context.Background(), "方法 %s 必须有2个返回值(result 和 error),跳过注册", method.Name) - continue - } - - // 最后一个返回值必须是 error - if !method.Type.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { - g.Log().Warningf(context.Background(), "方法 %s 的最后一个返回值必须是 error,跳过注册", method.Name) - continue - } - - // 生成服务名称:前缀.方法名(保持原始方法名) - serviceName := fmt.Sprintf("%s.%s", serviceNamePrefix, method.Name) - - // 创建 RPC handler - handler := func(ctx context.Context, req []byte) (any, error) { - // 准备方法调用参数 - // args[0] 是接收者, args[1] 是 ctx, args[2] 是请求参数 - args := make([]reflect.Value, 3) - args[0] = reflect.ValueOf(service) // 接收者 - args[1] = reflect.ValueOf(ctx) // context.Context - - // 解析请求参数 - if len(req) > 0 { - reqValuePtr := reflect.New(reqType) - - // 解析 JSON - if err := json.Unmarshal(req, reqValuePtr.Interface()); err != nil { - // 根据参数类型提供更友好的错误提示 - var typeHint string - if reqType.Kind() == reflect.Ptr { - typeHint = fmt.Sprintf("(期望类型: %s)", reqType.Elem().Name()) - } else { // reflect.Slice 或 reflect.Array - typeHint = fmt.Sprintf("(期望类型: %s,请确保客户端传递的是JSON数组格式)", reqType.String()) - } - return nil, fmt.Errorf("解析请求参数失败%s: %w", typeHint, err) - } - args[2] = reqValuePtr.Elem() - } else { - // 请求为空,创建零值 - args[2] = reflect.Zero(method.Type.In(2)) - } - - // 调用方法 - results := method.Func.Call(args) - - // 处理返回值 - var result any - - if len(results) == 1 { - // 只有 error - if !results[0].IsNil() { - err = results[0].Interface().(error) - } - } else if len(results) == 2 { - // (result, error) - result = results[0].Interface() - if !results[1].IsNil() { - err = results[1].Interface().(error) - } - } - if err != nil { - return nil, err - } - - return result, nil - } - - // 注册 RPC 服务 - var err error - if cfg.queueName != "" { - err = registerQueueRPCService(serviceName, cfg.queueName, handler) - } else { - err = registerRPCService(serviceName, handler) - } - - if err != nil { - g.Log().Errorf(context.Background(), "注册服务 %s 失败: %v", serviceName, err) - continue - } - - registeredCount++ - g.Log().Infof(context.Background(), "✅ 已自动注册 RPC 服务: %s -> %s", serviceName, method.Name) - } - - if registeredCount == 0 { - g.Log().Warningf(context.Background(), "未注册任何方法,请检查 %v 的方法签名", serviceNamePrefix) - return fmt.Errorf("未找到可注册的方法") - } - - g.Log().Infof(context.Background(), "✅ Service %v 共注册了 %d 个 RPC 方法", serviceNamePrefix, registeredCount) - return nil -} - -// ============ 上下文元数据工具函数 ============ -// 以下函数用于在 context 和 NATS 消息头之间互转元数据 - -// 定义常见的上下文元数据 key(私有) -const ( - traceIDKey = "trace_id" - tokenKey = "token" -) - -func getTraceID(ctx context.Context) (traceID string, err error) { - // 提取 traceId:首先尝试从 OpenTelemetry Span 中提取,从 context 中提取 TraceID - span := trace.SpanFromContext(ctx) - if span != nil && span.SpanContext().HasTraceID() { - traceID = span.SpanContext().TraceID().String() - } else if tid := ctx.Value(traceIDKey); tid != nil { - traceID = fmt.Sprintf("%v", tid) - } - if traceID == "" { - return traceID, fmt.Errorf("context 中没有 TraceID") - } - return -} - -// contextToHeaders 将 context 中的元数据转换为 NATS 消息头 -// 支持提取 user_id、tenant_id、trace_id、token 等常见字段 -func contextToHeaders(ctx context.Context) (nats.Header, error) { - headers := make(nats.Header) - - // 提取 traceId:首先尝试从 OpenTelemetry Span 中提取 - if traceID, err := getTraceID(ctx); err != nil { - return headers, err - } else { - headers.Set(traceIDKey, traceID) - } - - // 提取 token(优先级:context value > HTTP Authorization header) - token := "" - if t := ctx.Value(tokenKey); t != nil { - token = fmt.Sprintf("%v", t) - } else if r := g.RequestFromCtx(ctx); r != nil { - // 从 HTTP 请求的 Authorization header 中提取 token - auth := r.GetHeader("Authorization") - if auth != "" { - // 移除 "Bearer " 前缀 - if len(auth) > 7 && auth[:7] == "Bearer " { - token = auth[7:] - } else { - token = auth - } - } - } - if token != "" { - headers.Set(tokenKey, token) - } - - return headers, nil -} - -// headersToContext 从 NATS 消息头重建 context -// 支持还原 user_id、tenant_id、trace_id、token 等字段 -func headersToContext(ctx context.Context, headers nats.Header) context.Context { - if headers == nil { - return ctx - } - - // 恢复 trace_id - if traceID := headers.Get(traceIDKey); traceID != "" { - ctx = context.WithValue(ctx, traceIDKey, traceID) - } - - // 恢复 token - if token := headers.Get(tokenKey); token != "" { - ctx = context.WithValue(ctx, tokenKey, token) - } - - return ctx -} diff --git a/message/rabbitmq_msg.go b/message/rabbitmq_msg.go deleted file mode 100644 index 3eab2a8..0000000 --- a/message/rabbitmq_msg.go +++ /dev/null @@ -1,311 +0,0 @@ -package message - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/gogf/gf/v2/frame/g" - amqp "github.com/rabbitmq/amqp091-go" -) - -type RabbitMQPublishMsgConfig struct { - QueueName string - Durable bool - Data any -} - -type RabbitMQPublishDelayMsgConfig struct { - QueueName string - Durable bool - DelayTime int - Data any -} - -type RabbitMQSubscribeMsgConfig struct { - QueueName string - ConsumerName string - AutoAck bool - PrefetchCount int - HandleFunc func(ctx context.Context, message map[string]interface{}) error -} - -func (*RabbitMQPublishMsgConfig) GetPublishMsgType() { - -} - -func (*RabbitMQPublishDelayMsgConfig) GetPublishDelayMsgType() {} - -func (*RabbitMQSubscribeMsgConfig) GetSubscribeMsgType() { - -} - -type rabbitMQ struct { - name string // 数据源名称 -} - -func init() { - // 注册 RabbitMQ 插件(默认数据源) - RegisterPlugin(context.Background(), "default", MessageRabbitMQ, func() messageUtil { - return &rabbitMQ{name: "default"} - }) -} - -// Connect 连接 RabbitMQ -func (c *rabbitMQ) Connect(ctx context.Context) error { - return rabbitmqConnect(ctx, c.name) -} - -// Ping 检测 RabbitMQ 连接状态 -func (c *rabbitMQ) Ping(ctx context.Context) bool { - return rabbitmqPing(ctx, c.name) -} - -// Close 关闭 RabbitMQ 连接 -func (c *rabbitMQ) Close(ctx context.Context) error { - return rabbitmqClose(ctx, c.name) -} - -// Publish 发布消息 -func (c *rabbitMQ) Publish(ctx context.Context, msgConfig messagePublishConfig) error { - cfg, ok := msgConfig.(*RabbitMQPublishMsgConfig) - if !ok { - return fmt.Errorf("无效的 RabbitMQ 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("队列名称不能为空") - } - if cfg.Data == nil { - return fmt.Errorf("数据不能为空") - } - return c.publishMessageInternal(ctx, cfg.QueueName, cfg.Durable, 0, cfg.Data) -} - -// PublishDelay 发布延迟消息 -func (c *rabbitMQ) PublishDelay(ctx context.Context, msgConfig messagePublishDelayConfig) error { - cfg, ok := msgConfig.(*RabbitMQPublishDelayMsgConfig) - if !ok { - return fmt.Errorf("无效的 RabbitMQ 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("队列名称不能为空") - } - if cfg.Data == nil { - return fmt.Errorf("数据不能为空") - } - return c.publishMessageInternal(ctx, cfg.QueueName, cfg.Durable, cfg.DelayTime, cfg.Data) -} - -// publishMessage 发布消息内部实现 -func (c *rabbitMQ) publishMessageInternal(ctx context.Context, queueName string, durable bool, delayTime int, data interface{}) error { - if !c.Ping(ctx) { - if err := commonConnect(ctx, MessageRabbitMQ, c.name, func(ctx context.Context) error { - return c.Connect(ctx) - }, func(ctx context.Context) error { - return c.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", MessageRabbitMQ, c.name, err) - return err - } - } - - channel := getRabbitMQChannel(c.name) - if channel == nil || channel.IsClosed() { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] Channel 不存在或已关闭", c.name) - return fmt.Errorf("RabbitMQ Channel 不存在或已关闭") - } - - delayMsg := delayTime > 0 - - // 1. 决定 Exchange 类型 - exchangeType := "fanout" - exchangeName := queueName - routingKey := queueName - args := amqp.Table{} - if delayMsg { - exchangeType = "x-delayed-message" - exchangeName = queueName + ".delayed" - args["x-delayed-type"] = "fanout" - } - - // 2. 声明 Exchange(使用 exchangeName 而不是 queueName) - if err := channel.ExchangeDeclare( - exchangeName, // 修复:使用正确的交换机名称 - exchangeType, - durable, - false, // autoDelete - false, // internal - false, // noWait - args, - ); err != nil { - g.Log().Errorf(ctx, "❌ 声明 Exchange 失败: %v", err) - return err - } - - // 3. 声明队列 - if _, err := channel.QueueDeclare( - queueName, - durable, - false, // autoDelete - false, // exclusive - false, // noWait - nil, // args - ); err != nil { - g.Log().Errorf(ctx, "❌ 声明队列失败: %v", err) - return err - } - - // 4. 绑定队列 - if err := channel.QueueBind( - queueName, - routingKey, // routingKey 路由键 - exchangeName, // exchange 交换机名称 - false, // noWait - nil, // args - ); err != nil { - g.Log().Errorf(ctx, "❌ 绑定队列失败: %v", err) - return err - } - - // 5. 序列化数据 - body, err := json.Marshal(data) - if err != nil { - g.Log().Errorf(ctx, "❌ 序列化数据失败: %v", err) - return err - } - // 6. 发布消息 - deliveryMode := amqp.Transient - if durable { - deliveryMode = amqp.Persistent - } - publishing := amqp.Publishing{ - ContentType: "application/json", - Body: body, - DeliveryMode: deliveryMode, - Timestamp: time.Now(), - } - if delayMsg { - duration := delayTime * 1000 // 延迟时间(毫秒)= 秒 * 1000 - publishing.Headers = amqp.Table{ - "x-delay": duration, - } - } - err = channel.PublishWithContext( - ctx, - exchangeName, - routingKey, - false, false, - publishing, - ) - if err != nil { - g.Log().Errorf(ctx, "❌ 发布消息失败: %v", err) - return err - } - g.Log().Infof(ctx, "📨 发布消息成功: queueName=%s, data=%v", queueName, data) - return err -} - -// Subscribe 订阅消息 -func (c *rabbitMQ) Subscribe(ctx context.Context, msgConfig messageSubscribeConfig) error { - cfg, ok := msgConfig.(*RabbitMQSubscribeMsgConfig) - if !ok { - return fmt.Errorf("无效的 RabbitMQ 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("队列名称不能为空") - } - if g.IsEmpty(cfg.ConsumerName) { - return fmt.Errorf("消费者名称不能为空") - } - if g.IsEmpty(cfg.PrefetchCount) { - cfg.PrefetchCount = 1 - } - if g.IsEmpty(cfg.HandleFunc) { - return fmt.Errorf("必须提供处理函数") - } - return c.createSubscribeInternal(ctx, cfg.QueueName, cfg.ConsumerName, cfg.PrefetchCount, cfg.AutoAck, cfg.HandleFunc) -} - -// createSubscribe 内部订阅消息 -func (c *rabbitMQ) createSubscribeInternal(ctx context.Context, queueName, consumerName string, prefetchCount int, autoAck bool, handler func(ctx context.Context, message map[string]interface{}) error) error { - g.Log().Infof(ctx, "🔔 RabbitMQ [%s] 开始订阅: queueName=%s, consumerName=%s", c.name, queueName, consumerName) - - if !c.Ping(ctx) { - if err := commonConnect(ctx, MessageRabbitMQ, c.name, func(ctx context.Context) error { - return c.Connect(ctx) - }, func(ctx context.Context) error { - return c.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", MessageRabbitMQ, c.name, err) - return err - } - } - - channel := getRabbitMQChannel(c.name) - if channel == nil || channel.IsClosed() { - g.Log().Errorf(ctx, "❌ RabbitMQ [%s] Channel 不存在或已关闭", c.name) - return fmt.Errorf("RabbitMQ Channel 不存在或已关闭") - } - - if err := channel.Qos(prefetchCount, 0, false); err != nil { - g.Log().Errorf(ctx, "❌ 设置 Qos 失败: %v", err) - return err - } - g.Log().Infof(ctx, "📊 设置 Prefetch Count: %d", prefetchCount) - - msg, err := channel.Consume( - queueName, // queue - consumerName, // consumer - autoAck, // auto-ack (根据配置决定) - false, // exclusive - false, // no-local - false, // no-wait - nil, // args - ) - if err != nil { - g.Log().Errorf(ctx, "❌ 消费消息失败: %v", err) - return err - } - g.Log().Infof(ctx, "👀 开始监听消息") - for { - select { - case <-ctx.Done(): - // Context 取消,退出 - g.Log().Infof(ctx, "context cancel 监听消息退出") - return nil - case m, ok := <-msg: - if !ok { - // Channel 关闭,退出 - g.Log().Infof(ctx, "channel close 监听消息退出") - return nil - } - g.Log().Infof(ctx, "📨 收到消息: %s", string(m.Body)) - - var data map[string]interface{} - if err := json.Unmarshal(m.Body, &data); err != nil { - // 如果不是 JSON,直接使用原始内容 - data = map[string]interface{}{ - "data": string(m.Body), - } - } - err := handler(ctx, data) - if err != nil { - g.Log().Errorf(ctx, "❌ 消息处理失败: %v", err) - // 仅在手动 ACK 模式下拒绝消息 - if !autoAck { - // 拒绝消息不再重新入队(避免死循环) - m.Nack(false, false) - continue - } - } - g.Log().Infof(ctx, "✅ 消息处理成功: %v", err) - // 仅在手动 ACK 模式下确认消息 - if err := m.Ack(false); err != nil { - g.Log().Errorf(ctx, "❌ AUTO ACK 消息失败: %v", err) - } else { - g.Log().Infof(ctx, "✅ AUTO ACK 消息成功") - } - } - } -} diff --git a/message/reconnect.go b/message/reconnect.go deleted file mode 100644 index f042397..0000000 --- a/message/reconnect.go +++ /dev/null @@ -1,73 +0,0 @@ -package message - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/gogf/gf/v2/frame/g" -) - -// connectFunc 连接函数类型 -type connectFunc func(ctx context.Context) error - -// closeFunc 关闭函数类型 -type closeFunc func(ctx context.Context) error - -// reconnectOption 重连选项 -type reconnectOption struct { - maxRetries int // 最大重试次数,0 表示无限重试 - interval time.Duration // 重试间隔 - componentType messageType // 组件类型(nats/redis/rabbitmq) - componentName string // 组件名称(数据源名称) -} - -// defaultReconnectOption 默认重连选项 -func defaultReconnectOption(componentType messageType, componentName string) *reconnectOption { - return &reconnectOption{ - maxRetries: 0, // 无限重试 - interval: 3 * time.Second, - componentType: componentType, - componentName: componentName, - } -} - -// commonReconnect 重连函数(NATS、Redis、RabbitMQ 共用) -func commonReconnect(ctx context.Context, connectFn connectFunc, closeFn closeFunc, opt *reconnectOption) error { - if opt == nil { - opt = defaultReconnectOption("unknown", "default") - } - - for attempt := 0; opt.maxRetries == 0 || attempt < opt.maxRetries; attempt++ { - err := connectFn(ctx) - if err == nil { - g.Log().Infof(ctx, "✅ 连接成功: type=%s, name=%s, attempt=%d", - opt.componentType, opt.componentName, attempt+1) - return nil - } - // 记录失败日志 - g.Log().Warningf(ctx, "⚠️ 连接失败: type=%s, name=%s, attempt=%d, err=%v, 重试中...", - opt.componentType, opt.componentName, attempt+1, err) - // 如果错误信息中包含 "does not exist",则认为是连接失败,不再重试 - if strings.Contains(err.Error(), "does not exist") { - return err - } - // 等待一段时间再重试 - select { - case <-time.After(opt.interval): - case <-ctx.Done(): - if err = closeFn(ctx); err != nil { - return err - } - return ctx.Err() - } - } - return fmt.Errorf("连接失败,已达最大重试次数") -} - -// connect 连接函数,直接调用 commonReconnect -func commonConnect(ctx context.Context, componentType messageType, name string, connectFn func(ctx context.Context) error, closeFn closeFunc) error { - opt := defaultReconnectOption(componentType, name) - return commonReconnect(ctx, connectFn, closeFn, opt) -} diff --git a/message/redis_msg.go b/message/redis_msg.go deleted file mode 100644 index 343acb9..0000000 --- a/message/redis_msg.go +++ /dev/null @@ -1,279 +0,0 @@ -package message - -import ( - "context" - "fmt" - "github.com/gogf/gf/v2/os/glog" - "strings" - "time" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/gconv" -) - -type RedisPublishMsgConfig struct { - QueueName string - Data any -} - -type RedisPublishDelayMsgConfig struct { -} - -type RedisSubscribeMsgConfig struct { - QueueName string - ConsumerName string - AutoAck bool - PrefetchCount int - HandleFunc func(ctx context.Context, message map[string]interface{}) error -} - -func (*RedisPublishMsgConfig) GetPublishMsgType() { - -} - -func (*RedisPublishDelayMsgConfig) GetPublishDelayMsgType() {} - -func (*RedisSubscribeMsgConfig) GetSubscribeMsgType() { - -} - -type redis struct { - name string // 数据源名称 -} - -func init() { - // 注册 Redis 插件(默认数据源) - RegisterPlugin(context.Background(), "default", MessageRedis, func() messageUtil { - return &redis{name: "default"} - }) -} - -// RedisStreamMessage Redis Stream 消息结构 -type redisStreamMessage struct { - ID string - Values map[string]interface{} -} - -// Connect 连接 Redis -func (c *redis) Connect(ctx context.Context) error { - return redisConnect(ctx, c.name) -} - -// Ping 检测 Redis 连接状态 -func (c *redis) Ping(ctx context.Context) bool { - return redisPing(ctx, c.name) -} - -// Close 关闭 Redis 连接 -func (c *redis) Close(ctx context.Context) error { - return redisClose(ctx, c.name) -} - -// Publish 发布消息 -func (c *redis) Publish(ctx context.Context, msgConfig messagePublishConfig) error { - cfg, ok := msgConfig.(*RedisPublishMsgConfig) - if !ok { - return fmt.Errorf("无效的 Redis 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("队列名称不能为空") - } - if g.IsEmpty(cfg.Data) { - return fmt.Errorf("数据不能为空") - } - - rc := getRedisConn(c.name) - if !c.Ping(ctx) { - if err := commonConnect(ctx, MessageRedis, c.name, func(ctx context.Context) error { - return c.Connect(ctx) - }, func(ctx context.Context) error { - return c.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", MessageRedis, c.name, err) - return err - } - } - - values := gconv.Map(cfg.Data) - args := make([]interface{}, 0, len(values)*2+2) - args = append(args, cfg.QueueName, "*") - for key, val := range values { - args = append(args, key, val) - } - result, err := rc.Do(ctx, "XADD", args...) - if err != nil { - g.Log().Errorf(ctx, "❌ Redis 发布消息失败: key=%s, err=%v", cfg.QueueName, err) - return err - } - g.Log().Infof(ctx, "✅ Redis 发布消息成功: key=%s, messageID=%s", cfg.QueueName, gconv.String(result)) - return nil -} - -// PublishDelay 发布延迟消息 -func (c *redis) PublishDelay(ctx context.Context, _ messagePublishDelayConfig) error { - g.Log().Errorf(ctx, "❌ Redis 不支持延迟消息") - return fmt.Errorf("❌ Redis 不支持延迟消息") -} - -// Subscribe 订阅消息 -func (c *redis) Subscribe(ctx context.Context, msgConfig messageSubscribeConfig) error { - cfg, ok := msgConfig.(*RedisSubscribeMsgConfig) - if !ok { - return fmt.Errorf("无效的 Redis 配置类型") - } - if g.IsEmpty(cfg.QueueName) { - return fmt.Errorf("队列名称不能为空") - } - if g.IsEmpty(cfg.ConsumerName) { - return fmt.Errorf("消费者名称不能为空") - } - if g.IsEmpty(cfg.HandleFunc) { - return fmt.Errorf("处理函数不能为空") - } - return c.createSubscribe(ctx, cfg.QueueName, cfg.ConsumerName, cfg.PrefetchCount, cfg.AutoAck, cfg.HandleFunc) -} - -// createSubscribe 内部订阅消息 -func (c *redis) createSubscribe(ctx context.Context, key, consumerName string, prefetchCount int, autoAck bool, handler func(ctx context.Context, message map[string]interface{}) error) error { - -LOOP: - err := c.consumeMessages(ctx, key, consumerName, prefetchCount, autoAck, handler) - if err != nil { - // 对于超时错误,返回nil继续循环,而不是返回错误 - if strings.Contains(err.Error(), "i/o timeout") || strings.Contains(err.Error(), "timeout") || - strings.Contains(err.Error(), "context deadline exceeded") || strings.Contains(err.Error(), "context canceled") { - - time.Sleep(time.Second) - goto LOOP - } else { - g.Log().Errorf(ctx, "❌ 严重错误: %v", err) - } - } - time.Sleep(time.Second) - goto LOOP -} - -// consumeMessages 消费消息 -func (c *redis) consumeMessages(ctx context.Context, key, consumerName string, prefetchCount int, autoAck bool, handler func(ctx context.Context, message map[string]interface{}) error) error { - if !c.Ping(ctx) { - if err := commonConnect(ctx, MessageRedis, c.name, func(ctx context.Context) error { - return c.Connect(ctx) - }, func(ctx context.Context) error { - return c.Close(ctx) - }); err != nil { - g.Log().Errorf(ctx, "❌ [%s][%s] 连接失败: %v", MessageRedis, c.name, err) - return err - } - } - - rc := getRedisConn(c.name) - if rc == nil { - g.Log().Errorf(ctx, "❌ Redis [%s] 连接不存在", c.name) - return fmt.Errorf("Redis 连接不存在") - } - - // 检查消费者组是否存在 - groupName := "default" - _, err := rc.Do(ctx, "XGROUP", "CREATE", key, groupName, "0", "MKSTREAM") - if err != nil { - errStr := err.Error() - if strings.Contains(errStr, "BUSYGROUP") && strings.Contains(errStr, "already exists") { - glog.Infof(ctx, "✅ Redis [%s] 消费者组已存在: %s", c.name, key) - return nil - } - g.Log().Errorf(ctx, "❌ 创建消费组失败: key=%s, err=%v", key, err) - return err - } - glog.Infof(ctx, "✅ Redis [%s] 消费者组创建成功: %s", c.name, key) - - // 使用带重试的命令执行 - result, err := rc.Do(ctx, "XREADGROUP", "GROUP", groupName, consumerName, "COUNT", prefetchCount, "BLOCK", 0, "STREAMS", key, ">") - if err != nil { - return err - } - messages, err := c.parseStreamResult(result) - if err != nil { - g.Log().Errorf(ctx, "❌ 解析消息失败: %v", err) - return err - } - for _, msg := range messages { - // 处理消息 - if err := handler(ctx, msg.Values); err != nil { - g.Log().Errorf(ctx, "❌ 消息处理失败: messageID=%s, err=%v", msg.ID, err) - // 如果不是自动ACK,则跳过当前消息 - if !autoAck { - continue - } - } else { - g.Log().Infof(ctx, "✅ 消息处理成功: messageID=%s", msg.ID) - } - // ACK 消息 - args := make([]interface{}, 0, len(msg.ID)+2) - args = append(args, key, groupName, msg.ID) - _, err = rc.Do(ctx, "XACK", args...) - if err != nil { - g.Log().Errorf(ctx, "❌ ACK 消息失败: messageID=%s, err=%v", msg.ID, err) - } else { - g.Log().Infof(ctx, "✅ ACK 消息成功: messageID=%s", msg.ID) - } - } - - return nil -} - -// parseStreamResult 解析 Stream 结果 -func (c *redis) parseStreamResult(result interface{}) ([]redisStreamMessage, error) { - if result == nil { - return []redisStreamMessage{}, nil - } - - var resultVal interface{} - - // 尝试获取 Val() 方法 - if valuer, ok := result.(interface{ Val() interface{} }); ok { - resultVal = valuer.Val() - } else { - resultVal = result - } - - // 检查是否为空 - if resultVal == nil { - return []redisStreamMessage{}, nil - } - - // 预分配切片容量,避免多次扩容 - messages := make([]redisStreamMessage, 0) - - if streamsMap, ok := resultVal.(map[interface{}]interface{}); ok { - for _, streamData := range streamsMap { - msgArray, ok := streamData.([]interface{}) - if !ok { - continue - } - for _, msgData := range msgArray { - msgArray, ok := msgData.([]interface{}) - if !ok || len(msgArray) < 2 { - continue - } - msgID := gconv.String(msgArray[0]) - fieldsArray, ok := msgArray[1].([]interface{}) - if !ok { - continue - } - values := make(map[string]interface{}, len(fieldsArray)/2) - for i := 0; i < len(fieldsArray); i += 2 { - if i+1 < len(fieldsArray) { - key := gconv.String(fieldsArray[i]) - values[key] = fieldsArray[i+1] - } - } - messages = append(messages, redisStreamMessage{ - ID: msgID, - Values: values, - }) - } - } - } - - return messages, nil -} diff --git a/message/store.go b/message/store.go deleted file mode 100644 index 15f8bc5..0000000 --- a/message/store.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019-2025 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package message - -import "fmt" - -type RetentionPolicy int - -const ( - // LimitsPolicy (default) means that messages are retained until any given limit is reached. - // This could be one of MaxMsgs, MaxBytes, or MaxAge. - LimitsPolicy RetentionPolicy = iota - // InterestPolicy specifies that when all known consumers have acknowledged a message it can be removed. - InterestPolicy - // WorkQueuePolicy specifies that when the first worker or subscriber acknowledges the message it can be removed. - WorkQueuePolicy -) - -// MarshalJSON 将 RetentionPolicy 序列化为字符串 -func (rp RetentionPolicy) MarshalJSON() ([]byte, error) { - switch rp { - case LimitsPolicy: - return []byte(`"limits"`), nil - case InterestPolicy: - return []byte(`"interest"`), nil - case WorkQueuePolicy: - return []byte(`"workqueue"`), nil - default: - return nil, fmt.Errorf("can not marshal %v", rp) - } -} - -// UnmarshalJSON 将字符串反序列化为 RetentionPolicy -func (rp *RetentionPolicy) UnmarshalJSON(data []byte) error { - switch string(data) { - case `"limits"`: - *rp = LimitsPolicy - case `"interest"`: - *rp = InterestPolicy - case `"workqueue"`: - *rp = WorkQueuePolicy - default: - return fmt.Errorf("unknown retention policy: %s", string(data)) - } - return nil -} - -type DiscardPolicy int - -const ( - // DiscardOld will remove older messages to return to the limits. - DiscardOld = iota - // DiscardNew will error on a StoreMsg call - DiscardNew -) - -// MarshalJSON 将 DiscardPolicy 序列化为字符串 -func (dp DiscardPolicy) MarshalJSON() ([]byte, error) { - switch dp { - case DiscardOld: - return []byte(`"old"`), nil - case DiscardNew: - return []byte(`"new"`), nil - default: - return nil, fmt.Errorf("can not marshal %v", dp) - } -} - -// UnmarshalJSON 将字符串反序列化为 DiscardPolicy -func (dp *DiscardPolicy) UnmarshalJSON(data []byte) error { - switch string(data) { - case `"old"`: - *dp = DiscardOld - case `"new"`: - *dp = DiscardNew - default: - return fmt.Errorf("unknown discard policy: %s", string(data)) - } - return nil -} - -type StorageType int - -const ( - // FileStorage specifies on disk, designated by the JetStream config StoreDir. - FileStorage = StorageType(22) - // MemoryStorage specifies in memory only. - MemoryStorage = StorageType(33) -) - -// MarshalJSON 将 StorageType 序列化为字符串 -func (st StorageType) MarshalJSON() ([]byte, error) { - switch st { - case MemoryStorage: - return []byte(`"memory"`), nil - case FileStorage: - return []byte(`"file"`), nil - default: - return nil, fmt.Errorf("can not marshal %v", st) - } -} - -// UnmarshalJSON 将字符串反序列化为 StorageType -func (st *StorageType) UnmarshalJSON(data []byte) error { - switch string(data) { - case `"memory"`: - *st = MemoryStorage - case `"file"`: - *st = FileStorage - default: - return fmt.Errorf("unknown storage type: %s", string(data)) - } - return nil -} diff --git a/message/stream.go b/message/stream.go deleted file mode 100644 index 4f2a7c1..0000000 --- a/message/stream.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019-2026 The NATS Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package message - -import ( - "fmt" - "time" -) - -// StreamConfig will determine the name, subjects and retention policy -// for a given stream. If subjects is empty the name will be used. -type StreamConfig struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` - Subjects []string `json:"subjects,omitempty"` - Retention RetentionPolicy `json:"retention"` - MaxConsumers int `json:"max_consumers"` - MaxMsgs int64 `json:"max_msgs"` - MaxBytes int64 `json:"max_bytes"` - MaxAge time.Duration `json:"max_age"` - MaxMsgsPer int64 `json:"max_msgs_per_subject"` - MaxMsgSize int32 `json:"max_msg_size,omitempty"` - Discard DiscardPolicy `json:"discard"` - Storage StorageType `json:"storage"` - Replicas int `json:"num_replicas"` - NoAck bool `json:"no_ack,omitempty"` - Duplicates time.Duration `json:"duplicate_window,omitempty"` - Placement *Placement `json:"placement,omitempty"` - Mirror *StreamSource `json:"mirror,omitempty"` - Sources []*StreamSource `json:"sources,omitempty"` - Compression StoreCompression `json:"compression"` - FirstSeq uint64 `json:"first_seq,omitempty"` - - // Allow applying a subject transform to incoming messages before doing anything else - SubjectTransform *SubjectTransformConfig `json:"subject_transform,omitempty"` - - // Allow republish of the message after being sequenced and stored. - RePublish *RePublish `json:"republish,omitempty"` - - // Allow higher performance, direct access to get individual messages. E.g. KeyValue - AllowDirect bool `json:"allow_direct"` - // Allow higher performance and unified direct access for mirrors as well. - MirrorDirect bool `json:"mirror_direct"` - - // Allow KV like semantics to also discard new on a per subject basis - DiscardNewPer bool `json:"discard_new_per_subject,omitempty"` - - // Optional qualifiers. These can not be modified after set to true. - - // Sealed will seal a stream so no messages can get out or in. - Sealed bool `json:"sealed"` - // DenyDelete will restrict the ability to delete messages. - DenyDelete bool `json:"deny_delete"` - // DenyPurge will restrict the ability to purge messages. - DenyPurge bool `json:"deny_purge"` - // AllowRollup allows messages to be placed into the system and purge - // all older messages using a special msg header. - AllowRollup bool `json:"allow_rollup_hdrs"` - - // The following defaults will apply to consumers when created against - // this stream, unless overridden manually. - // TODO(nat): Can/should we name these better? - ConsumerLimits StreamConsumerLimits `json:"consumer_limits"` - - // AllowMsgTTL allows header initiated per-message TTLs. If disabled, - // then the `NATS-TTL` header will be ignored. - AllowMsgTTL bool `json:"allow_msg_ttl"` - - // SubjectDeleteMarkerTTL sets the TTL of delete marker messages left behind by - // subject delete markers. - SubjectDeleteMarkerTTL time.Duration `json:"subject_delete_marker_ttl,omitempty"` - - // AllowMsgCounter allows a stream to use (only) counter CRDTs. - AllowMsgCounter bool `json:"allow_msg_counter,omitempty"` - - // AllowAtomicPublish allows atomic batch publishing into the stream. - AllowAtomicPublish bool `json:"allow_atomic,omitempty"` - - // AllowMsgSchedules allows the scheduling of messages. - AllowMsgSchedules bool `json:"allow_msg_schedules,omitempty"` - - // PersistMode allows to opt-in to different persistence mode settings. - PersistMode PersistModeType `json:"persist_mode,omitempty"` - - // Metadata is additional metadata for the Stream. - Metadata map[string]string `json:"metadata,omitempty"` -} - -// Used to guide placement of streams and meta controllers in clustered JetStream. -type Placement struct { - Cluster string `json:"cluster,omitempty"` - Tags []string `json:"tags,omitempty"` - Preferred string `json:"preferred,omitempty"` -} - -// StreamSource dictates how streams can source from other streams. -type StreamSource struct { - Name string `json:"name"` - OptStartSeq uint64 `json:"opt_start_seq,omitempty"` - OptStartTime *time.Time `json:"opt_start_time,omitempty"` - FilterSubject string `json:"filter_subject,omitempty"` - SubjectTransforms []SubjectTransformConfig `json:"subject_transforms,omitempty"` - External *ExternalStream `json:"external,omitempty"` - - // Internal - iname string // For indexing when stream names are the same for multiple sources. -} - -// SubjectTransformConfig is for applying a subject transform (to matching messages) before doing anything else when a new message is received -type SubjectTransformConfig struct { - Source string `json:"src"` - Destination string `json:"dest"` -} - -// ExternalStream allows you to qualify access to a stream source in another account or domain. -type ExternalStream struct { - ApiPrefix string `json:"api"` - DeliverPrefix string `json:"deliver"` -} - -// RePublish is for republishing messages once committed to a stream. -type RePublish struct { - Source string `json:"src,omitempty"` - Destination string `json:"dest"` - HeadersOnly bool `json:"headers_only,omitempty"` -} - -type StreamConsumerLimits struct { - InactiveThreshold time.Duration `json:"inactive_threshold,omitempty"` - MaxAckPending int `json:"max_ack_pending,omitempty"` -} - -// PersistModeType determines what persistence mode the stream uses. -type PersistModeType int - -const ( - // DefaultPersistMode specifies the default persist mode. Writes to the stream will immediately be flushed. - // The publish acknowledgement will be sent after the persisting completes. - DefaultPersistMode = PersistModeType(iota) - // AsyncPersistMode specifies writes to the stream will be flushed asynchronously. - // The publish acknowledgement may be sent before the persisting completes. - // This means writes could be lost if they weren't flushed prior to a hard kill of the server. - AsyncPersistMode -) - -// MarshalJSON 将 PersistModeType 序列化为字符串 -func (pm PersistModeType) MarshalJSON() ([]byte, error) { - switch pm { - case DefaultPersistMode: - return []byte(`"default"`), nil - case AsyncPersistMode: - return []byte(`"async"`), nil - default: - return nil, fmt.Errorf("can not marshal %v", pm) - } -} - -// UnmarshalJSON 将字符串反序列化为 PersistModeType -func (pm *PersistModeType) UnmarshalJSON(data []byte) error { - switch string(data) { - case `"default"`: - *pm = DefaultPersistMode - case `"async"`: - *pm = AsyncPersistMode - default: - return fmt.Errorf("unknown persist mode: %s", string(data)) - } - return nil -} - -type StoreCompression uint8 - -const ( - NoCompression StoreCompression = iota - S2Compression -) - -// MarshalJSON 将 StoreCompression 序列化为字符串 -func (sc StoreCompression) MarshalJSON() ([]byte, error) { - switch sc { - case NoCompression: - return []byte(`"none"`), nil - case S2Compression: - return []byte(`"s2"`), nil - default: - return nil, fmt.Errorf("can not marshal %v", sc) - } -} - -// UnmarshalJSON 将字符串反序列化为 StoreCompression -func (sc *StoreCompression) UnmarshalJSON(data []byte) error { - switch string(data) { - case `"none"`: - *sc = NoCompression - case `"s2"`: - *sc = S2Compression - default: - return fmt.Errorf("unknown store compression: %s", string(data)) - } - return nil -} diff --git a/middleware/rate_limiter.go b/middleware/rate_limiter.go index 4306c5a..cbca58e 100644 --- a/middleware/rate_limiter.go +++ b/middleware/rate_limiter.go @@ -1,25 +1,48 @@ package middleware import ( + "context" "fmt" "strings" - "gitea.com/red-future/common/redis" "gitea.com/red-future/common/utils" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/util/gconv" ) +// 限流 Redis Key 常量 +const ( + RateLimitKeyPrefix = "ragflow:ratelimit:" // 限流Key前缀 + RateLimitKeyIP = "ip:%s" // IP限流: ip:192.168.1.1 + RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1 + RateLimitKeyService = "service:%s" // 服务限流: service:customerService + RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests +) + +func IncrRateLimit(ctx context.Context, key string, windowSeconds int64) (count int64, err error) { + fullKey := RateLimitKeyPrefix + key + count, err = g.Redis().Incr(ctx, fullKey) + if err != nil { + return + } + + // 首次设置过期时间 + if count == 1 { + g.Redis().Expire(ctx, fullKey, windowSeconds) + } + return +} + // GlobalLimiter 全局限流中间件(使用Redis分布式控制) func GlobalLimiter(r *ghttp.Request) { // 从配置文件读取全局限流参数 globalLimit := g.Cfg().MustGet(r.GetCtx(), "rate.limit", 800).Int64() - key := redis.RateLimitKeyGlobal + key := RateLimitKeyGlobal // 使用Redis计数器进行全局限流 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 1) // 1秒窗口 + count, err := IncrRateLimit(r.GetCtx(), key, 1) // 1秒窗口 if err != nil { g.Log().Errorf(r.GetCtx(), "全局限流Redis错误: %v", err) r.Middleware.Next() @@ -38,13 +61,13 @@ func GlobalLimiter(r *ghttp.Request) { // IPLimiter IP限流中间件(防DDoS) func IPLimiter(r *ghttp.Request) { ip := r.GetClientIp() - key := fmt.Sprintf(redis.RateLimitKeyIP, ip) + key := fmt.Sprintf(RateLimitKeyIP, ip) // 从配置文件读取IP限流参数 ipLimit := g.Cfg().MustGet(r.GetCtx(), "rate.ip.limit", 100).Int64() // 使用Redis计数器 - count, err := redis.IncrRateLimit(r.GetCtx(), key, 1) // 1秒窗口 + count, err := IncrRateLimit(r.GetCtx(), key, 1) // 1秒窗口 if err != nil { g.Log().Errorf(r.GetCtx(), "IP限流Redis错误: %v", err) r.Middleware.Next() @@ -75,8 +98,8 @@ func UserLimiter(r *ghttp.Request) { userName = gconv.String(user.UserName) // 从配置文件读取用户限流参数 userLimit := g.Cfg().MustGet(r.GetCtx(), "rate.user.limit", 50).Int64() - key := fmt.Sprintf(redis.RateLimitKeyUser, userName) - count, err := redis.IncrRateLimit(r.GetCtx(), key, 1) + key := fmt.Sprintf(RateLimitKeyUser, userName) + count, err := IncrRateLimit(r.GetCtx(), key, 1) if err != nil { g.Log().Errorf(r.GetCtx(), "用户限流Redis错误: %v", err) return @@ -111,8 +134,8 @@ func ServiceLimiter(r *ghttp.Request) { return } - key := fmt.Sprintf(redis.RateLimitKeyService, serverName) - count, err := redis.IncrRateLimit(r.GetCtx(), key, 1) + key := fmt.Sprintf(RateLimitKeyService, serverName) + count, err := IncrRateLimit(r.GetCtx(), key, 1) if err != nil { g.Log().Errorf(r.GetCtx(), "服务限流Redis错误: %v", err) r.Middleware.Next() diff --git a/minio/minio.go b/minio/minio.go deleted file mode 100644 index c478e68..0000000 --- a/minio/minio.go +++ /dev/null @@ -1,129 +0,0 @@ -package minio - -import ( - "context" - "fmt" - "net/http" - "path/filepath" - "strings" - "time" - - "gitea.com/red-future/common/utils" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/os/glog" - "github.com/google/uuid" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" -) - -// IoConfig 映射 YAML 中的 minio 配置节点 -type IoConfig struct { - Endpoint string `yaml:"endpoint"` // MinIO API 地址 - AccessKey string `yaml:"accessKey"` // AK - SecretKey string `yaml:"secretKey"` // SK - Secure bool `yaml:"secure"` // 是否启用 SSL - Region string `yaml:"region"` // 区域 -} - -// 全局 MinIO 客户端(初始化一次,避免重复创建) -var minioClient *minio.Client -var minioCfg IoConfig - -// initMinIO 初始化 MinIO 客户端。 -func init() { - ctx := context.Background() - if !g.Cfg().MustGet(ctx, "minio").IsEmpty() { - // 加载 MinIO 配置(可从配置文件/环境变量读取,这里硬编码示例) - minioCfg = IoConfig{ - Endpoint: g.Cfg().MustGet(ctx, "minio.endpoint").String(), - AccessKey: g.Cfg().MustGet(ctx, "minio.accessKey").String(), - SecretKey: g.Cfg().MustGet(ctx, "minio.secretKey").String(), - Secure: g.Cfg().MustGet(ctx, "minio.secure").Bool(), - Region: g.Cfg().MustGet(ctx, "minio.region").String(), - } - // 创建 MinIO 客户端 - var err error - if minioClient, err = minio.New(minioCfg.Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(minioCfg.AccessKey, minioCfg.SecretKey, ""), - Secure: minioCfg.Secure, - Region: minioCfg.Region, - }); err != nil { - glog.Errorf(ctx, "初始化 MinIO 客户端失败: %v", err) - } - } -} - -func UploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, fileName string, fileFormat string, err error) { - return uploadFile(ctx, fileHeader) -} - -func uploadFile(ctx context.Context, fileHeader *ghttp.UploadFile) (imagesUrl string, fileName string, fileFormat string, err error) { - bucketName, err := utils.GetBucketName(ctx) - if err != nil { - glog.Errorf(ctx, "获取桶名称失败: %v", err) - return - } - // 检查/创建桶 - exists, err := minioClient.BucketExists(ctx, bucketName) - if err != nil { - glog.Errorf(ctx, "检查桶是否存在失败: %v", err) - return - } - if !exists { - if err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: minioCfg.Region}); err != nil { - glog.Errorf(ctx, "创建桶失败: %v", err) - return - } - glog.Infof(ctx, "成功创建 MinIO 桶: %s", bucketName) - } - // 打开文件,获取 io.Reader(*os.File 实现了 io.Reader) - file, err := fileHeader.Open() - if err != nil { - glog.Errorf(ctx, "打开文件失败: %v", err) - return - } - defer file.Close() // 必须关闭,避免文件句柄泄露 - // 获取文件类型 - buffer := make([]byte, 512) - _, err = file.Read(buffer) - if err != nil { - glog.Errorf(ctx, "读取文件头失败: %v", err) - return - } - contentType := http.DetectContentType(buffer) - // 重置文件读取位置,否则后续 PutObject 会从第512字节开始上传 - if _, err = file.Seek(0, 0); err != nil { - glog.Errorf(ctx, "重置文件读取位置失败: %v", err) - return - } - // 生成唯一的 MinIO 对象名(避免覆盖) - fileExt := filepath.Ext(fileHeader.Filename) // 原文件后缀(如 .jpg) - uniqueID := uuid.New().String()[:32] // 32位随机UUID - timestamp := time.Now().Format("2006-01-02") // 日期目录(便于管理) - objectName := fmt.Sprintf("/%s/%s%s", timestamp, uniqueID, fileExt) // 存储路径:20251209/abc12345.jpg - // 设置存储桶公共读权限 - policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::` + bucketName + `/*"]}]}` - if err = minioClient.SetBucketPolicy(ctx, bucketName, policy); err != nil { - glog.Errorf(ctx, "设置存储桶权限失败: %v", err) - return - } - // 执行图片上传 - _, err = minioClient.PutObject( - ctx, - bucketName, - objectName, - file, - fileHeader.Size, - minio.PutObjectOptions{ - ContentType: contentType, // 关键:指定图片MIME类型,S3会根据此类型处理 - // 若需要图片可公开访问,添加如下配置(根据需求选择) - //ACL: minio.ACLPublicRead, - }, - ) - if err != nil { - glog.Errorf(ctx, "上传图片失败: %v", err) - return - } - return objectName, fileHeader.Filename, strings.ReplaceAll(fileExt, ".", ""), err -} diff --git a/rabbitmq/consumer.go b/rabbitmq/consumer.go deleted file mode 100644 index 160d4bd..0000000 --- a/rabbitmq/consumer.go +++ /dev/null @@ -1,216 +0,0 @@ -package rabbitmq - -import ( - "context" - - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - amqp "github.com/rabbitmq/amqp091-go" -) - -// MessageHandler 消息处理函数 -type MessageHandler func(ctx context.Context, body []byte) error - -// Consumer 消费者 -type Consumer struct { - queue string - consumerTag string - prefetchCount int // QoS: 预取数量(并发控制) - autoAck bool // 是否自动确认 - handler MessageHandler - workerCount int // worker 数量 - cancel context.CancelFunc // 用于停止 worker - channel *amqp.Channel // 独立Channel(避免并发冲突) -} - -// ConsumerOption 消费者配置选项 -type ConsumerOption func(*Consumer) - -// WithPrefetchCount 设置预取数量(并发控制) -func WithPrefetchCount(count int) ConsumerOption { - return func(c *Consumer) { - c.prefetchCount = count - } -} - -// WithAutoAck 设置自动确认 -func WithAutoAck(autoAck bool) ConsumerOption { - return func(c *Consumer) { - c.autoAck = autoAck - } -} - -// WithWorkerCount 设置 worker 数量 -func WithWorkerCount(count int) ConsumerOption { - return func(c *Consumer) { - c.workerCount = count - } -} - -// WithConsumerTag 设置消费者标签 -func WithConsumerTag(tag string) ConsumerOption { - return func(c *Consumer) { - c.consumerTag = tag - } -} - -// NewConsumer 创建消费者 -func NewConsumer(queue string, handler MessageHandler, opts ...ConsumerOption) *Consumer { - c := &Consumer{ - queue: queue, - consumerTag: "", - prefetchCount: 1, // 默认 1 个 - autoAck: false, // 默认手动确认 - handler: handler, - workerCount: 1, // 默认 1 个 worker - } - - // 应用选项 - for _, opt := range opts { - opt(c) - } - - return c -} - -// Start 启动消费者 -func (c *Consumer) Start(ctx context.Context) (err error) { - // 创建可取消的 context - workerCtx, cancel := context.WithCancel(ctx) - c.cancel = cancel - - // 为每个消费者创建独立Channel(避免并发冲突) - conn, err := GetConnection() - if err != nil { - return gerror.Wrap(err, "获取RabbitMQ连接失败") - } - c.channel, err = conn.Channel() - if err != nil { - return gerror.Wrap(err, "创建独立Channel失败") - } - ch := c.channel - - // 声明队列(如果不存在则创建) - // 注意:Queue到Exchange的绑定应由message服务在发送响应时动态创建,或通过运维工具提前配置 - _, err = ch.QueueDeclare( - c.queue, // name - true, // durable(持久化) - false, // autoDelete(不自动删除) - false, // exclusive(非独占) - false, // noWait - nil, // arguments - ) - if err != nil { - return gerror.Newf("声明队列失败: %v", err) - } - - // 设置 QoS(并发控制) - err = ch.Qos( - c.prefetchCount, // prefetchCount: 每个 consumer 最多同时处理的消息数 - 0, // prefetchSize: 0 表示不限制 - false, // global: false 表示仅应用于当前 channel - ) - if err != nil { - return gerror.Newf("设置 QoS 失败: %v", err) - } - - // 开始消费 - msgs, err := ch.Consume( - c.queue, // queue - c.consumerTag, // consumer tag - c.autoAck, // auto-ack - false, // exclusive - false, // no-local - false, // no-wait - nil, // args - ) - if err != nil { - return gerror.Newf("开始消费失败: %v", err) - } - - g.Log().Infof(ctx, "消费者已启动: queue=%s, prefetch=%d, workers=%d", - c.queue, c.prefetchCount, c.workerCount) - - // 启动多个 worker - for i := 0; i < c.workerCount; i++ { - go c.worker(workerCtx, i, msgs) - } - - return -} - -// worker 工作协程 -func (c *Consumer) worker(ctx context.Context, workerID int, msgs <-chan amqp.Delivery) { - g.Log().Debugf(ctx, "Worker %d 已启动", workerID) - - for { - select { - case <-ctx.Done(): - // Context 取消,退出 - g.Log().Infof(ctx, "Worker %d 收到停止信号,正在退出", workerID) - return - case msg, ok := <-msgs: - if !ok { - // Channel 关闭,退出 - g.Log().Infof(ctx, "Worker %d 消息通道已关闭,退出", workerID) - return - } - - // 处理消息 - err := c.handler(ctx, msg.Body) - - if err != nil { - g.Log().Errorf(ctx, "Worker %d 处理消息失败: %v", workerID, err) - - // 如果不是自动确认,需要手动 Nack - if !c.autoAck { - // requeue=false: 不重新入队,进入死信队列 - msg.Nack(false, false) - } - } else { - // 处理成功,手动确认 - if !c.autoAck { - msg.Ack(false) - } - - g.Log().Debugf(ctx, "Worker %d 处理消息成功", workerID) - } - } - } -} - -// StartTypedConsumer 启动类型化消费者(自动反序列化) -func StartTypedConsumer[T any]( - ctx context.Context, - queue string, - handler func(ctx context.Context, msg *T) error, - opts ...ConsumerOption, -) error { - // 包装处理函数 - wrappedHandler := func(ctx context.Context, body []byte) error { - var msg T - if err := gjson.DecodeTo(body, &msg); err != nil { - return gerror.Newf("反序列化消息失败: %v", err) - } - - return handler(ctx, &msg) - } - - consumer := NewConsumer(queue, wrappedHandler, opts...) - return consumer.Start(ctx) -} - -// Stop 停止消费者 -func (c *Consumer) Stop(ctx context.Context) { - if c.cancel != nil { - c.cancel() - } - // 关闭独立Channel - if c.channel != nil && !c.channel.IsClosed() { - c.channel.Close() - g.Log().Debugf(ctx, "消费者Channel已关闭: queue=%s", c.queue) - } - g.Log().Infof(ctx, "正在停止消费者: queue=%s", c.queue) - c.cancel = nil -} diff --git a/rabbitmq/consumer_manager.go b/rabbitmq/consumer_manager.go deleted file mode 100644 index 54648ce..0000000 --- a/rabbitmq/consumer_manager.go +++ /dev/null @@ -1,175 +0,0 @@ -// Package rabbitmq 提供 RabbitMQ 消费者管理功能 -// -// 本文件实现消费者统一管理,简化业务层的启动逻辑 -package rabbitmq - -import ( - "context" - "sync" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/glog" -) - -// ManagedConsumer 托管消费者(包含启动和停止函数) -type ManagedConsumer struct { - Name string // 消费者名称 - Start func(ctx context.Context) error // 启动函数 - Stop func(ctx context.Context) // 停止函数 -} - -// ConsumerManager RabbitMQ 消费者管理器 -// -// 职责: -// 1. 统一管理所有 RabbitMQ 消费者的生命周期 -// 2. 初始化 RabbitMQ 连接和队列 -// 3. 启动/停止所有消费者 -// 4. 协调消费者的优雅退出 -// -// 使用示例: -// -// mgr := rabbitmq.NewConsumerManager(ctx) -// mgr.Register("响应消费者", responseConsumer.Start, responseConsumer.Stop) -// mgr.Init() -// defer mgr.Stop() -type ConsumerManager struct { - ctx context.Context // 全局上下文 - consumers []*ManagedConsumer // 消费者列表 - wg sync.WaitGroup // 等待所有消费者协程退出 -} - -// NewConsumerManager 创建消费者管理器 -// -// 参数: -// -// ctx: 上下文 -// -// 返回: -// -// *ConsumerManager: 消费者管理器实例 -func NewConsumerManager(ctx context.Context) *ConsumerManager { - return &ConsumerManager{ - ctx: ctx, - consumers: make([]*ManagedConsumer, 0), - } -} - -// Register 注册消费者 -// -// 参数: -// -// name: 消费者名称(用于日志) -// startFunc: 启动函数 -// stopFunc: 停止函数 -// -// 使用示例: -// -// consumer := service.NewResponseConsumer(ctx) -// mgr.Register("响应消费者", consumer.Start, consumer.Stop) -func (cm *ConsumerManager) Register(name string, startFunc func(ctx context.Context) error, stopFunc func(ctx context.Context)) { - cm.consumers = append(cm.consumers, &ManagedConsumer{ - Name: name, - Start: startFunc, - Stop: stopFunc, - }) -} - -// Init 初始化并启动所有消费者 -// -// 执行流程: -// 1. 检查 RabbitMQ 配置(未配置则跳过) -// 2. 初始化 RabbitMQ 连接 -// 3. 声明并绑定队列(响应队列、延时落库队列) -// 4. 异步启动所有已注册的消费者 -// -// 返回: -// -// err: 错误信息,成功返回 nil -// -// 注意: -// - 如果 RabbitMQ 未配置,不会报错,只是跳过初始化 -// - 响应队列初始化失败会导致 Fatal 退出 -// - 延时落库队列失败只会 Warning,不影响主流程 -func (cm *ConsumerManager) Init() (err error) { - // 检查配置文件中是否配置了 RabbitMQ - if g.Cfg().MustGet(cm.ctx, "rabbitmq").IsEmpty() { - glog.Info(cm.ctx, "RabbitMQ未配置,跳过消费者初始化") - return - } - - // 初始化 RabbitMQ 连接(从 config.yml 读取配置) - if err = InitFromConfig(cm.ctx); err != nil { - glog.Fatalf(cm.ctx, "初始化 RabbitMQ 失败: %v", err) - return - } - glog.Info(cm.ctx, "RabbitMQ 连接已初始化") - - // 声明响应Exchange(队列由各消费者自己声明和绑定) - if err = DeclareExchange(cm.ctx, &ExchangeConfig{ - Name: "ragflow.response", - Type: "topic", - Durable: true, - }); err != nil { - glog.Fatalf(cm.ctx, "声明响应Exchange失败: %v", err) - return - } - - // 设置延时落库队列(对话缓存兜底机制) - // 失败不影响主流程,只记录 Warning - if err = SetupDelayedFlushQueue(cm.ctx); err != nil { - glog.Warningf(cm.ctx, "设置延时落库队列失败: %v", err) - } - - // 异步启动所有已注册的消费者 - cm.startConsumers() - return -} - -// startConsumers 启动所有消费者(内部方法) -// -// 实现: -// 1. 遍历已注册的消费者 -// 2. 每个消费者在独立的 goroutine 中运行 -// 3. 使用 WaitGroup 追踪所有消费者协程 -func (cm *ConsumerManager) startConsumers() { - for _, c := range cm.consumers { - cm.wg.Add(1) - go func(consumer *ManagedConsumer) { - defer cm.wg.Done() - if err := consumer.Start(cm.ctx); err != nil { - glog.Errorf(cm.ctx, "%s启动失败: %v", consumer.Name, err) - } - }(c) - glog.Infof(cm.ctx, "%s已启动", c.Name) - } -} - -// Stop 停止所有消费者(优雅退出) -// -// 执行流程: -// 1. 依次停止所有消费者(调用各自的 Stop 方法) -// 2. 等待所有消费者协程退出(WaitGroup.Wait) -// 3. 关闭 RabbitMQ 连接 -// -// 使用场景: -// - 收到 SIGINT/SIGTERM 信号时 -// - 程序正常退出时 -// - defer mgr.Stop() -// -// 注意: -// - Stop 方法会阻塞直到所有消费者完全退出 -// - 确保消费者能正确响应 Stop 信号 -func (cm *ConsumerManager) Stop() { - // 依次停止所有消费者 - for _, c := range cm.consumers { - c.Stop(cm.ctx) - glog.Infof(cm.ctx, "%s已停止", c.Name) - } - - // 等待所有消费者协程退出 - cm.wg.Wait() - - // 关闭 RabbitMQ 连接 - Close(cm.ctx) - glog.Info(cm.ctx, "所有消费者已停止,RabbitMQ连接已关闭") -} diff --git a/rabbitmq/delay.go b/rabbitmq/delay.go deleted file mode 100644 index 82faa66..0000000 --- a/rabbitmq/delay.go +++ /dev/null @@ -1,95 +0,0 @@ -// Package rabbitmq - RabbitMQ延时消息发布 -package rabbitmq - -import ( - "context" - "time" - - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - amqp "github.com/rabbitmq/amqp091-go" -) - -// PublishWithDelay 发布延时消息到RabbitMQ -// delaySeconds: 延时秒数 -func PublishWithDelay(ctx context.Context, routingKey string, message interface{}, delaySeconds int) error { - ch, err := GetChannel() - if err != nil { - return gerror.Wrap(err, "获取RabbitMQ通道失败") - } - if ch == nil { - return gerror.New("RabbitMQ通道未初始化") - } - - // 序列化消息 - body, err := gjson.Encode(message) - if err != nil { - return gerror.Wrapf(err, "序列化消息失败") - } - - // 声明延时交换机(x-delayed-message类型) - // 注意:需要RabbitMQ安装延时插件 rabbitmq-plugins enable rabbitmq_delayed_message_exchange - exchangeName := "delayed.exchange" - err = ch.ExchangeDeclare( - exchangeName, - "x-delayed-message", // 延时交换机类型 - true, // durable - false, // auto-deleted - false, // internal - false, // no-wait - amqp.Table{ - "x-delayed-type": "direct", // 底层交换机类型 - }, - ) - if err != nil { - return gerror.Wrapf(err, "声明延时交换机失败") - } - - // 声明队列 - queue, err := ch.QueueDeclare( - routingKey, // 队列名使用routingKey - true, // durable - false, // delete when unused - false, // exclusive - false, // no-wait - nil, - ) - if err != nil { - return gerror.Wrapf(err, "声明队列失败") - } - - // 绑定队列到交换机 - err = ch.QueueBind( - queue.Name, // queue name - routingKey, // routing key - exchangeName, // exchange - false, - nil, - ) - if err != nil { - return gerror.Wrapf(err, "绑定队列失败") - } - - // 发布延时消息 - err = ch.PublishWithContext( - ctx, - exchangeName, // exchange - routingKey, // routing key - false, // mandatory - false, // immediate - amqp.Publishing{ - ContentType: "application/json", - Body: body, - DeliveryMode: amqp.Persistent, // 持久化消息 - Headers: amqp.Table{ - "x-delay": delaySeconds * 1000, // 延时时间(毫秒) - }, - Timestamp: time.Now(), - }, - ) - if err != nil { - return gerror.Wrapf(err, "发布延时消息失败") - } - - return nil -} diff --git a/rabbitmq/instance.go b/rabbitmq/instance.go deleted file mode 100644 index 8ac30dc..0000000 --- a/rabbitmq/instance.go +++ /dev/null @@ -1,59 +0,0 @@ -package rabbitmq - -import ( - "context" - "fmt" - "os" - "sync" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/guid" -) - -var ( - instanceId string - instanceOnce sync.Once -) - -// getInstanceId 获取当前实例的唯一标识(单例) -// 优先级:配置文件 > 环境变量 > 容器名/主机名 > 随机UUID -func getInstanceId() string { - instanceOnce.Do(func() { - ctx := context.Background() - - // 1. 优先从配置文件读取(手动指定,最高优先级) - instanceId = g.Cfg().MustGet(ctx, "rabbitmq.instanceName").String() - if instanceId != "" { - return - } - - // 2. 读取环境变量(Docker/K8s部署时设置) - instanceId = os.Getenv("INSTANCE_NAME") - if instanceId != "" { - return - } - - // 3. 使用主机名(Docker容器名/主机名) - hostname, err := os.Hostname() - if err != nil || hostname == "" { - hostname = "unknown" - } - - // 4. 如果主机名是默认值(本地开发),添加随机后缀避免冲突 - if hostname == "localhost" || hostname == "unknown" { - instanceId = hostname + "." + guid.S()[:4] - } else { - instanceId = hostname - } - }) - return instanceId -} - -// GetInstanceQueueName 获取当前实例的响应队列名 -// 格式:{baseQueue}.{hostname}.{uuid8} -func GetInstanceQueueName(baseQueue string) string { - if baseQueue == "" { - baseQueue = "ragflow.response" - } - return fmt.Sprintf("%s.%s", baseQueue, getInstanceId()) -} diff --git a/rabbitmq/publisher.go b/rabbitmq/publisher.go deleted file mode 100644 index d9753b3..0000000 --- a/rabbitmq/publisher.go +++ /dev/null @@ -1,152 +0,0 @@ -package rabbitmq - -import ( - "context" - - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - amqp "github.com/rabbitmq/amqp091-go" -) - -// Publisher 消息发布器 -type Publisher struct { - exchange string - routingKey string -} - -// NewPublisher 创建发布器 -func NewPublisher(exchange, routingKey string) *Publisher { - return &Publisher{ - exchange: exchange, - routingKey: routingKey, - } -} - -// Publish 发布消息(使用默认 routing key) -func (p *Publisher) Publish(ctx context.Context, message interface{}) (err error) { - return p.PublishWithRoutingKey(ctx, p.routingKey, message) -} - -// PublishWithRoutingKey 发布消息(指定 routing key) -func (p *Publisher) PublishWithRoutingKey(ctx context.Context, routingKey string, message interface{}) (err error) { - ch, err := GetChannel() - if err != nil { - return err - } - - // 序列化消息 - body, err := gjson.Encode(message) - if err != nil { - return gerror.Newf("消息序列化失败: %v", err) - } - - // 发布消息 - err = ch.PublishWithContext( - ctx, - p.exchange, // exchange - routingKey, // routing key - false, // mandatory - false, // immediate - amqp.Publishing{ - DeliveryMode: amqp.Persistent, // 持久化 - ContentType: "application/json", - Body: body, - }, - ) - - if err != nil { - g.Log().Errorf(ctx, "发布消息失败: exchange=%s, routingKey=%s, err=%v", - p.exchange, routingKey, err) - return err - } - - g.Log().Debugf(ctx, "消息发布成功: exchange=%s, routingKey=%s", - p.exchange, routingKey) - - return -} - -// PublishDelayed 发布延时消息 -// delaySeconds: 延时秒数 -func (p *Publisher) PublishDelayed(ctx context.Context, message interface{}, delaySeconds int) (err error) { - ch, err := GetChannel() - if err != nil { - return err - } - - // 序列化消息 - body, err := gjson.Encode(message) - if err != nil { - return gerror.Newf("消息序列化失败: %v", err) - } - - // 发布延时消息(需要 rabbitmq_delayed_message_exchange 插件) - err = ch.PublishWithContext( - ctx, - p.exchange, // exchange(必须是 x-delayed-message 类型) - p.routingKey, // routing key - false, // mandatory - false, // immediate - amqp.Publishing{ - DeliveryMode: amqp.Persistent, - ContentType: "application/json", - Body: body, - Headers: amqp.Table{ - "x-delay": delaySeconds * 1000, // 延时(毫秒) - }, - }, - ) - - if err != nil { - g.Log().Errorf(ctx, "发布延时消息失败: exchange=%s, routingKey=%s, delay=%ds, err=%v", - p.exchange, p.routingKey, delaySeconds, err) - return err - } - - g.Log().Debugf(ctx, "延时消息发布成功: exchange=%s, routingKey=%s, delay=%ds", - p.exchange, p.routingKey, delaySeconds) - - return -} - -// PublishBatch 批量发布消息 -func (p *Publisher) PublishBatch(ctx context.Context, messages []interface{}) (err error) { - if len(messages) == 0 { - return - } - - ch, err := GetChannel() - if err != nil { - return err - } - - for i, message := range messages { - body, err := gjson.Encode(message) - if err != nil { - g.Log().Errorf(ctx, "消息 %d 序列化失败: %v", i, err) - continue - } - - err = ch.PublishWithContext( - ctx, - p.exchange, - p.routingKey, - false, - false, - amqp.Publishing{ - DeliveryMode: amqp.Persistent, - ContentType: "application/json", - Body: body, - }, - ) - - if err != nil { - g.Log().Errorf(ctx, "消息 %d 发布失败: %v", i, err) - continue - } - } - - g.Log().Infof(ctx, "批量发布完成: 共 %d 条消息", len(messages)) - return -} diff --git a/rabbitmq/queue_setup.go b/rabbitmq/queue_setup.go deleted file mode 100644 index dbb3130..0000000 --- a/rabbitmq/queue_setup.go +++ /dev/null @@ -1,111 +0,0 @@ -// Package rabbitmq 提供 RabbitMQ 队列初始化的封装方法 -// -// 本文件包含常用队列的声明和绑定逻辑,简化业务层的队列配置代码 -package rabbitmq - -import ( - "context" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/glog" -) - -// SetupResponseQueue 初始化 RAGFlow 响应队列 -// -// 功能: -// 1. 声明持久化队列(从配置文件读取队列名,默认 ragflow.response.queue) -// 2. 绑定到 ragflow.response Exchange(Topic 类型) -// 3. 使用通配符 # 匹配所有 routing key(userId) -// -// 参数: -// -// ctx: 上下文 -// -// 返回: -// -// err: 错误信息,成功返回 nil -// -// 配置示例(config.yml): -// -// rabbitmq: -// responseQueue: "ragflow.response.queue" # 可选,默认值 -func SetupResponseQueue(ctx context.Context) (err error) { - // 从配置文件读取队列名(支持每个开发者配置独立队列名) - responseQueue := g.Cfg().MustGet(ctx, "rabbitmq.responseQueue", "ragflow.response.queue").String() - - // 声明持久化队列(服务器重启后队列仍存在) - if err = DeclareQueue(ctx, &QueueConfig{ - Name: responseQueue, - Durable: true, // 持久化,防止数据丢失 - }); err != nil { - glog.Errorf(ctx, "声明响应队列失败: %v", err) - return - } - - // 绑定队列到 Exchange - // Exchange 类型为 topic,routing key 使用通配符 # 匹配所有 userId - if err = BindQueue(ctx, &BindingConfig{ - Queue: responseQueue, - Exchange: "ragflow.response", // RAGFlow 响应 Exchange - RoutingKey: "#", // 通配符,匹配所有消息 - }); err != nil { - glog.Errorf(ctx, "绑定响应队列失败: %v", err) - return - } - - glog.Infof(ctx, "响应队列已绑定: %s -> ragflow.response (routingKey=#)", responseQueue) - return -} - -// SetupDelayedFlushQueue 初始化延时落库队列 -// -// 功能: -// 1. 声明延时 Exchange(x-delayed-message 插件) -// 2. 声明持久化队列 conversation.flush.queue -// 3. 绑定队列到延时 Exchange -// -// 用途: -// -// 对话缓存延时落库机制的兜底策略 -// 当对话少于5句时,10分钟后触发延时消息将缓存写入MongoDB -// -// 参数: -// -// ctx: 上下文 -// -// 返回: -// -// err: 错误信息,成功返回 nil -// -// 相关: -// - service/conversation_service.go: handleResponse() -// - service/conversation_service.go: handleDelayedFlush() -func SetupDelayedFlushQueue(ctx context.Context) (err error) { - // 声明延时 Exchange(需要 RabbitMQ 安装 x-delayed-message 插件) - if err = SetupDelayExchange(ctx, "conversation.flush.delayed"); err != nil { - glog.Warningf(ctx, "声明延时落库 Exchange 失败: %v", err) - return - } - - // 声明持久化队列 - if err = DeclareQueue(ctx, &QueueConfig{ - Name: "conversation.flush.queue", - Durable: true, // 持久化,防止延时消息丢失 - }); err != nil { - glog.Warningf(ctx, "声明延时落库 Queue 失败: %v", err) - return - } - - // 绑定队列到延时 Exchange - if err = BindQueue(ctx, &BindingConfig{ - Queue: "conversation.flush.queue", - Exchange: "conversation.flush.delayed", - RoutingKey: "flush", // 延时落库消息的 routing key - }); err != nil { - glog.Warningf(ctx, "绑定延时落库 Queue 失败: %v", err) - return - } - - glog.Info(ctx, "延时落库队列已配置") - return -} diff --git a/rabbitmq/rabbitmq_client.go b/rabbitmq/rabbitmq_client.go deleted file mode 100644 index d6a4585..0000000 --- a/rabbitmq/rabbitmq_client.go +++ /dev/null @@ -1,210 +0,0 @@ -package rabbitmq - -import ( - "context" - "sync" - "time" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/gconv" - amqp "github.com/rabbitmq/amqp091-go" -) - -var ( - conn *amqp.Connection - channel *amqp.Channel - rabbitmqOnce sync.Once - rabbitmqMu sync.RWMutex - closeWatcher chan struct{} // 用于停止监听 goroutine - watcherStarted bool // 防止重复启动监听 -) - -// Config RabbitMQ 配置 -type Config struct { - Host string - Port int - Username string - Password string - VHost string -} - -// Init 初始化 RabbitMQ 连接 -func Init(ctx context.Context, cfg *Config) error { - var err error - rabbitmqOnce.Do(func() { - // 构建连接字符串 - url := "amqp://" + cfg.Username + ":" + cfg.Password + "@" + cfg.Host + ":" + gconv.String(cfg.Port) + "/" + cfg.VHost - - // 创建连接 - conn, err = amqp.Dial(url) - if err != nil { - g.Log().Errorf(ctx, "RabbitMQ 连接失败: %v", err) - return - } - - // 创建 Channel - channel, err = conn.Channel() - if err != nil { - g.Log().Errorf(ctx, "创建 RabbitMQ Channel 失败: %v", err) - return - } - - // 初始化关闭监听器 - closeWatcher = make(chan struct{}) - - // 监听连接关闭(只启动一次) - if !watcherStarted { - go handleConnectionClose(ctx) - watcherStarted = true - } - - g.Log().Info(ctx, "RabbitMQ 连接成功") - }) - - return err -} - -// InitFromConfig 从配置文件初始化 -func InitFromConfig(ctx context.Context) error { - cfg := &Config{ - Host: g.Cfg().MustGet(ctx, "rabbitmq.host").String(), - Port: g.Cfg().MustGet(ctx, "rabbitmq.port").Int(), - Username: g.Cfg().MustGet(ctx, "rabbitmq.username").String(), - Password: g.Cfg().MustGet(ctx, "rabbitmq.password").String(), - VHost: g.Cfg().MustGet(ctx, "rabbitmq.vhost", "/").String(), - } - - return Init(ctx, cfg) -} - -// GetChannel 获取 Channel -func GetChannel() (*amqp.Channel, error) { - rabbitmqMu.RLock() - defer rabbitmqMu.RUnlock() - - if channel == nil || channel.IsClosed() { - return nil, gerror.New("RabbitMQ Channel 未初始化或已关闭") - } - - return channel, nil -} - -// GetConnection 获取连接 -func GetConnection() (*amqp.Connection, error) { - rabbitmqMu.RLock() - defer rabbitmqMu.RUnlock() - - if conn == nil || conn.IsClosed() { - return nil, gerror.New("RabbitMQ 连接未初始化或已关闭") - } - - return conn, nil -} - -// handleConnectionClose 监听连接关闭并重连 -func handleConnectionClose(ctx context.Context) { - for { - // 检查是否需要停止监听 - select { - case <-closeWatcher: - g.Log().Info(ctx, "停止监听 RabbitMQ 连接状态") - return - default: - } - - rabbitmqMu.RLock() - currentConn := conn - rabbitmqMu.RUnlock() - - if currentConn == nil { - return - } - - // 创建关闭通知 channel - closeErr := make(chan *amqp.Error, 1) - currentConn.NotifyClose(closeErr) - - // 等待连接关闭或停止信号 - select { - case err := <-closeErr: - if err != nil { - g.Log().Errorf(ctx, "RabbitMQ 连接关闭: %v,尝试重连...", err) - reconnect(ctx) - } - case <-closeWatcher: - g.Log().Info(ctx, "停止监听 RabbitMQ 连接状态") - return - } - } -} - -// reconnect 重新连接 -func reconnect(ctx context.Context) { - rabbitmqMu.Lock() - defer rabbitmqMu.Unlock() - - for i := 0; i < 10; i++ { - time.Sleep(time.Duration(i+1) * time.Second) - - cfg := &Config{ - Host: g.Cfg().MustGet(ctx, "rabbitmq.host").String(), - Port: g.Cfg().MustGet(ctx, "rabbitmq.port").Int(), - Username: g.Cfg().MustGet(ctx, "rabbitmq.username").String(), - Password: g.Cfg().MustGet(ctx, "rabbitmq.password").String(), - VHost: g.Cfg().MustGet(ctx, "rabbitmq.vhost", "/").String(), - } - - url := "amqp://" + cfg.Username + ":" + cfg.Password + "@" + cfg.Host + ":" + gconv.String(cfg.Port) + "/" + cfg.VHost - - var err error - conn, err = amqp.Dial(url) - if err != nil { - g.Log().Errorf(ctx, "重连失败 (尝试 %d/10): %v", i+1, err) - continue - } - - channel, err = conn.Channel() - if err != nil { - g.Log().Errorf(ctx, "创建 Channel 失败 (尝试 %d/10): %v", i+1, err) - continue - } - - g.Log().Info(ctx, "RabbitMQ 重连成功") - // 不再重复启动监听 goroutine - return - } - - g.Log().Fatal(ctx, "RabbitMQ 重连失败,已达到最大重试次数") -} - -// Close 关闭连接 -func Close(ctx context.Context) (err error) { - rabbitmqMu.Lock() - defer rabbitmqMu.Unlock() - - // 停止监听 goroutine - if closeWatcher != nil { - close(closeWatcher) - closeWatcher = nil - } - - if channel != nil { - if err = channel.Close(); err != nil { - g.Log().Errorf(ctx, "关闭 RabbitMQ Channel 失败: %v", err) - } - channel = nil - } - - if conn != nil { - if err = conn.Close(); err != nil { - g.Log().Errorf(ctx, "关闭 RabbitMQ 连接失败: %v", err) - return - } - conn = nil - } - - watcherStarted = false - g.Log().Info(ctx, "RabbitMQ 连接已关闭") - return -} diff --git a/rabbitmq/setup.go b/rabbitmq/setup.go deleted file mode 100644 index 47793fd..0000000 --- a/rabbitmq/setup.go +++ /dev/null @@ -1,231 +0,0 @@ -package rabbitmq - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - amqp "github.com/rabbitmq/amqp091-go" -) - -// QueueConfig 队列配置 -type QueueConfig struct { - Name string - Durable bool // 持久化 - AutoDelete bool // 自动删除 - Exclusive bool // 排他 - Args amqp.Table // 额外参数 -} - -// ExchangeConfig Exchange 配置 -type ExchangeConfig struct { - Name string - Type string // direct/topic/fanout/x-delayed-message - Durable bool - AutoDelete bool - Args amqp.Table -} - -// BindingConfig 绑定配置 -type BindingConfig struct { - Queue string - Exchange string - RoutingKey string - Args amqp.Table -} - -// DeclareQueue 声明队列 -func DeclareQueue(ctx context.Context, cfg *QueueConfig) (err error) { - ch, err := GetChannel() - if err != nil { - return err - } - - _, err = ch.QueueDeclare( - cfg.Name, - cfg.Durable, - cfg.AutoDelete, - cfg.Exclusive, - false, // no-wait - cfg.Args, - ) - - if err != nil { - g.Log().Errorf(ctx, "声明队列失败: %s, err=%v", cfg.Name, err) - return err - } - - g.Log().Infof(ctx, "队列声明成功: %s", cfg.Name) - return -} - -// DeclareExchange 声明 Exchange -func DeclareExchange(ctx context.Context, cfg *ExchangeConfig) (err error) { - ch, err := GetChannel() - if err != nil { - return err - } - - err = ch.ExchangeDeclare( - cfg.Name, - cfg.Type, - cfg.Durable, - cfg.AutoDelete, - false, // internal - false, // no-wait - cfg.Args, - ) - - if err != nil { - g.Log().Errorf(ctx, "声明 Exchange 失败: %s, err=%v", cfg.Name, err) - return err - } - - g.Log().Infof(ctx, "Exchange 声明成功: %s (type=%s)", cfg.Name, cfg.Type) - return -} - -// BindQueue 绑定队列到 Exchange -func BindQueue(ctx context.Context, cfg *BindingConfig) (err error) { - ch, err := GetChannel() - if err != nil { - return err - } - - err = ch.QueueBind( - cfg.Queue, - cfg.RoutingKey, - cfg.Exchange, - false, // no-wait - cfg.Args, - ) - - if err != nil { - g.Log().Errorf(ctx, "绑定队列失败: queue=%s, exchange=%s, routingKey=%s, err=%v", - cfg.Queue, cfg.Exchange, cfg.RoutingKey, err) - return err - } - - g.Log().Infof(ctx, "队列绑定成功: queue=%s → exchange=%s (routingKey=%s)", - cfg.Queue, cfg.Exchange, cfg.RoutingKey) - return -} - -// SetupDelayExchange 设置延时 Exchange(需要 rabbitmq_delayed_message_exchange 插件) -func SetupDelayExchange(ctx context.Context, exchangeName string) error { - return DeclareExchange(ctx, &ExchangeConfig{ - Name: exchangeName, - Type: "x-delayed-message", - Durable: true, - Args: amqp.Table{ - "x-delayed-type": "direct", - }, - }) -} - -// SetupDeadLetterQueue 设置死信队列 -func SetupDeadLetterQueue(ctx context.Context, queueName, exchangeName string) error { - // 1. 声明死信 Exchange - err := DeclareExchange(ctx, &ExchangeConfig{ - Name: exchangeName, - Type: "direct", - Durable: true, - }) - if err != nil { - return err - } - - // 2. 声明死信队列 - err = DeclareQueue(ctx, &QueueConfig{ - Name: queueName, - Durable: true, - }) - if err != nil { - return err - } - - // 3. 绑定 - return BindQueue(ctx, &BindingConfig{ - Queue: queueName, - Exchange: exchangeName, - RoutingKey: queueName, - }) -} - -// SetupQueueWithDLX 创建带死信队列的普通队列 -func SetupQueueWithDLX(ctx context.Context, queueName, dlxExchange, dlxRoutingKey string) error { - return DeclareQueue(ctx, &QueueConfig{ - Name: queueName, - Durable: true, - Args: amqp.Table{ - "x-dead-letter-exchange": dlxExchange, - "x-dead-letter-routing-key": dlxRoutingKey, - }, - }) -} - -// SetupBasicTopology 设置基础拓扑(适用于小红书客服场景) -func SetupBasicTopology(ctx context.Context) (err error) { - // 1. 声明普通 Exchange - err = DeclareExchange(ctx, &ExchangeConfig{ - Name: "ragflow_exchange", - Type: "direct", - Durable: true, - }) - if err != nil { - return err - } - - // 2. 声明延时 Exchange - err = SetupDelayExchange(ctx, "delay_exchange") - if err != nil { - return gerror.Newf("延时 Exchange 声明失败(可能未安装插件): %v", err) - } - - // 3. 声明死信队列 - err = SetupDeadLetterQueue(ctx, "dead_letter_queue", "dlx_exchange") - if err != nil { - return err - } - - // 4. 声明业务队列 - queues := []struct { - name string - dlx bool // 是否需要死信队列 - }{ - {"ragflow_request_queue", true}, - {"follow_up_queue", true}, - {"archive_queue", true}, - } - - for _, q := range queues { - if q.dlx { - err = SetupQueueWithDLX(ctx, q.name, "dlx_exchange", "dead_letter_queue") - } else { - err = DeclareQueue(ctx, &QueueConfig{ - Name: q.name, - Durable: true, - }) - } - if err != nil { - return err - } - } - - // 5. 绑定队列 - bindings := []BindingConfig{ - {Queue: "ragflow_request_queue", Exchange: "ragflow_exchange", RoutingKey: "ragflow_request_queue"}, - {Queue: "follow_up_queue", Exchange: "delay_exchange", RoutingKey: "follow_up_queue"}, - {Queue: "archive_queue", Exchange: "delay_exchange", RoutingKey: "archive_queue"}, - } - - for _, b := range bindings { - err = BindQueue(ctx, &b) - if err != nil { - return err - } - } - - g.Log().Info(ctx, "RabbitMQ 拓扑结构设置完成") - return -} diff --git a/ragflow/agent.go b/ragflow/agent.go deleted file mode 100644 index e389e59..0000000 --- a/ragflow/agent.go +++ /dev/null @@ -1,141 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" -) - -// Agent AGENT 管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#agent-管理 - -// Agent Agent 结构体 -type Agent struct { - ID string `json:"id"` // Agent ID - Title string `json:"title"` // Agent 标题 - Description string `json:"description"` // Agent 描述 - Avatar string `json:"avatar"` // 头像(Base64 编码) - CanvasType string `json:"canvas_type"` // 画布类型 - CreateDate string `json:"create_date"` // 创建日期(格式化字符串) - CreateTime int64 `json:"create_time"` // 创建时间(Unix 时间戳) - UpdateDate string `json:"update_date"` // 更新日期(格式化字符串) - UpdateTime int64 `json:"update_time"` // 更新时间(Unix 时间戳) - UserID string `json:"user_id"` // 用户 ID - DSL map[string]interface{} `json:"dsl"` // Canvas DSL 对象,定义 Agent 的工作流 -} - -// CreateAgentReq 创建 Agent 请求 -type CreateAgentReq struct { - Title string `json:"title"` // 必需 - Description string `json:"description,omitempty"` // 可选,默认为 None - DSL map[string]interface{} `json:"dsl"` // 必需,Canvas DSL 对象 -} - -// UpdateAgentReq 更新 Agent 请求 -type UpdateAgentReq struct { - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - DSL map[string]interface{} `json:"dsl,omitempty"` -} - -// ListAgentsReq 列出 Agent 请求 -type ListAgentsReq struct { - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - OrderBy string `json:"orderby,omitempty"` - Desc bool `json:"desc,omitempty"` - Title string `json:"title,omitempty"` - ID string `json:"id,omitempty"` -} - -// ListAgentsRes 列出 Agent 响应 -// 注意:API 不返回 total 字段,仅返回 data 数组 -type ListAgentsRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data []*Agent `json:"data"` // Agent 列表 -} - -// CreateAgent 创建 Agent -// POST /api/v1/agents -func (c *Client) CreateAgent(ctx context.Context, req *CreateAgentReq) (err error) { - var res CommonResponse - if err = c.request(ctx, "POST", "/api/v1/agents", req, &res); err != nil { - return gerror.Newf("create agent failed: %v", err) - } - if !res.IsSuccess() { - return gerror.Newf("create agent failed: %s", res.Message) - } - return -} - -// UpdateAgent 更新 Agent -// PUT /api/v1/agents/{agent_id} -func (c *Client) UpdateAgent(ctx context.Context, agentID string, req *UpdateAgentReq) (err error) { - path := "/api/v1/agents/" + agentID - var res CommonResponse - if err = c.request(ctx, "PUT", path, req, &res); err != nil { - return gerror.Newf("update agent failed: %v", err) - } - if !res.IsSuccess() { - return gerror.Newf("update agent failed: %s", res.Message) - } - return -} - -// DeleteAgent 删除 Agent -// DELETE /api/v1/agents/{agent_id} -func (c *Client) DeleteAgent(ctx context.Context, agentID string) (err error) { - path := "/api/v1/agents/" + agentID - var res CommonResponse - // 官方文档要求传空对象,不是 nil - if err = c.request(ctx, "DELETE", path, map[string]interface{}{}, &res); err != nil { - return gerror.Newf("delete agent failed: %v", err) - } - if !res.IsSuccess() { - return gerror.Newf("delete agent failed: %s", res.Message) - } - return -} - -// ListAgents 列出 Agent -// GET /api/v1/agents -func (c *Client) ListAgents(ctx context.Context, req *ListAgentsReq) (*ListAgentsRes, error) { - path := "/api/v1/agents" - if req != nil { - params := map[string]interface{}{} - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.OrderBy != "" { - params["orderby"] = req.OrderBy - } - if req.Desc { - params["desc"] = "true" - } else { - params["desc"] = "false" - } - if req.Title != "" { - params["title"] = req.Title - } - if req.ID != "" { - params["id"] = req.ID - } - - query := buildQueryString(params) - if query != "" { - path += "?" + query - } - } - - var res ListAgentsRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, gerror.Newf("list agents failed: %v", err) - } - if res.Code != 0 { - return nil, gerror.Newf("list agents failed: code=%d", res.Code) - } - return &res, nil -} diff --git a/ragflow/chat.go b/ragflow/chat.go deleted file mode 100644 index bdce50b..0000000 --- a/ragflow/chat.go +++ /dev/null @@ -1,198 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" -) - -// CreateChatReq 创建对话配置请求 -type CreateChatReq struct { - Name string `json:"name"` // 对话配置名称(助理姓名) - Description string `json:"description,omitempty"` // 助理描述 - DatasetIds []string `json:"dataset_ids"` // 关联的知识库ID列表 - Prompt *PromptConfig `json:"prompt"` // 提示词配置 - Llm *Llm `json:"llm,omitempty"` // LLM配置 -} - -// PromptConfig 提示词配置 -type PromptConfig struct { - Prompt string `json:"prompt"` // 提示词内容 - SimilarityThreshold float64 `json:"similarity_threshold"` // 相似度阈值 - KeywordsSimilarityWeight float64 `json:"keywords_similarity_weight"` // 关键词相似度权重 - TopN int `json:"top_n"` // 返回顶部N个chunk - EmptyResponse string `json:"empty_response"` // 无匹配时回复(必须显式传入空字符串才能让LLM自由发挥,不传入会使用RAGFlow默认提示词) - Opener string `json:"opener,omitempty"` // 开场白 - ShowQuote bool `json:"show_quote,omitempty"` // 是否显示引用 - Variables []map[string]interface{} `json:"variables,omitempty"` // 变量列表 -} - -// CreateChatRes 创建对话配置响应 -type CreateChatRes struct { - ChatId string `json:"id"` // 对话配置ID -} - -// UpdateChatReq 更新对话配置请求 -type UpdateChatReq struct { - Name string `json:"name,omitempty"` // 对话配置名称 - Description string `json:"description,omitempty"` // 对话描述 - DatasetIds []string `json:"dataset_ids,omitempty"` // 关联的知识库ID列表(RAGFlow API使用下划线格式) - Prompt *PromptConfig `json:"prompt,omitempty"` // 提示词配置 -} - -// 聊天助手管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#聊天助手管理 - -// Chat 聊天助手结构体 -type Chat struct { - Id string `json:"id"` - Name string `json:"name"` - Avatar string `json:"avatar"` - DatasetIds []string `json:"dataset_ids"` - Llm Llm `json:"llm"` - Prompt Prompt `json:"prompt"` - Description string `json:"description"` - DoRefer string `json:"do_refer"` - Language string `json:"language"` - PromptType string `json:"prompt_type"` - Status string `json:"status"` - TenantId string `json:"tenant_id"` - TopK int `json:"top_k"` - CreateDate string `json:"create_date"` - CreateTime int64 `json:"create_time"` - UpdateDate string `json:"update_date"` - UpdateTime int64 `json:"update_time"` -} - -type Llm struct { - ModelName string `json:"model_name,omitempty"` - Temperature float64 `json:"temperature,omitempty"` - TopP float64 `json:"top_p,omitempty"` - PresencePenalty float64 `json:"presence_penalty,omitempty"` - FrequencyPenalty float64 `json:"frequency_penalty,omitempty"` -} - -type Prompt struct { - SimilarityThreshold float64 `json:"similarity_threshold,omitempty"` - KeywordsSimilarityWeight float64 `json:"keywords_similarity_weight,omitempty"` - Opener string `json:"opener,omitempty"` - Prompt string `json:"prompt,omitempty"` - RerankModel string `json:"rerank_model,omitempty"` - TopN int `json:"top_n,omitempty"` - Variables []Variable `json:"variables,omitempty"` - EmptyResponse string `json:"empty_response,omitempty"` -} - -type Variable struct { - Key string `json:"key"` - Optional bool `json:"optional"` -} - -// ListChatsReq 列出聊天助手请求 -type ListChatsReq struct { - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - OrderBy string `json:"orderby,omitempty"` - Desc bool `json:"desc,omitempty"` - Name string `json:"name,omitempty"` - Id string `json:"id,omitempty"` -} - -// ListChatsRes 列出聊天助手响应 -// 注意:API 不返回 total 字段,仅返回 data 数组 -type ListChatsRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data []*Chat `json:"data"` // 聊天助手列表 -} - -// DeleteChatsReq 删除聊天助手请求 -type DeleteChatsReq struct { - Ids []string `json:"ids"` -} - -// CreateChat 创建聊天助手 -func (c *Client) CreateChat(ctx context.Context, req *CreateChatReq) (*Chat, error) { - var res struct { - Code int `json:"code"` - Data *Chat `json:"data"` - Msg string `json:"message"` - } - if err := c.request(ctx, "POST", "/api/v1/chats", req, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("create chat failed: %s", res.Msg) - } - // 检查响应数据是否为空:防止RAGFlow API返回 {"code":0, "data":null} - // 如果不检查直接返回,调用方会收到 (nil, nil),导致空指针异常 - if res.Data == nil { - return nil, gerror.Newf("create chat returned null data: %s", res.Msg) - } - return res.Data, nil -} - -// ListChats 列出聊天助手 -func (c *Client) ListChats(ctx context.Context, req *ListChatsReq) (*ListChatsRes, error) { - path := "/api/v1/chats" - params := map[string]interface{}{} - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.OrderBy != "" { - params["orderby"] = req.OrderBy - } - if req.Desc { - params["desc"] = "true" - } else { - params["desc"] = "false" - } - if req.Name != "" { - params["name"] = req.Name - } - if req.Id != "" { - params["id"] = req.Id - } - - query := buildQueryString(params) - if query != "" { - path += "?" + query - } - - var res ListChatsRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("list chats failed: code=%d", res.Code) - } - return &res, nil -} - -// DeleteChats 删除聊天助手 -func (c *Client) DeleteChats(ctx context.Context, ids []string) (err error) { - req := DeleteChatsReq{Ids: ids} - var res CommonResponse - if err = c.request(ctx, "DELETE", "/api/v1/chats", req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("delete chats failed: %s", res.Message) - } - return -} - -// UpdateChat 更新聊天助手 -func (c *Client) UpdateChat(ctx context.Context, id string, req *UpdateChatReq) (err error) { - var res CommonResponse - path := "/api/v1/chats/" + id - if err = c.request(ctx, "PUT", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("update chat failed: %s", res.Message) - } - return -} diff --git a/ragflow/chunk.go b/ragflow/chunk.go deleted file mode 100644 index 030d21a..0000000 --- a/ragflow/chunk.go +++ /dev/null @@ -1,180 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" -) - -// 数据集内知识块管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#数据集内知识块管理 - -// Chunk 知识块结构体 -type Chunk struct { - Id string `json:"id"` - Content string `json:"content"` - DocumentId string `json:"document_id"` - DatasetId string `json:"dataset_id"` - CreateTime string `json:"create_time"` - CreateTimestamp float64 `json:"create_timestamp"` - ImportantKeywords []string `json:"important_keywords"` - Questions []string `json:"questions"` - Available bool `json:"available"` - ImageId string `json:"image_id"` - Positions []string `json:"positions"` -} - -// AddChunkReq 添加知识块请求 -type AddChunkReq struct { - Content string `json:"content"` - ImportantKeywords []string `json:"important_keywords,omitempty"` - Questions []string `json:"questions,omitempty"` -} - -// ListChunksReq 列出知识块请求 -type ListChunksReq struct { - Keywords string `json:"keywords,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - Id string `json:"id,omitempty"` -} - -// ListChunksRes 列出知识块响应 -// 注意:响应结构包含 chunks(知识块列表)、doc(关联文档信息)和 total(总数) -type ListChunksRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data struct { - Chunks []*Chunk `json:"chunks"` // 知识块列表 - Doc interface{} `json:"doc"` // 关联文档信息(完整的 Document 对象) - Total int `json:"total"` // 知识块总数 - } `json:"data"` -} - -// DeleteChunksReq 删除知识块请求 -type DeleteChunksReq struct { - ChunkIds []string `json:"chunk_ids,omitempty"` // 如果为空,删除所有 -} - -// UpdateChunkReq 更新知识块请求 -type UpdateChunkReq struct { - Content string `json:"content,omitempty"` - ImportantKeywords []string `json:"important_keywords,omitempty"` - Available *bool `json:"available,omitempty"` -} - -// RetrieveChunksReq 检索知识块请求 -type RetrieveChunksReq struct { - Question string `json:"question"` - DatasetIds []string `json:"dataset_ids,omitempty"` - DocumentIds []string `json:"document_ids,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - SimilarityThreshold float64 `json:"similarity_threshold,omitempty"` - VectorSimilarityWeight float64 `json:"vector_similarity_weight,omitempty"` - TopK int `json:"top_k,omitempty"` - RerankId string `json:"rerank_id,omitempty"` - Keyword bool `json:"keyword,omitempty"` - Highlight bool `json:"highlight,omitempty"` - CrossLanguages []string `json:"cross_languages,omitempty"` - MetadataCondition map[string]interface{} `json:"metadata_condition,omitempty"` -} - -// RetrieveChunksRes 检索知识块响应 (结构比较复杂,暂时简化,根据实际返回调整) -// 官方文档未给出详细响应结构,假设返回 chunks 列表 -type RetrieveChunksRes struct { - Code int `json:"code"` - Data struct { - Chunks []interface{} `json:"chunks"` // 检索结果可能包含额外信息 - Total int `json:"total"` - } `json:"data"` -} - -// AddChunk 添加知识块 -func (c *Client) AddChunk(ctx context.Context, datasetId, documentId string, req *AddChunkReq) (*Chunk, error) { - path := "/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks" - var res struct { - Code int `json:"code"` - Data struct { - Chunk *Chunk `json:"chunk"` - } `json:"data"` - Msg string `json:"message"` - } - if err := c.request(ctx, "POST", path, req, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("add chunk failed: %s", res.Msg) - } - return res.Data.Chunk, nil -} - -// ListChunks 列出知识块 -func (c *Client) ListChunks(ctx context.Context, datasetId, documentId string, req *ListChunksReq) (*ListChunksRes, error) { - path := "/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks" - params := map[string]interface{}{} - if req.Keywords != "" { - params["keywords"] = req.Keywords - } - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.Id != "" { - params["id"] = req.Id - } - - query := buildQueryString(params) - if query != "" { - path += "?" + query - } - - var res ListChunksRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("list chunks failed: code=%d", res.Code) - } - return &res, nil -} - -// DeleteChunks 删除知识块 -func (c *Client) DeleteChunks(ctx context.Context, datasetId, documentId string, chunkIds []string) (err error) { - req := DeleteChunksReq{ChunkIds: chunkIds} - var res CommonResponse - path := "/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks" - if err = c.request(ctx, "DELETE", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("delete chunks failed: %s", res.Message) - } - return -} - -// UpdateChunk 更新知识块 -func (c *Client) UpdateChunk(ctx context.Context, datasetId, documentId, chunkId string, req *UpdateChunkReq) (err error) { - var res CommonResponse - path := "/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks/" + chunkId - if err = c.request(ctx, "PUT", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("update chunk failed: %s", res.Message) - } - return -} - -// RetrieveChunks 检索知识块 -func (c *Client) RetrieveChunks(ctx context.Context, req *RetrieveChunksReq) (*RetrieveChunksRes, error) { - var res RetrieveChunksRes - if err := c.request(ctx, "POST", "/api/v1/retrieval", req, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("retrieve chunks failed: code=%d", res.Code) - } - return &res, nil -} diff --git a/ragflow/client.go b/ragflow/client.go deleted file mode 100644 index f88b0ba..0000000 --- a/ragflow/client.go +++ /dev/null @@ -1,195 +0,0 @@ -package ragflow - -import ( - "context" - "encoding/json" - "net/url" - "strings" - "sync" - "sync/atomic" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/gclient" -) - -var ( - // globalClient 全局 RAGFlow 客户端(单例,延迟初始化) - globalClient *Client - clientOnce sync.Once -) - -// initClient 延迟初始化客户端 -func initClient() { - clientOnce.Do(func() { - ctx := context.Background() - - // 读取配置 - endpoints, apiKey := loadConfig(ctx) - - // 如果配置不完整,跳过初始化 - if len(endpoints) == 0 || apiKey == "" { - g.Log().Warning(ctx, "⚠️ RAGFlow 配置未找到,请在 config.yml 中添加 ragflow.base_url 或在 Consul 中配置 ragflow.endpoints") - return - } - - globalClient = &Client{ - Endpoints: endpoints, - APIKey: apiKey, - } - - if len(endpoints) == 1 { - g.Log().Infof(ctx, "✅ RAGFlow 客户端初始化成功: endpoint=%s", endpoints[0]) - } else { - g.Log().Infof(ctx, "✅ RAGFlow 客户端初始化成功: endpoints=%v (负载均衡)", endpoints) - } - }) -} - -// loadConfig 从配置加载 RAGFlow 配置(支持实例级配置) -// 优先级: -// 1. Consul实例级配置 ragflow.endpoints (数组) -// 2. Consul全局配置 ragflow.endpoints (数组) -// 3. config.yml的 ragflow.base_url (单个URL,向后兼容) -func loadConfig(ctx context.Context) (endpoints []string, apiKey string) { - // 尝试从Consul读取endpoints(支持实例级配置) - // 注意:这里不能直接导入customerService/service包,会造成循环依赖 - // 所以只能从config.yml读取,Consul配置需要在customerservice层面调用时传入 - - // 读取API Key - apiKey = g.Cfg().MustGet(ctx, "ragflow.api_key", "").String() - - // 尝试读取endpoints数组(从config.yml或Consul同步的配置) - endpointsConfig := g.Cfg().MustGet(ctx, "ragflow.endpoints") - if !endpointsConfig.IsEmpty() { - endpoints = endpointsConfig.Strings() - // 去除尾部斜杠 - for i := range endpoints { - endpoints[i] = strings.TrimSuffix(endpoints[i], "/") - } - return - } - - // Fallback到单个base_url(向后兼容) - baseURL := g.Cfg().MustGet(ctx, "ragflow.base_url", "").String() - if baseURL != "" { - endpoints = []string{strings.TrimSuffix(baseURL, "/")} - } - - return -} - -// GetGlobalClient 获取全局客户端(延迟初始化) -func GetGlobalClient() *Client { - initClient() - return globalClient -} - -// Client RAGFlow API 客户端(支持负载均衡) -type Client struct { - Endpoints []string // RAGFlow实例列表 - APIKey string // API密钥 - currentIndex atomic.Uint64 // 当前轮询索引(原子操作) -} - -// getNextEndpoint 获取下一个endpoint(轮询算法) -func (c *Client) getNextEndpoint() string { - if len(c.Endpoints) == 0 { - return "" - } - if len(c.Endpoints) == 1 { - return c.Endpoints[0] - } - // 原子递增并取模,实现轮询 - idx := c.currentIndex.Add(1) % uint64(len(c.Endpoints)) - return c.Endpoints[idx] -} - -// CommonResponse 通用响应结构 -type CommonResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data,omitempty"` -} - -// IsSuccess 检查响应是否成功 -func (r *CommonResponse) IsSuccess() bool { - return r.Code == 0 -} - -// request 发送 HTTP 请求 -// -// 为什么不使用 common/http 包: -// common/http包统一处理内部API响应格式(ghttp.DefaultHandlerResponse), -// RAGFlow API返回格式为{code,data,message}一层结构,与内部API不同。 -// 因此直接使用 g.Client() 调用第三方API,在此处理RAGFlow特有的响应格式。 -func (c *Client) request(ctx context.Context, method, path string, body interface{}, result interface{}) (err error) { - endpoint := c.getNextEndpoint() - if endpoint == "" { - return gerror.New("RAGFlow endpoints not configured") - } - - fullURL := endpoint + path - - // 创建HTTP客户端并设置RAGFlow专用请求头 - client := g.Client() - client.SetHeader("Authorization", "Bearer "+c.APIKey) - client.SetHeader("Content-Type", "application/json") - - // 发送HTTP请求(避免data展开导致的双重包装) - var response *gclient.Response - switch method { - case "GET": - if body != nil { - response, err = client.Get(ctx, fullURL, body) - } else { - response, err = client.Get(ctx, fullURL) - } - case "POST": - if body != nil { - response, err = client.Post(ctx, fullURL, body) - } else { - response, err = client.Post(ctx, fullURL) - } - case "PUT": - if body != nil { - response, err = client.Put(ctx, fullURL, body) - } else { - response, err = client.Put(ctx, fullURL) - } - case "DELETE": - if body != nil { - response, err = client.Delete(ctx, fullURL, body) - } else { - response, err = client.Delete(ctx, fullURL) - } - default: - return gerror.Newf("unsupported method: %s", method) - } - - if err != nil { - return - } - defer response.Close() - - // RAGFlow API响应格式:{code,data,message}一层结构,直接解析 - responseBody := response.ReadAll() - if err = json.Unmarshal(responseBody, result); err != nil { - return gerror.Newf("RAGFlow响应解析失败: %v, 原始响应: %s", err, string(responseBody)) - } - - return -} - -// buildQueryString 构建查询字符串 -func buildQueryString(params map[string]interface{}) string { - if len(params) == 0 { - return "" - } - - parts := make([]string, 0, len(params)) - for k, v := range params { - parts = append(parts, url.QueryEscape(k)+"="+url.QueryEscape(g.NewVar(v).String())) - } - return strings.Join(parts, "&") -} diff --git a/ragflow/dataset.go b/ragflow/dataset.go deleted file mode 100644 index c5ac88b..0000000 --- a/ragflow/dataset.go +++ /dev/null @@ -1,190 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -// 数据集管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#数据集管理 - -// Dataset 数据集结构体 -type Dataset struct { - Id string `json:"id"` - Name string `json:"name"` - Avatar string `json:"avatar"` - TenantId string `json:"tenant_id"` - Description string `json:"description"` - Language string `json:"language"` - EmbeddingModel string `json:"embedding_model"` - Permission string `json:"permission"` - DocumentCount int `json:"document_count"` - ChunkCount int `json:"chunk_count"` - ParseStatus string `json:"parse_status"` - CreatedBy string `json:"created_by"` - CreateTime int64 `json:"create_time"` - UpdateDate string `json:"update_date"` - UpdateTime int64 `json:"update_time"` - Status string `json:"status"` - ChunkMethod string `json:"chunk_method"` - ParserConfig map[string]interface{} `json:"parser_config"` - VectorSimilarityWeight float64 `json:"vector_similarity_weight"` - SimilarityThreshold float64 `json:"similarity_threshold"` - TokenNum int `json:"token_num"` -} - -// CreateDatasetReq 创建数据集请求 -type CreateDatasetReq struct { - Name string `json:"name"` - Avatar string `json:"avatar,omitempty"` - Description string `json:"description,omitempty"` - EmbeddingModel string `json:"embedding_model,omitempty"` - Permission string `json:"permission,omitempty"` - ChunkMethod string `json:"chunk_method,omitempty"` - ParserConfig map[string]interface{} `json:"parser_config,omitempty"` -} - -// UpdateDatasetReq 更新数据集请求 -type UpdateDatasetReq struct { - Name string `json:"name,omitempty"` - Avatar string `json:"avatar,omitempty"` - Description string `json:"description,omitempty"` - EmbeddingModel string `json:"embedding_model,omitempty"` - Permission string `json:"permission,omitempty"` - ChunkMethod string `json:"chunk_method,omitempty"` - PageRank int `json:"pagerank,omitempty"` - ParserConfig map[string]interface{} `json:"parser_config,omitempty"` -} - -// ListDatasetsReq 列出数据集请求 -type ListDatasetsReq struct { - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - OrderBy string `json:"orderby,omitempty"` - Desc bool `json:"desc,omitempty"` - Name string `json:"name,omitempty"` - Id string `json:"id,omitempty"` -} - -// ListDatasetsRes 列出数据集响应 -// 注意:与 Agent/Chat 等接口不同,Dataset API 会返回 total 字段 -type ListDatasetsRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data []*Dataset `json:"data"` // 数据集列表 - Total int `json:"total"` // 总数据集数 -} - -// DeleteDatasetsReq 删除数据集请求 -type DeleteDatasetsReq struct { - Ids []string `json:"ids"` -} - -// CreateDataset 创建数据集 -func (c *Client) CreateDataset(ctx context.Context, req *CreateDatasetReq) (*Dataset, error) { - g.Log().Infof(ctx, "CreateDataset请求: name=%s, description=%s, embedding_model=%s", req.Name, req.Description, req.EmbeddingModel) - - var res struct { - Code int `json:"code"` - Data *Dataset `json:"data"` - Msg string `json:"message"` - } - if err := c.request(ctx, "POST", "/api/v1/datasets", req, &res); err != nil { - g.Log().Errorf(ctx, "CreateDataset请求失败: %v", err) - return nil, err - } - - g.Log().Infof(ctx, "CreateDataset响应: code=%d, msg=%s, data_is_nil=%v", res.Code, res.Msg, res.Data == nil) - - // code=101表示dataset名称已存在(正常业务场景,不是错误) - // 调用方应该通过ListDatasets查找已有dataset并复用 - if res.Code == 101 { - return nil, gerror.Newf("Dataset名称已存在: %s", res.Msg) - } - - // 其他非0的code表示真正的错误 - if res.Code != 0 { - return nil, gerror.Newf("创建知识库失败(code=%d): %s", res.Code, res.Msg) - } - - // code=0但data=null,表示创建异常(可能是RAGFlow配置问题,如embedding模型不可用、权限不足等) - // 这不是正常状态,应该返回错误而不是(nil, nil) - if res.Data == nil { - return nil, gerror.Newf("创建知识库返回空数据(code=0,data=null),可能是RAGFlow配置问题: %s", res.Msg) - } - - g.Log().Infof(ctx, "CreateDataset成功: id=%s, name=%s", res.Data.Id, res.Data.Name) - return res.Data, nil -} - -// ListDatasets 列出数据集 -func (c *Client) ListDatasets(ctx context.Context, req *ListDatasetsReq) (*ListDatasetsRes, error) { - // 构建查询参数 - path := "/api/v1/datasets" - params := map[string]interface{}{} - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.OrderBy != "" { - params["orderby"] = req.OrderBy - } - // desc 默认为 true,如果显式设置为 false 才传递,或者根据 API 行为调整 - // 这里简单处理,如果设置了就传 - if req.Desc { - params["desc"] = "true" - } else { - params["desc"] = "false" - } - if req.Name != "" { - params["name"] = req.Name - } - if req.Id != "" { - params["id"] = req.Id - } - - // 拼接 query string - query := buildQueryString(params) - if query != "" { - path += "?" + query - } - - var res ListDatasetsRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, err - } - - if res.Code != 0 { - return nil, gerror.Newf("list datasets failed: code=%d", res.Code) - } - return &res, nil -} - -// DeleteDataset 删除数据集 -func (c *Client) DeleteDataset(ctx context.Context, ids []string) (err error) { - req := DeleteDatasetsReq{Ids: ids} - var res CommonResponse - if err = c.request(ctx, "DELETE", "/api/v1/datasets", req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("delete dataset failed: %s", res.Message) - } - return -} - -// UpdateDataset 更新数据集 -func (c *Client) UpdateDataset(ctx context.Context, id string, req *UpdateDatasetReq) (err error) { - var res CommonResponse - path := "/api/v1/datasets/" + id - if err = c.request(ctx, "PUT", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("update dataset failed: %s", res.Message) - } - return -} diff --git a/ragflow/document.go b/ragflow/document.go deleted file mode 100644 index b2c9002..0000000 --- a/ragflow/document.go +++ /dev/null @@ -1,274 +0,0 @@ -// Package ragflow - RAGFlow文档管理 -// 功能:RAGFlow知识库文档的上传、列表、删除操作 -package ragflow - -import ( - "bytes" - "context" - "encoding/json" - "mime/multipart" - "strings" - - commonHttp "gitea.com/red-future/common/http" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -// 数据集内文件管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#数据集内文件管理 - -// ... (rest of the code remains the same) -type Document struct { - Id string `json:"id"` - DatasetId string `json:"dataset_id"` - Name string `json:"name"` - Size int64 `json:"size"` - Location string `json:"location"` - CreatedBy string `json:"created_by"` - CreateTime int64 `json:"create_time"` - Thumbnail string `json:"thumbnail"` - Type string `json:"type"` - RunStatus string `json:"run_status"` // 对应 API 返回的 "run" 字段,可能需要确认 - Status string `json:"status"` - ChunkMethod string `json:"chunk_method"` - ParserConfig map[string]interface{} `json:"parser_config"` - TokenNum int `json:"token_num"` - ChunkCount int `json:"chunk_count"` - ProcessBegin int64 `json:"process_begin"` - ProcessDu int64 `json:"process_du"` - Progress float64 `json:"progress"` - ProgressMsg string `json:"progress_msg"` -} - -// UploadDocumentReq 上传文档请求 -// 注意:上传文件通常需要 multipart/form-data,这里仅定义结构,实际逻辑在方法中处理 -type UploadDocumentReq struct { - FilePaths []string // 本地文件路径列表 -} - -// UploadDocumentRes 上传文档响应 -type UploadDocumentRes struct { - Id string `json:"id"` // 文档ID -} - -// ListDocumentsReq 列出文档请求 -type ListDocumentsReq struct { - Page int `json:"page,omitempty"` // 页码,默认 1 - PageSize int `json:"page_size,omitempty"` // 每页数量,默认 30 - OrderBy string `json:"orderby,omitempty"` // 排序字段:create_time(默认)或 update_time - Desc bool `json:"desc,omitempty"` // 是否降序,默认 true - Keywords string `json:"keywords,omitempty"` // 关键词过滤(匹配文档标题) - Id string `json:"id,omitempty"` // 文档 ID 过滤 - Name string `json:"name,omitempty"` // 文档名称过滤 - CreateTimeFrom int64 `json:"create_time_from,omitempty"` // 创建时间起始(Unix 时间戳),0 表示无限制 - CreateTimeTo int64 `json:"create_time_to,omitempty"` // 创建时间截止(Unix 时间戳),0 表示无限制 - Suffix []string `json:"suffix,omitempty"` // 文件后缀过滤,如 ["pdf", "txt", "docx"] - Run []string `json:"run,omitempty"` // 处理状态过滤,支持 ["UNSTART", "RUNNING", "CANCEL", "DONE", "FAIL"] 或数字格式 ["0", "1", "2", "3", "4"] -} - -// ListDocumentsRes 列出文档响应 -// 注意:响应结构与其他 List 接口不同,data 是一个对象而非数组 -type ListDocumentsRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data struct { - Docs []*Document `json:"docs"` // 文档列表 - TotalDatasets int `json:"total_datasets"` // 总文档数 - } `json:"data"` -} - -// DeleteDocumentsReq 删除文档请求 -type DeleteDocumentsReq struct { - Ids []string `json:"ids"` -} - -// ListDocuments 列出文档 -func (c *Client) ListDocuments(ctx context.Context, datasetId string, req *ListDocumentsReq) (*ListDocumentsRes, error) { - path := "/api/v1/datasets/" + datasetId + "/documents" - params := map[string]interface{}{} - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.OrderBy != "" { - params["orderby"] = req.OrderBy - } - if req.Desc { - params["desc"] = "true" - } else { - params["desc"] = "false" - } - if req.Keywords != "" { - params["keywords"] = req.Keywords - } - if req.Id != "" { - params["id"] = req.Id - } - if req.Name != "" { - params["name"] = req.Name - } - if req.CreateTimeFrom > 0 { - params["create_time_from"] = req.CreateTimeFrom - } - if req.CreateTimeTo > 0 { - params["create_time_to"] = req.CreateTimeTo - } - - // 构造查询字符串 - query := buildQueryString(params) - var queryParts []string - if query != "" { - queryParts = append(queryParts, query) - } - - // 处理数组参数:suffix(文件后缀过滤) - // API 要求多个值时重复参数名,如:suffix=pdf&suffix=txt - for _, suffix := range req.Suffix { - queryParts = append(queryParts, "suffix="+suffix) - } - - // 处理数组参数:run(处理状态过滤) - // 支持数字格式("0"-"4")或文本格式("UNSTART", "RUNNING", "CANCEL", "DONE", "FAIL") - for _, run := range req.Run { - queryParts = append(queryParts, "run="+run) - } - - // 构造最终请求路径 - if len(queryParts) > 0 { - path += "?" + strings.Join(queryParts, "&") - } - - // 发送请求并处理响应 - var res ListDocumentsRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("list documents failed: code=%d", res.Code) - } - return &res, nil -} - -// UploadDocumentFromText 上传文本内容作为文档 -func (c *Client) UploadDocumentFromText(ctx context.Context, datasetId, content, filename string) (documentId string, err error) { - if datasetId == "" { - return "", gerror.New("datasetId不能为空") - } - if content == "" { - return "", gerror.New("文档内容不能为空") - } - if filename == "" { - filename = "document.txt" - } - - // 构造URL(使用负载均衡) - endpoint := c.getNextEndpoint() - if endpoint == "" { - return "", gerror.New("RAGFlow endpoints not configured") - } - url := endpoint + "/api/v1/datasets/" + datasetId + "/documents" - - // 创建multipart writer - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - - // 添加文件字段 - part, err := writer.CreateFormFile("file", filename) - if err != nil { - return "", gerror.Wrap(err, "创建form file失败") - } - - // 写入内容 - if _, err = part.Write([]byte(content)); err != nil { - return "", gerror.Wrap(err, "写入文件内容失败") - } - - // 关闭multipart writer - if err = writer.Close(); err != nil { - return "", gerror.Wrap(err, "关闭multipart writer失败") - } - - // 发送请求 - client := commonHttp.Httpclient.Clone() - client.SetHeader("Authorization", "Bearer "+c.APIKey) - client.SetHeader("Content-Type", writer.FormDataContentType()) - - resp, err := client.Post(ctx, url, body.Bytes()) - if err != nil { - return "", gerror.Wrap(err, "上传文档请求失败") - } - defer resp.Close() - - // 解析响应 - var response struct { - Code int `json:"code"` - Message string `json:"message"` - Data []UploadDocumentRes `json:"data"` // RAGFlow返回数组 - } - - respBody := resp.ReadAll() - - if err := json.Unmarshal(respBody, &response); err != nil { - g.Log().Errorf(ctx, "解析RAGFlow响应失败: %v, 原始响应: %s", err, string(respBody)) - return "", gerror.Newf("json Decode failed: %v", err) - } - - // 先检查code,再检查data - if response.Code != 0 { - g.Log().Errorf(ctx, "RAGFlow返回错误: code=%d, message=%s", response.Code, response.Message) - return "", gerror.Newf("上传文档失败 (code=%d): %s", response.Code, response.Message) - } - - if len(response.Data) == 0 { - g.Log().Errorf(ctx, "RAGFlow返回data为空, 完整响应: %s", string(respBody)) - return "", gerror.New("上传文档返回data为空") - } - - return response.Data[0].Id, nil -} - -// UploadDocument 上传文档(保留兼容) -func (c *Client) UploadDocument(ctx context.Context, datasetId string, filePaths []string) (err error) { - return gerror.New("upload document from file not implemented yet, use UploadDocumentFromText instead") -} - -// ParseDocumentsReq 解析文档请求 -type ParseDocumentsReq struct { - DocumentIds []string `json:"document_ids"` // 要解析的文档ID列表 -} - -// ParseDocuments 解析文档(上传后必须调用此接口才会开始解析) -func (c *Client) ParseDocuments(ctx context.Context, datasetId string, documentIds []string) error { - if datasetId == "" { - return gerror.New("datasetId不能为空") - } - if len(documentIds) == 0 { - return gerror.New("documentIds不能为空") - } - - req := ParseDocumentsReq{DocumentIds: documentIds} - var res CommonResponse - path := "/api/v1/datasets/" + datasetId + "/chunks" - if err := c.request(ctx, "POST", path, req, &res); err != nil { - return err - } - if !res.IsSuccess() { - return gerror.Newf("解析文档失败: %s", res.Message) - } - return nil -} - -// DeleteDocument 删除文档 -func (c *Client) DeleteDocument(ctx context.Context, datasetId string, ids []string) (err error) { - req := DeleteDocumentsReq{Ids: ids} - var res CommonResponse - path := "/api/v1/datasets/" + datasetId + "/documents" - if err = c.request(ctx, "DELETE", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("delete document failed: %s", res.Message) - } - return -} diff --git a/ragflow/openai.go b/ragflow/openai.go deleted file mode 100644 index 4218592..0000000 --- a/ragflow/openai.go +++ /dev/null @@ -1,117 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" -) - -// OpenAICompatibleAPI 与 OpenAI 兼容的 API -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#与-openai-兼容的-api - -// ChatCompletionMessage OpenAI 格式的消息 -type ChatCompletionMessage struct { - Role string `json:"role"` // "user", "assistant", "system" - Content string `json:"content"` -} - -// ChatCompletionRequest OpenAI 格式的聊天补全请求 -type ChatCompletionRequest struct { - Model string `json:"model"` // 模型名称(服务器会自动解析,可设置为任意值) - Messages []ChatCompletionMessage `json:"messages"` // 消息列表,必须至少包含一条 user 消息 - Stream bool `json:"stream,omitempty"` // 是否流式返回,默认 false -} - -// ChatCompletionResponse OpenAI 格式的聊天补全响应(非流式) -type ChatCompletionResponse struct { - ID string `json:"id"` - Object string `json:"object"` - Created int64 `json:"created"` - Model string `json:"model"` - Choices []struct { - Index int `json:"index"` - Message ChatCompletionMessage `json:"message"` - FinishReason string `json:"finish_reason"` - } `json:"choices"` - Usage struct { - PromptTokens int `json:"prompt_tokens"` - CompletionTokens int `json:"completion_tokens"` - TotalTokens int `json:"total_tokens"` - } `json:"usage"` -} - -// ChatCompletionChunk 流式响应块 -type ChatCompletionChunk struct { - ID string `json:"id"` - Object string `json:"object"` - Created int64 `json:"created"` - Model string `json:"model"` - Choices []struct { - Index int `json:"index"` - Delta struct { - Content string `json:"content"` - Role string `json:"role"` - } `json:"delta"` - FinishReason *string `json:"finish_reason"` - } `json:"choices"` - Usage *struct { - PromptTokens int `json:"prompt_tokens"` - CompletionTokens int `json:"completion_tokens"` - TotalTokens int `json:"total_tokens"` - } `json:"usage,omitempty"` -} - -// CreateChatCompletion 创建聊天补全(与聊天助手) -// POST /api/v1/chats_openai/{chat_id}/chat/completions -func (c *Client) CreateChatCompletion(ctx context.Context, chatID string, req *ChatCompletionRequest) (*ChatCompletionResponse, error) { - path := "/api/v1/chats_openai/" + chatID + "/chat/completions" - - var resp ChatCompletionResponse - if err := c.request(ctx, "POST", path, req, &resp); err != nil { - return nil, gerror.Newf("create chat completion failed: %v", err) - } - - return &resp, nil -} - -// CreateAgentCompletion 创建 Agent 补全 -// POST /api/v1/agents_openai/{agent_id}/chat/completions -func (c *Client) CreateAgentCompletion(ctx context.Context, agentID string, req *ChatCompletionRequest) (*ChatCompletionResponse, error) { - path := "/api/v1/agents_openai/" + agentID + "/chat/completions" - - var resp ChatCompletionResponse - if err := c.request(ctx, "POST", path, req, &resp); err != nil { - return nil, gerror.Newf("create agent completion failed: %v", err) - } - - return &resp, nil -} - -// CreateChatCompletionStream 创建流式聊天补全(与聊天助手) -// 注意:流式响应需要特殊处理,这里返回一个可用于读取流的接口 -func (c *Client) CreateChatCompletionStream(ctx context.Context, chatID string, req *ChatCompletionRequest) (*StreamReader, error) { - req.Stream = true - // TODO: 实现流式读取逻辑 - return nil, gerror.New("stream mode not implemented yet") -} - -// StreamReader 流式响应读取器 -type StreamReader struct { - _ *gjson.Json // TODO: 实现流式读取时使用 - close func() error -} - -// ReadChunk 读取下一个响应块 -// TODO: 实现流式读取逻辑 -func (sr *StreamReader) ReadChunk() (*ChatCompletionChunk, error) { - return nil, gerror.New("stream mode not implemented yet") -} - -// Close 关闭流 -func (sr *StreamReader) Close() (err error) { - if sr.close != nil { - return sr.close() - } - return -} diff --git a/ragflow/session.go b/ragflow/session.go deleted file mode 100644 index 966d0e8..0000000 --- a/ragflow/session.go +++ /dev/null @@ -1,178 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -// 会话管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#会话管理 - -// Session 会话结构体 -type Session struct { - Id string `json:"id"` - Name string `json:"name"` - ChatId string `json:"chat_id"` // 响应中是 "chat" 或 "chat_id",根据文档示例调整 - Messages []Message `json:"messages"` - CreateDate string `json:"create_date"` - CreateTime int64 `json:"create_time"` - UpdateDate string `json:"update_date"` - UpdateTime int64 `json:"update_time"` -} - -type Message struct { - Content string `json:"content"` - Role string `json:"role"` -} - -// CreateSessionReq 创建会话请求 -type CreateSessionReq struct { - Name string `json:"name"` - UserId string `json:"user_id,omitempty"` -} - -// ListSessionsReq 列出会话请求 -type ListSessionsReq struct { - Page int `json:"page,omitempty"` - PageSize int `json:"page_size,omitempty"` - OrderBy string `json:"orderby,omitempty"` - Desc bool `json:"desc,omitempty"` - Name string `json:"name,omitempty"` - Id string `json:"id,omitempty"` - UserId string `json:"user_id,omitempty"` -} - -// ListSessionsRes 列出会话响应 -// 注意:API 不返回 total 字段,仅返回 data 数组 -type ListSessionsRes struct { - Code int `json:"code"` // 状态码,0 表示成功 - Data []*Session `json:"data"` // 会话列表 -} - -// DeleteSessionsReq 删除会话请求 -type DeleteSessionsReq struct { - Ids []string `json:"ids"` -} - -// ChatCompletionReq 对话请求 -type ChatCompletionReq struct { - Question string `json:"question"` - Stream bool `json:"stream"` - SessionId string `json:"session_id,omitempty"` - UserId string `json:"user_id,omitempty"` -} - -// ChatCompletionRes 对话响应 (非流式) -type ChatCompletionRes struct { - Code int `json:"code"` - Message string `json:"message"` // 错误信息 - Data struct { - Answer string `json:"answer"` - Reference interface{} `json:"reference"` - AudioBinary interface{} `json:"audio_binary"` - Id interface{} `json:"id"` - SessionId string `json:"session_id"` - } `json:"data"` -} - -// CreateSession 创建会话 -func (c *Client) CreateSession(ctx context.Context, chatId string, req *CreateSessionReq) (*Session, error) { - path := "/api/v1/chats/" + chatId + "/sessions" - var res struct { - Code int `json:"code"` - Data *Session `json:"data"` - Msg string `json:"message"` - } - if err := c.request(ctx, "POST", path, req, &res); err != nil { - g.Log().Errorf(ctx, "❌ CreateSession请求失败: chatId=%s, req=%+v, error=%v", chatId, req, err) - return nil, err - } - if res.Code != 0 { - g.Log().Errorf(ctx, "❌ CreateSession返回失败: chatId=%s, req=%+v, code=%d, msg=%s", chatId, req, res.Code, res.Msg) - return nil, gerror.Newf("create session failed: %s", res.Msg) - } - // 检查响应数据是否为空:防止RAGFlow API返回 {"code":0, "data":null} - // 如果不检查直接返回,调用方会收到 (nil, nil),导致空指针异常 - if res.Data == nil { - return nil, gerror.Newf("create session returned null data: %s", res.Msg) - } - return res.Data, nil -} - -// ListSessions 列出会话 -func (c *Client) ListSessions(ctx context.Context, chatId string, req *ListSessionsReq) (*ListSessionsRes, error) { - path := "/api/v1/chats/" + chatId + "/sessions" - params := map[string]interface{}{} - if req.Page > 0 { - params["page"] = req.Page - } - if req.PageSize > 0 { - params["page_size"] = req.PageSize - } - if req.OrderBy != "" { - params["orderby"] = req.OrderBy - } - if req.Desc { - params["desc"] = "true" - } else { - params["desc"] = "false" - } - if req.Name != "" { - params["name"] = req.Name - } - if req.Id != "" { - params["id"] = req.Id - } - if req.UserId != "" { - params["user_id"] = req.UserId - } - - query := buildQueryString(params) - if query != "" { - path += "?" + query - } - - var res ListSessionsRes - if err := c.request(ctx, "GET", path, nil, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("list sessions failed: code=%d", res.Code) - } - return &res, nil -} - -// DeleteSessions 删除会话 -func (c *Client) DeleteSessions(ctx context.Context, chatId string, ids []string) (err error) { - req := DeleteSessionsReq{Ids: ids} - var res CommonResponse - path := "/api/v1/chats/" + chatId + "/sessions" - if err = c.request(ctx, "DELETE", path, req, &res); err != nil { - return - } - if !res.IsSuccess() { - return gerror.Newf("delete sessions failed: %s", res.Message) - } - return -} - -// ChatCompletion 对话 (目前仅支持非流式) -func (c *Client) ChatCompletion(ctx context.Context, chatId string, req *ChatCompletionReq) (*ChatCompletionRes, error) { - path := "/api/v1/chats/" + chatId + "/completions" - var res ChatCompletionRes - - // 如果需要流式支持,需要使用 gclient 的流式处理能力,这里暂只实现非流式 - if req.Stream { - return nil, gerror.New("stream mode not supported yet") - } - - if err := c.request(ctx, "POST", path, req, &res); err != nil { - return nil, err - } - if res.Code != 0 { - return nil, gerror.Newf("chat completion failed: code=%d, message=%s", res.Code, res.Message) - } - return &res, nil -} diff --git a/ragflow/system.go b/ragflow/system.go deleted file mode 100644 index 1a7da73..0000000 --- a/ragflow/system.go +++ /dev/null @@ -1,39 +0,0 @@ -package ragflow - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" -) - -// System 系统管理 -// 参考: https://ragflow.com.cn/docs/dev/http_api_reference#系统 - -// HealthStatus 健康状态 -type HealthStatus struct { - DB string `json:"db"` // "ok" 或 "nok" - Redis string `json:"redis"` // "ok" 或 "nok" - DocEngine string `json:"doc_engine"` // "ok" 或 "nok" - Storage string `json:"storage"` // "ok" 或 "nok" - Status string `json:"status"` // 整体状态: "ok" 或 "nok" - Meta map[string]interface{} `json:"_meta,omitempty"` // 详细错误信息 -} - -// CheckHealth 检查系统健康状况 -// GET /v1/system/healthz -func (c *Client) CheckHealth(ctx context.Context) (*HealthStatus, error) { - var status HealthStatus - if err := c.request(ctx, "GET", "/v1/system/healthz", nil, &status); err != nil { - return nil, gerror.Newf("check health failed: %v", err) - } - return &status, nil -} - -// IsHealthy 检查系统是否健康 -func (c *Client) IsHealthy(ctx context.Context) (bool, error) { - status, err := c.CheckHealth(ctx) - if err != nil { - return false, err - } - return status.Status == "ok", nil -} diff --git a/ragflow/worker_pool.go b/ragflow/worker_pool.go deleted file mode 100644 index 91eae0c..0000000 --- a/ragflow/worker_pool.go +++ /dev/null @@ -1,154 +0,0 @@ -package ragflow - -import ( - "context" - "runtime/debug" - "strings" - "sync" - "time" - - "gitea.com/red-future/common/redis" - "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/os/grpool" -) - -// 默认批量大小(每次从 Redis 读取并发送的消息数) -const defaultBatchSize = 200 - -// QueueProcessor Stream 处理器,批量读取消息并发送到 RAGFlow -type QueueProcessor struct { - streamKey string // Stream 键名 - groupName string // 消费者组名称 - consumerName string // 消费者名称 - timeout int64 // 阻塞超时时间(毫秒) - batchSize int64 // 最大并发数(协程池大小) - stopChan chan struct{} // 停止信号 - pool *grpool.Pool // GoFrame协程池 - handleFunc func(ctx context.Context, message map[string]interface{}) error - processingMsgs sync.Map // 正在处理的消息ID(去重用) -} - -// NewQueueProcessor 创建 Stream 处理器 -func NewQueueProcessor(streamKey, groupName, consumerName string, timeout, batchSize int64, handleFunc func(ctx context.Context, message map[string]interface{}) error) *QueueProcessor { - // 创建协程池:固定大小,避免频繁创建销毁goroutine - pool := grpool.New(int(batchSize)) - - return &QueueProcessor{ - streamKey: streamKey, - groupName: groupName, - consumerName: consumerName, - timeout: timeout, - batchSize: batchSize, - stopChan: make(chan struct{}), - pool: pool, // 使用GoFrame协程池 - handleFunc: handleFunc, - } -} - -// Start 启动 Stream 处理器 -// 削峰填谷:每次读取 batchSize 条消息,并发发送,发完立刻读下一批 -func (q *QueueProcessor) Start(ctx context.Context) error { - glog.Infof(ctx, "Stream 处理器启动 - Stream: %s, 消费者组: %s, 消费者: %s, 批量大小: %d", - q.streamKey, q.groupName, q.consumerName, q.batchSize) - - // 确保 Consumer Group 存在(重试直到成功) - for { - if err := redis.CreateConsumerGroup(ctx, q.streamKey, q.groupName); err != nil { - // BUSYGROUP 表示已存在,不是错误 - if strings.Contains(err.Error(), "BUSYGROUP") { - glog.Debugf(ctx, "Consumer Group 已存在") - break - } - glog.Warningf(ctx, "创建 Consumer Group 失败: %v,1秒后重试", err) - time.Sleep(time.Second) - continue - } - glog.Infof(ctx, "Consumer Group 创建成功") - break - } - - for { - select { - case <-q.stopChan: - glog.Info(ctx, "Stream 处理器收到停止信号") - return nil - default: - // 1. 从 Redis Stream 读取一批消息 - messages, err := redis.ReadFromStream(ctx, q.streamKey, q.groupName, q.consumerName, q.batchSize, q.timeout) - if err != nil { - glog.Errorf(ctx, "从 Stream 读取消息失败: %v", err) - continue - } - - if len(messages) == 0 { - continue - } - - glog.Infof(ctx, "✅ 从Stream读取到 %d 条消息,开始处理", len(messages)) - - // 2. 去重+立即ACK:对话场景优先实时性,失败不重试 - for i, msg := range messages { - m := msg // 捕获循环变量 - msgIndex := i + 1 - - // 去重:如果消息正在处理,跳过 - if _, exists := q.processingMsgs.LoadOrStore(m.ID, true); exists { - glog.Debugf(ctx, "⏭️ 跳过正在处理的消息 - ID: %s", m.ID) - continue - } - - // 立即ACK:对话场景不需要重试,避免重复消费 - if err := redis.AckMessage(ctx, q.streamKey, q.groupName, m.ID); err != nil { - glog.Errorf(ctx, "确认消息失败: %v, 消息ID: %s", err, m.ID) - } - - glog.Infof(ctx, "📨 准备处理第 %d/%d 条消息 - ID: %s", msgIndex, len(messages), m.ID) - - // 提交到协程池,池满时会阻塞等待空闲worker - q.pool.Add(ctx, func(ctx context.Context) { - defer q.processingMsgs.Delete(m.ID) // 处理完成后移除标记 - q.processMessage(ctx, m) - }) - } - // 3. 立刻读下一批(不等待,协程池自动控制并发数) - } - } -} - -// processMessage 处理单条消息(异步执行) -func (q *QueueProcessor) processMessage(ctx context.Context, message redis.StreamMessage) { - // 捕获panic,防止协程崩溃 - defer func() { - if r := recover(); r != nil { - glog.Errorf(ctx, "❌ PANIC: 消息处理发生panic - 消息ID: %s, panic内容: %v\n堆栈:\n%s", - message.ID, r, debug.Stack()) - } - }() - - glog.Infof(ctx, "🔄 开始处理消息 - ID: %s", message.ID) - - // 打印实际字段名(调试用) - var fieldNames []string - for key := range message.Values { - fieldNames = append(fieldNames, key) - } - glog.Infof(ctx, "📋 消息字段名列表: %v", fieldNames) - glog.Infof(ctx, "📦 消息完整内容: %+v", message.Values) - - // 调用处理函数发送到 RAGFlow - if err := q.handleFunc(ctx, message.Values); err != nil { - glog.Errorf(ctx, "❌ 消息处理失败: %v, 消息ID: %s", err, message.ID) - } else { - glog.Infof(ctx, "✅ 消息处理成功 - ID: %s", message.ID) - } - - // ACK已在读取后立即执行,此处无需重复ACK - // 对话场景:失败直接丢弃,不重试(实时性优先) -} - -// Stop 停止队列处理器 -func (q *QueueProcessor) Stop() { - close(q.stopChan) - // 关闭协程池,等待所有任务完成 - q.pool.Close() -} diff --git a/redis/keys.go b/redis/keys.go deleted file mode 100644 index ac62939..0000000 --- a/redis/keys.go +++ /dev/null @@ -1,19 +0,0 @@ -package redis - -// Redis 数据缓存 Key 常量 -const ( - CleanList = "list:tenantId-%v:collection-%s:*" // 清理列表Key - CleanCount = "count:tenantId-%v:collection-%s:*" // 清理计数Key - List = "list:tenantId-%v:collection-%s:filter:%s:options:%s" // 列表查询Key - Count = "count:tenantId-%v:collection-%s:filter:%s" // 计数查询Key - One = "one:tenantId-%v:collection-%s:filter:%s" // 单条查询Key -) - -// 限流 Redis Key 常量 -const ( - RateLimitKeyPrefix = "ragflow:ratelimit:" // 限流Key前缀 - RateLimitKeyIP = "ip:%s" // IP限流: ip:192.168.1.1 - RateLimitKeyUser = "user:%s" // 用户限流: user:123 或 user:anon:192.168.1.1 - RateLimitKeyService = "service:%s" // 服务限流: service:customerService - RateLimitKeyGlobal = "global:requests" // 全局限流: global:requests -) diff --git a/redis/message.go b/redis/message.go deleted file mode 100644 index a9cfcbe..0000000 --- a/redis/message.go +++ /dev/null @@ -1,13 +0,0 @@ -package redis - -import "context" - -type QueueMessage struct { - StreamKey string // Stream 键名 - GroupName string // 消费者组名称 - ConsumerName string // 消费者名称 - BatchSize int64 // 最大并发数(信号量容量) - BlockMs int64 // 阻塞时间 - AutoAck bool // ACK确认,true自动确认,false手动确认 - HandleFunc func(ctx context.Context, message map[string]interface{}) error -} diff --git a/redis/redis.go b/redis/redis.go deleted file mode 100644 index 577f8e7..0000000 --- a/redis/redis.go +++ /dev/null @@ -1,743 +0,0 @@ -package redis - -import ( - "context" - "strings" - "sync" - "time" - - "github.com/gogf/gf/v2/database/gredis" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -var ( - // redisClient 内部使用的 Redis 客户端(单例模式) - redisClient *gredis.Redis - redisOnce sync.Once -) - -// getClient 获取 Redis 客户端(延迟初始化) -func getClient() *gredis.Redis { - redisOnce.Do(func() { - redisClient = g.Redis() - }) - return redisClient -} - -// getClient 获取 Redis 客户端 临时方法 -func GetRedisClientTest(name string) *gredis.Redis { - return g.Redis(name) -} - -// RedisClient 获取 Redis 客户端(函数式,确保单例正确初始化) -func RedisClient() *gredis.Redis { - return getClient() -} - -func GetReadStream(ctx context.Context, msg ...QueueMessage) error { - for _, t := range msg { - err := GetReadFromStream(ctx, t.StreamKey, t.GroupName, t.ConsumerName, t.BatchSize, t.BlockMs, t.AutoAck, t.HandleFunc) - if err != nil { - glog.Infof(ctx, "读取ReadFromStream数据失败-> 键名: %s, 消费者组: %s, 消费者名称%v\n, 失败err:%v\n", t.StreamKey, t.GroupName, t.ConsumerName, err) - continue - } - } - return nil -} - -// GetReadFromStream 读取ReadFromStream数据 -func GetReadFromStream(ctx context.Context, streamKey, groupName, consumerName string, count, blockMs int64, autoAck bool, fn func(ctx context.Context, message map[string]interface{}) error) (err error) { - glog.Infof(ctx, "初始化 Stream: %s, 消费者组: %s", streamKey, groupName) - err = InitStreamGroup(ctx, streamKey, groupName) - if err != nil { - return err - } - for { - // 从 Redis Stream 读取一批消息 - messages, err := ReadFromStream(ctx, streamKey, groupName, consumerName, count, blockMs) - if err != nil { - glog.Errorf(ctx, "[DEBUG Redis] XREADGROUP 错误: %v", err) - return err - } - // 处理消息 - for _, msg := range messages { - glog.Infof(ctx, "消费者 '%s' -> 接收到消息 ID: %s, 内容: %v\n", consumerName, msg.ID, msg.Values) - // 业务处理 - if err = fn(ctx, msg.Values); err != nil { - glog.Infof(ctx, "业务处理失败-> err:%v\n", err) - continue - } - // 确认消息 (ACK) - if autoAck { - // 处理成功后,必须调用 XAck,否则消息会一直留在 PEL 中 - err = AckMessage(ctx, streamKey, groupName, msg.ID) - if err != nil { - glog.Infof(ctx, "消费者 '%s' 确认消息 ID %s 失败: %v\n", consumerName, msg.ID, err) - } else { - glog.Infof(ctx, "消费者 '%s' -> 已确认消息 ID: %s\n", consumerName, msg.ID) - } - } - - } - } - return -} - -// Stream 和消费者组常量 -const ( - // RAGFlow 请求 Stream Key - RAGFlowRequestStreamKey = "ragflow:request:stream" - // RAGFlow 响应 Stream Key - RAGFlowResponseStreamKey = "ragflow:response:stream" - // RAGFlow 请求消费者组名称 - RAGFlowRequestConsumerGroup = "ragflow:request:consumer:group" - // RAGFlow 响应消费者组名称 - RAGFlowResponseConsumerGroup = "ragflow:response:consumer:group" - // RAGFlow 消费者组名称(兼容旧代码) - RAGFlowConsumerGroup = "ragflow:consumer:group" - // 会话最后活跃时间 Key 前缀 - SessionLastActiveKeyPrefix = "ragflow:session:" -) - -// StreamMessage Redis Stream 消息结构 -type StreamMessage struct { - ID string // 消息ID(自动生成) - Values map[string]interface{} // 消息内容 -} - -// InitStreamGroup 初始化 Stream 和消费者组 -// 使用 gredis Do() 方法执行 XGROUP CREATE 命令 -func InitStreamGroup(ctx context.Context, streamKey, groupName string) error { - // XGROUP CREATE streamKey groupName 0 MKSTREAM - _, err := getClient().Do(ctx, "XGROUP", "CREATE", streamKey, groupName, "0", "MKSTREAM") - if err != nil { - // 如果组已存在,忽略错误 - errStr := err.Error() - if strings.Contains(errStr, "BUSYGROUP") || strings.Contains(errStr, "already exists") { - return nil - } - return err - } - return nil -} - -// AddToStream 将消息添加到 Stream -// 使用 gredis Do() 方法执行 XADD 命令 -// msg 可以是结构体或 map,内部自动转换 -func AddToStream(ctx context.Context, streamKey string, msg interface{}) (messageID string, err error) { - // 将结构体转换为 map - values := gconv.Map(msg) - - // XADD streamKey * field1 value1 field2 value2 ... - args := make([]interface{}, 0, len(values)*2+2) - args = append(args, streamKey, "*") // "*" 自动生成ID - for key, val := range values { - args = append(args, key, val) - } - - result, err := getClient().Do(ctx, "XADD", args...) - if err != nil { - return - } - - messageID = result.String() - return -} - -// CreateConsumerGroup 创建消费者组(如果不存在) -// XGROUP CREATE streamKey groupName 0 MKSTREAM -// 使用0作为起始ID,从Stream开头读取所有未消费消息 -func CreateConsumerGroup(ctx context.Context, streamKey, groupName string) error { - _, err := getClient().Do(ctx, "XGROUP", "CREATE", streamKey, groupName, "0", "MKSTREAM") - return err -} - -// ReadFromStream 从 Stream 读取消息(消费者组模式) -// 使用 gredis Do() 方法执行 XREADGROUP 命令 -func ReadFromStream(ctx context.Context, streamKey, groupName, consumerName string, count int64, blockMs int64) ([]StreamMessage, error) { - // 检查是否需要记录trace(避免轮询产生大量trace) - execCtx := ctx - if !g.Cfg().MustGet(ctx, "jaeger.traceStream", true).Bool() { - // 不记录trace:使用background context(不继承span) - execCtx = context.Background() - } - -RECONNECT: - // 先尝试读取pending消息(ID=0),处理积压 - result, err := getClient().Do(execCtx, - "XREADGROUP", "GROUP", groupName, consumerName, - "COUNT", count, - "BLOCK", 0, // 不阻塞,立即返回 - "STREAMS", streamKey, "0", // ID=0 读取pending消息 - ) - if err != nil { - g.Log().Errorf(ctx, "❌ XREADGROUP读取pending失败: stream=%s, error=%v", streamKey, err) - time.Sleep(time.Second) - goto RECONNECT - } - - // 检查pending结果是否为空(需要检查消息数组是否为空) - hasPending := false - if result != nil && !result.IsEmpty() { - // 尝试解析map格式 - if resultVal := result.Val(); resultVal != nil { - if streamsMap, ok := resultVal.(map[interface{}]interface{}); ok { - for _, streamMsgs := range streamsMap { - if msgsArray, ok := streamMsgs.([]interface{}); ok && len(msgsArray) > 0 { - hasPending = true - break - } - } - } - } - } - - // 如果没有pending消息,读取新消息 - if !hasPending { - result, err = getClient().Do(execCtx, - "XREADGROUP", "GROUP", groupName, consumerName, - "COUNT", count, - "BLOCK", blockMs, - "STREAMS", streamKey, ">", - ) - if err != nil { - g.Log().Errorf(ctx, "❌ XREADGROUP读取新消息失败: stream=%s, error=%v", streamKey, err) - time.Sleep(time.Second) - goto RECONNECT - } - } - - // 预分配容量,避免动态扩容 - messages := make([]StreamMessage, 0, int(count)) - - if result == nil || result.IsEmpty() { - // 超时或没有数据 - return messages, nil - } - - // GoFrame gredis 返回格式: map[streamKey:[[msgID [field1 value1 field2 value2 ...]] ...]] - resultVal := result.Val() - - // 尝试 map 格式(GoFrame gredis 返回) - if streamsMap, ok := resultVal.(map[interface{}]interface{}); ok { - for streamKey, streamMsgs := range streamsMap { - msgsArray, ok := streamMsgs.([]interface{}) - if !ok { - g.Log().Errorf(ctx, "❌ streamMsgs类型转换失败: streamKey=%v, 实际类型=%T", streamKey, streamMsgs) - continue - } - for i, msgData := range msgsArray { - msgArray, ok := msgData.([]interface{}) - if !ok { - g.Log().Errorf(ctx, "❌ msgData类型转换失败: index=%d, 实际类型=%T", i, msgData) - continue - } - if len(msgArray) < 2 { - g.Log().Errorf(ctx, "❌ msgArray长度不足: index=%d, len=%d", i, len(msgArray)) - continue - } - msgID := gconv.String(msgArray[0]) - fieldsArray, ok := msgArray[1].([]interface{}) - if !ok { - g.Log().Errorf(ctx, "❌ fieldsArray类型转换失败: msgID=%s, msgArray[1]类型=%T", msgID, msgArray[1]) - continue - } - values := make(map[string]interface{}, len(fieldsArray)/2) - for i := 0; i < len(fieldsArray); i += 2 { - if i+1 < len(fieldsArray) { - key := gconv.String(fieldsArray[i]) - values[key] = fieldsArray[i+1] - } - } - messages = append(messages, StreamMessage{ - ID: msgID, - Values: values, - }) - } - } - if len(messages) == 0 { - g.Log().Errorf(ctx, "❌ [ReadFromStream] map格式解析失败: streamsMap长度=%d, 但未提取到消息", len(streamsMap)) - } - return messages, nil - } - - // 尝试数组格式(标准 Redis 返回) - if streamsArray, ok := resultVal.([]interface{}); ok && len(streamsArray) > 0 { - for _, streamData := range streamsArray { - streamArray, ok := streamData.([]interface{}) - if !ok || len(streamArray) < 2 { - continue - } - messagesArray, ok := streamArray[1].([]interface{}) - if !ok { - continue - } - for _, msgData := range messagesArray { - msgArray, ok := msgData.([]interface{}) - if !ok || len(msgArray) < 2 { - continue - } - msgID := gconv.String(msgArray[0]) - fieldsArray, ok := msgArray[1].([]interface{}) - if !ok { - continue - } - values := make(map[string]interface{}, len(fieldsArray)/2) - for i := 0; i < len(fieldsArray); i += 2 { - if i+1 < len(fieldsArray) { - key := gconv.String(fieldsArray[i]) - values[key] = fieldsArray[i+1] - } - } - messages = append(messages, StreamMessage{ - ID: msgID, - Values: values, - }) - } - } - if len(messages) == 0 { - g.Log().Errorf(ctx, "❌ [ReadFromStream] 数组格式解析失败: streamsArray长度=%d, 但未提取到消息", len(streamsArray)) - } - return messages, nil - } - - g.Log().Errorf(ctx, "❌ [ReadFromStream] 无法识别的result格式, resultVal类型: %T, 值: %+v", resultVal, resultVal) - return messages, nil -} - -// AckMessage 确认消息已处理 -// 使用 gredis Do() 方法执行 XACK 命令 -func AckMessage(ctx context.Context, streamKey, groupName string, messageIDs ...string) error { - // XACK streamKey groupName messageID1 messageID2 ... - // 预分配容量,避免动态扩容 - args := make([]interface{}, 0, len(messageIDs)+2) - args = append(args, streamKey, groupName) - for _, id := range messageIDs { - args = append(args, id) - } - - _, err := getClient().Do(ctx, "XACK", args...) - return err -} - -// GetStreamLength 获取 Stream 当前长度 -// 使用 gredis Do() 方法执行 XLEN 命令 -func GetStreamLength(ctx context.Context, streamKey string) (int64, error) { - // XLEN streamKey - result, err := getClient().Do(ctx, "XLEN", streamKey) - if err != nil { - return 0, err - } - - length := gconv.Int64(result) - return length, nil -} - -// PendingMessage Pending 消息结构 -type PendingMessage struct { - ID string // 消息ID - Consumer string // 消费者名称 - Idle int64 // 空闲时间(毫秒) - RetryCount int64 // 重试次数 -} - -// GetPendingMessages 获取待处理消息 -// 使用 gredis Do() 方法执行 XPENDING 命令 -func GetPendingMessages(ctx context.Context, streamKey, groupName string, start, end string, count int64) ([]PendingMessage, error) { - // XPENDING streamKey groupName start end count - result, err := getClient().Do(ctx, "XPENDING", streamKey, groupName, start, end, count) - if err != nil { - return nil, err - } - - if result == nil { - return nil, nil - } - - // 解析返回值:[[ID, consumer, idle, retryCount], ...] - pendingArray, ok := result.Val().([]interface{}) - if !ok { - return nil, nil - } - - messages := make([]PendingMessage, 0, len(pendingArray)) - for _, item := range pendingArray { - itemArray, ok := item.([]interface{}) - if !ok || len(itemArray) < 4 { - continue - } - - messages = append(messages, PendingMessage{ - ID: gconv.String(itemArray[0]), - Consumer: gconv.String(itemArray[1]), - Idle: gconv.Int64(itemArray[2]), - RetryCount: gconv.Int64(itemArray[3]), - }) - } - - return messages, nil -} - -// ClaimPendingMessage 认领超时的 Pending 消息 -// 使用 gredis Do() 方法执行 XCLAIM 命令 -func ClaimPendingMessage(ctx context.Context, streamKey, groupName, consumerName string, minIdleTime int64, messageIDs ...string) ([]StreamMessage, error) { - // XCLAIM streamKey groupName consumerName minIdleTime messageID1 messageID2 ... - args := []interface{}{streamKey, groupName, consumerName, minIdleTime} - for _, id := range messageIDs { - args = append(args, id) - } - - result, err := getClient().Do(ctx, "XCLAIM", args...) - if err != nil { - return nil, err - } - - if result == nil { - return nil, nil - } - - // 解析返回值:类似 XREADGROUP - messagesArray, ok := result.Val().([]interface{}) - if !ok { - return nil, nil - } - - // 预分配容量,避免动态扩容 - messages := make([]StreamMessage, 0, len(messagesArray)) - for _, msgData := range messagesArray { - msgArray, ok := msgData.([]interface{}) - if !ok || len(msgArray) < 2 { - continue - } - - msgID := gconv.String(msgArray[0]) - fieldsArray, ok := msgArray[1].([]interface{}) - if !ok { - continue - } - - // 预分配 map 容量 ,避免动态扩容 - values := make(map[string]interface{}, len(fieldsArray)/2) - for i := 0; i < len(fieldsArray); i += 2 { - if i+1 < len(fieldsArray) { - key := gconv.String(fieldsArray[i]) - values[key] = fieldsArray[i+1] - } - } - - messages = append(messages, StreamMessage{ - ID: msgID, - Values: values, - }) - } - - return messages, nil -} - -// SetSessionLastActive 设置用户最后活跃时间 -// 使用 gredis SetEX 方法 -func SetSessionLastActive(ctx context.Context, userId string) error { - key := SessionLastActiveKeyPrefix + userId + ":last_active" - timestamp := gtime.Now().Timestamp() - - // SETEX key 7200 value (7200秒 = 2小时) - _, err := getClient().Do(ctx, "SETEX", key, 7200, timestamp) - return err -} - -// GetSessionLastActive 获取用户最后活跃时间 -// 使用 gredis Get 方法 -func GetSessionLastActive(ctx context.Context, userId string) (int64, error) { - key := SessionLastActiveKeyPrefix + userId + ":last_active" - result, err := getClient().Get(ctx, key) - if err != nil { - return 0, err - } - - if result.IsEmpty() { - return 0, nil - } - - timestamp := gconv.Int64(result.Val()) - return timestamp, nil -} - -// IsUserActive 检查用户是否在指定时间范围内活跃过 -// 用于追问逻辑:如果用户最近活跃过,则不发送追问消息 -// 参数: -// - userId: 用户ID -// - seconds: 时间范围(秒),例如传入300表示检查5分钟内是否活跃 -// -// 返回: -// - bool: true表示用户在指定时间内活跃过 -// - error: 操作失败时返回错误 -func IsUserActive(ctx context.Context, userId string, seconds int64) (bool, error) { - lastActive, err := GetSessionLastActive(ctx, userId) - if err != nil { - return false, err - } - - if lastActive == 0 { - return false, nil // 未找到记录,视为不活跃 - } - - // 检查时间差 - now := gtime.Now().Timestamp() - return (now - lastActive) < seconds, nil -} - -// ============== 限流相关 ============== - -// IncrRateLimit 增加限流计数器,返回当前计数 -// key: 限流key(需要包含完整路径,如 "ip:192.168.1.1") -// windowSeconds: 时间窗口(秒) -func IncrRateLimit(ctx context.Context, key string, windowSeconds int64) (count int64, err error) { - fullKey := RateLimitKeyPrefix + key - result, err := getClient().Do(ctx, "INCR", fullKey) - if err != nil { - return - } - count = result.Int64() - - // 首次设置过期时间 - if count == 1 { - getClient().Do(ctx, "EXPIRE", fullKey, windowSeconds) - } - return -} - -// GetRateLimit 获取当前限流计数 -func GetRateLimit(ctx context.Context, key string) (count int64, err error) { - fullKey := RateLimitKeyPrefix + key - result, err := getClient().Get(ctx, fullKey) - if err != nil { - return - } - if result.IsEmpty() { - return 0, nil - } - count = result.Int64() - return -} - -// SetSessionCache 缓存 RAGFlow Session ID(租户+用户隔离) -func SetSessionCache(ctx context.Context, tenantId, userId, sessionId string) error { - key := SessionLastActiveKeyPrefix + tenantId + ":" + userId + ":session_id" - // SETEX key 7200 value (7200秒 = 2小时,与last_active保持一致) - _, err := getClient().Do(ctx, "SETEX", key, 7200, sessionId) - return err -} - -// GetSessionCache 获取缓存的 RAGFlow Session ID(租户+用户隔离) -func GetSessionCache(ctx context.Context, tenantId, userId string) (string, error) { - key := SessionLastActiveKeyPrefix + tenantId + ":" + userId + ":session_id" - result, err := getClient().Get(ctx, key) - if err != nil { - return "", err - } - - if result.IsEmpty() { - return "", nil - } - - return result.String(), nil -} - -// DelSessionCache 删除缓存的 RAGFlow Session ID(归档时调用,租户+用户隔离) -func DelSessionCache(ctx context.Context, tenantId, userId string) error { - key := SessionLastActiveKeyPrefix + tenantId + ":" + userId + ":session_id" - _, err := getClient().Del(ctx, key) - return err -} - -// TryLock 尝试获取分布式锁(非阻塞) -// key: 锁的键名 -// expireSeconds: 锁的过期时间(秒),防止死锁 -// 返回 true 表示获取成功,false 表示锁已被其他节点持有 -func TryLock(ctx context.Context, key string, expireSeconds int) bool { - // SET key value NX EX expireSeconds - result, err := getClient().Do(ctx, "SET", key, gtime.Now().String(), "NX", "EX", expireSeconds) - if err != nil { - glog.Errorf(ctx, "获取分布式锁失败: %v", err) - return false - } - return result.String() == "OK" -} - -// Unlock 释放分布式锁 -func Unlock(ctx context.Context, key string) { - if _, err := getClient().Del(ctx, key); err != nil { - glog.Errorf(ctx, "释放分布式锁失败: %v", err) - } -} - -// ============== 对话计数相关(用于卡片触发)============== - -const ( - // UserStateKeyPrefix 用户会话状态 Key 前缀(融合阶段+计数) - UserStateKeyPrefix = "ragflow:user:state:" - // UserStateExpireSeconds 用户状态过期时间(5分钟) - UserStateExpireSeconds = 300 -) - -// UserState 用户会话状态(阶段+对话计数+咨询方向,统一5分钟过期) -type UserState struct { - Stage int `json:"stage"` // 当前阶段 - Direction string `json:"direction"` // 咨询方向 - Count int64 `json:"count"` // 对话计数(v5.2卡片触发) - AccountName string `json:"accountName"` // 用户选择的方向对应的客服账号名称 -} - -// GetUserState 获取用户状态(阶段+计数) -func GetUserState(ctx context.Context, userId, platform string) (state *UserState, err error) { - key := UserStateKeyPrefix + userId + "_" + platform - result, err := getClient().Do(ctx, "HGETALL", key) - if err != nil { - return - } - - state = &UserState{Stage: 5} // 默认状态5(未选择方向) - if result.IsEmpty() { - // Redis为空,初始化默认状态 - if initErr := SetUserStage(ctx, userId, platform, 5); initErr != nil { - err = initErr - return - } - return - } - - m := result.Map() - state.Stage = gconv.Int(m["stage"]) - state.Count = gconv.Int64(m["count"]) - state.Direction = gconv.String(m["direction"]) - return -} - -// SetUserStage 设置用户阶段,并刷新过期时间 -func SetUserStage(ctx context.Context, userId, platform string, stage int) error { - key := UserStateKeyPrefix + userId + "_" + platform - _, err := getClient().Do(ctx, "HSET", key, "stage", stage) - if err != nil { - return err - } - _, err = getClient().Do(ctx, "EXPIRE", key, UserStateExpireSeconds) - return err -} - -// SetUserAccountName 设置用户对应的客服账号名称,并刷新过期时间 -func SetUserAccountName(ctx context.Context, userId, platform, accountName string) error { - key := UserStateKeyPrefix + userId + "_" + platform - _, err := getClient().Do(ctx, "HSET", key, "accountName", accountName) - if err != nil { - return err - } - _, err = getClient().Do(ctx, "EXPIRE", key, UserStateExpireSeconds) - return err -} - -// SetUserDirection 设置用户选择的咨询方向,并刷新过期时间 -func SetUserDirection(ctx context.Context, userId, platform, direction string) error { - key := UserStateKeyPrefix + userId + "_" + platform - _, err := getClient().Do(ctx, "HSET", key, "direction", direction) - if err != nil { - return err - } - _, err = getClient().Do(ctx, "EXPIRE", key, UserStateExpireSeconds) - return err -} - -// IncrUserCount 增加用户对话计数,返回当前轮数,并刷新过期时间 -func IncrUserCount(ctx context.Context, userId, platform string) (count int64, err error) { - key := UserStateKeyPrefix + userId + "_" + platform - result, err := getClient().Do(ctx, "HINCRBY", key, "count", 1) - if err != nil { - return - } - count = result.Int64() - _, err = getClient().Do(ctx, "EXPIRE", key, UserStateExpireSeconds) - return -} - -// ResetUserState 重置用户状态(归档时调用) -func ResetUserState(ctx context.Context, userId, platform string) error { - key := UserStateKeyPrefix + userId + "_" + platform - _, err := getClient().Del(ctx, key) - return err -} - -// ============== 对话缓存相关(5句落库)============== - -const ( - // ConversationCacheKeyPrefix 对话缓存 Key 前缀 - ConversationCacheKeyPrefix = "ragflow:conversation:cache:" - // ConversationCacheExpireSeconds 对话缓存过期时间(10分钟) - ConversationCacheExpireSeconds = 600 -) - -// CacheConversation 缓存单条对话到Redis List(按sessionId存储) -func CacheConversation(ctx context.Context, sessionId string, data []byte) error { - key := ConversationCacheKeyPrefix + sessionId - _, err := getClient().Do(ctx, "RPUSH", key, string(data)) - if err != nil { - return err - } - _, err = getClient().Do(ctx, "EXPIRE", key, ConversationCacheExpireSeconds) - return err -} - -// GetCachedConversations 获取缓存的对话列表并清空(按sessionId查询) -func GetCachedConversations(ctx context.Context, sessionId string) (list []string, err error) { - key := ConversationCacheKeyPrefix + sessionId - result, err := getClient().Do(ctx, "LRANGE", key, 0, -1) - if err != nil { - return - } - if result.IsEmpty() { - return - } - list = result.Strings() - // 清空缓存 - getClient().Del(ctx, key) - return -} - -// GetCachedConversationCount 获取缓存的对话数量(按sessionId查询) -func GetCachedConversationCount(ctx context.Context, sessionId string) (count int64, err error) { - key := ConversationCacheKeyPrefix + sessionId - result, err := getClient().Do(ctx, "LLEN", key) - if err != nil { - return - } - return result.Int64(), nil -} - -// ClearCachedConversations 清空对话缓存(归档时调用,按sessionId) -func ClearCachedConversations(ctx context.Context, sessionId string) error { - key := ConversationCacheKeyPrefix + sessionId - _, err := getClient().Del(ctx, key) - return err -} - -// ========== 以下为兼容旧接口(内部调用新实现)========== - -// IncrConversationCount 增加用户对话计数(兼容旧接口) -func IncrConversationCount(ctx context.Context, userId, platform string, _ int64) (count int64, err error) { - return IncrUserCount(ctx, userId, platform) -} - -// GetConversationCount 获取用户当前对话轮数(兼容旧接口) -func GetConversationCount(ctx context.Context, userId, platform string) (count int64, err error) { - state, err := GetUserState(ctx, userId, platform) - if err != nil { - return - } - return state.Count, nil -} - -// ResetConversationCount 重置用户对话计数(兼容旧接口) -func ResetConversationCount(ctx context.Context, userId, platform string) error { - return ResetUserState(ctx, userId, platform) -} diff --git a/redis/types.go b/redis/types.go deleted file mode 100644 index 1e5a83f..0000000 --- a/redis/types.go +++ /dev/null @@ -1,129 +0,0 @@ -package redis - -import ( - "context" - - "github.com/gogf/gf/v2/frame/g" -) - -// HistoryMessage 历史消息结构(用于上下文注入) -type HistoryMessage struct { - Question string `json:"question"` // 用户问题 - Answer string `json:"answer"` // AI 回复 -} - -// SendStreamMessage 发送到 Redis Stream 的消息结构 -type SendStreamMessage struct { - UserId string `json:"userId"` // 用户ID - Content string `json:"content"` // 消息内容 - Timestamp int64 `json:"timestamp"` // 时间戳(秒) - MessageId string `json:"messageId"` // 消息唯一ID - Platform string `json:"platform,omitempty"` // 平台标识 - AccountId string `json:"accountId,omitempty"` // 账号ID - TenantId string `json:"tenantId,omitempty"` // 租户ID(数据隔离) - AccountName string `json:"accountName,omitempty"` // 客服账号名称 - ChatId string `json:"chatId,omitempty"` // RAGFlow Chat ID(从ragflow_config查询) - ReplyQueue string `json:"replyQueue,omitempty"` // 响应队列名称(支持多实例独立队列) - History []HistoryMessage `json:"history,omitempty"` // 历史对话(归档后恢复时携带) -} - -// BatchStreamMessage 批量消息结构 -type BatchStreamMessage struct { - UserId string `json:"userId"` // 用户ID - Content string `json:"content"` // 消息内容 - Timestamp int64 `json:"timestamp"` // 时间戳(秒) - BatchId string `json:"batchId"` // 批次ID - Index int `json:"index"` // 批次内序号 -} - -// ResponseStreamMessage RAGFlow 响应消息结构(MQ 消息) -type ResponseStreamMessage struct { - UserId string `json:"userId"` // 用户ID - Platform string `json:"platform"` // 平台标识 - TenantId string `json:"tenantId"` // 租户ID - AccountId string `json:"accountId,omitempty"` // 账号ID - AccountName string `json:"accountName,omitempty"` // 客服账号名称 - Question string `json:"question"` // 用户问题 - Content string `json:"content"` // RAGFlow 回复内容 - SessionId string `json:"sessionId"` // RAGFlow Session ID - Timestamp int64 `json:"timestamp"` // 时间戳(秒) - MessageId string `json:"messageId"` // 原始消息ID -} - -// FollowUpMessage 追问消息结构(RabbitMQ 延时队列) -type FollowUpMessage struct { - TenantId string `json:"tenantId"` // 租户ID - UserId string `json:"userId"` // 用户ID - Platform string `json:"platform"` // 平台标识 - Content string `json:"content"` // 追问内容 - FollowUpType int `json:"followUpType"` // 追问类型:1=30s, 2=60s, 3=180s - Timestamp int64 `json:"timestamp"` // 发送时间戳 -} - -// 追问类型常量 -const ( - FollowUpType1 = 1 // 第一次追问 - FollowUpType2 = 2 // 第二次追问 - FollowUpType3 = 3 // 第三次追问 -) - -// GetFollowUpContent 获取追问话术(从 config.yml 读取) -func GetFollowUpContent(followUpType int) string { - ctx := context.Background() - contents := g.Cfg().MustGet(ctx, "followUp.contents").Strings() - if len(contents) == 0 { - return "" - } - // followUpType: 1,2,3 对应数组索引 0,1,2 - index := followUpType - 1 - if index >= 0 && index < len(contents) { - return contents[index] - } - return "" -} - -// GetFollowUpDelay 获取追问延时(从 config.yml 读取) -func GetFollowUpDelay(followUpType int) int { - ctx := context.Background() - delays := g.Cfg().MustGet(ctx, "followUp.delays").Ints() - if len(delays) == 0 { - return 30 // 默认30秒 - } - // followUpType: 1,2,3 对应数组索引 0,1,2 - index := followUpType - 1 - if index >= 0 && index < len(delays) { - return delays[index] - } - return 30 -} - -// ArchiveMessage 会话归档消息结构(RabbitMQ 延时队列) -type ArchiveMessage struct { - UserId string `json:"userId"` // 用户ID - Platform string `json:"platform"` // 平台标识 - SessionId string `json:"sessionId"` // RAGFlow Session ID - TenantId string `json:"tenantId"` // 租户ID - Timestamp int64 `json:"timestamp"` // 发送时间戳 -} - -// GetArchiveDelay 获取归档延时(从 config.yml 读取) -func GetArchiveDelay() int { - ctx := context.Background() - return g.Cfg().MustGet(ctx, "archive.delay", 3600).Int() // 默认3600秒(1小时) -} - -// GetHistoryContextLimit 获取历史上下文轮数(从 config.yml 读取) -func GetHistoryContextLimit() int64 { - ctx := context.Background() - return g.Cfg().MustGet(ctx, "history.contextLimit", 5).Int64() // 默认5轮对话 -} - -// DocSyncMessage 文档同步消息结构(RAGFlow与MongoDB同步) -type DocSyncMessage struct { - DocId string `json:"docId"` // MongoDB文档ID - RagflowDocId string `json:"ragflowDocId"` // RAGFlow文档ID - TenantId string `json:"tenantId"` // 租户ID - DocType string `json:"docType"` // 文档类型:speechcraft/product - Action string `json:"action"` // 操作类型:sync_ragflow_id - Timestamp int64 `json:"timestamp"` // 时间戳 -} From ce4f10f40f1afb4c2a4e1e964026b9cd79921a83 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Thu, 16 Apr 2026 15:42:18 +0800 Subject: [PATCH 14/17] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20IsDelete?= =?UTF-8?q?d=20=E5=AD=97=E6=AE=B5=EF=BC=8C=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20DeletedAt=20=E8=BF=9B=E8=A1=8C=E8=BD=AF=E5=88=A0?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beans/beans.go | 3 - full-text-search/meilisearch/meilisearch.go | 160 +++++--------------- 2 files changed, 38 insertions(+), 125 deletions(-) diff --git a/beans/beans.go b/beans/beans.go index 8f60540..73aa8d6 100644 --- a/beans/beans.go +++ b/beans/beans.go @@ -47,7 +47,6 @@ type SQLBaseDO struct { Updater string `orm:"updater" json:"updater"` // 更新人 UpdatedAt *gtime.Time `orm:"updated_at" json:"updatedAt"` // 更新时间 DeletedAt *gtime.Time `orm:"deleted_at" json:"deletedAt"` // 软删除时间 - IsDeleted bool `orm:"is_deleted" json:"isDeleted"` // 是否删除 } type SQLBaseCol struct { @@ -58,7 +57,6 @@ type SQLBaseCol struct { Updater string UpdatedAt string DeletedAt string - IsDeleted string } var DefSQLBaseCol = SQLBaseCol{ @@ -69,7 +67,6 @@ var DefSQLBaseCol = SQLBaseCol{ Updater: "updater", UpdatedAt: "updated_at", DeletedAt: "deleted_at", - IsDeleted: "is_deleted", } type User struct { diff --git a/full-text-search/meilisearch/meilisearch.go b/full-text-search/meilisearch/meilisearch.go index 53d363c..bca5510 100644 --- a/full-text-search/meilisearch/meilisearch.go +++ b/full-text-search/meilisearch/meilisearch.go @@ -10,6 +10,7 @@ import ( "fmt" "time" + "gitea.com/red-future/common/beans" "gitea.com/red-future/common/utils" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/errors/gerror" @@ -87,81 +88,6 @@ func indexInterface(indexName string, client ms.ServiceManager) ms.IndexManager return client.Index(indexName) } -// ensureIndexExists 确保索引存在,不存在则自动创建 -// 同时会检查并更新 filterable attributes 设置 -func (m *meilisearchDB) ensureIndexExists(client ms.ServiceManager, indexName string) error { - // 使用 Index 方法获取索引(不存在时不会报错) - idx := client.Index(indexName) - - // 先获取索引信息,检查是否存在 - _, err := idx.FetchInfo() - if err != nil { - // 索引不存在,创建索引并等待完成 - task, err := client.CreateIndex(&ms.IndexConfig{ - Uid: indexName, - PrimaryKey: "id", - }) - if err != nil { - return err - } - // 等待索引创建完成(最多等待10秒) - if _, err = client.WaitForTask(task.TaskUID, 10*time.Second); err != nil { - return fmt.Errorf("等待索引创建失败: %w", err) - } - // 重新获取索引 - idx = client.Index(indexName) - } - - // 检查并更新 filterable attributes - settings, err := idx.GetSettings() - if err != nil { - return err - } - - requiredFilterable := []string{"tenantId", "isDeleted", "dataset_id", "creator", "updater"} - needUpdate := false - - // 检查是否缺少必要的 filterable attributes - existingFilterable := make(map[string]bool) - for _, attr := range settings.FilterableAttributes { - existingFilterable[attr] = true - } - - for _, attr := range requiredFilterable { - if !existingFilterable[attr] { - needUpdate = true - break - } - } - - if needUpdate { - // 合并现有的 filterable attributes 和新增的 - allFilterable := append(settings.FilterableAttributes, requiredFilterable...) - uniqueFilterable := make(map[string]bool) - var finalFilterable []string - for _, attr := range allFilterable { - if !uniqueFilterable[attr] { - uniqueFilterable[attr] = true - finalFilterable = append(finalFilterable, attr) - } - } - - updateSettings := &ms.Settings{ - FilterableAttributes: finalFilterable, - } - task, err := idx.UpdateSettings(updateSettings) - if err != nil { - return err - } - // 等待设置更新完成(最多等待10秒) - if _, err = client.WaitForTask(task.TaskUID, 10*time.Second); err != nil { - return fmt.Errorf("等待设置更新失败: %w", err) - } - } - - return nil -} - // buildSearchRequest 构建搜索请求 func (m *meilisearchDB) buildSearchRequest(ctx context.Context, searchParams *SearchParams) (*ms.SearchRequest, error) { user, err := utils.GetUserInfo(ctx) @@ -195,12 +121,12 @@ func (m *meilisearchDB) buildSearchRequest(ctx context.Context, searchParams *Se // 设置过滤条件(包含租户过滤和软删除过滤) filter := "" if !g.IsEmpty(user.TenantId) { - filter = fmt.Sprintf("tenantId = %s", gconv.String(user.TenantId)) + filter = fmt.Sprintf("%s = %s", beans.DefSQLBaseCol.TenantId, gconv.String(user.TenantId)) } if filter == "" { - filter = "isDeleted = false" + filter = fmt.Sprintf("%s = null", beans.DefSQLBaseCol.DeletedAt) } else { - filter += " AND isDeleted = false" + filter += fmt.Sprintf("AND %s = null", beans.DefSQLBaseCol.DeletedAt) } // 添加用户自定义过滤条件 @@ -254,7 +180,7 @@ func (m *meilisearchDB) Search(ctx context.Context, searchParams *SearchParams, if err != nil { return } - cacheKey := fmt.Sprintf("meilisearch:search:%s:%s:%+v", user.TenantId, indexName, searchParams) + cacheKey := fmt.Sprintf("meilisearch:search:%v:%s:%+v", user.TenantId, indexName, searchParams) if !m.noCache { var resultStr *gvar.Var resultStr, err = g.Redis().Get(ctx, cacheKey) @@ -343,11 +269,6 @@ func (m *meilisearchDB) Insert(ctx context.Context, document interface{}, indexN return 0, err } - // 确保索引存在 - if err = m.ensureIndexExists(c, indexName); err != nil { - return 0, err - } - user, err := utils.GetUserInfo(ctx) if err != nil { return @@ -357,32 +278,32 @@ func (m *meilisearchDB) Insert(ctx context.Context, document interface{}, indexN docMap := gconv.Map(document) // 设置租户ID - if !g.IsEmpty(user.TenantId) && g.IsEmpty(docMap["tenantId"]) { - docMap["tenantId"] = user.TenantId + if !g.IsEmpty(user.TenantId) && g.IsEmpty(docMap[beans.DefSQLBaseCol.TenantId]) { + docMap[beans.DefSQLBaseCol.TenantId] = user.TenantId } // 设置创建人 - if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap["creator"]) { - docMap["creator"] = user.UserName + if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap[beans.DefSQLBaseCol.Creator]) { + docMap[beans.DefSQLBaseCol.Creator] = user.UserName } // 设置更新人 - if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap["updater"]) { - docMap["updater"] = user.UserName + if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap[beans.DefSQLBaseCol.Updater]) { + docMap[beans.DefSQLBaseCol.Updater] = user.UserName } // 设置时间 now := gtime.Now().Time - if g.IsEmpty(docMap["createdAt"]) { - docMap["createdAt"] = now.Unix() + if g.IsEmpty(docMap[beans.DefSQLBaseCol.CreatedAt]) { + docMap[beans.DefSQLBaseCol.CreatedAt] = now.Unix() } - if g.IsEmpty(docMap["updatedAt"]) { - docMap["updatedAt"] = now.Unix() + if g.IsEmpty(docMap[beans.DefSQLBaseCol.UpdatedAt]) { + docMap[beans.DefSQLBaseCol.UpdatedAt] = now.Unix() } // 设置删除标记 - if g.IsEmpty(docMap["isDeleted"]) { - docMap["isDeleted"] = false + if g.IsEmpty(docMap[beans.DefSQLBaseCol.DeletedAt]) { + docMap[beans.DefSQLBaseCol.DeletedAt] = nil } // 执行插入 @@ -409,11 +330,6 @@ func (m *meilisearchDB) InsertMany(ctx context.Context, documents []interface{}, return 0, err } - // 确保索引存在 - if err = m.ensureIndexExists(c, indexName); err != nil { - return 0, err - } - user, err := utils.GetUserInfo(ctx) if err != nil { return 0, err @@ -424,32 +340,32 @@ func (m *meilisearchDB) InsertMany(ctx context.Context, documents []interface{}, docMap := gconv.Map(document) // 设置租户ID - if !g.IsEmpty(user.TenantId) && g.IsEmpty(docMap["tenantId"]) { - docMap["tenantId"] = user.TenantId + if !g.IsEmpty(user.TenantId) && g.IsEmpty(docMap[beans.DefSQLBaseCol.TenantId]) { + docMap[beans.DefSQLBaseCol.TenantId] = user.TenantId } // 设置创建人 - if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap["creator"]) { - docMap["creator"] = user.UserName + if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap[beans.DefSQLBaseCol.Creator]) { + docMap[beans.DefSQLBaseCol.Creator] = user.UserName } // 设置更新人 - if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap["updater"]) { - docMap["updater"] = user.UserName + if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap[beans.DefSQLBaseCol.Updater]) { + docMap[beans.DefSQLBaseCol.Updater] = user.UserName } // 设置时间 now := gtime.Now().Time - if g.IsEmpty(docMap["createdAt"]) { - docMap["createdAt"] = now.Unix() + if g.IsEmpty(docMap[beans.DefSQLBaseCol.CreatedAt]) { + docMap[beans.DefSQLBaseCol.CreatedAt] = now.Unix() } - if g.IsEmpty(docMap["updatedAt"]) { - docMap["updatedAt"] = now.Unix() + if g.IsEmpty(docMap[beans.DefSQLBaseCol.UpdatedAt]) { + docMap[beans.DefSQLBaseCol.UpdatedAt] = now.Unix() } // 设置删除标记 - if g.IsEmpty(docMap["isDeleted"]) { - docMap["isDeleted"] = false + if g.IsEmpty(docMap[beans.DefSQLBaseCol.DeletedAt]) { + docMap[beans.DefSQLBaseCol.DeletedAt] = nil } docs = append(docs, docMap) @@ -487,12 +403,12 @@ func (m *meilisearchDB) Update(ctx context.Context, document interface{}, indexN docMap := gconv.Map(document) // 设置更新人 - if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap["updater"]) { - docMap["updater"] = user.UserName + if !g.IsEmpty(user.UserName) && g.IsEmpty(docMap[beans.DefSQLBaseCol.Updater]) { + docMap[beans.DefSQLBaseCol.Updater] = user.UserName } // 设置更新时间 - docMap["updatedAt"] = gtime.Now().Unix() + docMap[beans.DefSQLBaseCol.UpdatedAt] = gtime.Now().Unix() // 执行更新 documents := []map[string]interface{}{docMap} @@ -552,10 +468,10 @@ func (m *meilisearchDB) DeleteSoft(ctx context.Context, id string, indexName str // 软删除:更新 isDeleted 字段 updateMap := map[string]interface{}{ - "id": id, - "isDeleted": true, - "updater": user.UserName, - "updatedAt": gtime.Now().Unix(), + beans.DefSQLBaseCol.Id: id, + beans.DefSQLBaseCol.DeletedAt: gtime.Now().Unix(), + beans.DefSQLBaseCol.Updater: user.UserName, + beans.DefSQLBaseCol.UpdatedAt: gtime.Now().Unix(), } // 执行更新 @@ -587,7 +503,7 @@ func (m *meilisearchDB) Get(ctx context.Context, id string, indexName string, re if err != nil { return } - cacheKey := fmt.Sprintf("meilisearch:doc:%s:%s:%s", user.TenantId, indexName, id) + cacheKey := fmt.Sprintf("meilisearch:doc:%v:%s:%s", user.TenantId, indexName, id) if !m.noCache { var resultStr *gvar.Var resultStr, err = g.Redis().Get(ctx, cacheKey) @@ -608,7 +524,7 @@ func (m *meilisearchDB) Get(ctx context.Context, id string, indexName string, re } // 过滤已删除的文档 - if gconv.Bool(doc["isDeleted"]) { + if !g.IsEmpty(doc[beans.DefSQLBaseCol.DeletedAt]) { return gerror.New("文档不存在") } From 2d2a43a4f431129d73ce9c036897166803bac179 Mon Sep 17 00:00:00 2001 From: lmk <1095689763@qq.com> Date: Mon, 20 Apr 2026 10:48:45 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beans/beans.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beans/beans.go b/beans/beans.go index 73aa8d6..9a26ed1 100644 --- a/beans/beans.go +++ b/beans/beans.go @@ -40,7 +40,7 @@ type MongoBaseDO struct { // SQLBaseDO SQL数据库基础实体 type SQLBaseDO struct { - Id int64 `orm:"id" json:"id"` // 主键ID + Id int64 `orm:"id" json:"id,string"` // 主键ID TenantId uint64 `orm:"tenant_id" json:"tenantId"` // 租户ID Creator string `orm:"creator" json:"creator"` // 创建人 CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建时间 From d757d255046088f1486513c4945f2a2e467e5715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Mon, 20 Apr 2026 19:42:46 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consul/consul.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consul/consul.go b/consul/consul.go index 166956a..4318161 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -67,7 +67,7 @@ func startHealthCheckAndReconnect() { // 初始化HTTP客户端用于健康检查 httpClient = &http.Client{ - Timeout: 5 * time.Second, + Timeout: 30 * time.Second, } for { From aeced7874cbf833aae355fdf2fae8048bb5e6136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Mon, 20 Apr 2026 19:46:33 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consul/consul.go | 74 ------------------------------------------------ 1 file changed, 74 deletions(-) diff --git a/consul/consul.go b/consul/consul.go index 4318161..fb5b69a 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -53,78 +53,6 @@ func connectConsul(ctx context.Context) error { return nil } -// startHealthCheckAndReconnect 启动健康检查和自动重连 -func startHealthCheckAndReconnect() { - if reconnectDone != nil { - close(reconnectDone) - } - - reconnectDone = make(chan struct{}) - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - ctx := context.Background() - - // 初始化HTTP客户端用于健康检查 - httpClient = &http.Client{ - Timeout: 30 * time.Second, - } - - for { - select { - case <-ticker.C: - // 检查服务发现是否正常工作 - if checkConsulHealth(ctx) { - continue - } - - g.Log().Warning(ctx, "⚠️ Consul 连接异常,尝试重新连接...") - - // 重置连接状态并重连 - reconnectMutex.Lock() - connected = false - registry = nil - reconnectMutex.Unlock() - - if err := connectConsul(ctx); err != nil { - g.Log().Errorf(ctx, "❌ Consul 重连失败: %v,30秒后重试...", err) - } - - case <-reconnectDone: - g.Log().Info(ctx, "🛑 Consul 健康检查已停止") - return - } - } -} - -// checkConsulHealth 检查 Consul 健康状态 -func checkConsulHealth(ctx context.Context) bool { - reconnectMutex.RLock() - defer reconnectMutex.RUnlock() - - if registry == nil || !connected { - return false - } - - // 使用consul原生API进行健康检查 - // 调用 /v1/agent/self 接口检测连接状态 - url := fmt.Sprintf("http://%s/v1/agent/self", consulAddr) - resp, err := httpClient.Get(url) - if err != nil { - g.Log().Debugf(ctx, "Consul 健康检查失败: %v", err) - return false - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - g.Log().Debugf(ctx, "Consul 健康检查失败,状态码: %d", resp.StatusCode) - return false - } - - //g.Log().Debugf(ctx, "✅ Consul 健康检查通过") - return true -} - func init() { ctx := context.Background() @@ -136,8 +64,6 @@ func init() { if err := connectConsul(ctx); err != nil { g.Log().Errorf(ctx, "❌ Consul 初始化失败: %v", err) - } else { - go startHealthCheckAndReconnect() } loadConfigFromConsul()