From 74ede5bc0ff73534414424dee356ad1202cd0082 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Fri, 8 May 2026 11:35:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=88=9B=E4=BD=9C?= =?UTF-8?q?=E4=BD=9C=E5=93=81=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yml | 36 +- go.mod | 26 ++ go.sum | 103 ++++- main.go | 2 + update.sql | 130 ++++++ workflow/consts/table_name.go | 5 + .../controller/creation_info_controller.go | 22 + workflow/dao/creation_info_dao.go | 43 ++ workflow/model/dto/creation_info_dto.go | 115 +++++ workflow/model/entity/creation_info.go | 31 ++ workflow/service/creation_info_service.go | 191 ++++++++ workflow/service/einograph/lambda_func.go | 409 ++++++++++++++++++ workflow/service/einograph/model.go | 98 +++++ workflow/service/einograph/orchestration.go | 80 ++++ workflow/skill/SKILL.md | 71 +++ workflow/skill/embed.go | 15 + 16 files changed, 1373 insertions(+), 4 deletions(-) create mode 100644 workflow/controller/creation_info_controller.go create mode 100644 workflow/dao/creation_info_dao.go create mode 100644 workflow/model/dto/creation_info_dto.go create mode 100644 workflow/model/entity/creation_info.go create mode 100644 workflow/service/creation_info_service.go create mode 100644 workflow/service/einograph/lambda_func.go create mode 100644 workflow/service/einograph/model.go create mode 100644 workflow/service/einograph/orchestration.go create mode 100644 workflow/skill/SKILL.md create mode 100644 workflow/skill/embed.go diff --git a/config.yml b/config.yml index 636de27..6b4f6a2 100644 --- a/config.yml +++ b/config.yml @@ -24,17 +24,47 @@ database: updatedAt: "updated_at" # (可选)自动更新时间字段名称 deletedAt: "deleted_at" # (可选)软删除时间字段名称 timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性,为true时CreatedAt/UpdatedAt/DeletedAt都将失效 + black_deacon: + - type: "pgsql" + host: "116.204.74.41" + port: "15432" + user: "postgres" + pass: "Bjang09@686^*^" + name: "black-deacon" + prefix: "black_deacon_" # (可选)表名前缀 + role: "master" + debug: true # (可选)开启调试模式 + dryRun: false # (可选)ORM空跑(只读不写) + charset: "utf8" # (可选)数据库编码(如: utf8mb4/utf8/gbk/gb2312),一般设置为utf8mb4。默认为utf8。 + timezone: "Asia/Shanghai" # (可选)时区配置,例如:Local + maxIdle: 5 # (可选)连接池最大闲置的连接数(默认10) + maxOpen: 20 # (可选)连接池最大打开的连接数(默认无限制) + maxLifetime: "30s" # (可选)连接对象可重复使用的时间长度(默认30秒) + maxIdleConnTime: "30s" # (可选,v2.10新增)连接池中空闲连接的最大生存时间(默认30秒)。可以通过配置文件或SetConnMaxIdleTime方法设置,避免长时间空闲连接占用资源。 + createdAt: "created_at" # (可选)自动创建时间字段名称 + updatedAt: "updated_at" # (可选)自动更新时间字段名称 + deletedAt: "deleted_at" # (可选)软删除时间字段名称 + timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性,为true时CreatedAt/UpdatedAt/DeletedAt都将失效 redis: default: - address: "116.204.74.41:6379" + address: 116.204.74.41:6379 db: 0 - + idleTimeout: "60s" #连接最大空闲时间,使用时间字符串例如30s/1m/1d + maxConnLifetime: "90s" #连接最长存活时间,使用时间字符串例如30s/1m/1d + waitTimeout: "60s" #等待连接池连接的超时时间,使用时间字符串例如30s/1m/1d + dialTimeout: "30s" #TCP连接的超时时间,使用时间字符串例如30s/1m/1d + readTimeout: "30s" #TCP的Read操作超时时间,使用时间字符串例如30s/1m/1d + writeTimeout: "30s" #TCP的Write操作超时时间,使用时间字符串例如30s/1m/1d + maxActive: 100 consul: address: 116.204.74.41:8500 jaeger: addr: 116.204.74.41:4318 +# 文件上传服务地址,与oss模块minio中的endpoint一致 +filePrefix: "http://116.204.74.41:9000" + model-asynch: - addr: "127.0.0.1:8001" \ No newline at end of file + addr: "127.0.0.1:8001" diff --git a/go.mod b/go.mod index cb0eacf..e080b35 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.26.0 require ( gitea.com/red-future/common v0.0.12 + github.com/cloudwego/eino v0.8.13 + github.com/cloudwego/eino-ext/components/model/qwen v0.1.9 github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0 github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0 github.com/gogf/gf/v2 v2.10.0 @@ -14,15 +16,24 @@ require ( require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // 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/libs/acl/openai v0.1.17 // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/eino-contrib/jsonschema v1.0.3 // indirect github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect + github.com/evanphx/json-patch v0.5.2 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ego/gse v1.0.2 // indirect @@ -38,6 +49,7 @@ require ( github.com/golang/snappy v1.0.0 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/goph/emperror v0.17.2 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect @@ -50,22 +62,35 @@ 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/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.10 // indirect + github.com/mailru/easyjson v0.9.0 // indirect 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.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/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/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/redis/go-redis/v9 v9.12.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect github.com/tiger1103/gfast-token v1.0.10 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/vcaesar/cedar v0.30.0 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + github.com/yargevad/filepathx v1.0.0 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go.opencensus.io v0.23.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -76,6 +101,7 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect + golang.org/x/arch v0.11.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect diff --git a/go.sum b/go.sum index 8a839e9..5a573db 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 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/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -15,19 +16,36 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ 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/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/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/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.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= 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/v2 v2.1.1/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= @@ -36,6 +54,14 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp 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/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.8.13 h1:z5dhaZNN8TWZbP/lgKxGmF26Ii8fPeUlQCGV/NTtms0= +github.com/cloudwego/eino v0.8.13/go.mod h1:+2N4nsMPxA6kGBHpH+75JuTfEcGprAMTdsZESrShKpU= +github.com/cloudwego/eino-ext/components/model/qwen v0.1.9 h1:xCz/mp43JeWqupjPR3zLRArmwC6P29/6lTwbwh1yzYM= +github.com/cloudwego/eino-ext/components/model/qwen v0.1.9/go.mod h1:slTGTuhzkzhNavf+1UtUg1FvUSA31iNAF+rq1mT4SnI= +github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17 h1:EeVcR1TslRA2IdNW1h/2LaGbPlffwGhQm99jM3zWZiI= +github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17/go.mod h1:Zkcx6DPTR2NfWmtSXbhItswGw6hqUezNPhNcke0pOG8= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 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= @@ -52,19 +78,27 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu 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/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0= +github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4= github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= 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/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/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.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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -77,6 +111,7 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0 h1:39+jbTenm7KBj4hO2C8ANAxVHpX/7OuRDs1VcGC9ylA= github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0/go.mod h1:B0s0fVzn0W220E8UTpSGzrrGKsop5KcB90twBeLCiz0= github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0 h1:N/F9CuDdUZLoM1nVRqrDE/33pDZuhVxpNY4wYdeIaBs= @@ -130,6 +165,10 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/uuid v1.1.2/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/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 v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 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/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= @@ -180,13 +219,22 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= 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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 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.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -200,6 +248,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 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.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 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= @@ -217,6 +267,10 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D 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.2 h1:iXombGGjqjBrmE9WaSidUhhi3YQhf42QTHvHLMkgvCA= +github.com/meguminnnnnnnnn/go-openai v0.1.2/go.mod h1:qs96ysDmxhE4BZoU45I43zcyfnaYxU3X+aRzLko/htY= +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/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= @@ -228,19 +282,29 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c= +github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4= github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY= github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 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= @@ -269,29 +333,53 @@ 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/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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.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/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 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.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +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.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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s= github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s= 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/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/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/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= +github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= @@ -318,11 +406,17 @@ go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOV go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= 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= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= 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= @@ -335,6 +429,7 @@ golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= 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= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -362,6 +457,7 @@ golang.org/x/sync v0.19.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= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -380,12 +476,15 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-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.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= 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= @@ -441,6 +540,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 5cc1435..00906fd 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( digitalhumanController "ai-agent/digital-human/controller" + workflowController "ai-agent/workflow/controller" "context" _ "gitea.com/red-future/common/config" @@ -23,6 +24,7 @@ func main() { digitalhumanController.DigitalHuman, // 数字人相关接口 digitalhumanController.Video, // 视频相关接口 digitalhumanController.AsyncTask, // 异步任务相关接口 + workflowController.CreationInfo, }) // 保持应用运行 select {} diff --git a/update.sql b/update.sql index de7cb9e..4787301 100644 --- a/update.sql +++ b/update.sql @@ -228,3 +228,133 @@ COMMENT ON COLUMN digital_human_async_task_ref.table_name IS '业务表名'; COMMENT ON COLUMN digital_human_async_task_ref.biz_id IS '业务表主键ID'; COMMENT ON COLUMN digital_human_async_task_ref.oss_file IS '已转移后的业务侧OSS地址'; COMMENT ON COLUMN digital_human_async_task_ref.error_msg IS '错误信息'; + + + +-- ============================================================= +-- 低代码流程编排平台 - 数据库表结构 +-- Author: AI Assistant +-- ============================================================= + +-- 素材/创作信息表 +CREATE TABLE IF NOT EXISTS black_deacon_creation_info ( + -- 基础字段(完全对齐项目规范) + id BIGINT PRIMARY KEY, -- 主键ID(非自增) + tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8 + creator VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at timestamp(6), + + -- 业务字段 + html_file_url VARCHAR(512) DEFAULT '', -- HTML文件地址 + image_urls TEXT[] DEFAULT '{}', -- 图片地址列表 + content_type VARCHAR(255) DEFAULT '', -- 素材类型 + theme VARCHAR(255) DEFAULT '', -- 主题 + title VARCHAR(255) NOT NULL -- 标题 + ); + +-- 索引(高频查询) +CREATE INDEX idx_creation_tenant_id ON black_deacon_creation_info(tenant_id); +CREATE INDEX idx_creation_content_type ON black_deacon_creation_info(content_type); +CREATE INDEX idx_creation_theme ON black_deacon_creation_info(theme); +CREATE INDEX idx_creation_title ON black_deacon_creation_info(title); +CREATE INDEX idx_creation_deleted_at ON black_deacon_creation_info(deleted_at); + +-- 表和字段注释 +COMMENT ON TABLE black_deacon_creation_info IS '素材/创作信息表'; +COMMENT ON COLUMN black_deacon_creation_info.id IS '主键ID(非自增)'; +COMMENT ON COLUMN black_deacon_creation_info.tenant_id IS '租户ID'; +COMMENT ON COLUMN black_deacon_creation_info.creator IS '创建人'; +COMMENT ON COLUMN black_deacon_creation_info.created_at IS '创建时间'; +COMMENT ON COLUMN black_deacon_creation_info.updater IS '更新人'; +COMMENT ON COLUMN black_deacon_creation_info.updated_at IS '更新时间'; +COMMENT ON COLUMN black_deacon_creation_info.deleted_at IS '删除时间(软删)'; +COMMENT ON COLUMN black_deacon_creation_info.html_file_url IS 'HTML文件地址'; +COMMENT ON COLUMN black_deacon_creation_info.image_urls IS '图片地址列表'; +COMMENT ON COLUMN black_deacon_creation_info.content_type IS '素材类型'; +COMMENT ON COLUMN black_deacon_creation_info.theme IS '主题'; +COMMENT ON COLUMN black_deacon_creation_info.title IS '标题'; + +--------------------pgsql创建creation_info表语句--------------------------- + + +--------------------pgsql创建black_deacon_flow_execution表语句--------------------------- +-- 流程执行记录表 +CREATE TABLE IF NOT EXISTS black_deacon_flow_execution ( + -- 基础字段(完全对齐项目规范) + id BIGINT PRIMARY KEY, -- 主键ID(非自增) + tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8 + creator VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at timestamp(6), + + -- 业务字段 + flow_user_id BIGINT NOT NULL, -- 流程ID + trigger_type VARCHAR(32) NOT NULL DEFAULT '', -- 触发类型 + duration_ms BIGINT NOT NULL DEFAULT 0, -- 执行时长(毫秒) + status SMALLINT NOT NULL DEFAULT 1, -- 状态:1-运行中,2-成功,3-失败 + flow_content JSONB DEFAULT '{}', -- 流程模板内容 + node_input_params JSONB DEFAULT '[]'::JSONB, + output_params JSONB DEFAULT '[]'::JSONB, + error_message TEXT DEFAULT '', -- 错误信息 + trace_id VARCHAR(64) DEFAULT '' -- 跟踪ID + ); + +-- 索引(高频查询) +CREATE INDEX idx_bfe_tenant_id ON black_deacon_flow_execution(tenant_id); +CREATE INDEX idx_bfe_flow_user_id ON black_deacon_flow_execution(flow_user_id); +CREATE INDEX idx_bfe_trace_id ON black_deacon_flow_execution(trace_id); +CREATE INDEX idx_bfe_status ON black_deacon_flow_execution(status); +CREATE INDEX idx_bfe_deleted_at ON black_deacon_flow_execution(deleted_at); + +-- 表和字段注释 +COMMENT ON TABLE black_deacon_flow_execution IS '流程执行记录表'; +COMMENT ON COLUMN black_deacon_flow_execution.id IS '主键ID(非自增)'; +COMMENT ON COLUMN black_deacon_flow_execution.tenant_id IS '租户ID'; +COMMENT ON COLUMN black_deacon_flow_execution.creator IS '创建人'; +COMMENT ON COLUMN black_deacon_flow_execution.created_at IS '创建时间'; +COMMENT ON COLUMN black_deacon_flow_execution.updater IS '更新人'; +COMMENT ON COLUMN black_deacon_flow_execution.updated_at IS '更新时间'; +COMMENT ON COLUMN black_deacon_flow_execution.deleted_at IS '删除时间(软删)'; +COMMENT ON COLUMN black_deacon_flow_execution.flow_user_id IS '流程ID'; +COMMENT ON COLUMN black_deacon_flow_execution.trigger_type IS '触发类型'; +COMMENT ON COLUMN black_deacon_flow_execution.duration_ms IS '执行时长(毫秒)'; +COMMENT ON COLUMN black_deacon_flow_execution.status IS '状态:1-运行中,2-成功,3-失败'; +COMMENT ON COLUMN black_deacon_flow_execution.flow_content IS '流程模板内容'; +COMMENT ON COLUMN black_deacon_flow_execution.node_input_params IS '节点输入参数'; +COMMENT ON COLUMN black_deacon_flow_execution.output_params IS '输出参数'; +COMMENT ON COLUMN black_deacon_flow_execution.error_message IS '错误信息'; +COMMENT ON COLUMN black_deacon_flow_execution.trace_id IS '跟踪ID'; +--------------------pgsql创建black_deacon_flow_execution表语句--------------------------- + +--------------------pgsql创建black_deacon_flow_user表语句--------------------------- +-- 用户流程表 +CREATE TABLE IF NOT EXISTS black_deacon_flow_user ( + id BIGINT PRIMARY KEY, + tenant_id BIGINT NOT NULL DEFAULT 0, + creator VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted_at timestamp(6), + + flow_name VARCHAR(128) NOT NULL DEFAULT '', + description TEXT DEFAULT '', + flow_content JSONB DEFAULT '{}', + node_input_params JSONB DEFAULT '[]'::JSONB, + access_level VARCHAR(32) NOT NULL DEFAULT '1', + source_flow_template_id BIGINT NOT NULL DEFAULT 0 + ); +CREATE INDEX idx_flow_user_tenant ON black_deacon_flow_user(tenant_id); +COMMENT ON TABLE black_deacon_flow_user IS '用户流程表'; +COMMENT ON COLUMN black_deacon_flow_user.flow_name IS '流程名称'; +COMMENT ON COLUMN black_deacon_flow_user.description IS '流程描述'; +COMMENT ON COLUMN black_deacon_flow_user.flow_content IS '流程内容'; +COMMENT ON COLUMN black_deacon_flow_user.node_input_params IS '节点输入参数'; +COMMENT ON COLUMN black_deacon_flow_user.access_level IS '访问权限:1私有,2团队,3公开'; +COMMENT ON COLUMN black_deacon_flow_user.source_flow_template_id IS '来源流程模板ID'; +--------------------pgsql创建black_deacon_flow_user表语句--------------------------- \ No newline at end of file diff --git a/workflow/consts/table_name.go b/workflow/consts/table_name.go index 8993b29..ddffdf2 100644 --- a/workflow/consts/table_name.go +++ b/workflow/consts/table_name.go @@ -1,5 +1,10 @@ package consts +// 数据库名称 +const ( + DbNameBlackDeacon = "black_deacon" +) + // 数据库表名 const ( TableNameCreationInfo = "creation_info" diff --git a/workflow/controller/creation_info_controller.go b/workflow/controller/creation_info_controller.go new file mode 100644 index 0000000..3886ca5 --- /dev/null +++ b/workflow/controller/creation_info_controller.go @@ -0,0 +1,22 @@ +package controller + +import ( + "ai-agent/workflow/model/dto" + "ai-agent/workflow/service" + "context" + + "gitea.com/red-future/common/beans" +) + +type creationInfo struct{} + +var CreationInfo = new(creationInfo) + +func (c *creationInfo) GraphInvoke(ctx context.Context, req *dto.CreationInput) (res *beans.ResponseEmpty, err error) { + err = service.CreationInfoService.Creation(ctx, req) + return +} + +func (c *creationInfo) List(ctx context.Context, req *dto.ListCreationInfoReq) (res *dto.ListCreationInfoRes, err error) { + return service.CreationInfoService.List(ctx, req) +} diff --git a/workflow/dao/creation_info_dao.go b/workflow/dao/creation_info_dao.go new file mode 100644 index 0000000..acba202 --- /dev/null +++ b/workflow/dao/creation_info_dao.go @@ -0,0 +1,43 @@ +package dao + +import ( + "ai-agent/workflow/consts" + "ai-agent/workflow/model/dto" + "ai-agent/workflow/model/entity" + "context" + + "gitea.com/red-future/common/db/gfdb" + "github.com/gogf/gf/v2/util/gconv" +) + +var CreationInfoDao = &creationInfoDao{} + +type creationInfoDao struct{} + +func (d *creationInfoDao) List(ctx context.Context, req *dto.ListCreationInfoReq, fields ...string) (res []*entity.CreationInfo, total int, err error) { + model := gfdb.DB(ctx, consts.DbNameBlackDeacon).Model(ctx, consts.TableNameCreationInfo).Fields(fields).OmitEmpty() + model.Where(entity.CreationInfoCol.Creator, req.Creator) + model.OrderDesc(entity.CreationInfoCol.CreatedAt) + if req.Page != nil { + model.Page(int(req.Page.PageNum), int(req.Page.PageSize)) + } + r, total, err := model.AllAndCount(false) + if err != nil { + return + } + err = r.Structs(&res) + return +} + +// Insert 插入 +func (d *creationInfoDao) Insert(ctx context.Context, req *dto.Create) (id int64, err error) { + e := &entity.CreationInfo{} + if err = gconv.Struct(req, e); err != nil { + return + } + r, err := gfdb.DB(ctx, consts.DbNameBlackDeacon).Model(ctx, consts.TableNameCreationInfo).Insert(e) + if err != nil { + return + } + return r.LastInsertId() +} diff --git a/workflow/model/dto/creation_info_dto.go b/workflow/model/dto/creation_info_dto.go new file mode 100644 index 0000000..02046fc --- /dev/null +++ b/workflow/model/dto/creation_info_dto.go @@ -0,0 +1,115 @@ +package dto + +import ( + "gitea.com/red-future/common/beans" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +type CreationInput struct { + g.Meta `path:"/creation" method:"post" tags:"创作作品管理" summary:"作品创作" dc:"作品创作"` + + Mode string `json:"mode"` + ContentType string `json:"content_type"` + Theme string `json:"theme"` + Title string `json:"title"` + Style string `json:"style"` + Count int `json:"count"` + ImagePerPost int `json:"image_per_post"` + ImageRatio string `json:"image_ratio"` + Desc string `json:"desc"` + Content string `json:"content"` +} + +type ImageUploadItem struct { + Title string `json:"title"` // 内容标题 + Index int `json:"index"` // 第几条 + ImageUrls []string `json:"image_urls"` // 上传成功的图片URL列表 + HtmlFileUrl string `json:"html_file_url"` // 上传成功的HTML文件URL(如有) + Theme string `json:"theme"` + ContentType string `json:"content_type"` +} + +// CreationOutput 接口最终返回结构体(你原有dto,这里补充完整) +type CreationOutput struct { + SuccessCount int `json:"success_count"` // 成功条数 + Items []ImageUploadItem `json:"items"` // 所有上传成功的详情 +} + +type Create struct { + HtmlFileUrl string + ImageUrls []string + ContentType string + Theme string + Title string +} + +// UploadFileBytesReq 上传文件请求(字节流) +type UploadFileBytesReq struct { + FileName string `json:"fileName" dc:"文件名"` + FileBytes []byte `json:"fileBytes" dc:"文件字节流"` + FileStoreURL string `json:"fileStoreURL" dc:"文件存储URL"` +} + +type UploadFileBytesRes struct { + FileURL string `json:"fileURL" dc:"上传地址"` + FileSize int `json:"fileSize" dc:"文件大小"` + FileName string `json:"fileName" dc:"文件名称"` + FileFormat string `json:"fileFormat" dc:"文件格式"` + FileAddressPrefix string `json:"fileAddressPrefix"` +} + +type ListCreationInfoReq struct { + g.Meta `path:"/list" method:"get" tags:"创作作品管理" summary:"作品列表" dc:"作品列表"` + + Page *beans.Page `json:"page"` + Creator string `json:"creator"` +} + +type ListCreationInfoRes struct { + List []*CreationInfoVO `json:"list"` + Total int `json:"total"` + Tree []TimeNode + ImgAddressPrefix string `json:"imgAddressPrefix"` +} + +type CreationInfoVO struct { + Id int64 `json:"id,string" dc:"id"` + HtmlFileUrl string `json:"htmlFileUrl"` + ImageUrls []string `json:"imageUrls"` + Theme string `json:"theme"` + Title string `json:"title"` + CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"` +} + +// 第一层:日期 +type TimeNode struct { + CreatedDate string `json:"createdDate"` + ContentTypes []ContentTypeNode `json:"contentTypes"` +} + +// 第二层:ContentType +type ContentTypeNode struct { + ContentType string `json:"contentType"` + Themes []ThemeNode `json:"themes"` +} + +// 第三层:Theme +type ThemeNode struct { + Theme string `json:"theme"` + Titles []TitleNode `json:"titles"` +} + +// Title 节点:Title-1、Title-2... +type TitleNode struct { + Title string `json:"title"` // 标题+编号:如 通勤-1 + HtmlFileUrl string `json:"htmlFileUrl"` // html地址 + ImageUrls []ImgNode `json:"imageUrls"` // 图片列表 +} + +// 图片 +type ImgNode struct { + Name string `json:"name"` // img+1 + Url string `json:"url"` +} diff --git a/workflow/model/entity/creation_info.go b/workflow/model/entity/creation_info.go new file mode 100644 index 0000000..72ebe24 --- /dev/null +++ b/workflow/model/entity/creation_info.go @@ -0,0 +1,31 @@ +package entity + +import "gitea.com/red-future/common/beans" + +type CreationInfo struct { + beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt + // 业务字段 + HtmlFileUrl string `orm:"html_file_url" json:"htmlFileUrl"` + ImageUrls []string `orm:"image_urls" json:"imageUrls"` + ContentType string `orm:"content_type" json:"contentType"` + Theme string `orm:"theme" json:"theme"` + Title string `orm:"title" json:"title"` +} + +type creationInfoCol struct { + beans.SQLBaseCol + HtmlFileUrl string + ImageUrls string + ContentType string + Theme string + Title string +} + +var CreationInfoCol = creationInfoCol{ + SQLBaseCol: beans.DefSQLBaseCol, + HtmlFileUrl: "html_file_url", + ImageUrls: "image_urls", + ContentType: "content_type", + Theme: "theme", + Title: "title", +} diff --git a/workflow/service/creation_info_service.go b/workflow/service/creation_info_service.go new file mode 100644 index 0000000..2de532c --- /dev/null +++ b/workflow/service/creation_info_service.go @@ -0,0 +1,191 @@ +package service + +import ( + "ai-agent/workflow/dao" + "ai-agent/workflow/model/dto" + "ai-agent/workflow/model/entity" + "ai-agent/workflow/service/einograph" + "context" + "fmt" + "sort" + + "gitea.com/red-future/common/utils" + "github.com/gogf/gf/v2/util/gconv" +) + +var CreationInfoService = new(creationInfoService) + +type creationInfoService struct{} + +func (s *creationInfoService) Creation(ctx context.Context, req *dto.CreationInput) (err error) { + run, err := einograph.BuildCreationGraph(ctx) + if err != nil { + return fmt.Errorf("构建失败: %w", err) + } + + output, err := run.Invoke(ctx, req) + if err != nil { + return fmt.Errorf("执行失败: %w", err) + } + // ========== 核心修复:先转 map,再取 output ========== + var resultMap map[string]any + if err := gconv.Scan(output, &resultMap); err != nil { + return fmt.Errorf("解析结果map失败: %w", err) + } + + // 取出内层 output + realOutput := resultMap["output"] + + // 解析到你的结构体 + var creationOutput dto.CreationOutput + if err := gconv.Scan(realOutput, &creationOutput); err != nil { + return fmt.Errorf("解析CreationOutput失败: %w", err) + } + for _, item := range creationOutput.Items { + dao.CreationInfoDao.Insert(ctx, &dto.Create{ + ImageUrls: item.ImageUrls, + HtmlFileUrl: item.HtmlFileUrl, + Title: item.Title, + Theme: item.Theme, + ContentType: item.ContentType, + }) + } + return +} + +func (s *creationInfoService) List(ctx context.Context, req *dto.ListCreationInfoReq) (res *dto.ListCreationInfoRes, err error) { + user, err := utils.GetUserInfo(ctx) + if err != nil { + return + } + req.Creator = user.UserName + list, total, err := dao.CreationInfoDao.List(ctx, req) + if err != nil { + return + } + res = &dto.ListCreationInfoRes{ + Total: total, + } + res.ImgAddressPrefix, err = utils.GetFileAddressPrefix(ctx) + if err != nil { + return + } + tree := s.ConvertToTree(list) + res.Tree = tree + return +} + +// ConvertToTree +// 第一层:日期 倒序 +// 第二层:ContentType 倒序 +// 第三层:Theme 倒序 +// Theme 下的 Title 按时间倒序编号 Title-1、Title-2… +func (s *creationInfoService) ConvertToTree(list []*entity.CreationInfo) []dto.TimeNode { + // ========== 1. 按日期分组 ========== + timeMap := make(map[string][]*entity.CreationInfo) + for _, item := range list { + dateStr := item.CreatedAt.Format("Y-m-d") + timeMap[dateStr] = append(timeMap[dateStr], item) + } + + // 日期倒序 + var dateList []string + for dateStr := range timeMap { + dateList = append(dateList, dateStr) + } + sort.Slice(dateList, func(i, j int) bool { + return dateList[i] > dateList[j] + }) + + var tree []dto.TimeNode + + // ========== 2. 遍历日期 ========== + for _, dateStr := range dateList { + items := timeMap[dateStr] + + // ========== ContentType 分组 + 【时间倒序】 ========== + ctMap := make(map[string][]*entity.CreationInfo) + for _, item := range items { + ctMap[item.ContentType] = append(ctMap[item.ContentType], item) + } + + // 把 ContentType 按【最新时间倒序】排序 + var ctList []string + for ct := range ctMap { + ctList = append(ctList, ct) + } + sort.Slice(ctList, func(i, j int) bool { + ctiItems := ctMap[ctList[i]] + ctjItems := ctMap[ctList[j]] + return ctiItems[0].CreatedAt.After(ctjItems[0].CreatedAt) + }) + + var ctNodes []dto.ContentTypeNode + for _, ct := range ctList { + ctItems := ctMap[ct] + + // ========== Theme 分组 + 【时间倒序】 ========== + themeMap := make(map[string][]*entity.CreationInfo) + for _, item := range ctItems { + themeMap[item.Theme] = append(themeMap[item.Theme], item) + } + + // Theme 按【最新时间倒序】排序 + var themeList []string + for theme := range themeMap { + themeList = append(themeList, theme) + } + sort.Slice(themeList, func(i, j int) bool { + tiItems := themeMap[themeList[i]] + tjItems := themeMap[themeList[j]] + return tiItems[0].CreatedAt.After(tjItems[0].CreatedAt) + }) + + var themeNodes []dto.ThemeNode + for _, theme := range themeList { + themeItems := themeMap[theme] + + // ========== Theme 下的 Title 按【时间倒序】排列 + 编号 ========== + sort.Slice(themeItems, func(a, b int) bool { + return themeItems[a].CreatedAt.After(themeItems[b].CreatedAt) + }) + + var titleNodes []dto.TitleNode + for idx, item := range themeItems { + titleName := fmt.Sprintf("%s-%d", item.Title, idx+1) + + var imgList []dto.ImgNode + for imgIdx, url := range item.ImageUrls { + imgList = append(imgList, dto.ImgNode{ + Name: fmt.Sprintf("img-%d", imgIdx+1), + Url: url, + }) + } + + titleNodes = append(titleNodes, dto.TitleNode{ + Title: titleName, + HtmlFileUrl: item.HtmlFileUrl, + ImageUrls: imgList, + }) + } + + themeNodes = append(themeNodes, dto.ThemeNode{ + Theme: theme, + Titles: titleNodes, + }) + } + + ctNodes = append(ctNodes, dto.ContentTypeNode{ + ContentType: ct, + Themes: themeNodes, + }) + } + + tree = append(tree, dto.TimeNode{ + CreatedDate: dateStr, + ContentTypes: ctNodes, + }) + } + + return tree +} diff --git a/workflow/service/einograph/lambda_func.go b/workflow/service/einograph/lambda_func.go new file mode 100644 index 0000000..df19d25 --- /dev/null +++ b/workflow/service/einograph/lambda_func.go @@ -0,0 +1,409 @@ +package einograph + +import ( + "ai-agent/workflow/model/dto" + "ai-agent/workflow/skill" + "context" + "fmt" + "io" + "net/http" + "strings" + + commonHttp "gitea.com/red-future/common/http" + "gitea.com/red-future/common/utils" + "github.com/cloudwego/eino/schema" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/util/gconv" +) + +// 全局保存请求(最简单、最稳定、Eino 必不报错) +var currentReq *dto.CreationInput + +// ==================== 1. 输入适配 ==================== +func InputAdaptLambda(ctx context.Context, input any) (any, error) { + // 直接保存到全局 + req, ok := input.(*dto.CreationInput) + if !ok { + return nil, fmt.Errorf("input must be CreationInput") + } + currentReq = req + return input, nil +} + +// ==================== 2. 构建Prompt ==================== +func BuildPromptLambda(ctx context.Context, input any) (any, error) { + req, ok := input.(*dto.CreationInput) + if !ok { + return nil, fmt.Errorf("input must be CreationInput") + } + count := req.Count + if req.Count > 3 { + req.Count = 3 + } + + imagePerPost := req.ImagePerPost + if req.ImagePerPost > 3 { + imagePerPost = 3 + } + + var imgPlaceholder strings.Builder + for i := 1; i <= imagePerPost; i++ { + imgPlaceholder.WriteString(fmt.Sprintf(`
`, i)) + } + mode := req.Mode + + // 核心:先用 embed 读取技能文案(完全不依赖运行目录) + skillContent, err := skill.ReadSkillMD() + prompt := "" + if len(skillContent) != 0 && err == nil { + prompt = skillContent + } + + // 根据模式拼接(动态图片数量 + 动态比例 + 严格数量控制) + switch mode { + case "混合模式(文案 + 图片)": + s1 := `1. 整个输出只能有 %d 个 标签和 %d 个 标签` + var a = fmt.Sprintf(`%s%d%d`, s1, count, count) + s2 := `2. 生成完成后立即停止,不续写、不扩展、不重复 +3. 不要输出任何解释、前言、后语 +4. 只输出纯HTML代码 +5. 内容绝对不能重复!` + var b = fmt.Sprintf(`%s%s`, a, s2) + s3 := `6. 多个HTML文件之间必须用 --- 分隔,分隔符前后不能有任何多余文字!` + if count > 1 { + b = fmt.Sprintf(`%s%s`, b, s3) + } + prompt = fmt.Sprintf(`%s + +【🔴 最高优先级强制规则】 +%s + +【📄 HTML结构强制模板】 + + + + + + + + +
+

