feat: 新增工作流执行模块
新增流程执行记录的实体、DTO、DAO、控制器和服务层,支持工作流的执行、回调及结果树状列表查询;同时更新服务名称为 ai-agent。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
server:
|
||||
address: ":3006"
|
||||
name: "digital-human"
|
||||
name: "ai-agent"
|
||||
workerId: 1 # 雪花算法worker ID
|
||||
database:
|
||||
default:
|
||||
@@ -57,6 +57,7 @@ redis:
|
||||
readTimeout: "30s" #TCP的Read操作超时时间,使用时间字符串例如30s/1m/1d
|
||||
writeTimeout: "30s" #TCP的Write操作超时时间,使用时间字符串例如30s/1m/1d
|
||||
maxActive: 100
|
||||
|
||||
consul:
|
||||
address: 116.204.74.41:8500
|
||||
|
||||
|
||||
10
go.mod
10
go.mod
@@ -3,12 +3,13 @@ module ai-agent
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
gitea.com/red-future/common v0.0.12
|
||||
gitea.com/red-future/common v0.0.19
|
||||
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
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
)
|
||||
|
||||
//replace gitea.com/red-future/common => ../common
|
||||
@@ -50,7 +51,7 @@ require (
|
||||
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/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // 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
|
||||
@@ -75,13 +76,14 @@ require (
|
||||
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/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // 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/r3labs/diff/v2 v2.15.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
|
||||
@@ -89,6 +91,7 @@ require (
|
||||
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/vmihailenco/msgpack v4.0.4+incompatible // 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
|
||||
@@ -99,7 +102,6 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
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
|
||||
|
||||
13
go.sum
13
go.sum
@@ -1,6 +1,8 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.com/red-future/common v0.0.12 h1:whaCAiH33orl0P+oDpxzC4VoNluHKNYKGZ+FcUWw85Q=
|
||||
gitea.com/red-future/common v0.0.12/go.mod h1:3a7cwZNvgpKw5FzE8x5MZImd7NBePGXRGFSMjt90158=
|
||||
gitea.com/red-future/common v0.0.19 h1:9/WrfCFUCeFUYwuhBYF+JOQi5F5xuOy+gVnf2ZvHZu4=
|
||||
gitea.com/red-future/common v0.0.19/go.mod h1:6/nqIucVzmjOyqDTIq71feYBXXFNBy0rFwzaQ0/Ueoo=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
@@ -171,6 +173,8 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d
|
||||
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/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/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
@@ -288,6 +292,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
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/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/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=
|
||||
@@ -326,6 +332,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
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/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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@@ -356,6 +364,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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -374,6 +383,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/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=
|
||||
@@ -434,6 +445,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -510,6 +522,7 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
|
||||
15
main.go
15
main.go
@@ -2,13 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
digitalhumanController "ai-agent/digital-human/controller"
|
||||
workflowController "ai-agent/workflow/controller"
|
||||
workController "ai-agent/workflow/controller"
|
||||
workflowController "ai-agent/workflow/controller/flow"
|
||||
workflowNodeController "ai-agent/workflow/controller/node"
|
||||
workflowSkillController "ai-agent/workflow/controller/skill"
|
||||
"context"
|
||||
|
||||
_ "gitea.com/red-future/common/config"
|
||||
"gitea.com/red-future/common/http"
|
||||
"gitea.com/red-future/common/jaeger"
|
||||
_ "gitea.com/red-future/common/ragflow"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
)
|
||||
@@ -24,7 +25,13 @@ func main() {
|
||||
digitalhumanController.DigitalHuman, // 数字人相关接口
|
||||
digitalhumanController.Video, // 视频相关接口
|
||||
digitalhumanController.AsyncTask, // 异步任务相关接口
|
||||
workflowController.CreationInfo,
|
||||
workController.CreationInfo,
|
||||
workflowController.FlowExecution,
|
||||
workflowController.FlowUser,
|
||||
workflowController.FlowTemplate,
|
||||
workflowNodeController.NodeLibrary,
|
||||
workflowSkillController.SkillTemplate,
|
||||
workflowSkillController.SkillUser,
|
||||
})
|
||||
// 保持应用运行
|
||||
select {}
|
||||
|
||||
131
update.sql
131
update.sql
@@ -279,6 +279,87 @@ COMMENT ON COLUMN black_deacon_creation_info.title IS '标题';
|
||||
|
||||
--------------------pgsql创建creation_info表语句---------------------------
|
||||
|
||||
--------------------pgsql创建black_deacon_skill_template表语句---------------------------
|
||||
-- 技能模板表
|
||||
CREATE TABLE IF NOT EXISTS black_deacon_skill_template (
|
||||
-- 基础字段(完全对齐项目规范)
|
||||
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),
|
||||
|
||||
-- 业务字段
|
||||
name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
description TEXT DEFAULT '',
|
||||
category VARCHAR(64) NOT NULL DEFAULT '',
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT '',
|
||||
file_url VARCHAR(512) NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_skill_template_tenant_id ON black_deacon_skill_template(tenant_id);
|
||||
CREATE INDEX idx_skill_template_category ON black_deacon_skill_template(category);
|
||||
CREATE INDEX idx_skill_template_deleted_at ON black_deacon_skill_template(deleted_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE black_deacon_skill_template IS '技能模板表';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.id IS '主键ID';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.creator IS '创建人';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.updater IS '更新人';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.name IS '技能模板名称';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.description IS '描述';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.category IS '分类';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.file_name IS '文件名称';
|
||||
COMMENT ON COLUMN black_deacon_skill_template.file_url IS '文件地址';
|
||||
--------------------pgsql创建black_deacon_skill_template表语句---------------------------
|
||||
|
||||
--------------------pgsql创建black_deacon_skill_user表语句---------------------------
|
||||
-- 技能用户表
|
||||
CREATE TABLE IF NOT EXISTS black_deacon_skill_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),
|
||||
|
||||
-- 业务字段
|
||||
name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
description TEXT DEFAULT '',
|
||||
category VARCHAR(64) NOT NULL DEFAULT '',
|
||||
file_name VARCHAR(255) NOT NULL DEFAULT '',
|
||||
file_url VARCHAR(512) NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_skill_user_tenant_id ON black_deacon_skill_user(tenant_id);
|
||||
CREATE INDEX idx_skill_user_category ON black_deacon_skill_user(category);
|
||||
CREATE INDEX idx_skill_user_deleted_at ON black_deacon_skill_user(deleted_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE black_deacon_skill_user IS '技能用户表';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.id IS '主键ID';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.creator IS '创建人';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.updater IS '更新人';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.name IS '技能名称';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.description IS '描述';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.category IS '分类';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.file_name IS '文件名称';
|
||||
COMMENT ON COLUMN black_deacon_skill_user.file_url IS '文件地址';
|
||||
--------------------pgsql创建black_deacon_skill_user表语句---------------------------
|
||||
|
||||
--------------------pgsql创建black_deacon_flow_execution表语句---------------------------
|
||||
-- 流程执行记录表
|
||||
@@ -294,6 +375,7 @@ CREATE TABLE IF NOT EXISTS black_deacon_flow_execution (
|
||||
|
||||
-- 业务字段
|
||||
flow_user_id BIGINT NOT NULL, -- 流程ID
|
||||
flow_name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
trigger_type VARCHAR(32) NOT NULL DEFAULT '', -- 触发类型
|
||||
duration_ms BIGINT NOT NULL DEFAULT 0, -- 执行时长(毫秒)
|
||||
status SMALLINT NOT NULL DEFAULT 1, -- 状态:1-运行中,2-成功,3-失败
|
||||
@@ -302,6 +384,7 @@ CREATE TABLE IF NOT EXISTS black_deacon_flow_execution (
|
||||
output_params JSONB DEFAULT '[]'::JSONB,
|
||||
error_message TEXT DEFAULT '', -- 错误信息
|
||||
trace_id VARCHAR(64) DEFAULT '' -- 跟踪ID
|
||||
session_id VARCHAR(64) DEFAULT '' -- 会话ID
|
||||
);
|
||||
|
||||
-- 索引(高频查询)
|
||||
@@ -321,6 +404,7 @@ 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.flow_name IS '流程名称';
|
||||
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-失败';
|
||||
@@ -329,6 +413,7 @@ 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';
|
||||
COMMENT ON COLUMN black_deacon_flow_execution.session_id IS '会话ID';
|
||||
--------------------pgsql创建black_deacon_flow_execution表语句---------------------------
|
||||
|
||||
--------------------pgsql创建black_deacon_flow_user表语句---------------------------
|
||||
@@ -358,3 +443,49 @@ 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表语句---------------------------
|
||||
|
||||
--------------------pgsql创建black_deacon_flow_template表语句---------------------------
|
||||
-- 流程模板表
|
||||
CREATE TABLE IF NOT EXISTS black_deacon_flow_template (
|
||||
-- 基础字段(完全对齐项目规范)
|
||||
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_template_name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
description TEXT DEFAULT '',
|
||||
category_code VARCHAR(64) NOT NULL DEFAULT '',
|
||||
category_name VARCHAR(64) NOT NULL DEFAULT '',
|
||||
flow_content JSONB DEFAULT '{}',
|
||||
node_input_params JSONB DEFAULT '[]',
|
||||
status SMALLINT NOT NULL DEFAULT 1
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX idx_flow_template_tenant_id ON black_deacon_flow_template(tenant_id);
|
||||
CREATE INDEX idx_flow_template_category_code ON black_deacon_flow_template(category_code);
|
||||
CREATE INDEX idx_flow_template_status ON black_deacon_flow_template(status);
|
||||
CREATE INDEX idx_flow_template_deleted_at ON black_deacon_flow_template(deleted_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE black_deacon_flow_template IS '流程模板表';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.id IS '主键ID';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.creator IS '创建人';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.updater IS '更新人';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.flow_template_name IS '流程模板名称';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.description IS '流程描述';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.category_code IS '流程分类编码';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.category_name IS '流程分类名称';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.flow_content IS '流程内容';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.node_input_params IS '节点输入参数';
|
||||
COMMENT ON COLUMN black_deacon_flow_template.status IS '流程状态:1启用/0停用';
|
||||
--------------------pgsql创建black_deacon_flow_template表语句---------------------------
|
||||
28
workflow/consts/flow/flow_execution_status.go
Normal file
28
workflow/consts/flow/flow_execution_status.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package flow
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
var (
|
||||
FlowExecutionStatusRunning = newFlowExecutionStatus(gconv.PtrInt8(1), "running") // 运行中
|
||||
FlowExecutionStatusSuccess = newFlowExecutionStatus(gconv.PtrInt8(2), "success") // 成功
|
||||
FlowExecutionStatusFailed = newFlowExecutionStatus(gconv.PtrInt8(3), "failed") // 失败
|
||||
FlowExecutionStatusPaused = newFlowExecutionStatus(gconv.PtrInt8(4), "paused") // 暂停
|
||||
)
|
||||
|
||||
type FlowExecutionStatus *int8
|
||||
|
||||
type flowExecutionStatus struct {
|
||||
code FlowExecutionStatus
|
||||
desc string
|
||||
}
|
||||
|
||||
func (s flowExecutionStatus) Code() FlowExecutionStatus {
|
||||
return s.code
|
||||
}
|
||||
func (s flowExecutionStatus) Desc() string {
|
||||
return s.desc
|
||||
}
|
||||
|
||||
func newFlowExecutionStatus(code FlowExecutionStatus, desc string) flowExecutionStatus {
|
||||
return flowExecutionStatus{code: code, desc: desc}
|
||||
}
|
||||
27
workflow/consts/flow/flow_execution_trigger_type.go
Normal file
27
workflow/consts/flow/flow_execution_trigger_type.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package flow
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
type FlowExecutionTriggerType *int8
|
||||
|
||||
var (
|
||||
FlowExecutionTriggerTypeManual = newFlowExecutionTriggerType(gconv.PtrInt8(1), "manual")
|
||||
FlowExecutionTriggerTypeAuto = newFlowExecutionTriggerType(gconv.PtrInt8(2), "auto")
|
||||
FlowExecutionTriggerTypeTime = newFlowExecutionTriggerType(gconv.PtrInt8(3), "time")
|
||||
)
|
||||
|
||||
type flowExecutionTriggerType struct {
|
||||
code FlowExecutionTriggerType
|
||||
desc string
|
||||
}
|
||||
|
||||
func (s flowExecutionTriggerType) Code() FlowExecutionTriggerType {
|
||||
return s.code
|
||||
}
|
||||
func (s flowExecutionTriggerType) Desc() string {
|
||||
return s.desc
|
||||
}
|
||||
|
||||
func newFlowExecutionTriggerType(code FlowExecutionTriggerType, desc string) flowExecutionTriggerType {
|
||||
return flowExecutionTriggerType{code: code, desc: desc}
|
||||
}
|
||||
26
workflow/consts/flow/flow_template_status.go
Normal file
26
workflow/consts/flow/flow_template_status.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package flow
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
var (
|
||||
FlowTemplateStatusDisable = newFlowTemplateStatus(gconv.PtrInt8(0), "disable")
|
||||
FlowTemplateStatusEnable = newFlowTemplateStatus(gconv.PtrInt8(1), "enable")
|
||||
)
|
||||
|
||||
type FlowTemplateStatus *int8
|
||||
|
||||
type flowTemplateStatus struct {
|
||||
code FlowTemplateStatus
|
||||
desc string
|
||||
}
|
||||
|
||||
func (s flowTemplateStatus) Code() FlowTemplateStatus {
|
||||
return s.code
|
||||
}
|
||||
func (s flowTemplateStatus) Desc() string {
|
||||
return s.desc
|
||||
}
|
||||
|
||||
func newFlowTemplateStatus(code FlowTemplateStatus, desc string) flowTemplateStatus {
|
||||
return flowTemplateStatus{code: code, desc: desc}
|
||||
}
|
||||
27
workflow/consts/flow/flow_user_access_level.go
Normal file
27
workflow/consts/flow/flow_user_access_level.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package flow
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
var (
|
||||
FlowUserAccessLevelPrivate = newFlowUserAccessLevel(gconv.PtrInt8(1), "private")
|
||||
FlowUserAccessLevelTeam = newFlowUserAccessLevel(gconv.PtrInt8(2), "team")
|
||||
FlowUserAccessLevelPublic = newFlowUserAccessLevel(gconv.PtrInt8(3), "public")
|
||||
)
|
||||
|
||||
type FlowUserAccessLevel *int8
|
||||
|
||||
type flowUserAccessLevel struct {
|
||||
code FlowUserAccessLevel
|
||||
desc string
|
||||
}
|
||||
|
||||
func (s flowUserAccessLevel) Code() FlowUserAccessLevel {
|
||||
return s.code
|
||||
}
|
||||
func (s flowUserAccessLevel) Desc() string {
|
||||
return s.desc
|
||||
}
|
||||
|
||||
func newFlowUserAccessLevel(code FlowUserAccessLevel, desc string) flowUserAccessLevel {
|
||||
return flowUserAccessLevel{code: code, desc: desc}
|
||||
}
|
||||
29
workflow/consts/node/node_execution_status.go
Normal file
29
workflow/consts/node/node_execution_status.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package node
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
var (
|
||||
NodeExecutionStatusRunning = newNodeExecutionStatus(gconv.PtrInt8(1), "running") // 运行中
|
||||
NodeExecutionStatusSuccess = newNodeExecutionStatus(gconv.PtrInt8(2), "success") // 成功
|
||||
NodeExecutionStatusFailed = newNodeExecutionStatus(gconv.PtrInt8(3), "failed") // 失败
|
||||
NodeExecutionStatusPaused = newNodeExecutionStatus(gconv.PtrInt8(4), "paused") // 暂停
|
||||
NodeExecutionStatusWait = newNodeExecutionStatus(gconv.PtrInt8(5), "wait") // 等待执行
|
||||
)
|
||||
|
||||
type NodeExecutionStatus *int8
|
||||
|
||||
type nodeExecutionStatus struct {
|
||||
code NodeExecutionStatus
|
||||
desc string
|
||||
}
|
||||
|
||||
func (s nodeExecutionStatus) Code() NodeExecutionStatus {
|
||||
return s.code
|
||||
}
|
||||
func (s nodeExecutionStatus) Desc() string {
|
||||
return s.desc
|
||||
}
|
||||
|
||||
func newNodeExecutionStatus(code NodeExecutionStatus, desc string) nodeExecutionStatus {
|
||||
return nodeExecutionStatus{code: code, desc: desc}
|
||||
}
|
||||
186
workflow/consts/node/node_template.go
Normal file
186
workflow/consts/node/node_template.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package node
|
||||
|
||||
// ======================== 【常量定义:所有中文文案放这里!】 ========================
|
||||
// 分组名称
|
||||
const (
|
||||
NodeGroupNameComponent = "组件"
|
||||
NodeGroupNameBase = "基础"
|
||||
NodeGroupNameCustom = "自定义"
|
||||
)
|
||||
|
||||
// 节点名称
|
||||
const (
|
||||
NodeNameTextModel = "生成文案"
|
||||
NodeNameImageModel = "生成图片"
|
||||
NodeNameVideoModel = "视频"
|
||||
NodeNameAudioModel = "音频"
|
||||
NodeNameModel = "模型"
|
||||
NodeNameMerge = "结果合并"
|
||||
NodeNameJudge = "判断节点"
|
||||
NodeNameForm = "表单"
|
||||
NodeNameCustomNode = "自定义节点"
|
||||
)
|
||||
|
||||
// 表单字段 Label
|
||||
const (
|
||||
FormLabelApiKey = "API Key"
|
||||
FormLabelModel = "模型名称"
|
||||
|
||||
FormLabelCondition = "判断条件"
|
||||
)
|
||||
|
||||
// ======================== 枚举类型 ========================
|
||||
type NodeGroup string
|
||||
|
||||
const (
|
||||
NodeGroupComponent NodeGroup = "component"
|
||||
NodeGroupBase NodeGroup = "base"
|
||||
NodeGroupCustom NodeGroup = "custom"
|
||||
)
|
||||
|
||||
type NodeType string
|
||||
|
||||
const (
|
||||
// 组件
|
||||
NodeTypeTextModel NodeType = "text_model"
|
||||
NodeTypeImageModel NodeType = "image_model"
|
||||
NodeTypeVideoModel NodeType = "video_model"
|
||||
NodeTypeAudioModel NodeType = "audio_model"
|
||||
|
||||
// 基础
|
||||
NodeTypeModel NodeType = "model"
|
||||
NodeTypeMerge NodeType = "merge"
|
||||
NodeTypeJudge NodeType = "judge"
|
||||
NodeTypeForm NodeType = "form"
|
||||
NodeTypeIntent NodeType = "intent"
|
||||
|
||||
// 自定义
|
||||
NodeTypeCustomNode NodeType = "custom_node"
|
||||
)
|
||||
|
||||
// ======================== 结构定义 ========================
|
||||
type NodeFormField struct {
|
||||
Value string `json:"value"`
|
||||
Field string `json:"field"`
|
||||
Label string `json:"label"` // 从常量来
|
||||
Type string `json:"type"`
|
||||
Required bool `json:"required"`
|
||||
Default any `json:"default,omitempty"`
|
||||
Options []SelectOption `json:"options"`
|
||||
Expand any `json:"expand"`
|
||||
}
|
||||
|
||||
type SelectOption struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type ModelItem struct {
|
||||
ModelApiKey string `json:"modelApiKey"`
|
||||
ModelName string `json:"modelName"`
|
||||
ModelForm map[string]any `json:"modelForm"`
|
||||
ModelResponse map[string]any `json:"modelResponse"`
|
||||
}
|
||||
|
||||
type NodeItem struct {
|
||||
NodeId string `json:"nodeId"`
|
||||
NodeCode NodeType `json:"nodeCode"`
|
||||
NodeName string `json:"nodeName"` // 从常量来
|
||||
SkillOption bool `json:"skillOption"`
|
||||
FormConfig []NodeFormField `json:"formConfig"`
|
||||
ModelConfig []ModelItem `json:"modelConfig"`
|
||||
}
|
||||
|
||||
type NodeGroupItem struct {
|
||||
Group NodeGroup `json:"group"`
|
||||
Label string `json:"label"` // 从常量来
|
||||
Items []NodeItem `json:"items"`
|
||||
}
|
||||
|
||||
//
|
||||
//// 文案模型节点定义
|
||||
//func NewTextModelNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeTextModel,
|
||||
// NodeName: NodeNameTextModel,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 图片模型节点
|
||||
//func NewImageModelNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeImageModel,
|
||||
// NodeName: NodeNameImageModel,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 音频模型节点
|
||||
//func NewAudioModelNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeAudioModel,
|
||||
// NodeName: NodeNameAudioModel,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 视频模型节点
|
||||
//func NewVideoModelNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeVideoModel,
|
||||
// NodeName: NodeNameVideoModel,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 基础模型节点
|
||||
//func NewModelNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeModel,
|
||||
// NodeName: NodeNameModel,
|
||||
// FormConfig: []ModelItem{
|
||||
// {
|
||||
// ModelName: "模型名称",
|
||||
// ModelForm: []NodeFormField{
|
||||
// {Field: "apiKey", Label: FormLabelApiKey, Type: "input", Required: true},
|
||||
// {Field: "model", Label: FormLabelModel, Type: "input", Required: true},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 判断节点
|
||||
//func NewJudgeNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeJudge,
|
||||
// NodeName: NodeNameJudge,
|
||||
// FormConfig: []ModelItem{
|
||||
// {
|
||||
// ModelName: "判断条件",
|
||||
// ModelForm: []NodeFormField{
|
||||
// {Field: "condition", Label: FormLabelCondition, Type: "input", Required: true},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 表单参数节点
|
||||
//func NewFormNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeForm,
|
||||
// NodeName: NodeNameForm,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 自定义节点
|
||||
//func NewCustomNode() NodeItem {
|
||||
// return NodeItem{
|
||||
// NodeCode: NodeTypeCustomNode,
|
||||
// NodeName: NodeNameCustomNode,
|
||||
// FormConfig: []ModelItem{},
|
||||
// }
|
||||
//}
|
||||
@@ -9,6 +9,7 @@ const (
|
||||
const (
|
||||
TableNameCreationInfo = "creation_info"
|
||||
TableNameFlowExecution = "flow_execution"
|
||||
TableNameFlowTemplate = "flow_template"
|
||||
TableNameFlowUser = "flow_user"
|
||||
TableNameSkillTemplate = "skill_template"
|
||||
TableNameSkillUser = "skill_user"
|
||||
|
||||
26
workflow/controller/flow/flow_execution_controller.go
Normal file
26
workflow/controller/flow/flow_execution_controller.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
flowService "ai-agent/workflow/service/flow"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type flowExecution struct{}
|
||||
|
||||
var FlowExecution = new(flowExecution)
|
||||
|
||||
func (c *flowExecution) Execute(ctx context.Context, req *flowDto.ExecuteReq) (res *flowDto.ExecuteRes, err error) {
|
||||
return flowService.FlowExecutionService.Execute(ctx, req)
|
||||
}
|
||||
|
||||
func (c *flowExecution) ModelCallback(ctx context.Context, req *flowDto.ModelCallbackReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = flowService.FlowExecutionService.ModelCallback(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowExecution) List(ctx context.Context, req *flowDto.ListFlowExecutionReq) (res *flowDto.ListFlowExecutionTreeRes, err error) {
|
||||
return flowService.FlowExecutionService.List(ctx, req)
|
||||
}
|
||||
41
workflow/controller/flow/flow_template_controller.go
Normal file
41
workflow/controller/flow/flow_template_controller.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
flowService "ai-agent/workflow/service/flow"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type flowTemplate struct{}
|
||||
|
||||
var FlowTemplate = new(flowTemplate)
|
||||
|
||||
func (c *flowTemplate) Create(ctx context.Context, req *flowDto.CreateFlowTemplateReq) (res *flowDto.CreateFlowTemplateRes, err error) {
|
||||
res, err = flowService.FlowTemplateService.Create(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowTemplate) Update(ctx context.Context, req *flowDto.UpdateFlowTemplateReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = flowService.FlowTemplateService.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowTemplate) Delete(ctx context.Context, req *flowDto.DeleteFlowTemplateReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = flowService.FlowTemplateService.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowTemplate) Get(ctx context.Context, req *flowDto.GetFlowTemplateReq) (res *flowDto.FlowTemplateVO, err error) {
|
||||
return flowService.FlowTemplateService.Get(ctx, req)
|
||||
}
|
||||
|
||||
func (c *flowTemplate) List(ctx context.Context, req *flowDto.ListFlowTemplateReq) (res *flowDto.ListFlowTemplateRes, err error) {
|
||||
if !g.IsEmpty(req.Page) {
|
||||
req.Page = &beans.Page{PageNum: 1, PageSize: 20}
|
||||
}
|
||||
res, err = flowService.FlowTemplateService.List(ctx, req)
|
||||
return
|
||||
}
|
||||
41
workflow/controller/flow/flow_user_controller.go
Normal file
41
workflow/controller/flow/flow_user_controller.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
flowService "ai-agent/workflow/service/flow"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type flowUser struct{}
|
||||
|
||||
var FlowUser = new(flowUser)
|
||||
|
||||
func (c *flowUser) Create(ctx context.Context, req *flowDto.CreateFlowUserReq) (res *flowDto.CreateFlowUserRes, err error) {
|
||||
res, err = flowService.FlowUserService.Create(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowUser) Update(ctx context.Context, req *flowDto.UpdateFlowUserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = flowService.FlowUserService.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowUser) Delete(ctx context.Context, req *flowDto.DeleteFlowUserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = flowService.FlowUserService.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *flowUser) Get(ctx context.Context, req *flowDto.GetFlowUserReq) (res *flowDto.FlowUserVO, err error) {
|
||||
return flowService.FlowUserService.Get(ctx, req)
|
||||
}
|
||||
|
||||
func (c *flowUser) List(ctx context.Context, req *flowDto.ListFlowUserReq) (res *flowDto.ListFlowRes, err error) {
|
||||
if !g.IsEmpty(req.Page) {
|
||||
req.Page = &beans.Page{PageNum: 1, PageSize: 20}
|
||||
}
|
||||
res, err = flowService.FlowUserService.List(ctx, req)
|
||||
return
|
||||
}
|
||||
15
workflow/controller/node/node_library_controller.go
Normal file
15
workflow/controller/node/node_library_controller.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
nodeDto "ai-agent/workflow/model/dto/node"
|
||||
nodeService "ai-agent/workflow/service/node"
|
||||
"context"
|
||||
)
|
||||
|
||||
type nodeLibrary struct{}
|
||||
|
||||
var NodeLibrary = new(nodeLibrary)
|
||||
|
||||
func (c *nodeLibrary) List(ctx context.Context, req *nodeDto.WorkflowNodeTreeReq) (res *nodeDto.WorkflowNodeTreeRes, err error) {
|
||||
return nodeService.NodeLibraryService.GetNodeLibrary(ctx, req)
|
||||
}
|
||||
26
workflow/controller/skill/skill_template_controller.go
Normal file
26
workflow/controller/skill/skill_template_controller.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
skillService "ai-agent/workflow/service/skill"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type skillTemplate struct{}
|
||||
|
||||
var SkillTemplate = new(skillTemplate)
|
||||
|
||||
func (c *skillTemplate) Create(ctx context.Context, req *skillDto.CreateSkillTemplateReq) (res *skillDto.CreateSkillTemplateRes, err error) {
|
||||
return skillService.SkillTemplateService.Create(ctx, req)
|
||||
}
|
||||
|
||||
func (c *skillTemplate) Delete(ctx context.Context, req *skillDto.DeleteSkillTemplateReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = skillService.SkillTemplateService.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *skillTemplate) List(ctx context.Context, req *skillDto.ListSkillTemplateReq) (res *skillDto.ListSkillTemplateRes, err error) {
|
||||
return skillService.SkillTemplateService.List(ctx, req)
|
||||
}
|
||||
30
workflow/controller/skill/skill_user_controller.go
Normal file
30
workflow/controller/skill/skill_user_controller.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
skillService "ai-agent/workflow/service/skill"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type skillUser struct{}
|
||||
|
||||
var SkillUser = new(skillUser)
|
||||
|
||||
func (c *skillUser) Create(ctx context.Context, req *skillDto.CreateSkillUserReq) (res *skillDto.CreateSkillUserRes, err error) {
|
||||
return skillService.SkillUserService.Create(ctx, req)
|
||||
}
|
||||
|
||||
func (c *skillUser) Delete(ctx context.Context, req *skillDto.DeleteSkillUserReq) (res *beans.ResponseEmpty, err error) {
|
||||
err = skillService.SkillUserService.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *skillUser) List(ctx context.Context, req *skillDto.ListSkillReq) (res *skillDto.ListSkillUserRes, err error) {
|
||||
return skillService.SkillUserService.List(ctx, req)
|
||||
}
|
||||
|
||||
func (c *skillUser) ListUser(ctx context.Context, req *skillDto.ListSkillUserReq) (res *skillDto.ListSkillUserRes, err error) {
|
||||
return skillService.SkillUserService.ListUser(ctx, req)
|
||||
}
|
||||
64
workflow/dao/flow/flow_execution_dao.go
Normal file
64
workflow/dao/flow/flow_execution_dao.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/public"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FlowExecutionDao = &flowExecutionDao{}
|
||||
|
||||
type flowExecutionDao struct{}
|
||||
|
||||
// Insert 创建执行记录
|
||||
func (d *flowExecutionDao) Insert(ctx context.Context, req *flowDto.CreateFlowExecutionReq) (id int64, err error) {
|
||||
var flowExecution = new(entity.FlowExecution)
|
||||
err = gconv.Struct(req, &flowExecution)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowExecution).Insert(flowExecution)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *flowExecutionDao) Update(ctx context.Context, req *flowDto.UpdateFlowExecutionReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowExecution).OmitEmpty().Data(&req).Where(entity.FlowExecutionCol.Id, req.Id).Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *flowExecutionDao) Get(ctx context.Context, req *flowDto.GetFlowExecutionReq, fields ...string) (res *entity.FlowExecution, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowExecution).OmitEmpty().
|
||||
Where(entity.FlowExecutionCol.Id, req.Id).
|
||||
Where(entity.FlowExecutionCol.SessionId, req.SessionId).
|
||||
Fields(fields).One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&res)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *flowExecutionDao) List(ctx context.Context, req *flowDto.ListFlowExecutionReq, fields ...string) (res []*entity.FlowExecution, total int, err error) {
|
||||
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowExecution).Fields(fields).OmitEmpty()
|
||||
model.Where(entity.FlowExecutionCol.Creator, req.Creator)
|
||||
model.OrderDesc(entity.FlowExecutionCol.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
|
||||
}
|
||||
70
workflow/dao/flow/flow_template_dao.go
Normal file
70
workflow/dao/flow/flow_template_dao.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/public"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FlowTemplateDao = &flowTemplateDao{}
|
||||
|
||||
type flowTemplateDao struct{}
|
||||
|
||||
func (d *flowTemplateDao) Insert(ctx context.Context, req *flowDto.CreateFlowTemplateReq) (id int64, err error) {
|
||||
var e = new(entity.FlowTemplate)
|
||||
err = gconv.Struct(req, &e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowTemplate).Insert(e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *flowTemplateDao) Update(ctx context.Context, req *flowDto.UpdateFlowTemplateReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowTemplate).OmitEmpty().
|
||||
Where(entity.FlowTemplateCol.Id, req.Id).
|
||||
Data(req).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *flowTemplateDao) Delete(ctx context.Context, req *flowDto.DeleteFlowTemplateReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowTemplate).Where(entity.FlowTemplateCol.Id, req.Id).Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *flowTemplateDao) Get(ctx context.Context, req *flowDto.GetFlowTemplateReq, fields ...string) (res *entity.FlowTemplate, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowTemplate).NoTenantId(ctx).Where(entity.FlowTemplateCol.Id, req.Id).Fields(fields).One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&res)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *flowTemplateDao) List(ctx context.Context, req *flowDto.ListFlowTemplateReq, fields ...string) (res []*entity.FlowTemplate, total int, err error) {
|
||||
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowTemplate).NoTenantId(ctx).Fields(fields).OmitEmpty()
|
||||
model.OrderDesc(entity.FlowTemplateCol.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
|
||||
}
|
||||
71
workflow/dao/flow/flow_user_dao.go
Normal file
71
workflow/dao/flow/flow_user_dao.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/public"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FlowUserDao = &flowUserDao{}
|
||||
|
||||
type flowUserDao struct{}
|
||||
|
||||
func (d *flowUserDao) Insert(ctx context.Context, req *flowDto.CreateFlowUserReq) (id int64, err error) {
|
||||
var e = new(entity.FlowUser)
|
||||
err = gconv.Struct(req, &e)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowUser).Insert(e)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *flowUserDao) Update(ctx context.Context, req *flowDto.UpdateFlowUserReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowUser).OmitEmpty().
|
||||
Where(entity.FlowUserCol.Id, req.Id).
|
||||
Data(req).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *flowUserDao) Delete(ctx context.Context, req *flowDto.DeleteFlowUserReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowUser).Where(entity.FlowUserCol.Id, req.Id).Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *flowUserDao) Get(ctx context.Context, req *flowDto.GetFlowUserReq, fields ...string) (res *entity.FlowUser, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowUser).NoTenantId(ctx).Where(entity.FlowUserCol.Id, req.Id).Fields(fields).One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&res)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *flowUserDao) List(ctx context.Context, req *flowDto.ListFlowUserReq, fields ...string) (res []*entity.FlowUser, total int, err error) {
|
||||
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowUser).NoTenantId(ctx).Fields(fields).OmitEmpty()
|
||||
model.Where(entity.FlowUserCol.Creator, req.Creator)
|
||||
model.OrderDesc(entity.FlowUserCol.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
|
||||
}
|
||||
51
workflow/dao/skill/skill_template_dao.go
Normal file
51
workflow/dao/skill/skill_template_dao.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/public"
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var SkillTemplateDao = &skillTemplateDao{}
|
||||
|
||||
type skillTemplateDao struct{}
|
||||
|
||||
func (d *skillTemplateDao) Insert(ctx context.Context, req *skillDto.CreateSkillTemplateReq) (id int64, err error) {
|
||||
skillTemplate := new(entity.SkillTemplate)
|
||||
err = gconv.Struct(req, &skillTemplate)
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).Insert(&skillTemplate)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *skillTemplateDao) Delete(ctx context.Context, req *skillDto.DeleteSkillTemplateReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).Where(entity.SkillTemplateCol.Id, req.Id).Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *skillTemplateDao) List(ctx context.Context, req *skillDto.ListSkillTemplateReq, fields ...string) (res []*entity.SkillTemplate, total int, err error) {
|
||||
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).NoTenantId(ctx).Fields(fields).OmitEmpty()
|
||||
if !g.IsEmpty(req.Keyword) {
|
||||
model.WhereLike(entity.SkillTemplateCol.Name, "%"+req.Keyword+"%")
|
||||
}
|
||||
model.OrderDesc(entity.SkillTemplateCol.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
|
||||
}
|
||||
52
workflow/dao/skill/skill_user_dao.go
Normal file
52
workflow/dao/skill/skill_user_dao.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/public"
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var SkillUserDao = &skillUserDao{}
|
||||
|
||||
type skillUserDao struct{}
|
||||
|
||||
func (d *skillUserDao) Insert(ctx context.Context, req *skillDto.CreateSkillUserReq) (id int64, err error) {
|
||||
skillUser := new(entity.SkillUser)
|
||||
err = gconv.Struct(req, &skillUser)
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).Insert(&skillUser)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *skillUserDao) Delete(ctx context.Context, req *skillDto.DeleteSkillUserReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).Where(entity.SkillUserCol.Id, req.Id).Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
func (d *skillUserDao) List(ctx context.Context, req *skillDto.ListSkillUserReq, fields ...string) (res []*entity.SkillUser, total int, err error) {
|
||||
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).NoTenantId(ctx).Fields(fields).OmitEmpty()
|
||||
model.Where(entity.SkillUserCol.Creator, req.Creator)
|
||||
if !g.IsEmpty(req.Keyword) {
|
||||
model.WhereLike(entity.SkillUserCol.Name, "%"+req.Keyword+"%")
|
||||
}
|
||||
model.OrderDesc(entity.SkillUserCol.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
|
||||
}
|
||||
235
workflow/model/dto/flow/flow_execution_dto.go
Normal file
235
workflow/model/dto/flow/flow_execution_dto.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// NodeExecutionInput 节点执行入参(包含配置+表单架构)
|
||||
type NodeExecutionInput struct {
|
||||
Config *entity.FlowNode // 节点配置
|
||||
Global *FlowExecutionInput `json:"-"`
|
||||
}
|
||||
|
||||
// FlowExecutionInput 工作流执行入参(全程不变)
|
||||
type FlowExecutionInput struct {
|
||||
ExecutionId int64 `json:"executionId"`
|
||||
ConfigMap map[string]*entity.FlowNode `json:"configMap"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
Desc string `json:"desc"`
|
||||
SkillName string `json:"skillName"`
|
||||
FileUrl []string `json:"fileUrl"`
|
||||
ExecutedNodes []string `json:"executedNodes"`
|
||||
}
|
||||
|
||||
type ComposeMessagesReq struct {
|
||||
ModelTypeId int `json:"modelTypeId"`
|
||||
ModelName string `json:"modelName"`
|
||||
SkillName string `json:"skillName"`
|
||||
Form map[string]any `json:"form"`
|
||||
UserForm map[string]any `json:"userForm"`
|
||||
UserFiles []string `json:"userFiles"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
IsBuild bool `json:"isBuild"`
|
||||
Cause string `json:"cause"`
|
||||
}
|
||||
|
||||
type ComposeMessagesRes struct {
|
||||
Messages map[string]any `json:"messages"`
|
||||
EpicycleId int64 `json:"epicycleId" dc:"轮次ID"`
|
||||
}
|
||||
|
||||
type CreateTaskReq struct {
|
||||
ModelName string `json:"modelName"`
|
||||
ModelKey string `json:"modelKey"`
|
||||
BizName string `json:"bizName"`
|
||||
CallbackUrl string `json:"callbackUrl"`
|
||||
InputRef string `json:"inputRef"`
|
||||
RequestPayload map[string]any `json:"requestPayload"`
|
||||
EpicycleId int64 `json:"epicycleId" dc:"轮次ID"`
|
||||
}
|
||||
|
||||
type CreateTaskRes struct {
|
||||
TaskId string `json:"taskId"`
|
||||
}
|
||||
|
||||
type GetIsChatModelRes struct {
|
||||
ModelName string `json:"modelName"`
|
||||
ResponseBody map[string]any `json:"responseBody"`
|
||||
}
|
||||
|
||||
type ModelCallbackReq struct {
|
||||
g.Meta `path:"/modelCallback" method:"post" tags:"提示词处理" summary:"model-gateway 回调" dc:"model-gateway 成功后 GET 回调:callbackUrl/{bizName}"`
|
||||
TaskId string `p:"task_id" json:"task_id" v:"required#task_id不能为空" dc:"网关任务ID"`
|
||||
State int `p:"state" json:"state" dc:"网关任务状态"`
|
||||
OssFile string `p:"oss_file" json:"oss_file" dc:"结果文件地址"`
|
||||
FileType string `p:"file_type" json:"file_type" dc:"结果文件类型"`
|
||||
Text string `p:"text" json:"text" dc:"文本结果(可选,最多约 2000 字符)"`
|
||||
}
|
||||
|
||||
type TaskCallback struct {
|
||||
TaskID string `json:"taskId"`
|
||||
State int `json:"state"` // 0排队中/1执行中/2成功/3失败/4已下载
|
||||
OssFile string `json:"ossFile"`
|
||||
FileType string `json:"fileType"`
|
||||
Text string `json:"text"`
|
||||
//ImgContent *Image `json:"imgContent"`
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Choices []struct {
|
||||
FinishReason string `json:"finish_reason"`
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Content string `json:"content"`
|
||||
Role string `json:"role"`
|
||||
} `json:"message"`
|
||||
} `json:"choices"`
|
||||
Created int `json:"created"`
|
||||
Id string `json:"id"`
|
||||
Model string `json:"model"`
|
||||
Object string `json:"object"`
|
||||
Usage struct {
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
PromptTokensDetails struct {
|
||||
CachedTokens int `json:"cached_tokens"`
|
||||
}
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage"`
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Output struct {
|
||||
Choices []struct {
|
||||
FinishReason string `json:"finish_reason"`
|
||||
Message struct {
|
||||
Content []struct {
|
||||
Image string `json:"image"`
|
||||
} `json:"content"`
|
||||
Role string `json:"role"`
|
||||
} `json:"message"`
|
||||
} `json:"choices"`
|
||||
} `json:"output"`
|
||||
Usage struct {
|
||||
Height int `json:"height"`
|
||||
ImageCount int `json:"image_count"`
|
||||
Width int `json:"width"`
|
||||
} `json:"usage"`
|
||||
RequestId string `json:"request_id"`
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
type ExecuteReq struct {
|
||||
g.Meta `path:"/execute" method:"post" tags:"任务管理" summary:"执行任务" dc:"执行任务"`
|
||||
|
||||
FlowId int64 `json:"flowId" dc:"用户流程ID"`
|
||||
FlowName string `json:"flowName"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
Desc string `json:"desc"`
|
||||
SkillName string `json:"skillName"`
|
||||
FileUrl []string `json:"fileUrl"`
|
||||
}
|
||||
|
||||
type ExecuteRes struct {
|
||||
Id int64 `json:"id,string" dc:"执行记录ID,用于查询执行状态和结果"`
|
||||
}
|
||||
|
||||
type CancelReq struct {
|
||||
g.Meta `path:"/cancel" method:"get" tags:"任务管理" summary:"取消任务" dc:"取消任务"`
|
||||
|
||||
FlowId int64 `json:"flowId" dc:"用户流程ID"`
|
||||
}
|
||||
|
||||
type CreateFlowExecutionReq struct {
|
||||
FlowUserId int64 `json:"flowUserId" description:"流程ID"`
|
||||
FlowName string `json:"flowName"`
|
||||
TriggerType flow.FlowExecutionTriggerType `json:"triggerType" description:"触发类型"`
|
||||
DurationMs int64 `json:"durationMs" description:"执行时长(毫秒)"`
|
||||
Status flow.FlowExecutionStatus `json:"status" description:"状态:1-运行中,2-成功,3-失败"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"`
|
||||
ErrorMessage string `json:"errorMessage" description:"错误信息"`
|
||||
TraceId string `json:"traceId" description:"跟踪ID"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
}
|
||||
|
||||
type CreateFlowExecutionRes struct {
|
||||
Id int64 `json:"id,string"`
|
||||
}
|
||||
|
||||
type UpdateFlowExecutionReq struct {
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
DurationMs int64 `json:"durationMs" description:"执行时长(毫秒)"`
|
||||
Status flow.FlowExecutionStatus `json:"status" description:"状态:1-运行中,2-成功,3-失败"`
|
||||
OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"`
|
||||
ErrorMessage string `json:"errorMessage" description:"错误信息"`
|
||||
TraceId string `json:"traceId" description:"跟踪ID"`
|
||||
}
|
||||
|
||||
type GetFlowExecutionReq struct {
|
||||
g.Meta `path:"/get" method:"get" tags:"任务管理" summary:"获取任务详情" dc:"获取任务详情"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
}
|
||||
|
||||
type ListFlowExecutionReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"任务管理" summary:"任务列表" dc:"任务列表"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Creator string `json:"creator"`
|
||||
}
|
||||
|
||||
type ListFlowExecutionRes struct {
|
||||
List []*VOFlowExecution `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type VOFlowExecution struct {
|
||||
Id int64 `json:"id,string" dc:"id"`
|
||||
FlowUserId int64 `json:"flowUserId,string" description:"流程ID"`
|
||||
FlowName string `json:"flowName"`
|
||||
TriggerType flow.FlowExecutionTriggerType `json:"triggerType" description:"触发类型"`
|
||||
DurationMs int64 `json:"durationMs" description:"执行时长(毫秒)"`
|
||||
Status flow.FlowExecutionStatus `json:"status" description:"状态:1-运行中,2-成功,3-失败"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"`
|
||||
ErrorMessage string `json:"errorMessage" description:"错误信息"`
|
||||
TraceId string `json:"traceId" description:"跟踪ID"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// ========== 核心:构建树状结构 ==========
|
||||
// 定义树结构
|
||||
type OutputItem struct {
|
||||
Timestamp string `json:"timestamp" description:"时间戳key"`
|
||||
Content string `json:"content" description:"内容值"`
|
||||
Label string `json:"label" description:"后缀+数字标号"`
|
||||
}
|
||||
type FlowNode struct {
|
||||
FlowName string `json:"flowName" description:"流程名称"`
|
||||
FlowId int64 `json:"flowId,string" description:"流程ID"`
|
||||
SessionId string `json:"sessionId" description:"会话ID"`
|
||||
Items []OutputItem `json:"items" description:"输出项列表"`
|
||||
}
|
||||
type DateNode struct {
|
||||
CreateDate string `json:"createDate" description:"创建日期"`
|
||||
Flows []FlowNode `json:"flows" description:"流程列表"`
|
||||
}
|
||||
|
||||
// 最终树结构返回体
|
||||
type ListFlowExecutionTreeRes struct {
|
||||
Tree []DateNode `json:"tree"`
|
||||
ImgAddressPrefix string `json:"imgAddressPrefix"`
|
||||
}
|
||||
76
workflow/model/dto/flow/flow_template_dto.go
Normal file
76
workflow/model/dto/flow/flow_template_dto.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type CreateFlowTemplateReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"系统流程管理" summary:"创建系统流程" dc:"创建系统流程"`
|
||||
|
||||
FlowTemplateName string `json:"flowTemplateName" description:"流程模板名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
CategoryCode string `json:"categoryCode" description:"流程分类"`
|
||||
CategoryName string `json:"categoryName" description:"流程分类名称"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
Status flow.FlowTemplateStatus `description:"流程状态:1启用/0停用"`
|
||||
}
|
||||
|
||||
type CreateFlowTemplateRes struct {
|
||||
Id int64 `json:"id,string"`
|
||||
}
|
||||
|
||||
type UpdateFlowTemplateReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"系统流程管理" summary:"更新系统流程" dc:"更新系统流程"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
FlowTemplateName string `json:"flowTemplateName" description:"流程模板名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
CategoryCode string `json:"categoryCode" description:"流程分类"`
|
||||
CategoryName string `json:"categoryName" description:"流程分类名称"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
Status flow.FlowTemplateStatus `description:"流程状态:1启用/0停用"`
|
||||
}
|
||||
|
||||
type DeleteFlowTemplateReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"系统流程管理" summary:"删除系统流程" dc:"删除系统流程"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type GetFlowTemplateReq struct {
|
||||
g.Meta `path:"/get" method:"get" tags:"系统流程管理" summary:"获取系统流程详情" dc:"获取系统流程详情"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type ListFlowTemplateReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"系统流程管理" summary:"获取系统流程列表" dc:"分页查询系统流程列表,支持多条件筛选"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Keyword string `json:"keyword" dc:"关键词搜索"`
|
||||
}
|
||||
|
||||
type ListFlowTemplateRes struct {
|
||||
List []*FlowTemplateVO `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type FlowTemplateVO struct {
|
||||
Id int64 `json:"id,string" dc:"id"`
|
||||
FlowTemplateName string `json:"flowTemplateName" description:"流程模板名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
CategoryCode string `json:"categoryCode" description:"流程分类"`
|
||||
CategoryName string `json:"categoryName" description:"流程分类名称"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
Status flow.FlowTemplateStatus `description:"流程状态:1启用/0停用"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
80
workflow/model/dto/flow/flow_user_dto.go
Normal file
80
workflow/model/dto/flow/flow_user_dto.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type CreateFlowUserReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"用户流程管理" summary:"创建用户流程" dc:"创建用户流程"`
|
||||
|
||||
FlowName string `json:"flowName" description:"流程名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
AccessLevel flow.FlowUserAccessLevel `json:"accessLevel" description:"访问权限:1私有,2团队,3公开"`
|
||||
SourceFlowTemplateId int64 `json:"sourceFlowTemplateId" description:"来源流程模板ID"`
|
||||
}
|
||||
|
||||
type CreateFlowUserRes struct {
|
||||
Id int64 `json:"id,string"`
|
||||
}
|
||||
|
||||
type UpdateFlowUserReq struct {
|
||||
g.Meta `path:"/update" method:"put" tags:"用户流程管理" summary:"更新用户流程" dc:"更新用户流程"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
FlowName string `json:"flowName" description:"流程名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
AccessLevel flow.FlowUserAccessLevel `json:"accessLevel" description:"访问权限:1私有,2团队,3公开"`
|
||||
SourceFlowTemplateId int64 `json:"sourceFlowTemplateId" description:"来源流程模板ID"`
|
||||
}
|
||||
|
||||
type DeleteFlowUserReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"用户流程管理" summary:"删除用户流程" dc:"删除用户流程"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type GetFlowUserReq struct {
|
||||
g.Meta `path:"/get" method:"get" tags:"用户流程管理" summary:"获取用户流程详情" dc:"获取用户流程详情"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type ListFlowUserReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"用户流程管理" summary:"获取用户流程列表" dc:"分页查询用户流程列表,支持多条件筛选"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Creator string `json:"creator"`
|
||||
Keyword string `json:"keyword" dc:"关键词搜索"`
|
||||
}
|
||||
|
||||
type ListFlowUserRes struct {
|
||||
List []*FlowUserVO `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type FlowUserVO struct {
|
||||
Id int64 `json:"id,string" dc:"id"`
|
||||
FlowName string `json:"flowName" description:"流程名称"`
|
||||
Description string `json:"description" description:"流程描述"`
|
||||
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
|
||||
AccessLevel flow.FlowUserAccessLevel `json:"accessLevel" description:"访问权限:1私有,2团队,3公开"`
|
||||
SourceFlowTemplateId int64 `json:"sourceFlowTemplateId,string" description:"来源流程模板ID"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
|
||||
type ListFlowRes struct {
|
||||
ListFlowUserRes *ListFlowUserRes `json:"listFlowUserRes"`
|
||||
ListFlowTemplateRes *ListFlowTemplateRes `json:"listFlowTemplateRes"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
}
|
||||
29
workflow/model/dto/node/node_library_dto.go
Normal file
29
workflow/model/dto/node/node_library_dto.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/node"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type WorkflowNodeTreeReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"节点组件库管理" summary:"节点组件库列表" dc:"节点组件库列表"`
|
||||
|
||||
Creator string `json:"creator"`
|
||||
}
|
||||
|
||||
type WorkflowNodeTreeRes struct {
|
||||
Groups []node.NodeGroupItem `json:"groups"`
|
||||
}
|
||||
|
||||
type TypeGroup struct {
|
||||
TypeId int `json:"typeId"`
|
||||
Type string `json:"type"`
|
||||
Items []ModelItem `json:"items"`
|
||||
}
|
||||
|
||||
type ModelItem struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Form []node.NodeFormField `json:"form"`
|
||||
}
|
||||
50
workflow/model/dto/skill/skill_template_dto.go
Normal file
50
workflow/model/dto/skill/skill_template_dto.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type CreateSkillTemplateReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"Skill技能管理" summary:"创建Skill技能" dc:"创建Skill技能"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
FileName string `json:"fileName"`
|
||||
FileUrl string `json:"fileUrl"`
|
||||
}
|
||||
|
||||
type CreateSkillTemplateRes struct {
|
||||
Id int64 `json:"id,string"`
|
||||
}
|
||||
|
||||
type DeleteSkillTemplateReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"Skill技能管理" summary:"删除Skill技能" dc:"删除Skill技能"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type ListSkillTemplateReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"Skill技能管理" summary:"Skill技能列表" dc:"Skill技能列表"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Keyword string `json:"keyword" dc:"关键词搜索"`
|
||||
}
|
||||
|
||||
type ListSkillTemplateRes struct {
|
||||
List []*SkillTemplateVO `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type SkillTemplateVO struct {
|
||||
Id int64 `json:"id,string" dc:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
FileName string `json:"fileName"`
|
||||
FileUrl string `json:"fileUrl"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
59
workflow/model/dto/skill/skill_user_dto.go
Normal file
59
workflow/model/dto/skill/skill_user_dto.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
type CreateSkillUserReq struct {
|
||||
g.Meta `path:"/create" method:"post" tags:"Skill用户技能管理" summary:"创建Skill用户技能" dc:"创建Skill用户技能"`
|
||||
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
FileName string `json:"fileName"`
|
||||
FileUrl string `json:"fileUrl"`
|
||||
}
|
||||
|
||||
type CreateSkillUserRes struct {
|
||||
Id int64 `json:"id,string"`
|
||||
}
|
||||
|
||||
type DeleteSkillUserReq struct {
|
||||
g.Meta `path:"/delete" method:"delete" tags:"Skill用户技能管理" summary:"删除Skill用户技能" dc:"删除Skill用户技能"`
|
||||
|
||||
Id int64 `json:"id" v:"required#ID不能为空"`
|
||||
}
|
||||
|
||||
type ListSkillReq struct {
|
||||
g.Meta `path:"/list" method:"get" tags:"Skill用户技能管理" summary:"Skill用户技能列表" dc:"Skill用户技能列表"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Creator string `json:"creator"`
|
||||
Keyword string `json:"keyword" dc:"关键词搜索"`
|
||||
}
|
||||
|
||||
type ListSkillUserReq struct {
|
||||
g.Meta `path:"/listUser" method:"get" tags:"Skill用户技能管理" summary:"Skill仅用户技能列表" dc:"Skill仅用户技能列表"`
|
||||
|
||||
Page *beans.Page `json:"page"`
|
||||
Creator string `json:"creator"`
|
||||
Keyword string `json:"keyword" dc:"关键词搜索"`
|
||||
}
|
||||
|
||||
type ListSkillUserRes struct {
|
||||
List []*SkillUserVO `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type SkillUserVO struct {
|
||||
Id int64 `json:"id,string" dc:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
FileName string `json:"fileName"`
|
||||
FileUrl string `json:"fileUrl"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
53
workflow/model/entity/flow_execution.go
Normal file
53
workflow/model/entity/flow_execution.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type FlowExecution struct {
|
||||
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
|
||||
// 业务字段
|
||||
FlowUserId int64 `orm:"flow_user_id" json:"flowUserId" description:"流程ID"`
|
||||
FlowName string `orm:"flow_name" json:"flowName" description:"流程名称"`
|
||||
TriggerType flow.FlowExecutionTriggerType `orm:"trigger_type" json:"triggerType" description:"触发类型"`
|
||||
DurationMs int64 `orm:"duration_ms" json:"durationMs" description:"执行时长(毫秒)"`
|
||||
Status flow.FlowExecutionStatus `orm:"status" json:"status" description:"状态:1-运行中,2-成功,3-失败"`
|
||||
FlowContent *FlowInfo `orm:"flow_content" json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*FlowNode `orm:"node_input_params" json:"nodeInputParams" description:"节点输入参数"`
|
||||
OutputParams []map[string]interface{} `orm:"output_params" json:"outputParams" description:"输出参数"`
|
||||
ErrorMessage string `orm:"error_message" json:"errorMessage" description:"错误信息"`
|
||||
TraceId string `orm:"trace_id" json:"traceId" description:"跟踪ID"`
|
||||
SessionId string `orm:"session_id" json:"sessionId" description:"会话ID"`
|
||||
}
|
||||
|
||||
type flowExecutionCol struct {
|
||||
beans.SQLBaseCol
|
||||
FlowUserId string
|
||||
FlowName string
|
||||
TriggerType string
|
||||
DurationMs string
|
||||
Status string
|
||||
FlowContent string
|
||||
NodeInputParams string
|
||||
OutputParams string
|
||||
ErrorMessage string
|
||||
TraceId string
|
||||
SessionId string
|
||||
}
|
||||
|
||||
var FlowExecutionCol = flowExecutionCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
FlowUserId: "flow_user_id",
|
||||
FlowName: "flow_name",
|
||||
TriggerType: "trigger_type",
|
||||
DurationMs: "duration_ms",
|
||||
Status: "status",
|
||||
FlowContent: "flow_content",
|
||||
NodeInputParams: "node_input_params",
|
||||
OutputParams: "output_params",
|
||||
ErrorMessage: "error_message",
|
||||
TraceId: "trace_id",
|
||||
SessionId: "session_id",
|
||||
}
|
||||
41
workflow/model/entity/flow_template.go
Normal file
41
workflow/model/entity/flow_template.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type FlowTemplate struct {
|
||||
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
|
||||
// 业务字段
|
||||
FlowTemplateName string `orm:"flow_template_name" json:"flowTemplateName" description:"流程模板名称"`
|
||||
Description string `orm:"description" json:"description" description:"流程描述"`
|
||||
CategoryCode string `orm:"category_code" json:"categoryCode" description:"流程分类"`
|
||||
CategoryName string `orm:"category_name" json:"categoryName" description:"流程分类名称"`
|
||||
FlowContent *FlowInfo `orm:"flow_content" json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*FlowNode `orm:"node_input_params" json:"nodeInputParams" description:"节点输入参数"`
|
||||
Status flow.FlowTemplateStatus `orm:"status" json:"status" description:"流程状态:1启用/0停用"`
|
||||
}
|
||||
|
||||
type flowTemplateCol struct {
|
||||
beans.SQLBaseCol
|
||||
FlowTemplateName string
|
||||
Description string
|
||||
CategoryCode string
|
||||
CategoryName string
|
||||
FlowContent string
|
||||
NodeInputParams string
|
||||
Status string
|
||||
}
|
||||
|
||||
var FlowTemplateCol = flowTemplateCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
FlowTemplateName: "flow_template_name",
|
||||
Description: "description",
|
||||
CategoryCode: "category_code",
|
||||
CategoryName: "category_name",
|
||||
FlowContent: "flow_content",
|
||||
NodeInputParams: "node_input_params",
|
||||
Status: "status",
|
||||
}
|
||||
70
workflow/model/entity/flow_user.go
Normal file
70
workflow/model/entity/flow_user.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
"ai-agent/workflow/consts/node"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type FlowInfo struct {
|
||||
Version string `json:"version"`
|
||||
StartNodeId string `json:"startNodeId"`
|
||||
Nodes []FlowNode `json:"nodes"`
|
||||
Edges []FlowEdge `json:"edges"`
|
||||
}
|
||||
|
||||
type FlowNode struct {
|
||||
Id string `json:"id"`
|
||||
NodeCode node.NodeType `json:"nodeCode"`
|
||||
Name string `json:"name"`
|
||||
Config map[string]interface{} `json:"config"`
|
||||
SkillName string `json:"skillName"`
|
||||
InputSource []FlowNodeInputSource `json:"inputSource"` // 前端指定:来源节点ID
|
||||
FormConfig []node.NodeFormField `json:"formConfig"`
|
||||
ModelConfig node.ModelItem `json:"modelConfig"`
|
||||
OutputResult []node.NodeFormField `json:"outputResult" ds:"节点输出结果"`
|
||||
}
|
||||
|
||||
type FlowNodeInputSource struct {
|
||||
NodeId string `json:"nodeId"`
|
||||
QuoteOutput bool `json:"quoteOutput"`
|
||||
Field []string `json:"field"`
|
||||
}
|
||||
|
||||
type FlowEdge struct {
|
||||
Id string `json:"id"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
type FlowUser struct {
|
||||
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
|
||||
// 业务字段
|
||||
FlowName string `orm:"flow_name" json:"flowName" description:"流程名称"`
|
||||
Description string `orm:"description" json:"description" description:"流程描述"`
|
||||
FlowContent *FlowInfo `orm:"flow_content" json:"flowContent" description:"流程内容"`
|
||||
NodeInputParams []*FlowNode `orm:"node_input_params" json:"nodeInputParams" description:"节点输入参数"`
|
||||
AccessLevel flow.FlowUserAccessLevel `orm:"access_level" json:"accessLevel" description:"访问权限:1私有,2团队,3公开"`
|
||||
SourceFlowTemplateId int64 `orm:"source_flow_template_id" json:"sourceFlowTemplateId" description:"来源流程模板ID"`
|
||||
}
|
||||
|
||||
type flowUserCol struct {
|
||||
beans.SQLBaseCol
|
||||
FlowName string
|
||||
Description string
|
||||
FlowContent string
|
||||
NodeInputParams string
|
||||
AccessLevel string
|
||||
SourceFlowTemplateId string
|
||||
}
|
||||
|
||||
var FlowUserCol = flowUserCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
FlowName: "flow_name",
|
||||
Description: "description",
|
||||
FlowContent: "flow_content",
|
||||
NodeInputParams: "node_input_params",
|
||||
AccessLevel: "access_level",
|
||||
SourceFlowTemplateId: "source_flow_template_id",
|
||||
}
|
||||
33
workflow/model/entity/skill_template.go
Normal file
33
workflow/model/entity/skill_template.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type SkillTemplate struct {
|
||||
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
|
||||
|
||||
Name string `orm:"name" json:"name"`
|
||||
Description string `orm:"description" json:"description"`
|
||||
Category string `orm:"category" json:"category"`
|
||||
FileName string `orm:"file_name" json:"fileName"`
|
||||
FileUrl string `orm:"file_url" json:"fileUrl"`
|
||||
}
|
||||
|
||||
type skillTemplateCol struct {
|
||||
beans.SQLBaseCol
|
||||
Name string
|
||||
Description string
|
||||
Category string
|
||||
FileName string
|
||||
FileUrl string
|
||||
}
|
||||
|
||||
var SkillTemplateCol = skillTemplateCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Name: "name",
|
||||
Description: "description",
|
||||
Category: "category",
|
||||
FileName: "file_name",
|
||||
FileUrl: "file_url",
|
||||
}
|
||||
31
workflow/model/entity/skill_user.go
Normal file
31
workflow/model/entity/skill_user.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package entity
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
type SkillUser struct {
|
||||
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
|
||||
|
||||
Name string `orm:"name" json:"name"`
|
||||
Description string `orm:"description" json:"description"`
|
||||
Category string `orm:"category" json:"category"`
|
||||
FileName string `orm:"file_name" json:"fileName"`
|
||||
FileUrl string `orm:"file_url" json:"fileUrl"`
|
||||
}
|
||||
|
||||
type skillUserCol struct {
|
||||
beans.SQLBaseCol
|
||||
Name string
|
||||
Description string
|
||||
Category string
|
||||
FileName string
|
||||
FileUrl string
|
||||
}
|
||||
|
||||
var SkillUserCol = skillUserCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
Name: "name",
|
||||
Description: "description",
|
||||
Category: "category",
|
||||
FileName: "file_name",
|
||||
FileUrl: "file_url",
|
||||
}
|
||||
612
workflow/service/flow/flow_execution_service.go
Normal file
612
workflow/service/flow/flow_execution_service.go
Normal file
@@ -0,0 +1,612 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
"ai-agent/workflow/consts/node"
|
||||
flowDao "ai-agent/workflow/dao/flow"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gitea.com/red-future/common/utils"
|
||||
"github.com/cloudwego/eino/compose"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var FlowExecutionService = &flowExecutionService{}
|
||||
|
||||
type flowExecutionService struct{}
|
||||
|
||||
func (s *flowExecutionService) List(ctx context.Context, req *flowDto.ListFlowExecutionReq) (res *flowDto.ListFlowExecutionTreeRes, err error) {
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Creator = user.UserName
|
||||
list, _, err := flowDao.FlowExecutionDao.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ===================== 核心修复:只统计【有数据】的执行记录,空的直接跳过 =====================
|
||||
|
||||
executionNumber := make(map[int64]int) // executionId -> 倒序编号(最新=1)
|
||||
|
||||
// 第一次遍历:只处理【有输出参数】的记录,统计并分配编号
|
||||
var validList []*entity.FlowExecution // 只存有效(非空)记录
|
||||
for _, execution := range list {
|
||||
if g.IsEmpty(execution.OutputParams) {
|
||||
continue // 空数据直接过滤,不参与编号、不展示
|
||||
}
|
||||
validList = append(validList, execution)
|
||||
}
|
||||
|
||||
// 给有效记录分配【时间倒序编号】(最新=1)
|
||||
totalValid := len(validList)
|
||||
for idx, execution := range validList {
|
||||
executionNumber[execution.Id] = totalValid - idx
|
||||
}
|
||||
|
||||
// 2. 分组映射:日期 -> 流程节点
|
||||
type flowWrap struct {
|
||||
flowNode flowDto.FlowNode
|
||||
createdAt *gtime.Time
|
||||
}
|
||||
dateMap := make(map[string]*[]flowWrap)
|
||||
|
||||
// 遍历【有效数据】构建结构
|
||||
for _, execution := range validList {
|
||||
createDate := execution.CreatedAt.Format("Y-m-d")
|
||||
flowName := execution.FlowName
|
||||
outputParams := execution.OutputParams
|
||||
|
||||
// 编号只算有效数据,不会把空的算进去
|
||||
num := executionNumber[execution.Id]
|
||||
displayFlowName := fmt.Sprintf("会话-%d(%s)", num, flowName)
|
||||
|
||||
// 3. 解析 outputParams
|
||||
var tempItems []flowDto.OutputItem
|
||||
for _, paramMap := range outputParams {
|
||||
for tsKey, value := range paramMap {
|
||||
if _, err := strconv.ParseInt(tsKey, 10, 64); err != nil {
|
||||
continue
|
||||
}
|
||||
tempItems = append(tempItems, flowDto.OutputItem{
|
||||
Timestamp: tsKey,
|
||||
Content: gconv.String(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 修复1:如果解析后依然为空,直接跳过,不生成第二层节点 =====================
|
||||
if len(tempItems) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 时间戳正序
|
||||
sort.Slice(tempItems, func(i, j int) bool {
|
||||
t1, _ := strconv.ParseInt(tempItems[i].Timestamp, 10, 64)
|
||||
t2, _ := strconv.ParseInt(tempItems[j].Timestamp, 10, 64)
|
||||
return t1 < t2
|
||||
})
|
||||
|
||||
// 标号:相同类型递增,不同重置
|
||||
suffixCount := make(map[string]int)
|
||||
for idx := range tempItems {
|
||||
item := &tempItems[idx]
|
||||
val := item.Content
|
||||
suffix := "内容"
|
||||
|
||||
switch {
|
||||
case strings.Contains(val, "img") || strings.Contains(val, "png") || strings.Contains(val, "jpg"):
|
||||
suffix = "图片"
|
||||
case strings.Contains(val, "html") || strings.Contains(val, "HTML"):
|
||||
suffix = "HTML"
|
||||
case strings.Contains(val, "txt") || len(val) > 50:
|
||||
suffix = "文案"
|
||||
}
|
||||
|
||||
suffixCount[suffix]++
|
||||
item.Label = fmt.Sprintf("%s_%d", suffix, suffixCount[suffix])
|
||||
}
|
||||
|
||||
// 组装节点
|
||||
node := flowDto.FlowNode{
|
||||
FlowName: displayFlowName,
|
||||
FlowId: execution.FlowUserId,
|
||||
SessionId: gconv.String(execution.SessionId),
|
||||
Items: tempItems,
|
||||
}
|
||||
|
||||
if dateMap[createDate] == nil {
|
||||
dateMap[createDate] = &[]flowWrap{}
|
||||
}
|
||||
*dateMap[createDate] = append(*dateMap[createDate], flowWrap{
|
||||
flowNode: node,
|
||||
createdAt: execution.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// 6. 构建树 + 排序
|
||||
var tree []flowDto.DateNode
|
||||
for date, wraps := range dateMap {
|
||||
// 第二层按创建时间倒序(最新在前)
|
||||
sort.Slice(*wraps, func(i, j int) bool {
|
||||
return (*wraps)[i].createdAt.After((*wraps)[j].createdAt)
|
||||
})
|
||||
|
||||
var flowNodes []flowDto.FlowNode
|
||||
for _, w := range *wraps {
|
||||
flowNodes = append(flowNodes, w.flowNode)
|
||||
}
|
||||
|
||||
// ===================== 修复2:日期下没有流程,也过滤掉 =====================
|
||||
if len(flowNodes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tree = append(tree, flowDto.DateNode{
|
||||
CreateDate: date,
|
||||
Flows: flowNodes,
|
||||
})
|
||||
}
|
||||
|
||||
// 第一层日期倒序
|
||||
sort.Slice(tree, func(i, j int) bool {
|
||||
return tree[i].CreateDate > tree[j].CreateDate
|
||||
})
|
||||
|
||||
imgPrefix, err := utils.GetFileAddressPrefix(ctx)
|
||||
return &flowDto.ListFlowExecutionTreeRes{
|
||||
Tree: tree,
|
||||
ImgAddressPrefix: imgPrefix,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ModelCallback 模型回调接口
|
||||
func (s *flowExecutionService) ModelCallback(ctx context.Context, req *flowDto.ModelCallbackReq) (err error) {
|
||||
// 唤醒等待的任务
|
||||
Notify(req.TaskId, req)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 全局等待任务回调的工具
|
||||
var (
|
||||
asyncMu sync.Mutex
|
||||
asyncTasks = make(map[string]chan any)
|
||||
)
|
||||
|
||||
// Wait 阻塞等待回调结果
|
||||
// 调用后会一直卡住,直到 Notify 唤醒 或 超时/取消
|
||||
func Wait(ctx context.Context, taskId string) (any, error) {
|
||||
asyncMu.Lock()
|
||||
ch := make(chan any, 1)
|
||||
asyncTasks[taskId] = ch
|
||||
asyncMu.Unlock()
|
||||
|
||||
select {
|
||||
case result := <-ch:
|
||||
return result, nil
|
||||
case <-ctx.Done():
|
||||
asyncMu.Lock()
|
||||
delete(asyncTasks, taskId)
|
||||
asyncMu.Unlock()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Notify 回调时调用,唤醒等待的任务
|
||||
func Notify(taskId string, result any) {
|
||||
asyncMu.Lock()
|
||||
defer asyncMu.Unlock()
|
||||
|
||||
ch, exist := asyncTasks[taskId]
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
|
||||
ch <- result
|
||||
delete(asyncTasks, taskId)
|
||||
}
|
||||
|
||||
//func (s *flowExecutionService) Cancel(ctx context.Context, req *flowDto.CancelReq) (err error) {
|
||||
// res, err := flowDao.FlowExecutionDao.Get(ctx, &flowDto.GetFlowExecutionReq{
|
||||
// Id: req.FlowId,
|
||||
// })
|
||||
// res.TraceId
|
||||
// return
|
||||
//}
|
||||
|
||||
func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.ExecuteReq) (res *flowDto.ExecuteRes, err error) {
|
||||
|
||||
flowInfo, err := flowDao.FlowExecutionDao.Get(ctx, &flowDto.GetFlowExecutionReq{
|
||||
SessionId: req.SessionId,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var executionId int64
|
||||
if flowInfo == nil {
|
||||
var r = new(flowDto.CreateFlowExecutionReq)
|
||||
r.FlowUserId = req.FlowId
|
||||
r.FlowName = req.FlowName
|
||||
r.TriggerType = flow.FlowExecutionTriggerTypeManual.Code()
|
||||
r.FlowContent = req.FlowContent
|
||||
r.NodeInputParams = req.NodeInputParams
|
||||
r.SessionId = req.SessionId
|
||||
r.Status = flow.FlowExecutionStatusRunning.Code()
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if span != nil && span.SpanContext().HasTraceID() {
|
||||
r.TraceId = span.SpanContext().TraceID().String()
|
||||
}
|
||||
executionId, err = flowDao.FlowExecutionDao.Insert(ctx, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
executionId = flowInfo.Id
|
||||
}
|
||||
|
||||
_, err = flowDao.FlowUserDao.Update(ctx, &flowDto.UpdateFlowUserReq{
|
||||
Id: req.FlowId,
|
||||
FlowContent: req.FlowContent,
|
||||
NodeInputParams: req.NodeInputParams,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//nodeInsert := make([]*nodeDto.CreateNodeExecutionReq, 0, len(flowInfo.NodeInputParams))
|
||||
//for _, flowNode := range flowInfo.NodeInputParams {
|
||||
// nodeInsert = append(nodeInsert, &nodeDto.CreateNodeExecutionReq{
|
||||
// FlowExecutionId: executionId,
|
||||
// NodeId: flowNode.Id,
|
||||
// Status: node.NodeExecutionStatusWait.Code(),
|
||||
// NodeInputParams: flowNode,
|
||||
// TraceId: r.TraceId,
|
||||
// })
|
||||
//}
|
||||
//_, err = nodeDao.NodeExecutionDao.BatchInsert(ctx, nodeInsert)
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
|
||||
// =========================================================================
|
||||
// ✅【第1步】给所有判断节点自动生成意图识别节点
|
||||
// =========================================================================
|
||||
judge2IntentNodeMap := make(map[string]string)
|
||||
finalNodes := make([]entity.FlowNode, 0, len(req.FlowContent.Nodes)*2)
|
||||
for _, item := range req.FlowContent.Nodes {
|
||||
finalNodes = append(finalNodes, item)
|
||||
// 判断节点自动加 intent 节点
|
||||
if item.NodeCode == node.NodeTypeJudge {
|
||||
intentNodeID := fmt.Sprintf("intent_%s", item.Id)
|
||||
intentNode := entity.FlowNode{
|
||||
Id: intentNodeID,
|
||||
NodeCode: node.NodeTypeIntent,
|
||||
Name: fmt.Sprintf("意图识别-%s", item.Name),
|
||||
InputSource: item.InputSource, // ✅ 正确赋值
|
||||
FormConfig: item.FormConfig, // ✅ 用户配置
|
||||
ModelConfig: item.ModelConfig, // ✅ 系统配置
|
||||
}
|
||||
finalNodes = append(finalNodes, intentNode)
|
||||
judge2IntentNodeMap[item.Id] = intentNodeID
|
||||
}
|
||||
}
|
||||
|
||||
summaryNodeID := "summary_node"
|
||||
summaryNode := entity.FlowNode{
|
||||
Id: summaryNodeID,
|
||||
NodeCode: node.NodeTypeCustomNode, // 复用自定义节点类型,也可新增专属类型
|
||||
Name: "结果汇总节点",
|
||||
InputSource: []entity.FlowNodeInputSource{}, // 后续自动聚合所有节点输出
|
||||
FormConfig: nil,
|
||||
ModelConfig: node.ModelItem{},
|
||||
}
|
||||
finalNodes = append(finalNodes, summaryNode)
|
||||
|
||||
// 替换节点列表
|
||||
req.FlowContent.Nodes = finalNodes
|
||||
|
||||
// =========================================================================
|
||||
// ✅【第2步】构建执行图
|
||||
// =========================================================================
|
||||
runGraph, err := BuildGraphFromFlowContent(ctx, req.FlowContent, judge2IntentNodeMap, summaryNodeID)
|
||||
if err != nil {
|
||||
executionReq := flowDto.UpdateFlowExecutionReq{
|
||||
Id: executionId,
|
||||
}
|
||||
executionReq.Status = flow.FlowExecutionStatusFailed.Code()
|
||||
executionReq.ErrorMessage = err.Error()
|
||||
_, err1 := flowDao.FlowExecutionDao.Update(ctx, &executionReq)
|
||||
if err1 != nil {
|
||||
return
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// ✅【第3步】构建 ConfigMap
|
||||
// =========================================================================
|
||||
configMap := make(map[string]*entity.FlowNode)
|
||||
for _, cfg := range req.NodeInputParams {
|
||||
configMap[cfg.Id] = cfg
|
||||
}
|
||||
// 自动给意图节点复制配置
|
||||
for judgeID, intentID := range judge2IntentNodeMap {
|
||||
if cfg, ok := configMap[judgeID]; ok {
|
||||
configMap[intentID] = cfg
|
||||
}
|
||||
}
|
||||
// 初始化汇总节点配置
|
||||
configMap[summaryNodeID] = &summaryNode
|
||||
|
||||
// =========================================================================
|
||||
// ✅【第4步】构建全局执行入参(现在 schemaMap 是有值的!)
|
||||
// =========================================================================
|
||||
execInput := &flowDto.FlowExecutionInput{
|
||||
ExecutionId: executionId,
|
||||
ConfigMap: configMap,
|
||||
SessionId: req.SessionId,
|
||||
Desc: req.Desc,
|
||||
SkillName: req.SkillName,
|
||||
FileUrl: req.FileUrl,
|
||||
}
|
||||
// 执行工作流
|
||||
_, err = runGraph.Invoke(ctx, execInput)
|
||||
if err != nil {
|
||||
executionReq := flowDto.UpdateFlowExecutionReq{
|
||||
Id: executionId,
|
||||
}
|
||||
executionReq.Status = flow.FlowExecutionStatusFailed.Code()
|
||||
executionReq.ErrorMessage = err.Error()
|
||||
_, err1 := flowDao.FlowExecutionDao.Update(ctx, &executionReq)
|
||||
if err1 != nil {
|
||||
return
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// BuildGraphFromFlowContent 根据前端保存的工作流JSON,自动构建执行图
|
||||
func BuildGraphFromFlowContent(ctx context.Context, flowContent *entity.FlowInfo, judge2IntentNodeMap map[string]string, summaryNodeID string) (compose.Runnable[any, any], error) {
|
||||
graph := compose.NewGraph[any, any]()
|
||||
nodeMap := make(map[string]entity.FlowNode)
|
||||
|
||||
// 注册所有节点
|
||||
for _, item := range flowContent.Nodes {
|
||||
nodeMap[item.Id] = item
|
||||
if item.NodeCode != node.NodeTypeJudge {
|
||||
registerNodeToGraph(graph, item)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建边关系
|
||||
upstreamMap := make(map[string][]string)
|
||||
edgeMap := make(map[string][]entity.FlowEdge)
|
||||
for _, edge := range flowContent.Edges {
|
||||
edgeMap[edge.From] = append(edgeMap[edge.From], edge)
|
||||
upstreamMap[edge.To] = append(upstreamMap[edge.To], edge.From)
|
||||
}
|
||||
|
||||
// 处理连线 & 分支
|
||||
for fromNodeID, edges := range edgeMap {
|
||||
fromNode := nodeMap[fromNodeID]
|
||||
|
||||
// --------------------------
|
||||
// 判断节点 → 分支处理
|
||||
// --------------------------
|
||||
if fromNode.NodeCode == node.NodeTypeJudge {
|
||||
intentNodeID, ok := judge2IntentNodeMap[fromNodeID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("判断节点[%s]未生成意图节点", fromNodeID)
|
||||
}
|
||||
|
||||
branchMap := make(map[string]bool)
|
||||
for _, e := range edges {
|
||||
branchMap[e.To] = true
|
||||
}
|
||||
|
||||
judgeLambda := func(ctx context.Context, input any) (string, error) {
|
||||
execInput, ok := input.(*flowDto.FlowExecutionInput)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("入参类型错误")
|
||||
}
|
||||
|
||||
currentConfig := execInput.ConfigMap[fromNodeID]
|
||||
if currentConfig == nil {
|
||||
return "", fmt.Errorf("判断节点%s无配置", fromNodeID)
|
||||
}
|
||||
|
||||
branchIdNameMap := make(map[string]string)
|
||||
var branchIDs []string
|
||||
for nodeID := range branchMap {
|
||||
branchIDs = append(branchIDs, nodeID)
|
||||
// 从configMap获取分支节点的名称
|
||||
if branchNodeCfg, ok := execInput.ConfigMap[nodeID]; ok {
|
||||
branchIdNameMap[nodeID] = branchNodeCfg.Name
|
||||
} else {
|
||||
branchIdNameMap[nodeID] = "未命名节点" // 兜底
|
||||
}
|
||||
}
|
||||
|
||||
// 把分支ID-名称映射塞进 ModelConfig,带给意图节点
|
||||
m := make(map[string]interface{})
|
||||
m["branch_ids"] = branchIDs
|
||||
m["branch_id_name_map"] = branchIdNameMap // 传递ID-名称映射
|
||||
currentConfig.Config = m
|
||||
|
||||
// 从意图节点取输出
|
||||
if intentCfg, ok := execInput.ConfigMap[intentNodeID]; ok {
|
||||
currentConfig.OutputResult = intentCfg.OutputResult
|
||||
}
|
||||
|
||||
// 关键修改:构造 NodeExecutionInput 传入 JudgeLambda
|
||||
nodeExecInput := &flowDto.NodeExecutionInput{
|
||||
Config: currentConfig, // 当前判断节点配置
|
||||
Global: execInput, // 全局执行入参
|
||||
}
|
||||
return JudgeLambda(ctx, nodeExecInput) // 传入 NodeExecutionInput 类型
|
||||
}
|
||||
|
||||
_ = graph.AddBranch(intentNodeID, compose.NewGraphBranch(judgeLambda, branchMap))
|
||||
continue
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
// 普通节点连线
|
||||
// --------------------------
|
||||
for _, e := range edges {
|
||||
toNode := nodeMap[e.To]
|
||||
if toNode.NodeCode == node.NodeTypeJudge {
|
||||
_ = graph.AddEdge(e.From, fmt.Sprintf("intent_%s", toNode.Id))
|
||||
continue
|
||||
}
|
||||
_ = graph.AddEdge(e.From, e.To)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 第四步:处理开始/结束节点 ====================
|
||||
if flowContent.StartNodeId != "" {
|
||||
_ = graph.AddEdge(compose.START, flowContent.StartNodeId)
|
||||
}
|
||||
originalEndNodes := findEndNodes(flowContent.StartNodeId, flowContent.Edges)
|
||||
for _, endID := range originalEndNodes {
|
||||
_ = graph.AddEdge(endID, summaryNodeID)
|
||||
}
|
||||
_ = graph.AddEdge(summaryNodeID, compose.END)
|
||||
|
||||
return graph.Compile(ctx, compose.WithGraphName("auto_build_workflow"))
|
||||
}
|
||||
|
||||
// -------------------------- 节点自动注册器(核心分发) --------------------------
|
||||
func registerNodeToGraph(graph *compose.Graph[any, any], flowNode entity.FlowNode) {
|
||||
nodeID := flowNode.Id
|
||||
code := flowNode.NodeCode
|
||||
|
||||
// 通用包装:全程入参都是 *FlowExecutionInput
|
||||
wrapLambda := func(lambda func(ctx context.Context, input any) (any, error)) func(ctx context.Context, input any) (any, error) {
|
||||
return func(ctx context.Context, input any) (any, error) {
|
||||
// ✅ 【关键】全程入参类型永远不变
|
||||
execInput, ok := input.(*flowDto.FlowExecutionInput)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("入参必须是 *FlowExecutionInput, 实际是 %T", input)
|
||||
}
|
||||
|
||||
configMap := execInput.ConfigMap
|
||||
currentConfig := configMap[nodeID]
|
||||
if currentConfig == nil {
|
||||
return nil, fmt.Errorf("节点%s无配置", nodeID)
|
||||
}
|
||||
|
||||
// 获取入参 - 适配切片类型:遍历所有来源节点
|
||||
var realInput any
|
||||
if len(flowNode.InputSource) > 0 { // 改为判断切片长度
|
||||
// 遍历所有指定的来源节点,聚合输出结果
|
||||
for _, inputSource := range flowNode.InputSource { // 遍历切片
|
||||
if sourceConfig, ok := configMap[inputSource.NodeId]; ok {
|
||||
currentConfig.OutputResult = append(currentConfig.OutputResult, sourceConfig.OutputResult...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 封装节点执行入参(配置+表单架构)
|
||||
realInput = &flowDto.NodeExecutionInput{
|
||||
Config: currentConfig,
|
||||
Global: execInput, // ✅ 把【全部节点】的对象直接塞进来
|
||||
}
|
||||
|
||||
// 执行节点
|
||||
output, err := lambda(ctx, realInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ✅ 自动把当前节点ID 加入已执行列表
|
||||
execInput.ExecutedNodes = append(execInput.ExecutedNodes, nodeID)
|
||||
|
||||
// 输出存入 FlowNodeConfig
|
||||
if outConfig, ok := output.(*entity.FlowNode); ok {
|
||||
currentConfig.OutputResult = outConfig.OutputResult
|
||||
}
|
||||
|
||||
// ✅ 关键:返回整个 execInput,让下一个节点继续用!
|
||||
return execInput, nil
|
||||
}
|
||||
}
|
||||
if nodeID == "summary_node" {
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(SummaryLambda)))
|
||||
return
|
||||
}
|
||||
switch code {
|
||||
case "__start__":
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(StartLambda)))
|
||||
case node.NodeTypeTextModel:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(TextModelLambda)))
|
||||
case node.NodeTypeImageModel:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(ImageModelLambda)))
|
||||
case node.NodeTypeVideoModel:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(VideoModelLambda)))
|
||||
case node.NodeTypeAudioModel:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(AudioModelLambda)))
|
||||
case node.NodeTypeCustomNode:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(CustomLambda)))
|
||||
case node.NodeTypeForm:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(FormLambda)))
|
||||
case node.NodeTypeIntent:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(IntentLambda)))
|
||||
case node.NodeTypeMerge:
|
||||
_ = graph.AddLambdaNode(nodeID, compose.InvokableLambda(wrapLambda(MergeLambda)))
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// ✅【工具方法】找出所有没有出边的节点 → 作为结束节点连接 END
|
||||
// --------------------------------------------------------------------
|
||||
func findEndNodes(startNodeId string, edges []entity.FlowEdge) []string {
|
||||
// 构建 节点 → 后续节点 的映射
|
||||
nextMap := make(map[string][]string)
|
||||
for _, e := range edges {
|
||||
nextMap[e.From] = append(nextMap[e.From], e.To)
|
||||
}
|
||||
|
||||
endNodeSet := make(map[string]struct{})
|
||||
|
||||
// 🚀 只从【开始节点】递归遍历(关键修复)
|
||||
findLeafNodes(startNodeId, nextMap, endNodeSet)
|
||||
|
||||
// 转成数组返回
|
||||
endNodes := make([]string, 0, len(endNodeSet))
|
||||
for id := range endNodeSet {
|
||||
endNodes = append(endNodes, id)
|
||||
}
|
||||
return endNodes
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// ✅ 递归:查找以 nodeId 开头的所有叶子节点
|
||||
// --------------------------------------------------------------------
|
||||
func findLeafNodes(nodeId string, nextMap map[string][]string, endNodeSet map[string]struct{}) {
|
||||
nextNodes := nextMap[nodeId]
|
||||
|
||||
// 🚩 没有下一个节点 = 真实结束节点
|
||||
if len(nextNodes) == 0 {
|
||||
endNodeSet[nodeId] = struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
// 递归继续找下一个
|
||||
for _, nextId := range nextNodes {
|
||||
findLeafNodes(nextId, nextMap, endNodeSet)
|
||||
}
|
||||
}
|
||||
55
workflow/service/flow/flow_template_service.go
Normal file
55
workflow/service/flow/flow_template_service.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
flowDao "ai-agent/workflow/dao/flow"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FlowTemplateService = &flowTemplateService{}
|
||||
|
||||
type flowTemplateService struct{}
|
||||
|
||||
func (s *flowTemplateService) Create(ctx context.Context, req *flowDto.CreateFlowTemplateReq) (res *flowDto.CreateFlowTemplateRes, err error) {
|
||||
req.NodeInputParams = ExtractFlowNodeFrom(req.FlowContent)
|
||||
id, err := flowDao.FlowTemplateDao.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return &flowDto.CreateFlowTemplateRes{Id: id}, nil
|
||||
}
|
||||
|
||||
func (s *flowTemplateService) Update(ctx context.Context, req *flowDto.UpdateFlowTemplateReq) (err error) {
|
||||
req.NodeInputParams = ExtractFlowNodeFrom(req.FlowContent)
|
||||
_, err = flowDao.FlowTemplateDao.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *flowTemplateService) Delete(ctx context.Context, req *flowDto.DeleteFlowTemplateReq) (err error) {
|
||||
_, err = flowDao.FlowTemplateDao.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *flowTemplateService) Get(ctx context.Context, req *flowDto.GetFlowTemplateReq) (res *flowDto.FlowTemplateVO, err error) {
|
||||
flowInfo, err := flowDao.FlowTemplateDao.Get(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(flowDto.FlowTemplateVO)
|
||||
err = gconv.Struct(flowInfo, res)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *flowTemplateService) List(ctx context.Context, req *flowDto.ListFlowTemplateReq) (res *flowDto.ListFlowTemplateRes, err error) {
|
||||
list, total, err := flowDao.FlowTemplateDao.List(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = &flowDto.ListFlowTemplateRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &res.List)
|
||||
return
|
||||
}
|
||||
185
workflow/service/flow/flow_user_service.go
Normal file
185
workflow/service/flow/flow_user_service.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/flow"
|
||||
flowDao "ai-agent/workflow/dao/flow"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
commonHttp "gitea.com/red-future/common/http"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FlowUserService = &flowUserService{}
|
||||
|
||||
type flowUserService struct{}
|
||||
|
||||
// IsAdmin 调用admin-go服务检查是否是管理员
|
||||
func IsAdmin(ctx context.Context) (res bool, err error) {
|
||||
headers := make(map[string]string)
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
var r = make(map[string]bool)
|
||||
if err = commonHttp.Get(ctx, "admin-go/api/v1/system/user/checkIsSuperAdmin", headers, &r); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return r["isSuperAdmin"], err
|
||||
}
|
||||
|
||||
func (s *flowUserService) Create(ctx context.Context, req *flowDto.CreateFlowUserReq) (res *flowDto.CreateFlowUserRes, err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.NodeInputParams = ExtractFlowNodeFrom(req.FlowContent)
|
||||
var id int64
|
||||
if admin {
|
||||
id, err = flowDao.FlowTemplateDao.Insert(ctx, &flowDto.CreateFlowTemplateReq{
|
||||
FlowTemplateName: req.FlowName,
|
||||
Description: req.Description,
|
||||
FlowContent: req.FlowContent,
|
||||
NodeInputParams: req.NodeInputParams,
|
||||
Status: flow.FlowTemplateStatusEnable.Code(),
|
||||
})
|
||||
} else {
|
||||
id, err = flowDao.FlowUserDao.Insert(ctx, req)
|
||||
}
|
||||
return &flowDto.CreateFlowUserRes{Id: id}, err
|
||||
}
|
||||
|
||||
func (s *flowUserService) Update(ctx context.Context, req *flowDto.UpdateFlowUserReq) (err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.NodeInputParams = ExtractFlowNodeFrom(req.FlowContent)
|
||||
get, err := flowDao.FlowTemplateDao.Get(ctx, &flowDto.GetFlowTemplateReq{
|
||||
Id: req.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !g.IsEmpty(get) && !admin {
|
||||
_, err = flowDao.FlowUserDao.Insert(ctx, &flowDto.CreateFlowUserReq{
|
||||
FlowName: req.FlowName,
|
||||
Description: req.Description,
|
||||
FlowContent: req.FlowContent,
|
||||
NodeInputParams: req.NodeInputParams,
|
||||
SourceFlowTemplateId: get.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if admin {
|
||||
_, err = flowDao.FlowTemplateDao.Update(ctx, &flowDto.UpdateFlowTemplateReq{
|
||||
Id: req.Id,
|
||||
FlowTemplateName: req.FlowName,
|
||||
Description: req.Description,
|
||||
FlowContent: req.FlowContent,
|
||||
NodeInputParams: req.NodeInputParams,
|
||||
Status: flow.FlowTemplateStatusEnable.Code(),
|
||||
})
|
||||
} else {
|
||||
_, err = flowDao.FlowUserDao.Update(ctx, req)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ExtractFlowNodeFrom(flowContent *entity.FlowInfo) []*entity.FlowNode {
|
||||
var flowNodes []*entity.FlowNode
|
||||
for _, item := range flowContent.Nodes {
|
||||
flowNodes = append(flowNodes, &item)
|
||||
}
|
||||
return flowNodes
|
||||
}
|
||||
|
||||
func (s *flowUserService) Delete(ctx context.Context, req *flowDto.DeleteFlowUserReq) (err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if admin {
|
||||
_, err = flowDao.FlowTemplateDao.Delete(ctx, &flowDto.DeleteFlowTemplateReq{
|
||||
Id: req.Id,
|
||||
})
|
||||
} else {
|
||||
_, err = flowDao.FlowUserDao.Delete(ctx, req)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *flowUserService) Get(ctx context.Context, req *flowDto.GetFlowUserReq) (res *flowDto.FlowUserVO, err error) {
|
||||
|
||||
var flowInfo *entity.FlowTemplate
|
||||
flowInfo, err = flowDao.FlowTemplateDao.Get(ctx, &flowDto.GetFlowTemplateReq{
|
||||
Id: req.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if flowInfo != nil {
|
||||
res = new(flowDto.FlowUserVO)
|
||||
res.FlowName = flowInfo.FlowTemplateName
|
||||
err = gconv.Struct(flowInfo, res)
|
||||
return
|
||||
}
|
||||
|
||||
var flowUserInfo *entity.FlowUser
|
||||
flowUserInfo, err = flowDao.FlowUserDao.Get(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = new(flowDto.FlowUserVO)
|
||||
err = gconv.Struct(flowUserInfo, res)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *flowUserService) List(ctx context.Context, req *flowDto.ListFlowUserReq) (res *flowDto.ListFlowRes, err error) {
|
||||
|
||||
l, t, err := flowDao.FlowTemplateDao.List(ctx, &flowDto.ListFlowTemplateReq{
|
||||
Keyword: req.Keyword,
|
||||
Page: req.Page,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r := &flowDto.ListFlowTemplateRes{
|
||||
Total: t,
|
||||
}
|
||||
err = gconv.Struct(l, &r.List)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, total, err := flowDao.FlowUserDao.List(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
re := &flowDto.ListFlowUserRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &re.List)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = &flowDto.ListFlowRes{
|
||||
ListFlowUserRes: re,
|
||||
ListFlowTemplateRes: r,
|
||||
IsAdmin: admin,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
1001
workflow/service/flow/lambda_node.go
Normal file
1001
workflow/service/flow/lambda_node.go
Normal file
File diff suppressed because it is too large
Load Diff
245
workflow/service/flow/lambda_node_util.go
Normal file
245
workflow/service/flow/lambda_node_util.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/model/dto"
|
||||
flowDto "ai-agent/workflow/model/dto/flow"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
commonHttp "gitea.com/red-future/common/http"
|
||||
"gitea.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func GetIsChatModel(ctx context.Context) (*flowDto.GetIsChatModelRes, error) {
|
||||
headers := make(map[string]string)
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
res := new(flowDto.GetIsChatModelRes)
|
||||
err := commonHttp.Get(ctx, "model-gateway/model/getIsChatModel", headers, res, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func CreateGatewayTask(ctx context.Context, req *flowDto.CreateTaskReq) (string, error) {
|
||||
headers := make(map[string]string)
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
res := new(flowDto.CreateTaskRes)
|
||||
err := commonHttp.Post(ctx, "model-gateway/task/createTask", headers, res, &req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res.TaskId, nil
|
||||
}
|
||||
|
||||
func ComposeMessages(ctx context.Context, req *flowDto.ComposeMessagesReq) (*flowDto.ComposeMessagesRes, error) {
|
||||
headers := make(map[string]string)
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
res := new(flowDto.ComposeMessagesRes)
|
||||
err := commonHttp.Post(ctx, "prompts-core/prompt/composeMessages", headers, res, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func GatewayTask(ctx context.Context, epicycleId int64, model string, content map[string]any) (any, error) {
|
||||
modelTaskId, err := CreateGatewayTask(ctx, &flowDto.CreateTaskReq{
|
||||
ModelName: model,
|
||||
BizName: g.Cfg().MustGet(ctx, "server.name").String(),
|
||||
CallbackUrl: "/flow/execution/modelCallback",
|
||||
RequestPayload: content,
|
||||
EpicycleId: epicycleId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Wait(ctx, modelTaskId)
|
||||
}
|
||||
|
||||
func GetTaskResult(ctx context.Context, result any) (*flowDto.TaskCallback, error) {
|
||||
task := new(flowDto.TaskCallback)
|
||||
if err := gconv.Struct(result, task); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url, err := utils.GetFileAddressPrefix(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取远程文件内容
|
||||
file, err := FetchRemoteJsonFile(ctx, url+task.OssFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.Text = gconv.String(file)
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func FetchRemoteJsonFile(ctx context.Context, fileUrl string) ([]byte, error) {
|
||||
// 1. 下载文件
|
||||
resp, err := g.Client().Get(ctx, fileUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get file failed: %w", err)
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http status error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
func GetImageBytesFromURL(url string) (all []byte, contentType string, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
all, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
contentType = resp.Header.Get("Content-Type")
|
||||
return
|
||||
}
|
||||
|
||||
func Upload(ctx context.Context, req *dto.UploadFileBytesReq) (*dto.UploadFileBytesRes, error) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
part, err := writer.CreateFormFile("file", req.FileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = part.Write(req.FileBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = writer.FormDataContentType()
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
if auth := r.Header.Get("Authorization"); auth != "" {
|
||||
headers["Authorization"] = auth
|
||||
}
|
||||
}
|
||||
|
||||
// 发起上传请求
|
||||
res := &dto.UploadFileBytesRes{}
|
||||
url := "oss/file/uploadFile"
|
||||
if err = commonHttp.Post(ctx, url, headers, res, body.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "[Upload] success url=%s size=%d", res.FileURL, res.FileSize)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func buildMergeHtml(texts []string, images []string) string {
|
||||
html := strings.Builder{}
|
||||
|
||||
html.WriteString(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
}
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* 图片:完全贴边,无额外间距 */
|
||||
.image-block {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.image-block img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 0;
|
||||
}
|
||||
/* 文案:极致紧凑 */
|
||||
.text-block {
|
||||
margin: 0;
|
||||
padding: 16px; /* 仅保留内边距,不设外边距 */
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
/* 分割线:完全去掉,改用内边距自然分隔 */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
`)
|
||||
|
||||
// 1. 先渲染图片(无任何上下边距,占满宽度)
|
||||
if len(images) > 0 {
|
||||
html.WriteString(`<div class="image-block">`)
|
||||
for _, img := range images {
|
||||
html.WriteString(fmt.Sprintf(`<img src="%s" alt="" />`, img))
|
||||
}
|
||||
html.WriteString(`</div>`)
|
||||
}
|
||||
|
||||
// 2. 渲染文案(紧贴图片下方,仅用内边距留白)
|
||||
if len(texts) > 0 {
|
||||
html.WriteString(`<div class="text-block">`)
|
||||
// 段落之间用 <br> 而不是 <br><br>,减少空行
|
||||
html.WriteString(strings.Join(texts, "<br>"))
|
||||
html.WriteString(`</div>`)
|
||||
}
|
||||
|
||||
html.WriteString(`
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
|
||||
return html.String()
|
||||
}
|
||||
147
workflow/service/node/node_library_service.go
Normal file
147
workflow/service/node/node_library_service.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"ai-agent/workflow/consts/node"
|
||||
nodeDto "ai-agent/workflow/model/dto/node"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var NodeLibraryService = &nodeLibraryService{}
|
||||
|
||||
type nodeLibraryService struct{}
|
||||
|
||||
func (s *nodeLibraryService) GetNodeLibrary(ctx context.Context, req *nodeDto.WorkflowNodeTreeReq) (*nodeDto.WorkflowNodeTreeRes, error) {
|
||||
|
||||
WorkflowNodeGroups := []node.NodeGroupItem{
|
||||
{
|
||||
Group: node.NodeGroupComponent,
|
||||
Label: node.NodeGroupNameComponent,
|
||||
Items: []node.NodeItem{
|
||||
{
|
||||
NodeCode: node.NodeTypeTextModel,
|
||||
NodeName: node.NodeNameTextModel,
|
||||
SkillOption: true,
|
||||
FormConfig: []node.NodeFormField{}, // 技能下拉
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
{
|
||||
NodeCode: node.NodeTypeImageModel,
|
||||
NodeName: node.NodeNameImageModel,
|
||||
SkillOption: true,
|
||||
FormConfig: []node.NodeFormField{}, // 技能下拉
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Group: node.NodeGroupBase,
|
||||
Label: node.NodeGroupNameBase,
|
||||
Items: []node.NodeItem{
|
||||
{
|
||||
NodeCode: node.NodeTypeMerge,
|
||||
NodeName: node.NodeNameMerge,
|
||||
SkillOption: false,
|
||||
FormConfig: []node.NodeFormField{},
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
{
|
||||
NodeCode: node.NodeTypeJudge,
|
||||
NodeName: node.NodeNameJudge,
|
||||
SkillOption: false,
|
||||
FormConfig: []node.NodeFormField{
|
||||
{Field: "condition", Label: node.FormLabelCondition, Type: "input", Required: true},
|
||||
},
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
{
|
||||
NodeCode: node.NodeTypeForm,
|
||||
NodeName: node.NodeNameForm,
|
||||
SkillOption: false,
|
||||
FormConfig: []node.NodeFormField{},
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
//{
|
||||
// NodeCode: node.NodeTypeModel,
|
||||
// NodeName: node.NodeNameModel,
|
||||
// SkillOption: true,
|
||||
// FormConfig: []node.NodeFormField{},
|
||||
// ModelConfig: []node.ModelItem{},
|
||||
//},
|
||||
},
|
||||
},
|
||||
{
|
||||
Group: node.NodeGroupCustom,
|
||||
Label: node.NodeGroupNameCustom,
|
||||
Items: []node.NodeItem{
|
||||
{
|
||||
NodeCode: node.NodeTypeCustomNode,
|
||||
NodeName: node.NodeNameCustomNode,
|
||||
SkillOption: true,
|
||||
FormConfig: []node.NodeFormField{
|
||||
{Field: "nodeName", Label: node.FormLabelApiKey, Type: "input", Required: true},
|
||||
{Field: "nodeType", Label: node.FormLabelModel, Type: "input", Required: true},
|
||||
},
|
||||
ModelConfig: []node.ModelItem{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tree := &nodeDto.WorkflowNodeTreeRes{
|
||||
Groups: WorkflowNodeGroups,
|
||||
}
|
||||
|
||||
// 3. 遍历分组,根据 typeId=1 给【文本模型节点】追加固定表单
|
||||
for gIdx := range tree.Groups {
|
||||
group := &tree.Groups[gIdx]
|
||||
|
||||
// 遍历分组下的每个节点
|
||||
for itemIdx := range group.Items {
|
||||
item := &group.Items[itemIdx]
|
||||
if item.NodeCode == node.NodeTypeTextModel {
|
||||
item.ModelConfig = append(item.ModelConfig, node.ModelItem{
|
||||
ModelName: "自定义",
|
||||
})
|
||||
}
|
||||
if item.NodeCode == node.NodeTypeImageModel {
|
||||
item.ModelConfig = append(item.ModelConfig, node.ModelItem{
|
||||
ModelName: "自定义",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
// SetUserInfo 设置用户信息
|
||||
func (s *nodeLibraryService) SetUserInfo(ctx context.Context, creator string, tenantId uint64) (headers map[string]string, err error) {
|
||||
// 创建完整的用户信息
|
||||
userInfo := &beans.User{
|
||||
UserName: creator,
|
||||
TenantId: tenantId,
|
||||
}
|
||||
ctx = context.WithValue(ctx, "user", *userInfo)
|
||||
// 提取并保存请求头(在连接升级前)
|
||||
headers = make(map[string]string)
|
||||
// 提取其他headers
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将完整用户信息序列化为JSON,放到X-User-Info请求头
|
||||
userInfoJson, err := gjson.Encode(userInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("用户信息序列化失败: %w", err)
|
||||
}
|
||||
headers["X-User-Info"] = string(userInfoJson)
|
||||
return
|
||||
}
|
||||
38
workflow/service/skill/skill_template_service.go
Normal file
38
workflow/service/skill/skill_template_service.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
skillDao "ai-agent/workflow/dao/skill"
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var SkillTemplateService = &skillTemplateService{}
|
||||
|
||||
type skillTemplateService struct{}
|
||||
|
||||
func (s *skillTemplateService) Create(ctx context.Context, req *skillDto.CreateSkillTemplateReq) (res *skillDto.CreateSkillTemplateRes, err error) {
|
||||
id, err := skillDao.SkillTemplateDao.Insert(ctx, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return &skillDto.CreateSkillTemplateRes{Id: id}, nil
|
||||
}
|
||||
|
||||
func (s *skillTemplateService) Delete(ctx context.Context, req *skillDto.DeleteSkillTemplateReq) (err error) {
|
||||
_, err = skillDao.SkillTemplateDao.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *skillTemplateService) List(ctx context.Context, req *skillDto.ListSkillTemplateReq) (res *skillDto.ListSkillTemplateRes, err error) {
|
||||
list, total, err := skillDao.SkillTemplateDao.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &skillDto.ListSkillTemplateRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &res.List)
|
||||
return
|
||||
}
|
||||
130
workflow/service/skill/skill_user_service.go
Normal file
130
workflow/service/skill/skill_user_service.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package skill
|
||||
|
||||
import (
|
||||
skillDao "ai-agent/workflow/dao/skill"
|
||||
skillDto "ai-agent/workflow/model/dto/skill"
|
||||
"ai-agent/workflow/model/entity"
|
||||
"context"
|
||||
|
||||
commonHttp "gitea.com/red-future/common/http"
|
||||
"gitea.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var SkillUserService = &skillUserService{}
|
||||
|
||||
type skillUserService struct{}
|
||||
|
||||
// IsAdmin 调用admin-go服务检查是否是管理员
|
||||
func IsAdmin(ctx context.Context) (res bool, err error) {
|
||||
headers := make(map[string]string)
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
for k, v := range r.Request.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
var r = make(map[string]bool)
|
||||
if err = commonHttp.Get(ctx, "admin-go/api/v1/system/user/checkIsSuperAdmin", headers, &r); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return r["isSuperAdmin"], err
|
||||
}
|
||||
|
||||
func (s *skillUserService) Create(ctx context.Context, req *skillDto.CreateSkillUserReq) (res *skillDto.CreateSkillUserRes, err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var id int64
|
||||
if admin {
|
||||
id, err = skillDao.SkillTemplateDao.Insert(ctx, &skillDto.CreateSkillTemplateReq{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Category: req.Category,
|
||||
FileName: req.FileName,
|
||||
FileUrl: req.FileUrl,
|
||||
})
|
||||
} else {
|
||||
id, err = skillDao.SkillUserDao.Insert(ctx, req)
|
||||
}
|
||||
return &skillDto.CreateSkillUserRes{Id: id}, err
|
||||
}
|
||||
|
||||
func (s *skillUserService) Delete(ctx context.Context, req *skillDto.DeleteSkillUserReq) (err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if admin {
|
||||
_, err = skillDao.SkillTemplateDao.Delete(ctx, &skillDto.DeleteSkillTemplateReq{
|
||||
Id: req.Id,
|
||||
})
|
||||
} else {
|
||||
_, err = skillDao.SkillUserDao.Delete(ctx, req)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *skillUserService) List(ctx context.Context, req *skillDto.ListSkillReq) (res *skillDto.ListSkillUserRes, err error) {
|
||||
admin, err := IsAdmin(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if admin {
|
||||
var total int
|
||||
var list []*entity.SkillTemplate
|
||||
list, total, err = skillDao.SkillTemplateDao.List(ctx, &skillDto.ListSkillTemplateReq{
|
||||
Keyword: req.Keyword,
|
||||
Page: req.Page,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &skillDto.ListSkillUserRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &res.List)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Creator = user.UserName
|
||||
list, total, err := skillDao.SkillUserDao.List(ctx, &skillDto.ListSkillUserReq{
|
||||
Keyword: req.Keyword,
|
||||
Creator: req.Creator,
|
||||
Page: req.Page,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &skillDto.ListSkillUserRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &res.List)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *skillUserService) ListUser(ctx context.Context, req *skillDto.ListSkillUserReq) (res *skillDto.ListSkillUserRes, err error) {
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Creator = user.UserName
|
||||
list, total, err := skillDao.SkillUserDao.List(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &skillDto.ListSkillUserRes{
|
||||
Total: total,
|
||||
}
|
||||
err = gconv.Struct(list, &res.List)
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user