%s

+ + + %s + +
+ 请按照小红书风格创作文案:分段清晰、使用emoji、重点内容加粗、排版美观 +
+ +
+ #干货分享 + #实用技巧 + #知识科普 +
+
+ + + +【📌 必须遵守的细节规则】 +1. 所有图片必须使用 .img-box 容器,不变形、不拉伸。 +2. 文案必须分段清晰、美观、小红书风格、使用emoji、重点内容加粗,排版符合小红书风格。 +3. 图片占位符里有几张图,就生成几张,不要自己额外添加或删除。 +4. 标签自动根据主题生成,无需外部参数。 + +内容信息: +主题:%s +标题:%s +风格:%s +`, + prompt, + b, + req.ImageRatio, + req.Title, + imgPlaceholder.String(), // 你传入的图片(多张自动适配) + req.Theme, + req.Title, + req.Style, + ) + + case "纯文案模式": + s1 := `1. 整个输出只能有 %d 个 标签和 %d 个 标签` + var a = fmt.Sprintf(`%s%d%d`, s1, count, count) + s2 := `2. 生成完成后立即停止,不续写、不扩展、不重复 +3. 不要输出任何解释、前言、后语 +4. 只输出纯HTML代码 +5. 内容绝对不能重复!` + var b = fmt.Sprintf(`%s%s`, a, s2) + s3 := `6. 多个HTML文件之间必须用 --- 分隔,分隔符前后不能有任何多余文字!` + if count > 1 { + b = fmt.Sprintf(`%s%s`, b, s3) + } + prompt = fmt.Sprintf(`%s + +【🔴 最高优先级强制规则】 +%s + +【📄 HTML结构强制模板】 + + + + + + + + +
+

%s

+
按照小红书风格创作文案:分段+emoji+重点加粗,排版美观
+
+ #干货分享 + #实用技巧 +
+
+ + + +【📌 必须遵守的细节规则】 +2. 必须分段清晰、美观、小红书风格、使用emoji、重点内容加粗,排版符合小红书风格。 +3. 标签自动根据主题生成,无需外部参数。 + +内容:主题=%s,标题=%s,风格=%s +`, + prompt, + b, + req.Title, + req.Theme, + req.Title, + req.Style, + ) + + case "纯图片模式": + s1 := `1. 必须**严格且只能生成 %d 组绘图关键词**,绝对不能多生成一组,也不能少生成一组!` + var a = fmt.Sprintf(`%s%d`, s1, count) + s2 := `2. 生成完成后立即停止,不续写、不扩展、不重复 +3. 所有输出内容只能是关键词本身,**绝对不能出现任何解释、说明、额外文字**! +4. 内容绝对不能重复!` + var b = fmt.Sprintf(`%s%s`, a, s2) + s3 := `5. 多组关键词之间必须用 --- 分隔,分隔符前后不能有任何多余文字!` + if count > 1 { + b = fmt.Sprintf(`%s%s`, b, s3) + } + + prompt = fmt.Sprintf(`%s + +【🔴 最高优先级强制规则】 +%s + +内容:主题:%s,比例:%s,风格:%s +`, + prompt, + b, + req.Theme, + req.ImageRatio, + req.Style, + ) + + default: + return nil, fmt.Errorf("不支持模式") + } + + if !g.IsEmpty(req.Desc) { + prompt = fmt.Sprintf(`%s,要求:%s`, prompt, req.Desc) + } + + return []*schema.Message{schema.UserMessage(prompt)}, nil +} + +// ==================== 4. 生成+上传(完全不解析input!) ==================== +func GenerateImageLambda(ctx context.Context, input any) (any, error) { + + msg, ok := input.(*schema.Message) + if !ok { + return nil, fmt.Errorf("input must be *schema.Message") + } + optimizedText := strings.TrimSpace(msg.Content) + + list := strings.Split(optimizedText, "---") + var items []string + for _, s := range list { + if s = strings.TrimSpace(s); s != "" { + items = append(items, s) + } + } + imgAddressPrefix, err := utils.GetFileAddressPrefix(ctx) + if err != nil { + return nil, err + + } + mode := currentReq.Mode + + imagePerPost := currentReq.ImagePerPost + if currentReq.ImagePerPost > 3 { + imagePerPost = 3 + } + // ============================================== + // ✅ 核心修复:构建正确的绘图关键词(主题+标题+风格+比例) + // ============================================== + prompt := fmt.Sprintf( + "%s,%s,%s,风格:%s,比例:%s", + currentReq.ContentType, + currentReq.Theme, + currentReq.Title, + currentReq.Style, + currentReq.ImageRatio, + ) + if !g.IsEmpty(currentReq.Desc) { + prompt = fmt.Sprintf(`%s,要求:%s`, prompt, currentReq.Desc) + } + // 初始化返回结果 + var output dto.CreationOutput + + for idx, item := range items { + base := fmt.Sprintf("%s_%d", currentReq.Title, idx+1) + dir := currentReq.Theme + num := imagePerPost + var uploadItem dto.ImageUploadItem + uploadItem.Title = currentReq.Title + uploadItem.Index = idx + 1 + uploadItem.Theme = currentReq.Theme + uploadItem.ContentType = currentReq.ContentType + + switch mode { + case "混合模式(文案 + 图片)": + // 生成并上传多张图片 + var imageUrls []string + imgMap := make(map[int]string) + for i := 1; i <= num; i++ { + url, _ := GenerateRealImage(prompt) + imageUrls = append(imageUrls, url) // 收集图片URL + bs, _ := getImageBytesFromURL(url) + uploadResp, err := Upload(ctx, &dto.UploadFileBytesReq{ + FileName: fmt.Sprintf("%s_%d.png", base, i), + FileBytes: bs, + FileStoreURL: dir, + }) + // 如果Upload返回了最终访问URL,替换成真实返回值 + if err != nil { + return nil, err + } + imageUrls[i-1] = uploadResp.FileURL + imgMap[i] = uploadResp.FileURL + } + uploadItem.ImageUrls = imageUrls // 存入结构体 + + // 替换HTML + final := item + if imagePerPost > 1 { + for i := 1; i <= num; i++ { + final = strings.ReplaceAll(final, fmt.Sprintf("{{IMG_URL_%d}}", i), imgAddressPrefix+imgMap[i]) + } + } else { + for i := 1; i <= num; i++ { + final = strings.ReplaceAll(final, fmt.Sprintf("{{IMG_URL_%d}}", idx+1), imgAddressPrefix+imgMap[i]) + } + } + htmlUploadResp, err := Upload(ctx, &dto.UploadFileBytesReq{ + FileName: base + ".html", + FileBytes: []byte(final), + FileStoreURL: dir, + }) + // 保存HTML上传地址 + if err != nil { + return nil, err + } + uploadItem.HtmlFileUrl = htmlUploadResp.FileURL + + case "纯图片模式": + var realImageUrls []string + for i := 1; i <= num; i++ { + tempUrl, _ := GenerateRealImage(prompt) + bs, _ := getImageBytesFromURL(tempUrl) + uploadResp, err := Upload(ctx, &dto.UploadFileBytesReq{ + FileName: fmt.Sprintf("%s_%d.png", base, i), + FileBytes: bs, + FileStoreURL: dir, + }) + if err != nil { + return nil, err + } + realImageUrls = append(realImageUrls, uploadResp.FileURL) + } + uploadItem.ImageUrls = realImageUrls + + case "纯文案模式": + htmlResp, err := Upload(ctx, &dto.UploadFileBytesReq{ + FileName: base + ".html", + FileBytes: []byte(item), + FileStoreURL: dir, + }) + if err != nil { + return nil, err + } + uploadItem.HtmlFileUrl = htmlResp.FileURL + } + + output.Items = append(output.Items, uploadItem) + fmt.Printf("✅ 第%d条上传成功\n,html:%s\n图片:%s\n", idx+1, gconv.String(uploadItem.HtmlFileUrl), gconv.String(uploadItem.ImageUrls)) + } + + // 统计成功数量 + output.SuccessCount = len(output.Items) + + return output, nil +} + +// ==================== 工具 ==================== +func getImageBytesFromURL(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return io.ReadAll(resp.Body) +} + +func Upload(ctx context.Context, req *dto.UploadFileBytesReq) (*dto.UploadFileBytesRes, error) { + headers := make(map[string]string) + if r := g.RequestFromCtx(ctx); r != nil { + for k, v := range r.Header { + headers[k] = v[0] + } + } + res := &dto.UploadFileBytesRes{} + err := commonHttp.Post(ctx, "oss/file/uploadFileBytes", headers, res, req) + if err != nil { + glog.Error(ctx, err) + return nil, err + } + return res, nil +} diff --git a/workflow/service/einograph/model.go b/workflow/service/einograph/model.go new file mode 100644 index 0000000..9dd9ee2 --- /dev/null +++ b/workflow/service/einograph/model.go @@ -0,0 +1,98 @@ +package einograph + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/cloudwego/eino-ext/components/model/qwen" + "github.com/cloudwego/eino/components/model" + "github.com/gogf/gf/v2/util/gconv" +) + +// NewChatModel component initialization function of node 'ChatModel1' in graph 'test' +func NewChatModel(ctx context.Context) (cm model.ChatModel, err error) { + // TODO Modify component configuration here. + config := &qwen.ChatModelConfig{ + APIKey: "sk-4a8b82770bf74bc490eb3e4c5a8e2be9", + Model: "qwen-turbo", + BaseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1", // 必须加! + MaxTokens: gconv.PtrInt(2000), + Temperature: gconv.PtrFloat32(float32(0.7))} + cm, err = qwen.NewChatModel(ctx, config) + if err != nil { + return nil, err + } + return cm, nil +} + +func GenerateRealImage(prompt string) (string, error) { + apiKey := "sk-4a8b82770bf74bc490eb3e4c5a8e2be9" + url := "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" + + body := map[string]any{ + "model": "qwen-image", + "input": map[string]any{ + "messages": []map[string]any{ + { + "role": "user", + "content": []map[string]string{ + {"type": "text", "text": prompt}, + }, + }, + }, + }, + "parameters": map[string]any{ + "size": "1024*1364", + "n": 1, + "watermark": false, + }, + } + + payload, _ := json.Marshal(body) + req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload)) + req.Header.Set("Authorization", "Bearer "+apiKey) + req.Header.Set("Content-Type", "application/json") + + client := &http.Client{Timeout: 30 * time.Second} + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + data, _ := io.ReadAll(resp.Body) + + // ✅ 修复:plus 模型返回的字段是 image 不是 image_url + var result struct { + Output struct { + Choices []struct { + Message struct { + Content []struct { + Image string `json:"image"` + } `json:"content"` + } `json:"message"` + } `json:"choices"` + } `json:"output"` + Code string `json:"code"` + } + + err = gconv.Struct(data, &result) + + if len(result.Output.Choices) == 0 || result.Code != "" { + return "", fmt.Errorf("生成失败: %s", string(data)) + } + + // ✅ 修复:直接取 base64,不再下载 + imgBase64 := result.Output.Choices[0].Message.Content[0].Image + if imgBase64 == "" { + return "", fmt.Errorf("图片为空") + } + + // 解码 base64 + return imgBase64, nil +} diff --git a/workflow/service/einograph/orchestration.go b/workflow/service/einograph/orchestration.go new file mode 100644 index 0000000..bd121ad --- /dev/null +++ b/workflow/service/einograph/orchestration.go @@ -0,0 +1,80 @@ +package einograph + +import ( + "context" + + "github.com/cloudwego/eino/compose" +) + +// 节点名称常量 +const ( + NodeInputAdapt = "input_adapt" // 输入适配 + NodeBuildPrompt = "build_prompt" // 构建提示词 + NodeGenText = "gen_text" // 大模型生成原始文案 + NodeGenImage = "gen_image" // 生成图片 + HTML +) + +// ==================== 构建流式图(已加入优化节点) ==================== +func BuildCreationGraph(ctx context.Context) (compose.Runnable[any, any], error) { + g := compose.NewGraph[any, any]() + + // 1. 输入适配 + err := g.AddLambdaNode( + NodeInputAdapt, + compose.InvokableLambda(InputAdaptLambda), + compose.WithOutputKey("input"), + ) + if err != nil { + return nil, err + } + + // 2. 构建提示词 + err = g.AddLambdaNode( + NodeBuildPrompt, + compose.InvokableLambda(BuildPromptLambda), + compose.WithInputKey("input"), + compose.WithOutputKey("messages"), + ) + if err != nil { + return nil, err + } + + // 3. LLM 生成原始文案 + chatModel, err := NewChatModel(ctx) + if err != nil { + return nil, err + } + err = g.AddChatModelNode( + NodeGenText, + chatModel, + compose.WithInputKey("messages"), + compose.WithOutputKey("text_result"), + ) + if err != nil { + return nil, err + } + + // 5. 生成图片 + HTML(使用优化后的文案) + err = g.AddLambdaNode( + NodeGenImage, + compose.InvokableLambda(GenerateImageLambda), + compose.WithInputKey("text_result"), + compose.WithOutputKey("output"), + ) + if err != nil { + return nil, err + } + + // ✅ 正确连线:优化节点必须放在生成文案之后、生成图片之前 + _ = g.AddEdge(compose.START, NodeInputAdapt) + _ = g.AddEdge(NodeInputAdapt, NodeBuildPrompt) + _ = g.AddEdge(NodeBuildPrompt, NodeGenText) + _ = g.AddEdge(NodeGenText, NodeGenImage) + _ = g.AddEdge(NodeGenImage, compose.END) + + // 编译 + return g.Compile(ctx, + compose.WithGraphName("xiaohongshu_content_creation"), + compose.WithNodeTriggerMode(compose.AnyPredecessor), + ) +} diff --git a/workflow/skill/SKILL.md b/workflow/skill/SKILL.md new file mode 100644 index 0000000..4a1c08c --- /dev/null +++ b/workflow/skill/SKILL.md @@ -0,0 +1,71 @@ +--- +name: xiaohongshu-writer-expert +description: 根据任意主题生成小红书爆款文案,自动匹配8种风格模板,输出包含正文、配图描述、封面文案和搜索关键词的完整图文方案 +version: 1.0.0 +--- + +# 小红书爆款图文写作专家 + +你是一位资深小红书内容创作专家,擅长将任意主题转化为吸引眼球、高互动率的爆款文案。 + +## 核心能力 +- 自动判断主题类型,匹配最合适的风格模板 +- 生成200-300字年轻化、口语化文案 +- 提供完整的图文配套方案(配图描述、封面文案、搜索关键词) + +## 可用风格模板 +1. **好物推荐型**:产品推荐、购物分享、开箱测评 +2. **干货教程型**:技能教学、知识分享、避坑指南 +3. **情感成长型**:个人经历、成长感悟、励志分享 +4. **探店打卡型**:美食探店、旅行打卡、咖啡店分享 +5. **日常vlog型**:生活方式、日常记录、好习惯养成 +6. **母婴亲子型**:育儿经验、宝宝好物、亲子互动 +7. **穿搭美妆型**:妆容教程、穿搭分享、变美技巧 +8. **创业副业型**:副业分享、搞钱经验、成长干货 + +## 工作流程 +1. **主题分析**:判断内容属于哪个类型,选择最匹配的模板 +2. **文案创作**: + - 开头:2-3个emoji + 10字以内爆款标题 + 一句话引入 + - 正文:3-5个要点,每个带emoji,语言口语化,适当加空行 + - 总结:金句/核心观点 + 行动呼吁 + - 互动:自然引导点赞、收藏、评论 + - 标签:5-8个相关话题 +3. **图文配套**: + - 配图描述:画面风格、主体、色调、氛围 + - 封面文案:1-2句,分行排版,每行不超过5字 + - 英文关键词:3-5个用于图库搜索 + +## 文案风格要点 +- 语言年轻化、口语化,善用"咱就是说"、"真的绝了"、"谁懂啊"等网络用语 +- 适当使用括号补充细节,增加真实感 +- 每段文字不宜过长,手机阅读舒适为主 +- emoji与文字比例适中,不喧宾夺主 +- 互动语要具体,不要只说"欢迎评论" +- 标题要有信息增量,让人有点击欲望 + +## 输出格式 +直接输出以下结构,无需额外说明: + +## 📝 小红书图文 + +[emoji] [标题] +[空行] +[正文内容,适当加空行分隔] +[空行] +[互动引导语] +[空行] +[标签列表] + +--- + +## 🎨 配图方案 + +**配图描述**:[描述图片风格、画面内容、色调、构图] + +**封面文案**: +[文案第1行] +[文案第2行] +[文案第3行] + +**搜索关键词**:[keyword1, keyword2, keyword3, keyword4, keyword5] diff --git a/workflow/skill/embed.go b/workflow/skill/embed.go new file mode 100644 index 0000000..412a306 --- /dev/null +++ b/workflow/skill/embed.go @@ -0,0 +1,15 @@ +package skill + +import "embed" + +//go:embed *.md +var SkillFS embed.FS + +// ReadSkillMD 读取 SKILL.md 内容 +func ReadSkillMD() (string, error) { + data, err := SkillFS.ReadFile("SKILL.md") + if err != nil { + return "", err + } + return string(data), nil +}