62 Commits
Author SHA1 Message Date
19904408334 f96d3362ac fix: 修正流程执行列表查询过滤逻辑
Deploy ai-agent to dev k3s / build-and-deploy (push) Failing after 2m24s
2026-08-17 09:57:43 +08:00
19904408334 89326b30ae fix: 修复网关任务并发等待竞态导致永久阻塞
Deploy ai-agent to dev k3s / build-and-deploy (push) Failing after 3m28s
2026-08-14 16:35:44 +08:00
admin 4c940c6753 gitea,gitea-runner写入docker-compose
Deploy ai-agent to dev k3s / build-and-deploy (push) Failing after 3m59s
2026-08-06 09:40:32 +08:00
admin ad9824aa55 k3s脚本
Deploy ai-agent to dev k3s / build-and-deploy (push) Successful in 3m58s
2026-08-03 16:29:47 +08:00
admin 8d7e260da0 gitea,gitea-runner写入docker-compose
Deploy ai-agent to dev k3s / build-and-deploy (push) Failing after 16m30s
2026-08-03 16:10:51 +08:00
19904408334 f8a28bf0f3 fix: 修复编号乱序及日期格式问题
先将日期倒序排序并按展示顺序统计各类型总数,确保倒序编号与前端展示一致;同时修正日期格式为 Y-m-d。
2026-07-22 15:38:48 +08:00
19904408334 ef8388f66b fix: 修正标签编号为倒序排列 2026-07-22 15:26:46 +08:00
19904408334 d2eef8d3ca Merge branch '2026-06-16' of http://116.204.74.41:3000/red-future/ai-agent into 2026-06-16 2026-07-22 15:17:05 +08:00
19904408334 031819f634 fix: 修复输出项编号为全局连续编号及日期格式 2026-07-22 15:16:41 +08:00
admin 20723f58ce ng代理minio支持websocket 2026-07-21 17:40:41 +08:00
19904408334 08251d9a73 fix: 修复并发取消逻辑与HTTP请求超时及响应解析 2026-07-09 13:44:13 +08:00
19904408334 cc29dd21e4 feat: 添加 ModelOutputFields 字段 2026-07-08 13:35:50 +08:00
19904408334 14c88efe79 feat: 新增会话与结果管理功能及扩展字段
重构执行列表树状结构,扁平化日期节点下的输出项,新增会话列表查询、结果删除与软删除标记,引入扩展字段与模板配置,升级依赖版本。
2026-07-08 09:22:54 +08:00
19904408334 4ccb81b34a feat: 添加表单视频参数校验 2026-07-04 16:39:59 +08:00
19904408334 e548f20b6e feat: 添加执行费用统计及token信息记录
在流程执行和节点执行中新增 TotalFee 和 TokenInfo 字段,用于记录每次模型调用的详细计费数据;流程执行完成时汇总所有节点的 token 消耗和费用;重构 token 更新逻辑,从响应字段提取改为使用回调返回的 billingData;优化 URL 后缀提取方法以正确处理带查询参数的链接。
2026-07-01 19:34:31 +08:00
19904408334 f5be9d8a40 feat: 支持视频模型首帧参数及顺序执行逻辑
添加 FirstFrame 字段到模型信息结构体,重构视频节点数据转换处理,支持从上游节点获取视频配置并区分输出字段。实现视频结果重新上传至 OSS,修正字段匹配逻辑为精确匹配,并基于 return_last_frame 参数优化顺序执行中的首帧与内容传递。
2026-06-30 15:15:02 +08:00
19904408334 b83b18a5c8 fix: 修复字幕分段逻辑及输出规范拼接
重写 splitTextByPunct 函数,使用正则索引切分并保留标点,避免原 Split 方法产生空串及奇偶位错位问题。优化 BuildSubtitles 中分段与 Word 的匹配逻辑,使用 strings.Builder 提升拼接性能,并修正字幕文本剔除标点。调整输出字段的 Prompt 拼接格式,改为直接输出模板结构规范。
2026-06-29 13:11:54 +08:00
admin fa2a07600b common版本更新 2026-06-25 17:05:14 +08:00
19904408334 0c50178f0a fix: 修复字幕节点数据转换逻辑 2026-06-25 14:10:21 +08:00
19904408334 8209219f92 fix: 修复单视频生成时无需拼接的逻辑 2026-06-25 10:54:40 +08:00
19904408334 48f3b46929 Merge branch '2026-06-16' of http://116.204.74.41:3000/red-future/ai-agent into 2026-06-16 2026-06-24 16:39:26 +08:00
19904408334 c19bd17632 fix: 修复遍历中删除map键及替换视频时长字段 2026-06-24 16:38:43 +08:00
admin bf5d88158e common版本回退 2026-06-24 10:56:52 +08:00
admin 8a639d3d09 common版本升级 2026-06-23 18:32:01 +08:00
admin d6e6d0b403 common版本更新 2026-06-23 16:56:33 +08:00
19904408334 ffba7ff726 chore: 升级依赖至最新版本 2026-06-23 16:54:39 +08:00
19904408334 6942e05f1a fix: 补充Lambda节点HTTP请求空结果错误提示 2026-06-23 16:40:42 +08:00
19904408334 36f9871deb fix: 替换CDN前缀为minio配置
修复视频URL拼接时硬编码CDN地址的问题,改为从配置中动态读取minio前缀。
2026-06-23 11:00:22 +08:00
19904408334 91a6436ad7 fix: 替换CDN前缀为minio配置
修复视频URL拼接时硬编码CDN地址的问题,改为从配置中动态读取minio前缀。
2026-06-23 10:42:58 +08:00
19904408334 e4bd08458a fix: 区分CDN与Minio内网地址配置 2026-06-23 08:59:02 +08:00
admin 243c9e291f 封版 2026-06-22 11:47:37 +08:00
admin 84592a09df 封版 2026-06-22 11:40:03 +08:00
19904408334 44702d32ad build: 升级 common 依赖至 v0.0.24 2026-06-22 11:36:52 +08:00
19904408334 29557bdc4d feat: 将用户提示词作为独立字段传递
重构用户提示词的传递方式,将其从嵌入到 userForm 中改为 ComposeCallbackReq 的独立字段 UserPrompt
2026-06-18 17:25:58 +08:00
19904408334 4df45069e0 feat: 重构节点上下文与并发执行逻辑
重构GetNodeContextContent返回类型为切片,修复并发竞态与协程泄漏问题;回调改用OSS文件获取结果;调整节点输入上传时序
2026-06-18 14:24:48 +08:00
19904408334 fba7d032ae feat: 将节点输入输出参数上传至OSS存储 2026-06-12 11:02:36 +08:00
19904408334 ddc4e0be63 fix: 移除已消费的文件URL并优化递归收集逻辑 2026-06-11 16:49:11 +08:00
19904408334 73dd18baf4 build: 更新common模块依赖仓库地址 2026-06-11 09:16:53 +08:00
19904408334 03c95c3601 feat: 新增主动拉取与多类型回调功能
- 新增 ActivePull 实体、DAO、DTO 及 Service,支持主动拉取任务管理
- 新增 ComposeCallback、VideoCallback、HttpNodeCallback 多类型回调接口
- FlowExecution 增加 NodeGroupId 和 TotalTokens 字段,支持节点组追踪与 Token 统计
- ExecutedNodes 结构由字符串列表改为包含执行状态的节点对象列表
- 重构回调通知机制,统一 Notify 函数调用
- 优化输出项类型判断逻辑,新增文件类型标识
2026-06-10 14:23:55 +08:00
admin ab3a2d967e ci/cd调整 2026-06-08 13:39:20 +08:00
19904408334 34c5eeaf63 feat: 查询流程用户列表时自动填充创建人 2026-05-20 13:11:59 +08:00
19904408334 de55c16734 refactor: 重构工作流执行逻辑并提取单模型调用 2026-05-18 18:58:46 +08:00
19904408334 1fbed2febd refactor: 重构工作流执行逻辑并提取单模型调用 2026-05-18 18:58:04 +08:00
19904408334 d5206df131 feat: 添加对话式工作流节点执行与结果合并逻辑 2026-05-15 18:30:44 +08:00
19904408334 ffba1f30ec Merge branch 'dev' of http://116.204.74.41:3000/red-future/ai-agent into dev 2026-05-15 09:37:54 +08:00
19904408334 b1ee117f6c feat: 添加工作流取消与临时文件管理功能
- 新增临时文件(FileTemp)的实体、DAO和DTO,支持文件临时存储与批量操作
- 实现工作流执行取消功能,使用sync.Map管理context.CancelFunc,支持按会话取消运行中的流程
- 将流程执行状态"暂停"变更为"取消",并处理取消导致的错误
- 引入IsDialogue标识区分对话模式,调整判断/文案/图片节点的表单数据组装逻辑
- 重构ComposeMessagesReq,使用BuildType替代IsBuild和ModelTypeId
- 优化HTML内容提取逻辑,修复文案纯文本与图片URL的标签过滤及标签命名
- 在结果汇总节点中使用事务更新执行状态并批量保存输出文件记录
2026-05-15 09:37:23 +08:00
admin d4614e3cc9 封版 2026-05-14 16:34:07 +08:00
19904408334 2c3cbab11d feat: 管理员列表接口直接返回模板数据
移除调试打印语句
2026-05-12 15:22:14 +08:00
19904408334 68576b2132 feat: 添加流程执行查询接口及模型类型与节点名称优化 2026-05-12 14:31:51 +08:00
19904408334 7c26914353 feat: 新增工作流执行模块
新增流程执行记录的实体、DTO、DAO、控制器和服务层,支持工作流的执行、回调及结果树状列表查询;同时更新服务名称为 ai-agent。
2026-05-12 13:34:28 +08:00
19904408334 2aec7fe30f feat: 新增创作作品管理模块及相关配置 2026-05-08 11:51:10 +08:00
19904408334 74ede5bc0f feat: 新增创作作品管理模块及相关配置 2026-05-08 11:35:15 +08:00
admin ed333dd15c 删除model-gateway模块 2026-04-28 09:51:56 +08:00
admin 1e693e32ca ai-agent增加model-gateway模块 2026-04-27 18:05:54 +08:00
admin 2600ed20ac 引包目录名调整 2026-04-27 14:02:49 +08:00
admin 11bf15e72b 引包目录名调整 2026-04-27 14:02:43 +08:00
WangLiZhao 6ba2262a17 refactor(main): 为控制器包添加别名以避免命名冲突 2026-04-27 12:11:08 +08:00
WangLiZhao 151e0bcd7d refactor: 移除router包并将路由注册直接移至main.go 2026-04-27 12:05:22 +08:00
WangLiZhao 8679832019 refactor: 重构项目结构,分离路由注册与主函数入口
- 将路由注册逻辑从主函数移至独立的 router 包
- 更新 main.go 以调用新的路由注册函数
- 删除旧的 digitalhuman/main.go 文件
- 简化常量文件中的注释
2026-04-27 11:52:03 +08:00
WangLiZhao f66925eb73 数字人项目迁移 2026-04-27 11:24:13 +08:00
WangLiZhao 995e038541 数字人项目迁移 2026-04-27 11:07:21 +08:00
admin ba360bc89b 初始化项目 2026-04-27 10:54:32 +08:00
123 changed files with 14806 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.git
+37
View File
@@ -0,0 +1,37 @@
# ============================================================
# ai-agent dev 分支自动发布
# 触发:push dev 分支
# 流程:构建镜像 -> push 本地仓库(127.0.0.1:5000) -> 部署到 dev 宿主机本地 k3s
# 前置:runner 挂载 dev k3s kubeconfig 到 /root/.kube/k3s-dev.yaml
# ============================================================
name: Deploy ai-agent to dev k3s
on:
push:
branches: [ 2026-06-16 ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 不依赖 actions/checkout(从 github.com 拉 action 源码,国内网络不稳定),
# 直接 clone 本地 Gitea,用 Actions 注入的 GITHUB_TOKEN 认证
- name: 拉取代码
run: |
git clone --branch dev http://x-access-token:${GITHUB_TOKEN}@127.0.0.1:3000/red-future/ai-agent.git .
git checkout ${GITHUB_SHA}
- name: 构建并推送镜像
run: |
TAG=dev-${GITHUB_SHA::8}
docker build -t 127.0.0.1:5000/ai-agent:${TAG} .
docker push 127.0.0.1:5000/ai-agent:${TAG}
- name: 部署到 dev k3s
env:
KUBECONFIG: /root/.kube/k3s-dev.yaml
run: |
kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f deploy/k8s/ai-agent.yaml
kubectl set image deployment/ai-agent ai-agent=127.0.0.1:5000/ai-agent:dev-${GITHUB_SHA::8} -n dev
kubectl rollout status deployment/ai-agent -n dev --timeout=5m
+25
View File
@@ -0,0 +1,25 @@
# 阶段1: 构建
FROM golang:alpine AS builder
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk add --no-cache git ca-certificates tzdata
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn,direct
ENV CGO_ENABLED=0
ENV GOTOOLCHAIN=auto
WORKDIR /build
COPY . .
RUN go mod download && go mod tidy
RUN go build -ldflags="-s -w" -o main ./main.go
EXPOSE 3005
CMD ["./main"]
+71
View File
@@ -0,0 +1,71 @@
server:
address: ":3005"
name: "ai-agent"
workerId: 1 # 雪花算法worker ID
database:
default:
- type: "pgsql"
host: "192.168.0.83"
port: "15432"
user: "postgres"
pass: "Bjang09@686^*^"
name: "digital-human"
prefix: "digital_human_" # (可选)表名前缀
role: "master" # (可选)数据库主从角色(master/slave),默认为master。如果不使用应用主从机制请不配置或留空即可。
debug: true # (可选)开启调试模式
dryRun: false # (可选)ORM空跑(只读不写)
charset: "utf8" # (可选)数据库编码(如: utf8mb4/utf8/gbk/gb2312),一般设置为utf8mb4。默认为utf8。
timezone: "Asia/Shanghai" # (可选)时区配置,例如:Local
maxIdle: 5 # (可选)连接池最大闲置的连接数(默认10)
maxOpen: 20 # (可选)连接池最大打开的连接数(默认无限制)
maxLifetime: "30s" # (可选)连接对象可重复使用的时间长度(默认30秒)
maxIdleConnTime: "30s" # (可选,v2.10新增)连接池中空闲连接的最大生存时间(默认30秒)。可以通过配置文件或SetConnMaxIdleTime方法设置,避免长时间空闲连接占用资源。
createdAt: "created_at" # (可选)自动创建时间字段名称
updatedAt: "updated_at" # (可选)自动更新时间字段名称
deletedAt: "deleted_at" # (可选)软删除时间字段名称
timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性,为true时CreatedAt/UpdatedAt/DeletedAt都将失效
black_deacon:
- type: "pgsql"
host: "192.168.0.83"
port: "15432"
user: "postgres"
pass: "Bjang09@686^*^"
name: "black-deacon"
prefix: "black_deacon_" # (可选)表名前缀
role: "master"
debug: true # (可选)开启调试模式
dryRun: false # (可选)ORM空跑(只读不写)
charset: "utf8" # (可选)数据库编码(如: utf8mb4/utf8/gbk/gb2312),一般设置为utf8mb4。默认为utf8。
timezone: "Asia/Shanghai" # (可选)时区配置,例如:Local
maxIdle: 5 # (可选)连接池最大闲置的连接数(默认10)
maxOpen: 20 # (可选)连接池最大打开的连接数(默认无限制)
maxLifetime: "30s" # (可选)连接对象可重复使用的时间长度(默认30秒)
maxIdleConnTime: "30s" # (可选,v2.10新增)连接池中空闲连接的最大生存时间(默认30秒)。可以通过配置文件或SetConnMaxIdleTime方法设置,避免长时间空闲连接占用资源。
createdAt: "created_at" # (可选)自动创建时间字段名称
updatedAt: "updated_at" # (可选)自动更新时间字段名称
deletedAt: "deleted_at" # (可选)软删除时间字段名称
timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性,为true时CreatedAt/UpdatedAt/DeletedAt都将失效
redis:
default:
address: 192.168.0.83:6379
db: 0
idleTimeout: "60s" #连接最大空闲时间,使用时间字符串例如30s/1m/1d
maxConnLifetime: "90s" #连接最长存活时间,使用时间字符串例如30s/1m/1d
waitTimeout: "60s" #等待连接池连接的超时时间,使用时间字符串例如30s/1m/1d
dialTimeout: "30s" #TCP连接的超时时间,使用时间字符串例如30s/1m/1d
readTimeout: "30s" #TCP的Read操作超时时间,使用时间字符串例如30s/1m/1d
writeTimeout: "30s" #TCP的Write操作超时时间,使用时间字符串例如30s/1m/1d
maxActive: 100
consul:
address: 192.168.0.83:8500
jaeger:
addr: 192.168.0.83:4318
# 文件上传服务地址,cdn访问地址
filePrefix: "http://cdn.redpowerfuture.com"
# 文件上传服务地址,minio内网访问地址
minioPrefix: "http://192.168.0.83:9000"
@@ -0,0 +1,39 @@
package account
import "github.com/gogf/gf/v2/util/gconv"
var (
PlatformXHS = newPlatform(gconv.PtrString("xiaohongshu"), "小红书")
PlatformDY = newPlatform(gconv.PtrString("douyin"), "抖音")
PlatformKS = newPlatform(gconv.PtrString("kuaishou"), "快手")
)
type Platform *string
type platform struct {
code Platform
desc string
}
func (s platform) Code() Platform {
return s.code
}
func (s platform) Desc() string {
return s.desc
}
func newPlatform(code Platform, desc string) platform {
return platform{code: code, desc: desc}
}
func GetDescByCode(code Platform) string {
switch *code {
case *PlatformXHS.Code():
return PlatformXHS.Desc()
case *PlatformDY.Code():
return PlatformDY.Desc()
case *PlatformKS.Code():
return PlatformKS.Desc()
}
return "未知平台"
}
+26
View File
@@ -0,0 +1,26 @@
package account
import "github.com/gogf/gf/v2/util/gconv"
var (
StatusDisable = newStatus(gconv.PtrInt8(0), "disable")
StatusEnable = newStatus(gconv.PtrInt8(1), "enable")
)
type Status *int8
type status struct {
code Status
desc string
}
func (s status) Code() Status {
return s.code
}
func (s status) Desc() string {
return s.desc
}
func newStatus(code Status, desc string) status {
return status{code: code, desc: desc}
}
+5
View File
@@ -0,0 +1,5 @@
package consts
const ReClick = "操作过于频繁,请稍后再试。"
const NoRow = "未找到可用数据。"
const GenerateQrCodeFail = "生成二维码失败。"
+16
View File
@@ -0,0 +1,16 @@
package public
const GmqMsgPluginsName = "gmq_msg"
const (
AccountMsgKey = "account:%s:%s:%s"
AccountDialogHistoryKey = "account:dialog:history:%s"
AccountGreetingOptionsKey = "account:greeting:options:%s"
)
const (
AccountFollowupTopic = "account:followup:stream" // 请求 Stream 键名(与发消息的key一致)
AccountFollowupConsumer = "account-followup-consumer" // 消费者名称(唯一标识)
AccountFollowupCount = 1 // 批处理大小(每次读取1条)
AccountFollowupAck = false // ACK是否自动确认(true自动确认,false不确认)
)
@@ -0,0 +1,18 @@
package public
// 欢迎语
const (
GreetingBegin = "您好,很高兴为您服务!请问有什么可以帮您?"
GreetingBetween = "💗回复数字就好~"
GreetingEnd = "🌟也可直接点击下方咨询专业老师~"
)
// 追问
const (
SceneOpeningRemark = "宝子,刚才给您发的信息您有看到吗?有任何问题都能直接问我,加微信也能更方便沟通~"
SceneDialog = "看您暂时没回复,是不是还有什么疑问?加微信我详细给您说明~"
SceneCardSend = "宝子,加上没~要及时加哦,不然卡片容易失效哒✨"
)
// 对话超时时间
const DialogTimeout = 10
@@ -0,0 +1,8 @@
package public
// sql 数据库表名
const (
TableNameAccount = "account"
TableNameAccountUserDialog = "account_user_dialog"
TableNameScriptedSpeech = "scripted_speech"
)
+4
View File
@@ -0,0 +1,4 @@
package consts
const QrCodeCount = "qrCodeCount:order:%s"
const QrCode = "qrCode:order:%s"
@@ -0,0 +1,39 @@
package scriptedSpeech
import "github.com/gogf/gf/v2/util/gconv"
var (
SceneTypeOpeningRemark = newSceneType(gconv.PtrInt8(1), "开场白无回应")
SceneTypeDialog = newSceneType(gconv.PtrInt8(2), "对话中途无回应")
SceneTypeCardSend = newSceneType(gconv.PtrInt8(3), "卡片发送后无回应")
)
type SceneType *int8
type sceneType struct {
code SceneType
desc string
}
func (s sceneType) Code() SceneType {
return s.code
}
func (s sceneType) Desc() string {
return s.desc
}
func newSceneType(code SceneType, desc string) sceneType {
return sceneType{code: code, desc: desc}
}
func GetDescByCode(code SceneType) string {
switch *code {
case *SceneTypeOpeningRemark.Code():
return SceneTypeOpeningRemark.Desc()
case *SceneTypeDialog.Code():
return SceneTypeDialog.Desc()
case *SceneTypeCardSend.Code():
return SceneTypeCardSend.Desc()
}
return "未知场景类型"
}
+46
View File
@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-agent
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: ai-agent
template:
metadata:
labels:
app: ai-agent
spec:
containers:
- name: ai-agent
image: 127.0.0.1:5000/ai-agent:dev-latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3005
env:
- name: TZ
value: Asia/Shanghai
readinessProbe:
tcpSocket:
port: 3005
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 3005
initialDelaySeconds: 30
periodSeconds: 15
---
apiVersion: v1
kind: Service
metadata:
name: ai-agent
namespace: dev
spec:
selector:
app: ai-agent
ports:
- port: 3005
targetPort: 3005
+195
View File
@@ -0,0 +1,195 @@
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
var text = "欢迎使用红动未来数字人服务平台,我们将为您提供最优质的AI数字人解决方案。"
type TTSCommonResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Text string `json:"text"`
Audio string `json:"audio"`
}
func main() {
// 获取当前工作目录
outputDir, err := os.Getwd()
if err != nil {
fmt.Printf("获取当前目录失败: %v\n", err)
os.Exit(1)
}
// 查找项目根目录(向上查找包含 go.mod 的目录)
outputDir = findProjectRoot(outputDir)
// 验证根目录是否正确(检查是否有 go.mod)
if _, err := os.Stat(outputDir + "/go.mod"); err != nil {
fmt.Printf("未找到项目根目录,当前目录: %s\n", outputDir)
os.Exit(1)
}
fmt.Println("=================== TTS测试开始 ===================")
fmt.Printf("输出目录: %s\n", outputDir)
fmt.Printf("随机文本: %s\n", text)
fmt.Printf("请求URL: http://127.0.0.1:8000/tts\n")
// 创建带超时的 HTTP 客户端(120秒超时)
client := &http.Client{
Timeout: 120 * time.Second,
}
resp, err := client.Post("http://127.0.0.1:8000/tts", "application/json", bytes.NewBufferString(fmt.Sprintf(`"%s"`, text)))
if err != nil {
fmt.Printf("请求失败: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
// 打印响应头
fmt.Printf("Content-Type: %s\n", resp.Header.Get("Content-Type"))
fmt.Printf("Content-Length: %s\n", resp.Header.Get("Content-Length"))
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("状态码: %d, 响应大小: %d字节\n", resp.StatusCode, len(body))
// 打印响应内容的前200字节(用于调试)
if len(body) > 0 {
previewLen := minInt(200, len(body))
fmt.Printf("响应内容预览(前%d字节): ", previewLen)
if len(body) >= 4 && string(body[:4]) == "RIFF" {
// WAV文件头
fmt.Printf("WAV文件格式 (RIFF...)\n")
} else if len(body) >= 3 && string(body[:3]) == "ID3" {
// MP3 ID3标签
fmt.Printf("MP3 ID3格式\n")
} else if len(body) >= 2 && body[0] == 0xFF && (body[1]&0xE0) == 0xE0 {
// MP3帧同步
fmt.Printf("MP3帧格式\n")
} else {
// 可能是JSON或其他格式
fmt.Printf("%s\n", string(body[:previewLen]))
}
} else {
fmt.Printf("响应内容为空!\n")
os.Exit(1)
}
// 尝试解析JSON响应(包含base64音频)
var commonResp TTSCommonResponse
var audioData []byte
var ext string
if json.Unmarshal(body, &commonResp) == nil && commonResp.Audio != "" && commonResp.Audio != "base64_placeholder" {
fmt.Printf("检测到JSON响应,code=%d, msg=%s\n", commonResp.Code, commonResp.Msg)
fmt.Printf("Audio字段长度: %d 字符\n", len(commonResp.Audio))
// 检查是否成功
if commonResp.Code != 0 {
fmt.Printf("TTS服务返回错误: %s\n", commonResp.Msg)
os.Exit(1)
}
// 解码base64音频数据
decoded, err := base64.StdEncoding.DecodeString(commonResp.Audio)
if err != nil {
fmt.Printf("base64解码失败: %v\n", err)
os.Exit(1)
}
if len(decoded) == 0 {
fmt.Printf("解码后数据为空!\n")
os.Exit(1)
}
audioData = decoded
fmt.Printf("解码后音频数据大小: %d 字节\n", len(audioData))
// 根据解码后的音频数据格式决定扩展名
if len(audioData) >= 4 && string(audioData[:4]) == "RIFF" {
ext = ".wav"
fmt.Printf("检测到WAV格式\n")
} else if len(audioData) >= 3 && string(audioData[:3]) == "ID3" || (len(audioData) >= 2 && audioData[0] == 0xFF && (audioData[1]&0xE0) == 0xE0) {
ext = ".mp3"
fmt.Printf("检测到MP3格式\n")
} else {
ext = ".wav" // 默认wav
fmt.Printf("未知格式,默认保存为 .wav\n")
}
} else {
// 直接是二进制音频数据
audioData = body
// 根据音频数据格式决定扩展名
if len(audioData) >= 4 && string(audioData[:4]) == "RIFF" {
ext = ".wav"
} else if len(audioData) >= 3 && string(audioData[:3]) == "ID3" || (len(audioData) >= 2 && audioData[0] == 0xFF && (audioData[1]&0xE0) == 0xE0) {
ext = ".mp3"
} else {
ext = ".wav" // 默认wav
}
}
// 保存音频文件
filename := fmt.Sprintf("%s/tts_output_%d%s", outputDir, time.Now().Unix(), ext)
if err = os.WriteFile(filename, audioData, 0644); err != nil {
fmt.Printf("写文件失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("音频已保存: %s (%d字节)\n", filename, len(audioData))
fmt.Println("=================== TTS测试成功 ===================")
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
// findProjectRoot 查找项目根目录(包含 go.mod 的目录)
func findProjectRoot(startDir string) string {
dir := startDir
for {
// 检查当前目录是否有 go.mod
if _, err := os.Stat(dir + "/go.mod"); err == nil {
return dir
}
// 如果已经是根目录或无法继续向上查找,返回当前目录
parentDir := dir[:maxInt(0, len(dir)-len("/"+getLastPathSegment(dir)))]
if parentDir == dir || parentDir == "" {
return startDir
}
dir = parentDir
}
}
// getLastPathSegment 获取路径的最后一部分
func getLastPathSegment(path string) string {
if idx := strings.LastIndex(path, "/"); idx != -1 {
return path[idx+1:]
}
return path
}
+52
View File
@@ -0,0 +1,52 @@
package consts
// Age 年龄段类型
type Age string
// 年龄段常量
const (
AgeChild Age = "child" // 儿童
AgeTeenager Age = "teenager" // 青少年
AgeYoung Age = "young" // 青年
AgeMiddle Age = "middle" // 中年
AgeSenior Age = "senior" // 老年
AgeUnlimited Age = "unlimited" // 不限
)
// GetAgeText 获取年龄段文本
func GetAgeText(age string) string {
switch age {
case string(AgeChild):
return "儿童"
case string(AgeTeenager):
return "青少年"
case string(AgeYoung):
return "青年"
case string(AgeMiddle):
return "中年"
case string(AgeSenior):
return "老年"
case string(AgeUnlimited):
return "不限"
default:
return "未知"
}
}
// GetAllAgeKeyValue 获取所有年龄段选项
func GetAllAgeKeyValue() []AgeKeyValue {
return []AgeKeyValue{
{Value: string(AgeChild), Label: "儿童"},
{Value: string(AgeTeenager), Label: "青少年"},
{Value: string(AgeYoung), Label: "青年"},
{Value: string(AgeMiddle), Label: "中年"},
{Value: string(AgeSenior), Label: "老年"},
{Value: string(AgeUnlimited), Label: "不限"},
}
}
// AgeKeyValue 年龄段键值对
type AgeKeyValue struct {
Value string `json:"value"`
Label string `json:"label"`
}
+40
View File
@@ -0,0 +1,40 @@
package consts
// AudioStatus 音频状态类型
type AudioStatus int
// 音频状态常量
const (
AudioStatusGenerating AudioStatus = 0 // 生成中
AudioStatusSuccess AudioStatus = 1 // 成功
AudioStatusFailed AudioStatus = 2 // 失败
)
// GetAudioStatusText 获取音频状态文本
func GetAudioStatusText(status int) string {
switch status {
case int(AudioStatusGenerating):
return "生成中"
case int(AudioStatusSuccess):
return "成功"
case int(AudioStatusFailed):
return "失败"
default:
return "未知"
}
}
// GetAllAudioStatusKeyValue 获取所有音频状态选项
func GetAllAudioStatusKeyValue() []AudioStatusKeyValue {
return []AudioStatusKeyValue{
{Value: int(AudioStatusGenerating), Label: "生成中"},
{Value: int(AudioStatusSuccess), Label: "成功"},
{Value: int(AudioStatusFailed), Label: "失败"},
}
}
// AudioStatusKeyValue 音频状态键值对
type AudioStatusKeyValue struct {
Value int `json:"value"`
Label string `json:"label"`
}
+8
View File
@@ -0,0 +1,8 @@
package consts
// MongoDB集合名称常量
const (
DigitalHumanCollection = "digital_human" // 数字人形象集合
AudioCollection = "audio" // 音频集合
VideoCollection = "video" // 视频集合
)
@@ -0,0 +1,10 @@
package consts
// CustomVoiceStatus 自定义音色状态类型
type CustomVoiceStatus int
const (
CustomVoiceStatusGenerating CustomVoiceStatus = 0 // 生成中
CustomVoiceStatusSuccess CustomVoiceStatus = 1 // 成功
CustomVoiceStatusFailed CustomVoiceStatus = 2 // 失败
)
@@ -0,0 +1,55 @@
package consts
// DigitalHumanStatus 数字人状态类型
type DigitalHumanStatus int
// 数字人状态常量
const (
DigitalHumanStatusInactive DigitalHumanStatus = 0 // 停用
DigitalHumanStatusActive DigitalHumanStatus = 1 // 启用
)
// GetDigitalHumanStatusText 获取数字人状态文本
func GetDigitalHumanStatusText(status int) string {
switch status {
case int(DigitalHumanStatusInactive):
return "停用"
case int(DigitalHumanStatusActive):
return "启用"
default:
return "未知"
}
}
// GetAllDigitalHumanStatusKeyValue 获取所有数字人状态选项
func GetAllDigitalHumanStatusKeyValue() []DigitalHumanStatusKeyValue {
return []DigitalHumanStatusKeyValue{
{Value: int(DigitalHumanStatusInactive), Label: "停用"},
{Value: int(DigitalHumanStatusActive), Label: "启用"},
}
}
// DigitalHumanStatusKeyValue 数字人状态键值对
type DigitalHumanStatusKeyValue struct {
Value int `json:"value"`
Label string `json:"label"`
}
// GetStatusText 获取状态文本(向后兼容)
func GetStatusText(status int) string {
return GetDigitalHumanStatusText(status)
}
// GetAllStatusKeyValue 获取所有状态选项(向后兼容)
func GetAllStatusKeyValue() []StatusKeyValue {
return []StatusKeyValue{
{Value: int(DigitalHumanStatusInactive), Label: "停用"},
{Value: int(DigitalHumanStatusActive), Label: "启用"},
}
}
// StatusKeyValue 状态键值对(向后兼容)
type StatusKeyValue struct {
Value int `json:"value"`
Label string `json:"label"`
}
+40
View File
@@ -0,0 +1,40 @@
package consts
// Gender 性别类型
type Gender string
// 性别常量
const (
GenderMale Gender = "male" // 男
GenderFemale Gender = "female" // 女
GenderOther Gender = "other" // 其他
)
// GetGenderText 获取性别文本
func GetGenderText(gender string) string {
switch gender {
case string(GenderMale):
return "男"
case string(GenderFemale):
return "女"
case string(GenderOther):
return "其他"
default:
return "未知"
}
}
// GetAllGenderKeyValue 获取所有性别选项
func GetAllGenderKeyValue() []GenderKeyValue {
return []GenderKeyValue{
{Value: string(GenderMale), Label: "男"},
{Value: string(GenderFemale), Label: "女"},
{Value: string(GenderOther), Label: "其他"},
}
}
// GenderKeyValue 性别键值对
type GenderKeyValue struct {
Value string `json:"value"`
Label string `json:"label"`
}
@@ -0,0 +1,7 @@
package public
const (
ModelNameCustomVoice = "qwen3-tts-customvoice" // 预设音频
ModelNameVoiceDesign = "qwen3-tts-voicedesign" // 设计音频
ModelNameBase = "qwen3-tts-base" // 克隆音频
)
@@ -0,0 +1,9 @@
package public
const (
TableNameAudio = "digital_human_audio"
TableNameCustomVoice = "digital_human_custom_voice"
TableNameAsyncTaskRef = "digital_human_async_task_ref"
TableNameVideo = "digital_human_video"
TableNameDigitalHuman = "digital_human"
)
+51
View File
@@ -0,0 +1,51 @@
package consts
// Resolution 视频分辨率
type Resolution string
const (
Resolution480P Resolution = "480p" // 标清
Resolution720P Resolution = "720p" // 高清
Resolution1080P Resolution = "1080p" // 全高清
Resolution2K Resolution = "2k" // 2K超清
Resolution4K Resolution = "4k" // 4K超高清
Resolution8K Resolution = "8k" // 8K超高清
)
// Text 获取分辨率文本描述
func (r Resolution) Text() string {
switch r {
case Resolution480P:
return "标清 (480p)"
case Resolution720P:
return "高清 (720p)"
case Resolution1080P:
return "全高清 (1080p)"
case Resolution2K:
return "2K超清 (1440p)"
case Resolution4K:
return "4K超高清 (2160p)"
case Resolution8K:
return "8K超高清 (4320p)"
default:
return string(r)
}
}
// ResolutionKeyValue 分辨率键值对(用于前端选项)
type ResolutionKeyValue struct {
Key string `json:"key"`
Value string `json:"value"`
}
// GetResolutionOptions 获取所有分辨率选项
func GetResolutionOptions() []ResolutionKeyValue {
return []ResolutionKeyValue{
{Key: string(Resolution480P), Value: Resolution480P.Text()},
{Key: string(Resolution720P), Value: Resolution720P.Text()},
{Key: string(Resolution1080P), Value: Resolution1080P.Text()},
{Key: string(Resolution2K), Value: Resolution2K.Text()},
{Key: string(Resolution4K), Value: Resolution4K.Text()},
{Key: string(Resolution8K), Value: Resolution8K.Text()},
}
}
+60
View File
@@ -0,0 +1,60 @@
package consts
// Style 风格类型
type Style string
// 风格常量
const (
StyleBusiness Style = "business" // 商务
StyleCasual Style = "casual" // 休闲
StyleFormal Style = "formal" // 正式
StyleCreative Style = "creative" // 创意
StyleElegant Style = "elegant" // 优雅
StyleFriendly Style = "friendly" // 友好
StyleProfessional Style = "professional" // 专业
StyleUnlimited Style = "unlimited" // 不限
)
// GetStyleText 获取风格文本
func GetStyleText(style string) string {
switch style {
case string(StyleBusiness):
return "商务"
case string(StyleCasual):
return "休闲"
case string(StyleFormal):
return "正式"
case string(StyleCreative):
return "创意"
case string(StyleElegant):
return "优雅"
case string(StyleFriendly):
return "友好"
case string(StyleProfessional):
return "专业"
case string(StyleUnlimited):
return "不限"
default:
return "未知"
}
}
// GetAllStyleKeyValue 获取所有风格选项
func GetAllStyleKeyValue() []StyleKeyValue {
return []StyleKeyValue{
{Value: string(StyleBusiness), Label: "商务"},
{Value: string(StyleCasual), Label: "休闲"},
{Value: string(StyleFormal), Label: "正式"},
{Value: string(StyleCreative), Label: "创意"},
{Value: string(StyleElegant), Label: "优雅"},
{Value: string(StyleFriendly), Label: "友好"},
{Value: string(StyleProfessional), Label: "专业"},
{Value: string(StyleUnlimited), Label: "不限"},
}
}
// StyleKeyValue 风格键值对
type StyleKeyValue struct {
Value string `json:"value"`
Label string `json:"label"`
}
+40
View File
@@ -0,0 +1,40 @@
package consts
// VideoStatus 视频状态类型
type VideoStatus int
// 视频生成状态常量
const (
VideoStatusGenerating VideoStatus = 0 // 生成中
VideoStatusSuccess VideoStatus = 1 // 成功
VideoStatusFailed VideoStatus = 2 // 失败
)
// GetVideoStatusText 获取视频状态文本
func GetVideoStatusText(status int) string {
switch status {
case int(VideoStatusGenerating):
return "生成中"
case int(VideoStatusSuccess):
return "成功"
case int(VideoStatusFailed):
return "失败"
default:
return "未知"
}
}
// GetAllVideoStatusKeyValue 获取所有视频状态选项
func GetAllVideoStatusKeyValue() []VideoStatusKeyValue {
return []VideoStatusKeyValue{
{Value: int(VideoStatusGenerating), Label: "生成中"},
{Value: int(VideoStatusSuccess), Label: "成功"},
{Value: int(VideoStatusFailed), Label: "失败"},
}
}
// VideoStatusKeyValue 视频状态键值对
type VideoStatusKeyValue struct {
Value int `json:"value"`
Label string `json:"label"`
}
@@ -0,0 +1,30 @@
package controller
import (
"context"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/service"
"github.com/gogf/gf/v2/util/gconv"
)
type asyncTask struct{}
// AsyncTask 异步任务同步控制器(供定时任务服务调用)
var AsyncTask = new(asyncTask)
// SyncAsyncTasks 扫描待处理任务并同步状态/转移结果
func (c *asyncTask) SyncAsyncTasks(ctx context.Context, req *dto.SyncAsyncTasksReq) (res *dto.SyncAsyncTasksRes, err error) {
// 从上下文获取用户信息(gfdb Hook 会自动填充)
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.AsyncTask.Sync(ctx, req)
}
@@ -0,0 +1,113 @@
package controller
import (
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/service"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/util/gconv"
)
type audio struct{}
// Audio 音频控制器
var Audio = new(audio)
// CreateAudio 创建音频
func (c *audio) CreateAudio(ctx context.Context, req *dto.CreateAudioReq) (res *dto.CreateAudioRes, err error) {
// 从上下文获取用户信息(gfdb Hook 会自动填充)
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.Audio.Create(ctx, req)
}
// ListAudio 获取音频列表
func (c *audio) ListAudio(ctx context.Context, req *dto.ListAudioReq) (res *dto.ListAudioRes, err error) {
// 从上下文获取用户信息
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.Audio.List(ctx, req)
}
// GetAudio 获取音频详情
func (c *audio) GetAudio(ctx context.Context, req *dto.GetAudioReq) (res *dto.GetAudioRes, err error) {
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.Audio.GetOne(ctx, req.ID)
}
// UpdateAudio 更新音频
func (c *audio) UpdateAudio(ctx context.Context, req *dto.UpdateAudioReq) (res *beans.ResponseEmpty, err error) {
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
err = service.Audio.Update(ctx, req)
return
}
// DeleteAudio 删除音频
func (c *audio) DeleteAudio(ctx context.Context, req *dto.DeleteAudioReq) (res *beans.ResponseEmpty, err error) {
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
err = service.Audio.Delete(ctx, req.ID)
return
}
// GenerateAudio 重新生成音频
func (c *audio) GenerateAudio(ctx context.Context, req *dto.GenerateAudioReq) (res *dto.GenerateAudioRes, err error) {
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.Audio.Generate(ctx, req)
}
// TTS 文本转语音
func (c *audio) TTS(ctx context.Context, req *dto.TTSReq) (res *dto.TTSRes, err error) {
return service.Audio.TTS(ctx, req)
}
// GetStatusOptions 获取状态选项
func (c *audio) GetStatusOptions(ctx context.Context, req *dto.GetAudioStatusOptionsReq) (res *dto.GetAudioStatusOptionsRes, err error) {
return service.Audio.GetStatusOptions(ctx, req)
}
@@ -0,0 +1,61 @@
package controller
import (
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/service"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/util/gconv"
)
type customVoice struct{}
// CustomVoice 自定义音色控制器
var CustomVoice = new(customVoice)
// CreateCustomVoice 创建自定义音色
func (c *customVoice) CreateCustomVoice(ctx context.Context, req *dto.CreateCustomVoiceReq) (res *dto.CreateCustomVoiceRes, err error) {
// 从上下文获取用户信息
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.CustomVoice.CreateCustomVoice(ctx, req)
}
// ListCustomVoices 获取自定义音色列表
func (c *customVoice) ListCustomVoices(ctx context.Context, req *dto.ListCustomVoiceReq) (res *dto.ListCustomVoiceRes, err error) {
// 从上下文获取用户信息
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
return service.CustomVoice.ListCustomVoices(ctx, req)
}
// DeleteCustomVoice 删除自定义音色
func (c *customVoice) DeleteCustomVoice(ctx context.Context, req *dto.DeleteCustomVoiceReq) (res *beans.ResponseEmpty, err error) {
// 从上下文获取用户信息
if ctx.Value("userId") == nil {
ctx = context.WithValue(ctx, "userId", gconv.String(1))
}
if ctx.Value("userName") == nil {
ctx = context.WithValue(ctx, "userName", "admin")
}
if ctx.Value("tenantId") == nil {
ctx = context.WithValue(ctx, "tenantId", uint64(1))
}
err = service.CustomVoice.DeleteCustomVoice(ctx, req)
return
}
@@ -0,0 +1,67 @@
package controller
import (
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/service"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type digitalhuman struct{}
// DigitalHuman 数字人形象控制器
var DigitalHuman = new(digitalhuman)
// CreateDigitalHuman 创建数字人形象
func (c *digitalhuman) CreateDigitalHuman(ctx context.Context, req *dto.CreateDigitalHumanReq) (res *dto.CreateDigitalHumanRes, err error) {
return service.DigitalHuman.Create(ctx, req)
}
// ListDigitalHuman 获取数字人形象列表
func (c *digitalhuman) ListDigitalHuman(ctx context.Context, req *dto.ListDigitalHumanReq) (res *dto.ListDigitalHumanRes, err error) {
return service.DigitalHuman.List(ctx, req)
}
// GetDigitalHuman 获取数字人形象详情
func (c *digitalhuman) GetDigitalHuman(ctx context.Context, req *dto.GetDigitalHumanReq) (res *dto.GetDigitalHumanRes, err error) {
return service.DigitalHuman.GetOne(ctx, req.ID)
}
// UpdateDigitalHuman 更新数字人形象
func (c *digitalhuman) UpdateDigitalHuman(ctx context.Context, req *dto.UpdateDigitalHumanReq) (res *beans.ResponseEmpty, err error) {
err = service.DigitalHuman.Update(ctx, req)
return
}
// UpdateDigitalHumanStatus 更新数字人形象状态
func (c *digitalhuman) UpdateDigitalHumanStatus(ctx context.Context, req *dto.UpdateDigitalHumanStatusReq) (res *beans.ResponseEmpty, err error) {
err = service.DigitalHuman.UpdateStatus(ctx, req.ID, req.Status)
return
}
// DeleteDigitalHuman 删除数字人形象
func (c *digitalhuman) DeleteDigitalHuman(ctx context.Context, req *dto.DeleteDigitalHumanReq) (res *beans.ResponseEmpty, err error) {
err = service.DigitalHuman.Delete(ctx, req.ID)
return
}
// GetDigitalHumanStatusOptions 获取数字人状态选项
func (c *digitalhuman) GetDigitalHumanStatusOptions(ctx context.Context, req *dto.GetDigitalHumanStatusOptionsReq) (res *dto.GetDigitalHumanStatusOptionsRes, err error) {
return service.DigitalHuman.GetStatusOptions(ctx, req)
}
// GetGenderOptions 获取性别选项
func (c *digitalhuman) GetGenderOptions(ctx context.Context, req *dto.GetGenderOptionsReq) (res *dto.GetGenderOptionsRes, err error) {
return service.DigitalHuman.GetGenderOptions(ctx, req)
}
// GetAgeOptions 获取年龄段选项
func (c *digitalhuman) GetAgeOptions(ctx context.Context, req *dto.GetAgeOptionsReq) (res *dto.GetAgeOptionsRes, err error) {
return service.DigitalHuman.GetAgeOptions(ctx, req)
}
// GetStyleOptions 获取风格选项
func (c *digitalhuman) GetStyleOptions(ctx context.Context, req *dto.GetStyleOptionsReq) (res *dto.GetStyleOptionsRes, err error) {
return service.DigitalHuman.GetStyleOptions(ctx, req)
}
@@ -0,0 +1,56 @@
package controller
import (
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/service"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type video struct{}
// Video 视频控制器
var Video = new(video)
// CreateVideo 创建视频
func (c *video) CreateVideo(ctx context.Context, req *dto.CreateVideoReq) (res *dto.CreateVideoRes, err error) {
return service.Video.Create(ctx, req)
}
// ListVideo 获取视频列表
func (c *video) ListVideo(ctx context.Context, req *dto.ListVideoReq) (res *dto.ListVideoRes, err error) {
return service.Video.List(ctx, req)
}
// GetVideo 获取视频详情
func (c *video) GetVideo(ctx context.Context, req *dto.GetVideoReq) (res *dto.GetVideoRes, err error) {
return service.Video.GetOne(ctx, req.ID)
}
// UpdateVideo 更新视频
func (c *video) UpdateVideo(ctx context.Context, req *dto.UpdateVideoReq) (res *beans.ResponseEmpty, err error) {
err = service.Video.Update(ctx, req)
return
}
// DeleteVideo 删除视频
func (c *video) DeleteVideo(ctx context.Context, req *dto.DeleteVideoReq) (res *beans.ResponseEmpty, err error) {
err = service.Video.Delete(ctx, req.ID)
return
}
// GenerateVideo 生成视频
func (c *video) GenerateVideo(ctx context.Context, req *dto.GenerateVideoReq) (res *dto.GenerateVideoRes, err error) {
return service.Video.Generate(ctx, req)
}
// GetVideoStatusOptions 获取视频状态选项
func (c *video) GetVideoStatusOptions(ctx context.Context, req *dto.GetVideoStatusOptionsReq) (res *dto.GetVideoStatusOptionsRes, err error) {
return service.Video.GetStatusOptions(ctx, req)
}
// GetResolutionOptions 获取分辨率选项
func (c *video) GetResolutionOptions(ctx context.Context, req *dto.GetResolutionOptionsReq) (res *dto.GetResolutionOptionsRes, err error) {
return service.Video.GetResolutionOptions(ctx, req)
}
+69
View File
@@ -0,0 +1,69 @@
package dao
import (
"context"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/model/entity"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
)
var AsyncTaskRef = &asyncTaskRefDao{}
type asyncTaskRefDao struct{}
func (d *asyncTaskRefDao) Insert(ctx context.Context, ref *entity.AsyncTaskRef) (id int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAsyncTaskRef).Data(ref).Insert()
if err != nil {
return 0, err
}
return r.LastInsertId()
}
// ListPending 列出待处理任务绑定(state=0/1)
func (d *asyncTaskRefDao) ListPending(ctx context.Context, limit int) (list []*entity.AsyncTaskRef, err error) {
if limit <= 0 {
limit = 200
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAsyncTaskRef).
Where("deleted_at IS NULL").
// 业务侧只维护三态:生成中/成功/失败;绑定表仅用于“待同步列表”
WhereIn(entity.AsyncTaskRefCol.State, []int{0, 1}).
OrderAsc(entity.AsyncTaskRefCol.UpdatedAt).
Limit(limit).
All()
if err != nil {
return nil, err
}
err = r.Structs(&list)
return
}
func (d *asyncTaskRefDao) UpdateByTaskID(ctx context.Context, taskID string, data gdb.Map) (rows int64, err error) {
// 触发 gfdb 的 updateHook 自动填充 updater,需要显式带 updater 字段
data[entity.AsyncTaskRefCol.Updater] = ""
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAsyncTaskRef).
Where(entity.AsyncTaskRefCol.TaskID, taskID).
Data(data).
Update()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
func (d *asyncTaskRefDao) GetByTaskID(ctx context.Context, taskID string) (ref *entity.AsyncTaskRef, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAsyncTaskRef).
Where(entity.AsyncTaskRefCol.TaskID, taskID).
One()
if err != nil {
return nil, err
}
if r.IsEmpty() {
return nil, nil
}
err = r.Struct(&ref)
return
}
+128
View File
@@ -0,0 +1,128 @@
package dao
import (
"ai-agent/digital-human/consts"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var Audio = &audio{}
type audio struct{}
// Insert 插入音频
func (d *audio) Insert(ctx context.Context, req *dto.CreateAudioReq) (id int64, err error) {
var res *entity.Audio
if err = gconv.Struct(req, &res); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).Data(&res).Insert()
if err != nil {
return
}
return r.LastInsertId()
}
// Update 更新音频
func (d *audio) Update(ctx context.Context, id int64, updateData *entity.Audio) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).Where(entity.AudioCol.Id, id).Data(&updateData).Update()
if err != nil {
return
}
return r.RowsAffected()
}
// UpdateStatus 更新音频状态
func (d *audio) UpdateStatus(ctx context.Context, id int64, status consts.AudioStatus, errorMsg string, audioURL string, duration int, externalID string) (rows int64, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).Where(entity.AudioCol.Id, id)
updateData := gdb.Map{
entity.AudioCol.Status: int(status),
}
if errorMsg != "" {
updateData[entity.AudioCol.ErrorMsg] = errorMsg
}
if audioURL != "" {
updateData[entity.AudioCol.AudioURL] = audioURL
}
if duration > 0 {
updateData[entity.AudioCol.Duration] = duration
}
if externalID != "" {
updateData[entity.AudioCol.ExternalID] = externalID
}
r, err := model.Data(&updateData).Update()
if err != nil {
return
}
return r.RowsAffected()
}
// Delete 删除音频
func (d *audio) Delete(ctx context.Context, id int64) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).Where(entity.AudioCol.Id, id).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
// GetOne 获取单个音频
func (d *audio) GetOne(ctx context.Context, id int64) (audio *entity.Audio, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).Where(entity.AudioCol.Id, id).One()
if err != nil {
return
}
err = r.Struct(&audio)
return
}
// List 获取音频列表
func (d *audio) List(ctx context.Context, req *dto.ListAudioReq) (res []*entity.Audio, total int, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameAudio).OmitEmpty()
// 构建查询过滤条件
if req.Status != consts.AudioStatusGenerating && req.Status != consts.AudioStatusSuccess && req.Status != consts.AudioStatusFailed {
// 不添加状态过滤
} else {
model = model.Where(entity.AudioCol.Status+" = ?", req.Status)
}
if !g.IsEmpty(req.Keyword) {
like := "%" + req.Keyword + "%"
model = model.Where(
"("+entity.AudioCol.Name+
" LIKE ? OR "+entity.AudioCol.Description+
" LIKE ? OR "+entity.AudioCol.ScriptText+
" LIKE ?)",
like, like, like,
)
}
model = model.OrderDesc(entity.AudioCol.CreatedAt)
if req.Page != nil {
if req.Page.PageNum <= 0 {
req.Page.PageNum = 1
}
if req.Page.PageSize <= 0 {
req.Page.PageSize = 10
}
model = 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
}
+135
View File
@@ -0,0 +1,135 @@
package dao
import (
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/util/gconv"
)
// CustomVoice 自定义音色数据访问层
var CustomVoice = &customVoice{}
type customVoice struct{}
// Insert 插入自定义音色
func (d *customVoice) Insert(ctx context.Context, req *dto.CreateCustomVoiceReq) (id int64, err error) {
var result *entity.CustomVoice
if err = gconv.Struct(req, &result); err != nil {
return
}
// 初始状态:生成中
result.Status = 0
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).Data(&result).Insert()
if err != nil {
return
}
return r.LastInsertId()
}
func (d *customVoice) UpdateReferenceAudio(ctx context.Context, id int64, referenceAudio []byte) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).
Where(entity.CustomVoiceCol.Id, id).
Data(gdb.Map{
entity.CustomVoiceCol.ReferenceAudio: referenceAudio,
}).
Update()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *customVoice) UpdateDescription(ctx context.Context, id int64, description string) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).
Where(entity.CustomVoiceCol.Id, id).
Data(gdb.Map{
entity.CustomVoiceCol.Description: description,
}).
Update()
if err != nil {
return
}
return r.RowsAffected()
}
// UpdateStatus 更新自定义音色状态/结果
func (d *customVoice) UpdateStatus(ctx context.Context, id int64, status int, errorMsg string, ossFile string) (rows int64, err error) {
data := gdb.Map{
entity.CustomVoiceCol.Status: status,
}
if errorMsg != "" {
data[entity.CustomVoiceCol.ErrorMsg] = errorMsg
}
if ossFile != "" {
data[entity.CustomVoiceCol.OssFile] = ossFile
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).
Where(entity.CustomVoiceCol.Id, id).
Data(data).
Update()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
// Delete 删除自定义音色
func (d *customVoice) Delete(ctx context.Context, id int64) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).Where(entity.CustomVoiceCol.Id, id).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
// GetOne 获取单个自定义音色
func (d *customVoice) GetOne(ctx context.Context, id int64) (customVoice *entity.CustomVoice, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice).Where(entity.CustomVoiceCol.Id, id).One()
if err != nil {
return
}
err = r.Struct(&customVoice)
return
}
// List 获取自定义音色列表
func (d *customVoice) List(ctx context.Context, req *dto.ListCustomVoiceReq) (res []*entity.CustomVoice, total int, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameCustomVoice)
// 处理分页
if req.Page == nil {
req.Page = &beans.Page{PageNum: 1, PageSize: 20}
}
r, total, err := model.OrderDesc(entity.CustomVoiceCol.CreatedAt).Page(int(req.Page.PageNum), int(req.Page.PageSize)).AllAndCount(false)
if err != nil {
return
}
err = r.Structs(&res)
return
}
// GetCustomVoiceItem 转换为 DTO 列表项
func (d *customVoice) GetCustomVoiceItem(entity *entity.CustomVoice) *dto.CustomVoiceItem {
item := &dto.CustomVoiceItem{
ID: gconv.String(entity.Id),
Name: entity.Name,
Description: entity.Description,
Status: entity.Status,
ErrorMsg: entity.ErrorMsg,
OssFile: entity.OssFile,
}
if entity.CreatedAt != nil {
item.CreatedAt = entity.CreatedAt
}
if entity.UpdatedAt != nil {
item.UpdatedAt = entity.UpdatedAt
}
return item
}
+125
View File
@@ -0,0 +1,125 @@
package dao
import (
"ai-agent/digital-human/consts"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
// DigitalHuman 数字人形象数据
var DigitalHuman = &digitalHuman{}
type digitalHuman struct{}
// Insert 插入数字人形象
func (d *digitalHuman) Insert(ctx context.Context, req *dto.CreateDigitalHumanReq) (ids []any, err error) {
var result entity.DigitalHuman
if err = gconv.Struct(req, &result); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Data(&result).Insert()
if err != nil {
return
}
lastInsertId, err := r.LastInsertId()
if err != nil {
return
}
ids = []any{lastInsertId}
return
}
// Update 更新数字人形象
func (d *digitalHuman) Update(ctx context.Context, id int64, updateData *entity.DigitalHuman) (err error) {
_, err = gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Data(updateData).Where("id = ?", id).Update()
return
}
// UpdateStatus 更新数字人形象状态
func (d *digitalHuman) UpdateStatus(ctx context.Context, id int64, status consts.DigitalHumanStatus) (rows int64, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Where("id = ?", id)
r, err := model.Data(g.Map{"status": status}).Update()
if err != nil {
return
}
return r.RowsAffected()
}
// Delete 删除数字人形象
func (d *digitalHuman) Delete(ctx context.Context, id int64) (err error) {
_, err = gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Where("id = ?", id).Delete()
return
}
// GetOne 获取单个数字人形象
func (d *digitalHuman) GetOne(ctx context.Context, id int64) (digitalHuman *entity.DigitalHuman, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Where("id = ?", id).One()
if err != nil {
return
}
err = r.Struct(&digitalHuman)
return
}
// List 获取数字人形象列表
func (d *digitalHuman) List(ctx context.Context, req *dto.ListDigitalHumanReq) (res []entity.DigitalHuman, total int64, err error) {
model := d.buildListFilter(ctx, req)
var totalCount int
totalCount, err = model.Count()
if err != nil {
return
}
total = gconv.Int64(totalCount)
if req.Page != nil {
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
}
r, err := model.OrderDesc("id").All()
if err != nil {
return
}
err = r.Structs(&res)
return
}
// buildListFilter 构建列表查询的过滤条件
func (d *digitalHuman) buildListFilter(ctx context.Context, req *dto.ListDigitalHumanReq) *gdb.Model {
model := gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).OmitEmpty()
// 状态字段允许查询所有状态值,包括0(停用),所以需要特别处理
if req.Status != consts.DigitalHumanStatusInactive && req.Status != consts.DigitalHumanStatusActive {
// 如果状态不是有效值之一,则不添加状态过滤条件
} else {
model = model.Where("status = ?", req.Status)
}
if !g.IsEmpty(req.Gender) {
model = model.Where("gender = ?", req.Gender)
}
if !g.IsEmpty(req.Style) {
model = model.Where("style = ?", req.Style)
}
if !g.IsEmpty(req.Keyword) {
model = model.Where("name LIKE ? OR description LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
}
return model
}
// Count 计数
func (d *digitalHuman) Count(ctx context.Context, req *dto.CreateDigitalHumanReq) (count int64, err error) {
var totalCount int
totalCount, err = gfdb.DB(ctx).Model(ctx, public.TableNameDigitalHuman).Where("name = ?", req.Name).Count()
if err != nil {
return
}
count = gconv.Int64(totalCount)
return
}
+131
View File
@@ -0,0 +1,131 @@
package dao
import (
"ai-agent/digital-human/consts"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
// Video 视频数据
var Video = &video{}
type video struct{}
// Insert 插入视频
func (d *video) Insert(ctx context.Context, req *dto.CreateVideoReq) (ids []any, err error) {
var result entity.Video
if err = gconv.Struct(req, &result); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameVideo).Data(&result).Insert()
if err != nil {
return
}
lastInsertId, err := r.LastInsertId()
if err != nil {
return
}
ids = []any{lastInsertId}
return
}
// Update 更新视频
func (d *video) Update(ctx context.Context, id int64, updateData *entity.Video) (err error) {
_, err = gfdb.DB(ctx).Model(ctx, public.TableNameVideo).Data(updateData).Where("id = ?", id).Update()
return
}
// Delete 删除视频
func (d *video) Delete(ctx context.Context, id int64) (err error) {
_, err = gfdb.DB(ctx).Model(ctx, public.TableNameVideo).Where("id = ?", id).Delete()
return
}
// GetOne 获取单个视频
func (d *video) GetOne(ctx context.Context, id int64) (video *entity.Video, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameVideo).Where("id = ?", id).One()
if err != nil {
return
}
err = r.Struct(&video)
return
}
// List 获取视频列表
func (d *video) List(ctx context.Context, req *dto.ListVideoReq) (res []entity.Video, total int64, err error) {
model := d.buildListFilter(ctx, req)
var totalCount int
totalCount, err = model.Count()
if err != nil {
return
}
total = gconv.Int64(totalCount)
if req.Page != nil {
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
}
r, err := model.OrderDesc("id").All()
if err != nil {
return
}
err = r.Structs(&res)
return
}
// buildListFilter 构建列表查询的过滤条件
func (d *video) buildListFilter(ctx context.Context, req *dto.ListVideoReq) *gdb.Model {
model := gfdb.DB(ctx).Model(ctx, public.TableNameVideo).OmitEmpty()
// 状态字段允许查询所有状态值,包括0(生成中),所以需要特别处理
if req.Status != consts.VideoStatusGenerating && req.Status != consts.VideoStatusSuccess && req.Status != consts.VideoStatusFailed {
// 如果状态不是有效值之一,则不添加状态过滤条件
} else {
model = model.Where("status = ?", req.Status)
}
if !g.IsEmpty(req.DigitalHumanID) {
model = model.Where("digitalHumanId = ?", req.DigitalHumanID)
}
if !g.IsEmpty(req.Keyword) {
model = model.Where("name LIKE ? OR description LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
}
return model
}
// UpdateStatus 更新视频状态
func (d *video) UpdateStatus(ctx context.Context, id int64, status consts.VideoStatus, errorMsg string, videoURL string, duration int, thumbnailURL string, externalTaskID string) (rows int64, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameVideo).Where("id = ?", id)
updateData := gdb.Map{
"status": status,
}
if errorMsg != "" {
updateData["errorMsg"] = errorMsg
}
if videoURL != "" {
updateData["videoUrl"] = videoURL
}
if duration > 0 {
updateData["duration"] = duration
}
if thumbnailURL != "" {
updateData["thumbnailUrl"] = thumbnailURL
}
if externalTaskID != "" {
updateData["externalTaskId"] = externalTaskID
}
r, err := model.Data(updateData).Update()
if err != nil {
return
}
return r.RowsAffected()
}
@@ -0,0 +1,24 @@
package dto
import "github.com/gogf/gf/v2/frame/g"
// SyncAsyncTasksReq 定时任务/业务侧轮询使用:同步处理中间件任务状态并转移结果
type SyncAsyncTasksReq struct {
g.Meta `path:"/syncAsyncTasks" method:"post" tags:"异步任务" summary:"同步异步任务" dc:"扫描本服务待处理任务(task_id),批量查询 model-asynch 状态,成功则转移OSS并更新业务表"`
Limit int `p:"limit" json:"limit" dc:"单次处理上限(默认200"`
}
type SyncAsyncTasksItem struct {
TaskID string `json:"taskId"`
State int `json:"state"`
TableName string `json:"tableName"`
BizID string `json:"bizId"`
OssFile string `json:"ossFile"`
ErrorMsg string `json:"errorMsg"`
}
type SyncAsyncTasksRes struct {
Total int `json:"total" dc:"本次扫描到的任务数"`
Handled int `json:"handled" dc:"本次成功处理数(含更新状态/转移)"`
List []SyncAsyncTasksItem `json:"list" dc:"任务明细"`
}
+151
View File
@@ -0,0 +1,151 @@
package dto
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// CreateAudioReq 创建音频请求
type CreateAudioReq struct {
g.Meta `path:"/createAudio" method:"post" tags:"音频管理" summary:"创建音频" dc:"创建新的音频"`
// 基础信息
Name string `json:"name" v:"required" dc:"音频名称"`
Description string `json:"description" dc:"音频描述"`
ScriptText string `json:"scriptText" v:"required" dc:"话术文本"`
// 音色配置
Voice string `json:"voice" dc:"音色:serena/vivian/uncle_fu/ryan/aiden/ono_anna/sohee/eric/dylan,默认serena"`
VoiceType string `json:"voiceType" dc:"音色类型:preset/custom(预设/自定义),默认preset"`
CustomVoice string `json:"customVoice" dc:"自定义音色ID(用于声音克隆),voiceType=custom时必填"`
}
// CreateAudioRes 创建音频响应
type CreateAudioRes struct {
Id int64 `json:"id" dc:"音频ID"`
}
// ListAudioReq 获取音频列表请求
type ListAudioReq struct {
g.Meta `path:"/listAudios" method:"get" tags:"音频管理" summary:"获取音频列表" dc:"分页查询音频列表,支持多条件筛选"`
*beans.Page
Status consts.AudioStatus `json:"status" dc:"状态:0生成中/1成功/2失败"`
Keyword string `json:"keyword" dc:"关键词搜索"`
}
// ListAudioRes 获取音频列表响应
type ListAudioRes struct {
List []*AudioListItem `json:"list" dc:"音频列表"`
Total int64 `json:"total" dc:"总数"`
}
// AudioListItem 音频列表项
type AudioListItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ScriptText string `json:"scriptText"`
AudioURL string `json:"audioUrl"`
Status consts.AudioStatus `json:"status"`
ErrorMsg string `json:"errorMsg"`
Duration int `json:"duration"`
ExternalID string `json:"externalId"`
Voice string `json:"voice"`
VoiceType string `json:"voiceType"`
CustomVoice string `json:"customVoice"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// GetAudioReq 获取音频详情请求
type GetAudioReq struct {
g.Meta `path:"/getAudio" method:"get" tags:"音频管理" summary:"获取音频详情" dc:"获取音频详情"`
ID int64 `json:"id" v:"required" dc:"音频ID"`
}
// GetAudioRes 获取音频详情响应
type GetAudioRes struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ScriptText string `json:"scriptText"`
AudioURL string `json:"audioUrl"`
Status consts.AudioStatus `json:"status"`
ErrorMsg string `json:"errorMsg"`
Duration int `json:"duration"`
ExternalID string `json:"externalId"`
Voice string `json:"voice"`
VoiceType string `json:"voiceType"`
CustomVoice string `json:"customVoice"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// UpdateAudioReq 更新音频请求
type UpdateAudioReq struct {
g.Meta `path:"/updateAudio" method:"put" tags:"音频管理" summary:"更新音频" dc:"更新音频信息"`
ID int64 `json:"id" v:"required" dc:"音频ID"`
// 基础信息
Name string `json:"name" dc:"音频名称"`
Description string `json:"description" dc:"音频描述"`
// 音色配置
Voice string `json:"voice" dc:"音色"`
VoiceType string `json:"voiceType" dc:"音色类型"`
CustomVoice string `json:"customVoice" dc:"自定义音色ID"`
}
// DeleteAudioReq 删除音频请求
type DeleteAudioReq struct {
g.Meta `path:"/deleteAudio" method:"delete" tags:"音频管理" summary:"删除音频" dc:"删除音频"`
ID int64 `json:"id" v:"required" dc:"音频ID"`
}
// GenerateAudioReq 生成音频请求
type GenerateAudioReq struct {
g.Meta `path:"/generateAudio" method:"post" tags:"音频管理" summary:"生成音频" dc:"根据话术文本生成音频"`
ID int64 `json:"id" v:"required" dc:"音频ID"`
}
// GenerateAudioRes 生成音频响应
type GenerateAudioRes struct {
TaskID string `json:"taskId" dc:"任务ID"`
}
// TTSReq 文本转语音请求
type TTSReq struct {
g.Meta `path:"/tts" method:"post" tags:"音频管理" summary:"文本转语音" dc:"将文本转换为语音,直接返回MP3二进制数据"`
Text string `json:"text" v:"required" dc:"要转换的文本内容"`
Voice string `json:"voice" dc:"音色:默认default"`
Speed int `json:"speed" dc:"语速:0.5-2.0,默认1.0"`
}
// TTSRes 文本转语音响应(返回二进制MP3数据)
type TTSRes struct {
g.Meta `mime:"audio/mpeg"`
Data []byte `json:"-" dc:"MP3音频二进制数据"`
}
// GetAudioStatusOptionsReq 获取音频状态选项请求
type GetAudioStatusOptionsReq struct {
g.Meta `path:"/getAudioStatusOptions" method:"get" tags:"音频管理" summary:"获取音频状态选项" dc:"获取所有音频状态的选项列表"`
}
// GetAudioStatusOptionsRes 获取音频状态选项响应
type GetAudioStatusOptionsRes struct {
Options []consts.AudioStatusKeyValue `json:"options" dc:"音频状态选项列表"`
}
// Qwen3TTSRequest Qwen3-TTS 请求结构
type Qwen3TTSRequest struct {
Text string `json:"text"`
Speaker string `json:"speaker"` // 预设音色名或克隆音色ID
VoiceID string `json:"voice_id,omitempty"` // 克隆音色ID(可选)
}
// Qwen3TTSResponse Qwen3-TTS 响应结构
type Qwen3TTSResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Audio string `json:"audio"` // base64编码的音频数据
}
@@ -0,0 +1,67 @@
package dto
import (
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// CreateCustomVoiceReq 创建自定义音色请求
type CreateCustomVoiceReq struct {
g.Meta `path:"/createCustomVoice" method:"post" tags:"自定义音色" summary:"创建自定义音色" dc:"上传参考音频创建自定义音色"`
VoiceType string `json:"voiceType" v:"required" dc:"音色类型 design/clone(设计/克隆)"`
Name string `json:"name" v:"required" dc:"音色名称"`
Description string `json:"description" dc:"音色描述"`
Text string `json:"text" dc:"参考文本"`
// 参考音频
ReferenceAudio []byte `json:"referenceAudio" dc:"参考音频数据(base64编码的WAV文件,voiceType=clone 时必填)"`
}
// CreateCustomVoiceRes 创建自定义音色响应
type CreateCustomVoiceRes struct {
VoiceID string `json:"voiceId" dc:"音色ID"`
}
// ListCustomVoiceReq 获取自定义音色列表请求
type ListCustomVoiceReq struct {
g.Meta `path:"/listCustomVoices" method:"get" tags:"自定义音色" summary:"获取自定义音色列表" dc:"分页查询自定义音色列表"`
*beans.Page
}
// ListCustomVoiceRes 获取自定义音色列表响应
type ListCustomVoiceRes struct {
List []*CustomVoiceItem `json:"list" dc:"自定义音色列表"`
Total int64 `json:"total" dc:"总数"`
}
// CustomVoiceItem 自定义音色列表项
type CustomVoiceItem struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status int `json:"status" dc:"状态:0生成中/1成功/2失败"`
ErrorMsg string `json:"errorMsg" dc:"错误信息"`
OssFile string `json:"ossFile" dc:"结果文件OSS地址"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// DeleteCustomVoiceReq 删除自定义音色请求
type DeleteCustomVoiceReq struct {
g.Meta `path:"/deleteCustomVoice" method:"delete" tags:"自定义音色" summary:"删除自定义音色" dc:"删除自定义音色"`
VoiceID string `json:"voiceId" v:"required" dc:"音色ID"`
}
// Qwen3VoiceCloneRequest Qwen3-TTS 音色克隆请求
type Qwen3VoiceCloneRequest struct {
Name string `json:"name"` // 音色名称
Audio string `json:"audio"` // base64编码的参考音频
Text string `json:"text"` // 参考文本(可选)
}
// Qwen3VoiceCloneResponse Qwen3-TTS 音色克隆响应
type Qwen3VoiceCloneResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
VoiceID string `json:"voice_id"` // 克隆后的音色ID
}
+158
View File
@@ -0,0 +1,158 @@
package dto
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// CreateDigitalHumanReq 创建数字人形象请求
type CreateDigitalHumanReq struct {
g.Meta `path:"/createDigitalHuman" method:"post" tags:"数字人形象管理" summary:"创建数字人形象" dc:"创建新的数字人形象"`
// 基础信息
Name string `json:"name" v:"required" dc:"数字人名称"`
Description string `json:"description" dc:"数字人描述"`
ImageURL string `json:"imageUrl" dc:"形象图片URL"`
VideoURL string `json:"videoUrl" dc:"形象视频URL"`
Status consts.DigitalHumanStatus `json:"status" dc:"状态:1启用/0停用" d:"1"`
Tags []string `json:"tags" dc:"标签"`
Gender consts.Gender `json:"gender" dc:"性别"`
Age consts.Age `json:"age" dc:"年龄段"`
Style consts.Style `json:"style" dc:"风格:商务/休闲/正式等"`
ExternalID string `json:"externalId" dc:"外部系统ID"`
Metadata []map[string]interface{} `json:"metadata" dc:"动态元数据"`
}
// CreateDigitalHumanRes 创建数字人形象响应
type CreateDigitalHumanRes struct {
Id int64 `json:"id" dc:"数字人形象ID"`
}
// ListDigitalHumanReq 获取数字人形象列表请求
type ListDigitalHumanReq struct {
g.Meta `path:"/listDigitalHumans" method:"get" tags:"数字人形象管理" summary:"获取数字人形象列表" dc:"分页查询数字人形象列表,支持多条件筛选"`
*beans.Page
Status consts.DigitalHumanStatus `json:"status" dc:"状态"`
Gender consts.Gender `json:"gender" dc:"性别"`
Style consts.Style `json:"style" dc:"风格"`
Keyword string `json:"keyword" dc:"关键词搜索"`
}
// ListDigitalHumanRes 获取数字人形象列表响应
type ListDigitalHumanRes struct {
List []*DigitalHumanListItem `json:"list" dc:"数字人形象列表"`
Total int64 `json:"total" dc:"总数"`
}
// DigitalHumanListItem 数字人形象列表项
type DigitalHumanListItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ImageURL string `json:"imageUrl"`
VideoURL string `json:"videoUrl"`
Status consts.DigitalHumanStatus `json:"status"`
Tags []string `json:"tags"`
Gender consts.Gender `json:"gender"`
Age consts.Age `json:"age"`
Style consts.Style `json:"style"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// GetDigitalHumanReq 获取数字人形象详情请求
type GetDigitalHumanReq struct {
g.Meta `path:"/getDigitalHuman" method:"get" tags:"数字人形象管理" summary:"获取数字人形象详情" dc:"获取数字人形象详情"`
ID int64 `json:"id" v:"required" dc:"数字人形象ID"`
}
// GetDigitalHumanRes 获取数字人形象详情响应
type GetDigitalHumanRes struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ImageURL string `json:"imageUrl"`
VideoURL string `json:"videoUrl"`
Status consts.DigitalHumanStatus `json:"status"`
Tags []string `json:"tags"`
Gender consts.Gender `json:"gender"`
Age consts.Age `json:"age"`
Style consts.Style `json:"style"`
ExternalID string `json:"externalId"`
Metadata []map[string]interface{} `json:"metadata"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// UpdateDigitalHumanReq 更新数字人形象请求
type UpdateDigitalHumanReq struct {
g.Meta `path:"/updateDigitalHuman" method:"put" tags:"数字人形象管理" summary:"更新数字人形象" dc:"更新数字人形象信息"`
ID int64 `json:"id" v:"required" dc:"数字人形象ID"`
// 基础信息
Name string `json:"name" dc:"数字人名称"`
Description string `json:"description" dc:"数字人描述"`
ImageURL string `json:"imageUrl" dc:"形象图片URL"`
VideoURL string `json:"videoUrl" dc:"形象视频URL"`
Status consts.DigitalHumanStatus `json:"status" dc:"状态:1启用/0停用"`
Tags []string `json:"tags" dc:"标签"`
Gender consts.Gender `json:"gender" dc:"性别"`
Age consts.Age `json:"age" dc:"年龄段"`
Style consts.Style `json:"style" dc:"风格:商务/休闲/正式等"`
ExternalID string `json:"externalId" dc:"外部系统ID"`
Metadata []map[string]interface{} `json:"metadata" dc:"动态元数据"`
}
// UpdateDigitalHumanStatusReq 更新数字人形象状态请求
type UpdateDigitalHumanStatusReq struct {
g.Meta `path:"/updateDigitalHumanStatus" method:"put" tags:"数字人形象管理" summary:"更新数字人形象状态" dc:"更新数字人形象状态"`
ID int64 `json:"id" v:"required" dc:"数字人形象ID"`
Status consts.DigitalHumanStatus `json:"status" v:"required|in:1,0" dc:"状态:1启用/0停用"`
}
// DeleteDigitalHumanReq 删除数字人形象请求
type DeleteDigitalHumanReq struct {
g.Meta `path:"/deleteDigitalHuman" method:"delete" tags:"数字人形象管理" summary:"删除数字人形象" dc:"删除数字人形象"`
ID int64 `json:"id" v:"required" dc:"数字人形象ID"`
}
// GetDigitalHumanStatusOptionsReq 获取数字人状态选项请求
type GetDigitalHumanStatusOptionsReq struct {
g.Meta `path:"/getDigitalHumanStatusOptions" method:"get" tags:"数字人形象管理" summary:"获取数字人状态选项" dc:"获取所有数字人状态的选项列表"`
}
// GetDigitalHumanStatusOptionsRes 获取数字人状态选项响应
type GetDigitalHumanStatusOptionsRes struct {
Options []consts.StatusKeyValue `json:"options" dc:"状态选项列表"`
}
// GetGenderOptionsReq 获取性别选项请求
type GetGenderOptionsReq struct {
g.Meta `path:"/getGenderOptions" method:"get" tags:"数字人形象管理" summary:"获取性别选项" dc:"获取所有性别选项列表"`
}
// GetGenderOptionsRes 获取性别选项响应
type GetGenderOptionsRes struct {
Options []consts.GenderKeyValue `json:"options" dc:"性别选项列表"`
}
// GetAgeOptionsReq 获取年龄段选项请求
type GetAgeOptionsReq struct {
g.Meta `path:"/getAgeOptions" method:"get" tags:"数字人形象管理" summary:"获取年龄段选项" dc:"获取所有年龄段选项列表"`
}
// GetAgeOptionsRes 获取年龄段选项响应
type GetAgeOptionsRes struct {
Options []consts.AgeKeyValue `json:"options" dc:"年龄段选项列表"`
}
// GetStyleOptionsReq 获取风格选项请求
type GetStyleOptionsReq struct {
g.Meta `path:"/getStyleOptions" method:"get" tags:"数字人形象管理" summary:"获取风格选项" dc:"获取所有风格选项列表"`
}
// GetStyleOptionsRes 获取风格选项响应
type GetStyleOptionsRes struct {
Options []consts.StyleKeyValue `json:"options" dc:"风格选项列表"`
}
+131
View File
@@ -0,0 +1,131 @@
package dto
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// CreateVideoReq 创建视频请求
type CreateVideoReq struct {
g.Meta `path:"/createVideo" method:"post" tags:"视频管理" summary:"创建视频" dc:"创建新的视频任务"`
// 基础信息
Name string `json:"name" v:"required" dc:"视频名称"`
Description string `json:"description" dc:"视频描述"`
DigitalHumanID int64 `json:"digitalHumanId" v:"required" dc:"数字人形象ID"`
AudioID int64 `json:"audioId" v:"required" dc:"音频ID"`
Resolution consts.Resolution `json:"resolution" dc:"分辨率:480p/720p/1080p/2k/4k/8k"`
}
// CreateVideoRes 创建视频响应
type CreateVideoRes struct {
Id int64 `json:"id" dc:"视频ID"`
}
// ListVideoReq 获取视频列表请求
type ListVideoReq struct {
g.Meta `path:"/listVideos" method:"get" tags:"视频管理" summary:"获取视频列表" dc:"分页查询视频列表,支持多条件筛选"`
*beans.Page
Status consts.VideoStatus `json:"status" dc:"状态:0生成中/1成功/2失败"`
DigitalHumanID int64 `json:"digitalHumanId" dc:"数字人形象ID"`
Keyword string `json:"keyword" dc:"关键词搜索"`
}
// ListVideoRes 获取视频列表响应
type ListVideoRes struct {
List []*VideoListItem `json:"list" dc:"视频列表"`
Total int64 `json:"total" dc:"总数"`
}
// VideoListItem 视频列表项
type VideoListItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
DigitalHumanID int64 `json:"digitalHumanId"`
DigitalHumanName string `json:"digitalHumanName"`
AudioID int64 `json:"audioId"`
AudioURL string `json:"audioUrl"`
VideoURL string `json:"videoUrl"`
Status consts.VideoStatus `json:"status"`
ErrorMsg string `json:"errorMsg"`
Duration int `json:"duration"`
Resolution consts.Resolution `json:"resolution"`
ThumbnailURL string `json:"thumbnailUrl"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// GetVideoReq 获取视频详情请求
type GetVideoReq struct {
g.Meta `path:"/getVideo" method:"get" tags:"视频管理" summary:"获取视频详情" dc:"获取视频详情"`
ID int64 `json:"id" v:"required" dc:"视频ID"`
}
// GetVideoRes 获取视频详情响应
type GetVideoRes struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
DigitalHumanID int64 `json:"digitalHumanId"`
DigitalHumanName string `json:"digitalHumanName"`
AudioID int64 `json:"audioId"`
AudioURL string `json:"audioUrl"`
VideoURL string `json:"videoUrl"`
Status consts.VideoStatus `json:"status"`
ErrorMsg string `json:"errorMsg"`
Duration int `json:"duration"`
Resolution consts.Resolution `json:"resolution"`
ThumbnailURL string `json:"thumbnailUrl"`
ExternalTaskID string `json:"externalTaskId"`
CreatedAt *gtime.Time `json:"createdAt"`
UpdatedAt *gtime.Time `json:"updatedAt"`
}
// UpdateVideoReq 更新视频请求
type UpdateVideoReq struct {
g.Meta `path:"/updateVideo" method:"put" tags:"视频管理" summary:"更新视频" dc:"更新视频信息"`
ID int64 `json:"id" v:"required" dc:"视频ID"`
// 基础信息
Name string `json:"name" dc:"视频名称"`
Description string `json:"description" dc:"视频描述"`
}
// DeleteVideoReq 删除视频请求
type DeleteVideoReq struct {
g.Meta `path:"/deleteVideo" method:"delete" tags:"视频管理" summary:"删除视频" dc:"删除视频"`
ID int64 `json:"id" v:"required" dc:"视频ID"`
}
// GenerateVideoReq 生成视频请求
type GenerateVideoReq struct {
g.Meta `path:"/generateVideo" method:"post" tags:"视频管理" summary:"生成视频" dc:"选择数字人,选择已生成的音频,调用数字人形象与音频合成形成视频"`
ID int64 `json:"id" v:"required" dc:"视频ID"`
}
// GenerateVideoRes 生成视频响应
type GenerateVideoRes struct {
TaskID string `json:"taskId" dc:"任务ID"`
}
// GetVideoStatusOptionsReq 获取视频状态选项请求
type GetVideoStatusOptionsReq struct {
g.Meta `path:"/getVideoStatusOptions" method:"get" tags:"视频管理" summary:"获取视频状态选项" dc:"获取所有视频状态的选项列表"`
}
// GetVideoStatusOptionsRes 获取视频状态选项响应
type GetVideoStatusOptionsRes struct {
Options []consts.VideoStatusKeyValue `json:"options" dc:"视频状态选项列表"`
}
// GetResolutionOptionsReq 获取分辨率选项请求
type GetResolutionOptionsReq struct {
g.Meta `path:"/getResolutionOptions" method:"get" tags:"视频管理" summary:"获取分辨率选项" dc:"获取所有分辨率的选项列表"`
}
// GetResolutionOptionsRes 获取分辨率选项响应
type GetResolutionOptionsRes struct {
Options []consts.ResolutionKeyValue `json:"options" dc:"分辨率选项列表"`
}
@@ -0,0 +1,37 @@
package entity
import (
"gitea.redpowerfuture.com/red-future/common/beans"
)
type asyncTaskRefCol struct {
beans.SQLBaseCol
TaskID string
State string
TableName string
BizID string
OssFile string
ErrorMsg string
}
var AsyncTaskRefCol = asyncTaskRefCol{
SQLBaseCol: beans.DefSQLBaseCol,
TaskID: "task_id",
State: "state",
TableName: "table_name",
BizID: "biz_id",
OssFile: "oss_file",
ErrorMsg: "error_msg",
}
// AsyncTaskRef 异步任务绑定记录(中间件 task_id 绑定业务表)
// - state: 保存中间件返回的任务状态(0排队中/1执行中/2成功/3失败/4已下载)
type AsyncTaskRef struct {
beans.SQLBaseDO `orm:",inline"`
TaskID string `orm:"task_id" json:"taskId"`
State int `orm:"state" json:"state"`
TableName string `orm:"table_name" json:"tableName"`
BizID int64 `orm:"biz_id" json:"bizId,string"`
OssFile string `orm:"oss_file" json:"ossFile"`
ErrorMsg string `orm:"error_msg" json:"errorMsg"`
}
+55
View File
@@ -0,0 +1,55 @@
package entity
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type audioCol struct {
beans.SQLBaseCol
Name string
Description string
ScriptText string
AudioURL string
Status string
ErrorMsg string
Duration string
ExternalID string
Voice string
VoiceType string
CustomVoice string
}
var AudioCol = audioCol{
SQLBaseCol: beans.DefSQLBaseCol,
Name: "name",
Description: "description",
ScriptText: "script_text",
AudioURL: "audio_url",
Status: "status",
ErrorMsg: "error_msg",
Duration: "duration",
ExternalID: "external_id",
Voice: "voice",
VoiceType: "voice_type",
CustomVoice: "custom_voice",
}
// Audio 音频实体
type Audio struct {
beans.SQLBaseDO `orm:",inline"`
// 基础信息
Name string `orm:"name" json:"name"` // 音频名称
Description string `orm:"description" json:"description"` // 音频描述
ScriptText string `orm:"script_text" json:"scriptText"` // 话术文本
AudioURL string `orm:"audio_url" json:"audioUrl"` // 音频文件URL
Status consts.AudioStatus `orm:"status" json:"status"` // 状态:0生成中/1成功/2失败
ErrorMsg string `orm:"error_msg" json:"errorMsg"` // 错误信息
Duration int `orm:"duration" json:"duration"` // 音频时长(秒)
ExternalID string `orm:"external_id" json:"externalId"` // 外部音频ID
// 音色相关
Voice string `orm:"voice" json:"voice"` // 音色:serena/vivian/uncle_fu/ryan/aiden/ono_anna/sohee/eric/dylan
VoiceType string `orm:"voice_type" json:"voiceType"` // 音色类型:preset/custom(预设/克隆)
CustomVoice string `orm:"custom_voice" json:"customVoice"` // 自定义音色ID(用于声音克隆)
}
@@ -0,0 +1,38 @@
package entity
import (
"gitea.redpowerfuture.com/red-future/common/beans"
)
type customVoiceCol struct {
beans.SQLBaseCol
Name string
Description string
Status string
ErrorMsg string
OssFile string
ReferenceAudio string
}
var CustomVoiceCol = customVoiceCol{
SQLBaseCol: beans.DefSQLBaseCol,
Name: "name",
Description: "description",
Status: "status",
ErrorMsg: "error_msg",
OssFile: "oss_file",
ReferenceAudio: "reference_audio",
}
// CustomVoice 自定义音色实体
type CustomVoice struct {
beans.SQLBaseDO `orm:",inline"`
// 基础信息
Name string `orm:"name" json:"name"` // 音色名称
Description string `orm:"description" json:"description"` // 音色描述
Text string `orm:"text" json:"text"` // 参考文本
Status int `orm:"status" json:"status"` // 状态:0生成中/1成功/2失败
ErrorMsg string `orm:"error_msg" json:"errorMsg"` // 错误信息
OssFile string `orm:"oss_file" json:"ossFile"` // 结果文件URL(如参考音频/特征文件等)
ReferenceAudio []byte `orm:"reference_audio" json:"referenceAudio"` // 参考音频数据(二进制)
}
@@ -0,0 +1,57 @@
package entity
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type digitalHumanCol struct {
beans.SQLBaseCol
Name string
Description string
AvatarURL string
VideoURL string
Voice string
Status string
Tags string
Gender string
Age string
Style string
ExternalID string
Metadata string
}
var DigitalHumanCol = digitalHumanCol{
SQLBaseCol: beans.DefSQLBaseCol,
Name: "name",
Description: "description",
AvatarURL: "avatar_url",
VideoURL: "video_url",
Voice: "voice",
Status: "status",
Tags: "tags",
Gender: "gender",
Age: "age",
Style: "style",
ExternalID: "external_id",
Metadata: "metadata",
}
// DigitalHuman 数字人形象实体
type DigitalHuman struct {
beans.SQLBaseDO `orm:",inline"`
// 基础信息
Name string `orm:"name" json:"name"` // 数字人名称
Description string `orm:"description" json:"description"` // 数字人描述
AvatarURL string `orm:"avatar_url" json:"imageUrl"` // 形象图片URL
VideoURL string `orm:"video_url" json:"videoUrl"` // 形象视频URL
Voice string `orm:"voice" json:"voice"` // 默认音色
Status consts.DigitalHumanStatus `orm:"status" json:"status"` // 状态:1启用/0停用
Tags []string `orm:"tags" json:"tags"` // 标签
Gender consts.Gender `orm:"gender" json:"gender"` // 性别
Age consts.Age `orm:"age" json:"age"` // 年龄段
Style consts.Style `orm:"style" json:"style"` // 风格:商务/休闲/正式等
ExternalID string `orm:"external_id" json:"externalId"` // 外部系统ID
Metadata []map[string]interface{} `orm:"metadata" json:"metadata"` // 动态元数据
}
+60
View File
@@ -0,0 +1,60 @@
package entity
import (
"ai-agent/digital-human/consts"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type videoCol struct {
beans.SQLBaseCol
Name string
Description string
AudioID string
ScriptText string
VideoURL string
Status string
ErrorMsg string
Duration string
ThumbnailURL string
ExternalID string
DigitalHumanID string
DigitalHumanName string
Resolution string
}
var VideoCol = videoCol{
SQLBaseCol: beans.DefSQLBaseCol,
Name: "name",
Description: "description",
AudioID: "audio_id",
ScriptText: "script_text",
VideoURL: "video_url",
Status: "status",
ErrorMsg: "error_msg",
Duration: "duration",
ThumbnailURL: "thumbnail_url",
ExternalID: "external_id",
DigitalHumanID: "digital_human_id",
DigitalHumanName: "digital_human_name",
Resolution: "resolution",
}
// Video 视频实体
type Video struct {
beans.SQLBaseDO `orm:",inline"`
// 基础信息
Name string `orm:"name" json:"name"` // 视频名称
Description string `orm:"description" json:"description"` // 视频描述
DigitalHumanID int64 `orm:"digital_human_id" json:"digitalHumanId"` // 数字人形象ID
AudioID int64 `orm:"audio_id" json:"audioId"` // 音频ID
ScriptText string `orm:"script_text" json:"scriptText"` // 话术文本
VideoURL string `orm:"video_url" json:"videoUrl"` // 合成视频URL
Status consts.VideoStatus `orm:"status" json:"status"` // 状态:0生成中/1成功/2失败
ErrorMsg string `orm:"error_msg" json:"errorMsg"` // 错误信息
Duration int `orm:"duration" json:"duration"` // 视频时长(秒)
ThumbnailURL string `orm:"thumbnail_url" json:"thumbnailUrl"` // 缩略图URL
ExternalID string `orm:"external_id" json:"externalId"` // 外部任务ID
DigitalHumanName string `orm:"digital_human_name" json:"digitalHumanName"` // 数字人名称(冗余字段)
Resolution consts.Resolution `orm:"resolution" json:"resolution"` // 分辨率
}
+195
View File
@@ -0,0 +1,195 @@
package service
import (
"context"
"fmt"
"ai-agent/digital-human/consts"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/dao"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type asyncTaskService struct{}
// AsyncTask 异步任务同步服务(供定时任务/业务轮询调用)
var AsyncTask = new(asyncTaskService)
// Sync
// 1) 扫描 digital_human_async_task_ref 中 state=0/1 的记录(业务“生成中”)
// 2) 组装 task_id 批量请求 model-asynch /task/get-task-batch
// 3) 中间件状态映射到业务状态(业务只维护三态:0生成中/1成功/2失败):
// - 中间件 0/1/3(能查到 task_id -> 业务 0(生成中)
// - 中间件 2/4(成功/已下载) -> 业务 1(成功)
// - 中间件 查不到 task_id(返回列表缺失) -> 业务 2(失败)
//
// 4) 绑定表仅用于“待同步列表”,因此:
// - 对中间件 0/1/3 不额外写库(减少查询/更新开销)
// - 对成功(2/4)与缺失(task_id 查不到)才更新绑定表
func (s *asyncTaskService) Sync(ctx context.Context, req *dto.SyncAsyncTasksReq) (res *dto.SyncAsyncTasksRes, err error) {
limit := 200
if req != nil && req.Limit > 0 {
limit = req.Limit
}
refs, err := dao.AsyncTaskRef.ListPending(ctx, limit)
if err != nil {
return nil, err
}
taskIDs := make([]string, 0, len(refs))
refMap := make(map[string]*entity.AsyncTaskRef, len(refs))
for _, r := range refs {
if r == nil || r.TaskID == "" {
continue
}
taskIDs = append(taskIDs, r.TaskID)
refMap[r.TaskID] = r
}
out := &dto.SyncAsyncTasksRes{
Total: len(taskIDs),
List: make([]dto.SyncAsyncTasksItem, 0, len(taskIDs)),
}
if len(taskIDs) == 0 {
return out, nil
}
items, err := getModelAsynchTaskBatch(ctx, taskIDs)
if err != nil {
return nil, err
}
seen := make(map[string]struct{}, len(items))
handled := 0
for _, it := range items {
r := refMap[it.TaskID]
if r == nil {
continue
}
seen[it.TaskID] = struct{}{}
switch it.State {
case 0, 1, 3:
// 排队中/执行中/失败(可能重试):业务侧仍视为生成中,不更新绑定表,减少更新开销
case 2, 4:
// 成功/已下载:业务侧写入 oss_file 并标记成功
if it.OssFile == "" {
errMsg := "中间件返回空oss地址"
_ = s.updateBizFailed(ctx, r, errMsg)
_, _ = dao.AsyncTaskRef.UpdateByTaskID(ctx, it.TaskID, gdb.Map{
entity.AsyncTaskRefCol.State: it.State,
entity.AsyncTaskRefCol.OssFile: "",
entity.AsyncTaskRefCol.ErrorMsg: errMsg,
})
out.List = append(out.List, dto.SyncAsyncTasksItem{
TaskID: it.TaskID,
State: it.State,
TableName: r.TableName,
BizID: fmt.Sprintf("%d", r.BizID),
OssFile: "",
ErrorMsg: errMsg,
})
continue
}
if err := s.updateBizSuccess(ctx, r, it.OssFile); err != nil {
errMsg := fmt.Sprintf("生成音频失败: %v", err)
_ = s.updateBizFailed(ctx, r, errMsg)
_, _ = dao.AsyncTaskRef.UpdateByTaskID(ctx, it.TaskID, gdb.Map{
entity.AsyncTaskRefCol.State: it.State,
entity.AsyncTaskRefCol.OssFile: it.OssFile,
entity.AsyncTaskRefCol.ErrorMsg: errMsg,
})
out.List = append(out.List, dto.SyncAsyncTasksItem{
TaskID: it.TaskID,
State: it.State,
TableName: r.TableName,
BizID: fmt.Sprintf("%d", r.BizID),
OssFile: it.OssFile,
ErrorMsg: errMsg,
})
continue
}
handled++
_, _ = dao.AsyncTaskRef.UpdateByTaskID(ctx, it.TaskID, gdb.Map{
entity.AsyncTaskRefCol.State: it.State,
entity.AsyncTaskRefCol.OssFile: it.OssFile,
entity.AsyncTaskRefCol.ErrorMsg: "",
})
default:
// 其他状态:不处理
}
out.List = append(out.List, dto.SyncAsyncTasksItem{
TaskID: it.TaskID,
State: it.State,
TableName: r.TableName,
BizID: fmt.Sprintf("%d", r.BizID),
OssFile: it.OssFile,
ErrorMsg: "",
})
}
// 处理“查不到 task_id”的情况:
// 中间件对失败重试耗尽的任务会硬删除,批量接口不会返回该 task_id。
// 业务侧把这种情况视为失败终态,并软删除绑定记录,避免重复轮询。
for _, taskID := range taskIDs {
if _, ok := seen[taskID]; ok {
continue
}
r := refMap[taskID]
if r == nil {
continue
}
msg := "模型任务不存在已失败"
_ = s.updateBizFailed(ctx, r, msg)
_, _ = dao.AsyncTaskRef.UpdateByTaskID(ctx, taskID, gdb.Map{
entity.AsyncTaskRefCol.State: 3,
entity.AsyncTaskRefCol.ErrorMsg: msg,
"deleted_at": gtime.Now(),
})
out.List = append(out.List, dto.SyncAsyncTasksItem{
TaskID: taskID,
State: 3,
TableName: r.TableName,
BizID: fmt.Sprintf("%d", r.BizID),
OssFile: "",
ErrorMsg: msg,
})
}
out.Handled = handled
g.Log().Infof(ctx, "[AsyncTask.Sync] total=%d handled=%d", out.Total, out.Handled)
return out, nil
}
// updateBizSuccess 更新业务侧状态为成功
func (s *asyncTaskService) updateBizSuccess(ctx context.Context, ref *entity.AsyncTaskRef, ossFile string) error {
switch ref.TableName {
case public.TableNameAudio:
_, err := dao.Audio.UpdateStatus(ctx, ref.BizID, consts.AudioStatusSuccess, "", ossFile, 0, "")
return err
case public.TableNameCustomVoice:
_, err := dao.CustomVoice.UpdateStatus(ctx, ref.BizID, 1, "", ossFile)
return err
default:
return fmt.Errorf("未知 table_name=%s", ref.TableName)
}
}
// updateBizFailed 更新业务侧状态为失败
func (s *asyncTaskService) updateBizFailed(ctx context.Context, ref *entity.AsyncTaskRef, msg string) error {
switch ref.TableName {
case public.TableNameAudio:
_, err := dao.Audio.UpdateStatus(ctx, ref.BizID, consts.AudioStatusFailed, msg, "", 0, "")
return err
case public.TableNameCustomVoice:
_, err := dao.CustomVoice.UpdateStatus(ctx, ref.BizID, 2, msg, "")
return err
default:
return nil
}
}
+268
View File
@@ -0,0 +1,268 @@
package service
import (
"context"
"encoding/base64"
"ai-agent/digital-human/consts"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/dao"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type audio struct{}
// Audio 音频服务
var Audio = new(audio)
// UploadFileResponse OSS 文件上传响应结构
type UploadFileResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
FileURL string `json:"fileURL" dc:"上传地址"`
FileSize int `json:"fileSize" dc:"文件大小"`
FileName string `json:"fileName" dc:"文件名称"`
FileFormat string `json:"fileFormat" dc:"文件格式"`
FileAddressPrefix string `json:"fileAddressPrefix"`
} `json:"data"`
}
// Create 创建音频
func (s *audio) Create(ctx context.Context, req *dto.CreateAudioReq) (res *dto.CreateAudioRes, err error) {
// 设置默认音色
if req.Voice == "" {
req.Voice = "Serena" // 默认音色
}
if req.VoiceType == "" {
req.VoiceType = "Preset" // 默认预设音色
}
// 如果是自定义音色,验证音色是否存在
if req.VoiceType == "custom" && req.CustomVoice != "" {
customVoiceID := gconv.Int64(req.CustomVoice)
_, err := dao.CustomVoice.GetOne(ctx, customVoiceID)
if err != nil {
return nil, gerror.Wrapf(err, "自定义音色不存在: %s", req.CustomVoice)
}
}
// 插入数据库(初始状态为生成中)
audioID, err := dao.Audio.Insert(ctx, req)
if err != nil {
return nil, err
}
// 通过 model-asynch 创建异步任务(由中间件执行模型调用与产物落 OSS)
// 约定:
// - custom(克隆音色) -> base 模型(需要参考音频/参考文本) 否则 -> customvoice 模型
var taskID string
if req.VoiceType == "custom" {
customVoiceID := gconv.Int64(req.CustomVoice)
// 1. 先获取自定义音色详情
cv, err := dao.CustomVoice.GetOne(ctx, customVoiceID)
if err != nil {
_, _ = dao.Audio.UpdateStatus(ctx, audioID, consts.AudioStatusFailed, "获取自定义音色失败: "+err.Error(), "", 0, "")
return nil, err
}
// 2. 调用模型生成音频
refAudioBase64 := base64.StdEncoding.EncodeToString(cv.ReferenceAudio)
xVectorOnlyMode := false
if cv.Text == "" {
xVectorOnlyMode = true
}
taskID, err = TTS.CreateBaseTask(asyncCtx(ctx), req.ScriptText, "Auto", cv.Text, cv.OssFile, refAudioBase64, xVectorOnlyMode, 1.0)
} else {
// 1. 调用模型生成音频
taskID, err = TTS.CreateCustomVoiceTask(asyncCtx(ctx), req.ScriptText, req.Voice, "Auto", "", 1.0)
}
if err != nil {
_, _ = dao.Audio.UpdateStatus(ctx, audioID, consts.AudioStatusFailed, "创建异步任务失败: "+err.Error(), "", 0, "")
return nil, err
}
_, _ = dao.AsyncTaskRef.Insert(ctx, &entity.AsyncTaskRef{
TaskID: taskID,
State: 0,
TableName: public.TableNameAudio,
BizID: audioID,
})
res = &dto.CreateAudioRes{
Id: audioID,
}
return
}
// List 获取音频列表
func (s *audio) List(ctx context.Context, req *dto.ListAudioReq) (res *dto.ListAudioRes, err error) {
audioList, total, err := dao.Audio.List(ctx, req)
if err != nil {
return nil, err
}
res = &dto.ListAudioRes{
Total: int64(total),
List: make([]*dto.AudioListItem, 0, len(audioList)),
}
for _, audio := range audioList {
res.List = append(res.List, &dto.AudioListItem{
ID: audio.Id,
Name: audio.Name,
Description: audio.Description,
ScriptText: audio.ScriptText,
AudioURL: audio.AudioURL,
Status: audio.Status,
ErrorMsg: audio.ErrorMsg,
Duration: audio.Duration,
ExternalID: audio.ExternalID,
Voice: audio.Voice,
VoiceType: audio.VoiceType,
CustomVoice: audio.CustomVoice,
CreatedAt: audio.CreatedAt,
UpdatedAt: audio.UpdatedAt,
})
}
return res, nil
}
// GetOne 获取单个音频
func (s *audio) GetOne(ctx context.Context, id int64) (*dto.GetAudioRes, error) {
audioOne, err := dao.Audio.GetOne(ctx, id)
if err != nil {
return nil, err
}
return &dto.GetAudioRes{
ID: audioOne.Id,
Name: audioOne.Name,
Description: audioOne.Description,
ScriptText: audioOne.ScriptText,
AudioURL: audioOne.AudioURL,
Status: audioOne.Status,
ErrorMsg: audioOne.ErrorMsg,
Duration: audioOne.Duration,
ExternalID: audioOne.ExternalID,
Voice: audioOne.Voice,
VoiceType: audioOne.VoiceType,
CustomVoice: audioOne.CustomVoice,
CreatedAt: audioOne.CreatedAt,
UpdatedAt: audioOne.UpdatedAt,
}, nil
}
// Update 更新音频
func (s *audio) Update(ctx context.Context, req *dto.UpdateAudioReq) (err error) {
// 先获取原始音频信息
audioOne, err := dao.Audio.GetOne(ctx, req.ID)
if err != nil {
return gerror.Wrap(err, "获取原始音频信息失败")
}
// 修改字段
if !g.IsEmpty(req.Name) {
audioOne.Name = req.Name
}
if !g.IsEmpty(req.Description) {
audioOne.Description = req.Description
}
if !g.IsEmpty(req.Voice) {
audioOne.Voice = req.Voice
}
if !g.IsEmpty(req.VoiceType) {
audioOne.VoiceType = req.VoiceType
}
if !g.IsEmpty(req.CustomVoice) {
audioOne.CustomVoice = req.CustomVoice
}
_, err = dao.Audio.Update(ctx, req.ID, audioOne)
return err
}
// Delete 删除音频
func (s *audio) Delete(ctx context.Context, id int64) error {
_, err := dao.Audio.Delete(ctx, id)
return err
}
// Generate 重新生成音频
func (s *audio) Generate(ctx context.Context, req *dto.GenerateAudioReq) (res *dto.GenerateAudioRes, err error) {
// 获取音频信息
audioOne, err := dao.Audio.GetOne(ctx, req.ID)
if err != nil {
return nil, gerror.Wrap(err, "获取音频信息失败")
}
// 重置状态为生成中
_, err = dao.Audio.UpdateStatus(ctx, req.ID, consts.AudioStatusGenerating, "", "", 0, "")
if err != nil {
return nil, err
}
// 构建请求
createReq := &dto.CreateAudioReq{
Name: audioOne.Name,
Description: audioOne.Description,
ScriptText: audioOne.ScriptText,
Voice: audioOne.Voice,
VoiceType: audioOne.VoiceType,
CustomVoice: audioOne.CustomVoice,
}
// 异步重新生成音频
var taskID string
if createReq.VoiceType == "custom" {
customVoiceID := gconv.Int64(createReq.CustomVoice)
cv, err := dao.CustomVoice.GetOne(ctx, customVoiceID)
if err != nil {
_, _ = dao.Audio.UpdateStatus(ctx, req.ID, consts.AudioStatusFailed, "获取自定义音色失败: "+err.Error(), "", 0, "")
return nil, err
}
refAudioBase64 := ""
if cv != nil && len(cv.ReferenceAudio) > 0 {
refAudioBase64 = base64.StdEncoding.EncodeToString(cv.ReferenceAudio)
}
refText := ""
if cv != nil {
refText = cv.Text
}
xVectorOnlyMode := false
if refText == "" {
xVectorOnlyMode = true
}
taskID, err = TTS.CreateBaseTask(asyncCtx(ctx), createReq.ScriptText, "Auto", refText, cv.OssFile, refAudioBase64, xVectorOnlyMode, 1.0)
} else {
taskID, err = TTS.CreateCustomVoiceTask(asyncCtx(ctx), createReq.ScriptText, createReq.Voice, "Auto", "", 1.0)
}
if err != nil {
_, _ = dao.Audio.UpdateStatus(ctx, req.ID, consts.AudioStatusFailed, "创建异步任务失败: "+err.Error(), "", 0, "")
return nil, err
}
_, _ = dao.AsyncTaskRef.Insert(ctx, &entity.AsyncTaskRef{
TaskID: taskID,
State: 0,
TableName: public.TableNameAudio,
BizID: req.ID,
})
res = &dto.GenerateAudioRes{
TaskID: gconv.String(req.ID),
}
return
}
// GetStatusOptions 获取状态选项
func (s *audio) GetStatusOptions(ctx context.Context, req *dto.GetAudioStatusOptionsReq) (res *dto.GetAudioStatusOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetAudioStatusOptionsRes)
res.Options = consts.GetAllAudioStatusKeyValue()
return res, nil
}
// TTS 文本转语音(使用 Qwen3-TTS
func (s *audio) TTS(ctx context.Context, req *dto.TTSReq) (res *dto.TTSRes, err error) {
_ = ctx
_ = req
return nil, gerror.New("该接口已迁移为异步:请使用 CreateAudio 创建异步任务并通过轮询/批量领取获取结果")
}
@@ -0,0 +1,106 @@
package service
import (
"context"
"encoding/base64"
"ai-agent/digital-human/consts/public"
"ai-agent/digital-human/dao"
"ai-agent/digital-human/model/dto"
"ai-agent/digital-human/model/entity"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type customVoice struct{}
// CustomVoice 自定义音色服务
var CustomVoice = new(customVoice)
// CreateCustomVoice 创建自定义音色
func (s *customVoice) CreateCustomVoice(ctx context.Context, req *dto.CreateCustomVoiceReq) (res *dto.CreateCustomVoiceRes, err error) {
g.Log().Infof(ctx, "创建自定义音色: name=%s, voiceType=%s", req.Name, req.VoiceType)
// 插入数据库(状态:生成中)
voiceID, err := dao.CustomVoice.Insert(ctx, req)
if err != nil {
return nil, err
}
switch req.VoiceType {
case "design":
// 设计音频:按模型约定只传 text + instruct
taskID, err := TTS.CreateVoiceDesignTask(asyncCtx(ctx), req.Text, req.Description, "", 0)
if err != nil {
_, _ = dao.CustomVoice.UpdateStatus(ctx, voiceID, 2, "创建异步任务失败: "+err.Error(), "")
return nil, err
}
_, _ = dao.AsyncTaskRef.Insert(ctx, &entity.AsyncTaskRef{
TaskID: taskID,
State: 0,
TableName: public.TableNameCustomVoice,
BizID: voiceID,
})
res = &dto.CreateCustomVoiceRes{VoiceID: gconv.String(voiceID)}
g.Log().Infof(ctx, "自定义音色创建成功: voiceId=%d taskId=%s", voiceID, taskID)
case "clone":
// TODO : 克隆音色:使用语音转文字暂预留,后续找模型对应处理
refAudioBase64 := base64.StdEncoding.EncodeToString(req.ReferenceAudio)
taskID, err := TTS.SpeechToText(asyncCtx(ctx), refAudioBase64)
if err != nil {
_, _ = dao.CustomVoice.UpdateStatus(ctx, voiceID, 2, "创建异步任务失败: "+err.Error(), "")
return nil, err
}
_, _ = dao.AsyncTaskRef.Insert(ctx, &entity.AsyncTaskRef{
TaskID: taskID,
State: 0,
TableName: public.TableNameCustomVoice,
BizID: voiceID,
})
res = &dto.CreateCustomVoiceRes{VoiceID: gconv.String(voiceID)}
g.Log().Infof(ctx, "克隆音色成功: voiceId=%d taskId=%s", voiceID, taskID)
default:
return nil, gerror.New("不支持的音色类型")
}
return
}
// ListCustomVoices 获取自定义音色列表
func (s *customVoice) ListCustomVoices(ctx context.Context, req *dto.ListCustomVoiceReq) (res *dto.ListCustomVoiceRes, err error) {
customVoices, total, err := dao.CustomVoice.List(ctx, req)
if err != nil {
return nil, err
}
res = &dto.ListCustomVoiceRes{
Total: int64(total),
List: make([]*dto.CustomVoiceItem, 0, len(customVoices)),
}
for _, cv := range customVoices {
res.List = append(res.List, dao.CustomVoice.GetCustomVoiceItem(cv))
}
return
}
// DeleteCustomVoice 删除自定义音色
func (s *customVoice) DeleteCustomVoice(ctx context.Context, req *dto.DeleteCustomVoiceReq) (err error) {
// 验证音色是否存在
voiceID := gconv.Int64(req.VoiceID)
_, err = dao.CustomVoice.GetOne(ctx, voiceID)
if err != nil {
return gerror.Wrapf(err, "音色不存在: %s", req.VoiceID)
}
// 删除音色
_, err = dao.CustomVoice.Delete(ctx, voiceID)
if err != nil {
return gerror.Wrapf(err, "删除音色失败: %s", req.VoiceID)
}
g.Log().Infof(ctx, "自定义音色删除成功: voiceId=%s", req.VoiceID)
return nil
}
@@ -0,0 +1,179 @@
package service
import (
"ai-agent/digital-human/consts"
"ai-agent/digital-human/dao"
"ai-agent/digital-human/model/dto"
"context"
"encoding/json"
"errors"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type digitalHuman struct{}
// DigitalHuman 数字人形象服务
var DigitalHuman = new(digitalHuman)
// Create 创建数字人形象
func (s *digitalHuman) Create(ctx context.Context, req *dto.CreateDigitalHumanReq) (res *dto.CreateDigitalHumanRes, err error) {
count, err := dao.DigitalHuman.Count(ctx, req)
if err != nil {
return nil, err
}
if count > 0 {
return nil, errors.New("数字人形象名称已存在")
}
// 插入数据库
ids, err := dao.DigitalHuman.Insert(ctx, req)
if err != nil {
return nil, err
}
// PostgreSQL 使用 int64
id := ids[0].(int64)
res = &dto.CreateDigitalHumanRes{
Id: id,
}
return
}
// List 获取数字人形象列表
func (s *digitalHuman) List(ctx context.Context, req *dto.ListDigitalHumanReq) (res *dto.ListDigitalHumanRes, error error) {
digitalHumanList, total, err := dao.DigitalHuman.List(ctx, req)
if err != nil {
return
}
res = &dto.ListDigitalHumanRes{
Total: total,
}
b, err := json.Marshal(digitalHumanList)
if err != nil {
return
}
err = json.Unmarshal(b, &res.List)
return
}
// GetOne 获取单个数字人形象
func (s *digitalHuman) GetOne(ctx context.Context, id int64) (*dto.GetDigitalHumanRes, error) {
digitalHumanOne, err := dao.DigitalHuman.GetOne(ctx, id)
if err != nil {
return nil, err
}
var createdAt, updatedAt *gtime.Time
if digitalHumanOne.CreatedAt != nil {
createdAt = digitalHumanOne.CreatedAt
}
if digitalHumanOne.UpdatedAt != nil {
updatedAt = digitalHumanOne.UpdatedAt
}
return &dto.GetDigitalHumanRes{
ID: digitalHumanOne.Id,
Name: digitalHumanOne.Name,
Description: digitalHumanOne.Description,
ImageURL: digitalHumanOne.AvatarURL,
VideoURL: digitalHumanOne.VideoURL,
Status: digitalHumanOne.Status,
Tags: digitalHumanOne.Tags,
Gender: digitalHumanOne.Gender,
Age: digitalHumanOne.Age,
Style: digitalHumanOne.Style,
ExternalID: digitalHumanOne.ExternalID,
Metadata: digitalHumanOne.Metadata,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}, nil
}
// Update 更新数字人形象
func (s *digitalHuman) Update(ctx context.Context, req *dto.UpdateDigitalHumanReq) error {
// 先获取原始数字人形象信息
digitalHumanOne, err := dao.DigitalHuman.GetOne(ctx, req.ID)
if err != nil {
return gerror.Wrap(err, "获取原始数字人形象信息失败")
}
// 修改字段
if !g.IsEmpty(req.Name) {
digitalHumanOne.Name = req.Name
}
if !g.IsEmpty(req.Description) {
digitalHumanOne.Description = req.Description
}
if !g.IsEmpty(req.ImageURL) {
digitalHumanOne.AvatarURL = req.ImageURL
}
if !g.IsEmpty(req.VideoURL) {
digitalHumanOne.VideoURL = req.VideoURL
}
digitalHumanOne.Status = req.Status
if req.Tags != nil {
digitalHumanOne.Tags = req.Tags
}
if !g.IsEmpty(req.Gender) {
digitalHumanOne.Gender = req.Gender
}
if !g.IsEmpty(req.Age) {
digitalHumanOne.Age = req.Age
}
if !g.IsEmpty(req.Style) {
digitalHumanOne.Style = req.Style
}
if !g.IsEmpty(req.ExternalID) {
digitalHumanOne.ExternalID = req.ExternalID
}
if req.Metadata != nil {
digitalHumanOne.Metadata = req.Metadata
}
return dao.DigitalHuman.Update(ctx, req.ID, digitalHumanOne)
}
// UpdateStatus 更新数字人形象状态
func (s *digitalHuman) UpdateStatus(ctx context.Context, id int64, status consts.DigitalHumanStatus) error {
_, err := dao.DigitalHuman.UpdateStatus(ctx, id, status)
return err
}
// Delete 删除数字人形象
func (s *digitalHuman) Delete(ctx context.Context, id int64) error {
return dao.DigitalHuman.Delete(ctx, id)
}
// GetStatusOptions 获取状态选项
func (s *digitalHuman) GetStatusOptions(ctx context.Context, req *dto.GetDigitalHumanStatusOptionsReq) (res *dto.GetDigitalHumanStatusOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetDigitalHumanStatusOptionsRes)
res.Options = consts.GetAllStatusKeyValue()
return res, nil
}
// GetGenderOptions 获取性别选项
func (s *digitalHuman) GetGenderOptions(ctx context.Context, req *dto.GetGenderOptionsReq) (res *dto.GetGenderOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetGenderOptionsRes)
res.Options = consts.GetAllGenderKeyValue()
return res, nil
}
// GetAgeOptions 获取年龄段选项
func (s *digitalHuman) GetAgeOptions(ctx context.Context, req *dto.GetAgeOptionsReq) (res *dto.GetAgeOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetAgeOptionsRes)
res.Options = consts.GetAllAgeKeyValue()
return res, nil
}
// GetStyleOptions 获取风格选项
func (s *digitalHuman) GetStyleOptions(ctx context.Context, req *dto.GetStyleOptionsReq) (res *dto.GetStyleOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetStyleOptionsRes)
res.Options = consts.GetAllStyleKeyValue()
return res, nil
}
+219
View File
@@ -0,0 +1,219 @@
package service
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"mime/multipart"
stdhttp "net/http"
"os"
"path/filepath"
"sync"
"time"
commonHttp "gitea.redpowerfuture.com/red-future/common/http"
"gitea.redpowerfuture.com/red-future/common/utils"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
var commonHttpTransportMu sync.Mutex
// asyncCtx 异步上下文处理
func asyncCtx(ctx context.Context) context.Context {
asyncCtx := context.WithoutCancel(ctx)
if r := g.RequestFromCtx(ctx); r != nil {
if token := r.Header.Get("Authorization"); token != "" {
asyncCtx = context.WithValue(asyncCtx, "token", token)
}
}
if user, uErr := utils.GetUserInfo(ctx); uErr == nil && user != nil {
asyncCtx = context.WithValue(asyncCtx, "user", user)
}
return asyncCtx
}
// setCommonHttpResponseHeaderTimeout 调整公共 HTTP 客户端响应头超时,避免长时推理被 30s 默认值打断。
func setCommonHttpResponseHeaderTimeout(d time.Duration) {
if d <= 0 {
return
}
commonHttpTransportMu.Lock()
defer commonHttpTransportMu.Unlock()
if tr, ok := commonHttp.Httpclient.Transport.(*stdhttp.Transport); ok && tr != nil {
if tr.ResponseHeaderTimeout < d {
tr.ResponseHeaderTimeout = d
}
}
}
// forwardHeaders 透传调用链路中必须的头信息,优先使用异步上下文里固化的 token。
func forwardHeaders(ctx context.Context) map[string]string {
headers := make(map[string]string)
if token, ok := ctx.Value("token").(string); ok && token != "" {
headers["Authorization"] = token
}
if r := g.RequestFromCtx(ctx); r != nil {
if headers["Authorization"] == "" {
if token := r.Header.Get("Authorization"); token != "" {
headers["Authorization"] = token
}
}
if userInfo := r.Header.Get("X-User-Info"); userInfo != "" {
headers["X-User-Info"] = userInfo
}
}
return headers
}
// commonPostJSON 使用 common/http 的底层客户端直连 JSON 接口,适配非统一响应包装结构。
func commonPostJSON(ctx context.Context, url string, headers map[string]string, req any, resp any) error {
client := commonHttp.Httpclient.Clone().ContentJson()
if deadline, ok := ctx.Deadline(); ok {
if d := time.Until(deadline); d > 0 {
client.SetTimeout(d)
}
}
if len(headers) > 0 {
client.SetHeaderMap(headers)
}
r, err := client.DoRequest(ctx, stdhttp.MethodPost, url, req)
if err != nil {
return err
}
defer r.Close()
body, err := io.ReadAll(r.Body)
if err != nil {
return gerror.Wrap(err, "读取响应失败")
}
if r.StatusCode != stdhttp.StatusOK {
return gerror.Newf("HTTP状态码异常: %d, body: %s", r.StatusCode, string(body))
}
if err := json.Unmarshal(body, resp); err != nil {
return gerror.Wrapf(err, "解析响应失败, body: %s", string(body))
}
return nil
}
func commonPostMultipartFile(ctx context.Context, url string, headers map[string]string, form map[string]string, fileField string, filePath string, resp any) error {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for k, v := range form {
if v == "" {
continue
}
if err := writer.WriteField(k, v); err != nil {
return gerror.Wrapf(err, "写入表单字段失败: %s", k)
}
}
f, err := os.Open(filePath)
if err != nil {
return gerror.Wrapf(err, "打开文件失败: %s", filePath)
}
defer f.Close()
part, err := writer.CreateFormFile(fileField, filepath.Base(filePath))
if err != nil {
return gerror.Wrapf(err, "创建表单文件失败: %s", fileField)
}
if _, err := io.Copy(part, f); err != nil {
return gerror.Wrap(err, "写入文件内容失败")
}
contentType := writer.FormDataContentType()
if err := writer.Close(); err != nil {
return gerror.Wrap(err, "关闭表单写入器失败")
}
client := commonHttp.Httpclient.Clone()
if deadline, ok := ctx.Deadline(); ok {
if d := time.Until(deadline); d > 0 {
client.SetTimeout(d)
}
}
if headers == nil {
headers = make(map[string]string)
}
headers["Content-Type"] = contentType
client.SetHeaderMap(headers)
r, err := client.DoRequest(ctx, stdhttp.MethodPost, url, body.Bytes())
if err != nil {
return err
}
defer r.Close()
raw, err := io.ReadAll(r.Body)
if err != nil {
return gerror.Wrap(err, "读取响应失败")
}
if r.StatusCode != stdhttp.StatusOK {
return gerror.Newf("HTTP状态码异常: %d, body: %s", r.StatusCode, string(raw))
}
if err := json.Unmarshal(raw, resp); err != nil {
return gerror.Wrapf(err, "解析响应失败, body: %s", string(raw))
}
return nil
}
// -------------------------- model-asynch 调用封装 --------------------------
const modelAsynchServiceName = "model-asynch"
type modelAsynchCreateTaskReq struct {
ModelName string `json:"modelName"`
InputRef string `json:"inputRef,omitempty"`
RequestPayload any `json:"requestPayload"`
}
type modelAsynchCreateTaskRes struct {
TaskID string `json:"taskId"`
}
// createModelAsynchTask 调用 model-asynch 创建任务
// 注意:路由以 GoFrame 默认输出为准(通常为 /task/create-task
func createModelAsynchTask(ctx context.Context, modelName string, payload any, inputRef string) (taskID string, err error) {
taskUrl := g.Cfg().MustGet(ctx, "model-asynch.addr", "127.0.0.1:8080")
headers := forwardHeaders(ctx)
req := &modelAsynchCreateTaskReq{
ModelName: modelName,
InputRef: inputRef,
RequestPayload: payload,
}
var res modelAsynchCreateTaskRes
if err := commonHttp.Post(ctx, fmt.Sprintf("%s/task/createTask", taskUrl), headers, &res, req); err != nil {
return "", err
}
return res.TaskID, nil
}
type modelAsynchBatchReq struct {
TaskIDs []string `json:"taskIds"`
}
type modelAsynchBatchItem struct {
TaskID string `json:"taskId"`
State int `json:"state"`
OssFile string `json:"ossFile"`
}
type modelAsynchBatchRes struct {
List []modelAsynchBatchItem `json:"list"`
}
// getModelAsynchTaskBatch 批量查询任务(成功 2->4 的逻辑由中间件内部处理)
func getModelAsynchTaskBatch(ctx context.Context, taskIDs []string) (items []modelAsynchBatchItem, err error) {
taskUrl := g.Cfg().MustGet(ctx, "model-asynch.addr", "127.0.0.1:8080")
headers := forwardHeaders(ctx)
req := &modelAsynchBatchReq{TaskIDs: taskIDs}
var res modelAsynchBatchRes
if err := commonHttp.Post(ctx, fmt.Sprintf("%s/task/getTaskBatch", taskUrl), headers, &res, req); err != nil {
return nil, err
}
return res.List, nil
}
+117
View File
@@ -0,0 +1,117 @@
package service
import (
"context"
"encoding/base64"
"ai-agent/digital-human/consts/public"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
type tts struct{}
// TTS 统一的模型异步调用封装(通过 model-asynch 中间件)
var TTS = new(tts)
// CreateVoiceDesignTask 设计音频任务(VoiceDesign
func (s *tts) CreateVoiceDesignTask(
ctx context.Context,
text string,
instruct string,
language string, // 空则 Auto
speed float64, // <=0 则 1.0
) (taskID string, err error) {
if language == "" {
language = "Auto"
}
if speed <= 0 {
speed = 1.0
}
payload := map[string]any{
"text": text,
"language": language,
"instruct": instruct,
"speed": speed,
"response_format": "wav",
}
g.Log().Info(ctx, "[CreateVoiceDesignTask] %v", payload)
return createModelAsynchTask(ctx, public.ModelNameVoiceDesign, payload, "")
}
// CreateCustomVoiceTask 预设音色(CustomVoice)任务
// - speaker: 预设说话人(如 Vivian/Serena/Ryan/...
// - instruct: 可选,情绪/风格控制
func (s *tts) CreateCustomVoiceTask(
ctx context.Context,
text string,
speaker string,
language string, // 例如 "Chinese"/"English"/"Auto",空则默认 "Auto"
instruct string, // 可空
speed float64, // 0.5~2.0<=0 则默认 1.0
) (taskID string, err error) {
if language == "" {
language = "Auto"
}
if speed <= 0 {
speed = 1.0
}
payload := map[string]any{
"text": text,
"language": language,
"speaker": speaker,
"instruct": instruct,
"speed": speed,
"response_format": "wav", // 建议统一用 wav
}
g.Log().Info(ctx, "[CreateCustomVoiceTask] %v", payload)
return createModelAsynchTask(ctx, public.ModelNameCustomVoice, payload, "")
}
// CreateBaseTask 声音克隆(Base / clone)任务
// 说明:ref_audio_url 与 ref_audio_base64 二选一
func (s *tts) CreateBaseTask(
ctx context.Context,
text string,
language string, // 例如 "Chinese"/"English"/"Auto",空则默认 "Auto"
refText string, // 当 xVectorOnlyMode=false 时必填
refAudioURL string, // 可空
refAudioBase64 string, // 可空(不带 data: 前缀也可以)
xVectorOnlyMode bool, // true=不需要 refText,但质量可能下降
speed float64, // 0.5~2.0<=0 则默认 1.0
) (taskID string, err error) {
if language == "" {
language = "Auto"
}
if speed <= 0 {
speed = 1.0
}
payload := map[string]any{
"text": text,
"language": language,
"ref_text": refText,
"ref_audio_url": refAudioURL,
"ref_audio_base64": refAudioBase64,
"x_vector_only_mode": xVectorOnlyMode,
"speed": speed,
"response_format": "wav",
}
g.Log().Info(ctx, "[CreateBaseTask] %v", payload)
return createModelAsynchTask(ctx, public.ModelNameBase, payload, "")
}
// SpeechToText 语音转文本(预留)
// audioBase64base64 编码的音频数据(WAV/MP3等)
func (s *tts) SpeechToText(ctx context.Context, audioBase64 string) (text string, err error) {
_ = ctx
if audioBase64 == "" {
return "", gerror.New("audioBase64 不能为空")
}
// 简单校验 base64 合法性
if _, err := base64.StdEncoding.DecodeString(audioBase64); err != nil {
return "", gerror.Wrap(err, "audioBase64 非法")
}
return "", gerror.New("SpeechToText 暂未实现:后续接入语音识别模型后补齐")
}
+218
View File
@@ -0,0 +1,218 @@
package service
import (
"ai-agent/digital-human/consts"
"ai-agent/digital-human/dao"
"ai-agent/digital-human/model/dto"
"context"
"encoding/json"
"errors"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
type video struct{}
// Video 视频服务
var Video = new(video)
// Create 创建视频
func (s *video) Create(ctx context.Context, req *dto.CreateVideoReq) (res *dto.CreateVideoRes, err error) {
// 验证数字人形象是否存在且启用
digitalHumanOne, err := dao.DigitalHuman.GetOne(ctx, req.DigitalHumanID)
if err != nil {
return nil, gerror.Wrap(err, "数字人形象不存在")
}
if digitalHumanOne.Status != consts.DigitalHumanStatusActive {
return nil, errors.New("数字人形象未启用")
}
// 验证音频是否存在且已生成成功
audioOne, err := dao.Audio.GetOne(ctx, req.AudioID)
if err != nil {
return nil, gerror.Wrap(err, "音频不存在")
}
if audioOne.Status != consts.AudioStatusSuccess {
return nil, errors.New("音频未生成成功,无法合成视频")
}
// 创建视频记录(初始状态为生成中)
ids, err := dao.Video.Insert(ctx, req)
if err != nil {
return nil, err
}
// 保存视频IDPostgreSQL 使用 int64
videoID := ids[0].(int64)
// 异步生成视频
go s.generateVideo(ctx, req.DigitalHumanID, digitalHumanOne.Name, req.AudioID, audioOne.AudioURL, audioOne.Duration, req.Resolution, videoID)
res = &dto.CreateVideoRes{
Id: videoID,
}
return
}
// generateVideo 生成视频(异步处理)
func (s *video) generateVideo(ctx context.Context, digitalHumanID int64, digitalHumanName string, audioID int64, audioURL string, duration int, resolution consts.Resolution, videoID int64) {
// 更新视频状态,设置音频URL和时长
_, _ = dao.Video.UpdateStatus(ctx, videoID, consts.VideoStatusGenerating, "", audioURL, duration, "", "")
// 调用数字人形象与音频合成服务
videoURL, thumbnailURL, externalTaskID, err := s.synthesizeVideo(ctx, digitalHumanID, audioURL, resolution)
if err != nil {
// 视频合成失败
_, _ = dao.Video.UpdateStatus(ctx, videoID, consts.VideoStatusFailed, "视频合成失败: "+err.Error(), "", 0, "", "")
return
}
// 更新视频生成状态为成功
_, _ = dao.Video.UpdateStatus(ctx, videoID, consts.VideoStatusSuccess, "", videoURL, duration, thumbnailURL, externalTaskID)
}
// synthesizeVideo 合成视频(模拟)
func (s *video) synthesizeVideo(ctx context.Context, digitalHumanID int64, audioURL string, resolution consts.Resolution) (videoURL string, thumbnailURL string, externalTaskID string, err error) {
// TODO: 调用真实的数字人视频合成服务API
// 这里模拟返回
g.Log().Info(ctx, "合成视频,数字人ID:", digitalHumanID, "音频URL:", audioURL, "分辨率:", resolution)
// 模拟外部任务ID(使用雪花算法或UUID)
externalTaskID = gconv.String(digitalHumanID) + "-" + gconv.String(gtime.Timestamp())
// 模拟视频URL(实际应该从视频合成服务获取)
videoURL = "https://example.com/video/" + externalTaskID + ".mp4"
// 模拟缩略图URL
thumbnailURL = "https://example.com/video/" + externalTaskID + "_thumb.jpg"
return videoURL, thumbnailURL, externalTaskID, nil
}
// List 获取视频列表
func (s *video) List(ctx context.Context, req *dto.ListVideoReq) (res *dto.ListVideoRes, error error) {
videoList, total, err := dao.Video.List(ctx, req)
if err != nil {
return
}
res = &dto.ListVideoRes{
Total: total,
}
b, err := json.Marshal(videoList)
if err != nil {
return
}
err = json.Unmarshal(b, &res.List)
return
}
// GetOne 获取单个视频
func (s *video) GetOne(ctx context.Context, id int64) (*dto.GetVideoRes, error) {
videoOne, err := dao.Video.GetOne(ctx, id)
if err != nil {
return nil, err
}
var createdAt, updatedAt *gtime.Time
if videoOne.CreatedAt != nil {
createdAt = videoOne.CreatedAt
}
if videoOne.UpdatedAt != nil {
updatedAt = videoOne.UpdatedAt
}
return &dto.GetVideoRes{
ID: videoOne.Id,
Name: videoOne.Name,
Description: videoOne.Description,
DigitalHumanID: videoOne.DigitalHumanID,
DigitalHumanName: videoOne.DigitalHumanName,
AudioID: videoOne.AudioID,
AudioURL: "",
VideoURL: videoOne.VideoURL,
Status: videoOne.Status,
ErrorMsg: videoOne.ErrorMsg,
Duration: videoOne.Duration,
Resolution: videoOne.Resolution,
ThumbnailURL: videoOne.ThumbnailURL,
ExternalTaskID: videoOne.ExternalID,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
}, nil
}
// Update 更新视频
func (s *video) Update(ctx context.Context, req *dto.UpdateVideoReq) error {
// 先获取原始视频信息
videoOne, err := dao.Video.GetOne(ctx, req.ID)
if err != nil {
return gerror.Wrap(err, "获取原始视频信息失败")
}
// 修改字段
if !g.IsEmpty(req.Name) {
videoOne.Name = req.Name
}
if !g.IsEmpty(req.Description) {
videoOne.Description = req.Description
}
return dao.Video.Update(ctx, req.ID, videoOne)
}
// Delete 删除视频
func (s *video) Delete(ctx context.Context, id int64) error {
return dao.Video.Delete(ctx, id)
}
// Generate 重新生成视频
func (s *video) Generate(ctx context.Context, req *dto.GenerateVideoReq) (res *dto.GenerateVideoRes, err error) {
// 获取视频信息
videoOne, err := dao.Video.GetOne(ctx, req.ID)
if err != nil {
return nil, gerror.Wrap(err, "获取视频信息失败")
}
// 验证音频是否仍然有效(已生成成功)
if videoOne.AudioID != 0 {
audioOne, err := dao.Audio.GetOne(ctx, videoOne.AudioID)
if err != nil {
return nil, gerror.Wrap(err, "获取音频信息失败")
}
if audioOne.Status != consts.AudioStatusSuccess {
return nil, errors.New("音频未生成成功,无法合成视频")
}
}
// 重置状态为生成中
_, err = dao.Video.UpdateStatus(ctx, req.ID, consts.VideoStatusGenerating, "", "", 0, "", "")
if err != nil {
return nil, err
}
// 异步重新生成视频
go s.generateVideo(ctx, videoOne.DigitalHumanID, videoOne.DigitalHumanName, videoOne.AudioID, "", videoOne.Duration, videoOne.Resolution, req.ID)
res = &dto.GenerateVideoRes{
TaskID: gconv.String(req.ID),
}
return
}
// GetStatusOptions 获取状态选项
func (s *video) GetStatusOptions(ctx context.Context, req *dto.GetVideoStatusOptionsReq) (res *dto.GetVideoStatusOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetVideoStatusOptionsRes)
res.Options = consts.GetAllVideoStatusKeyValue()
return res, nil
}
// GetResolutionOptions 获取分辨率选项
func (s *video) GetResolutionOptions(ctx context.Context, req *dto.GetResolutionOptionsReq) (res *dto.GetResolutionOptionsRes, err error) {
_ = ctx
_ = req
res = new(dto.GetResolutionOptionsRes)
res.Options = consts.GetResolutionOptions()
return res, nil
}
+698
View File
@@ -0,0 +1,698 @@
# Qwen3-TTS Docker 快速部署文档
## 模型版本选择
**当前部署模型:Qwen3-TTS-24Hz-1.7B-Base-VoiceClone**
这是 Qwen3-TTS 系列中**功能最全面、音质最高**的模型版本,支持高质量声音克隆。
### 模型对比表
| 模型名称 | 采样率 | 参数量 | 体积 | 音质 | 速度 | 特殊功能 | 适用场景 |
|---------|--------|--------|------|------|------|---------|---------|
| Qwen3-TTS-12Hz-0.6B-CustomVoice | 12kHz | 0.6B | 1.7GB | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 9种预设声音 | 性能优先 |
| Qwen3-TTS-12Hz-1.7B-CustomVoice | 12kHz | 1.7B | ~4GB | ⭐⭐⭐⭐ | ⭐⭐⭐ | 9种预设声音 | 需要更高音质 |
| Qwen3-TTS-24Hz-0.6B-CustomVoice | 24kHz | 0.6B | ~2GB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 9种预设声音 | 高质量需求 |
| Qwen3-TTS-24Hz-1.7B-CustomVoice | 24kHz | 1.7B | ~5GB | ⭐⭐⭐⭐⭐ | ⭐⭐ | 9种预设声音 | 最高音质(无克隆) |
| Qwen3-TTS-12Hz-Base-VoiceClone | 12kHz | 0.6B | ~2GB | ⭐⭐⭐ | ⭐⭐⭐⭐ | 声音克隆 | 自定义声音 |
| **Qwen3-TTS-24Hz-1.7B-Base-VoiceClone** | **24kHz** | **1.7B** | **~6.8GB** | **⭐⭐⭐⭐⭐** | **⭐⭐** | **声音克隆** | **功能最全面** |
**当前模型特点:**
- **24kHz 采样率**:双倍于 12Hz 模型,音质更清晰自然
- **1.7B 参数**:模型表达能力最强
- **声音克隆**:支持自定义声音训练和生成
- **功能最全面**:兼具基础模型 + 声音克隆功能
**推荐方案:**
- **默认当前选择**`Qwen3-TTS-24Hz-1.7B-Base-VoiceClone`(功能最全面 + 最高音质)
- **性能优先**`Qwen3-TTS-12Hz-0.6B-CustomVoice`
- **高质量(无克隆)**`Qwen3-TTS-24Hz-1.7B-CustomVoice`
---
## 快速开始(3 步部署)
### 步骤 1:下载模型
```bash
# 进入工作目录
cd ~/Qwen3-TTS
mkdir -p model
cd model
# 安装 ModelScope CLI(国内用户推荐)
python3 -m pip install -U modelscope
# 下载 Tokenizer(约 651MB
modelscope download --model Qwen/Qwen3-TTS-Tokenizer-24Hz --local_dir ./Qwen3-TTS-Tokenizer-24Hz
# 下载 Qwen3-TTS-24Hz-1.7B-Base-VoiceClone 模型(约 6.8GB
# 这是功能最全面、音质最高的模型,支持声音克隆
modelscope download --model Qwen/Qwen3-TTS-24Hz-1.7B-Base-VoiceClone --local_dir ./Qwen3-TTS-24Hz-1.7B-Base-VoiceClone
```
**说明:**
- **ModelScope**:阿里云模型托管平台,国内下载速度快
- **Tokenizer**:分词器,将文本转换为模型能理解的 token(24Hz 版本)
- **TTS 模型**:核心模型,1.7B 参数 + 24Hz 采样率 + 声音克隆功能
### 步骤 2:创建文件
```bash
cd ~/Qwen3-TTS
```
#### 创建 app.py(已配置为 24Hz-1.7B-Base-VoiceClone 模型)
```bash
cat > app.py << 'EOF'
from fastapi import FastAPI, Body
import soundfile as sf
import io
import torch
import sys
import base64
# 添加路径以支持导入
sys.path.insert(0, '/app')
from qwen_tts import Qwen3TTSModel
# ========== 当前配置:Qwen3-TTS-24Hz-1.7B-Base-VoiceClone ==========
# 功能最全面、音质最高的模型,支持声音克隆
# - 24kHz 采样率:音质更清晰自然
# - 1.7B 参数:模型表达能力最强
# - Base-VoiceClone:支持自定义声音克隆
MODEL_PATH = '/app/model/Qwen3-TTS-24Hz-1.7B-Base-VoiceClone'
TOKENIZER_PATH = '/app/model/Qwen3-TTS-Tokenizer-24Hz'
# 如需切换到其他模型,请修改以下路径并重新构建镜像
# ==========================================================
app = FastAPI(title="Qwen3-TTS API")
# 全局模型
model = None
@app.on_event("startup")
async def startup_event():
global model
print("正在加载 TTS 模型...")
print(f"模型路径: {MODEL_PATH}")
print(f"模型版本: Qwen3-TTS-24Hz-1.7B-Base-VoiceClone")
print("提示: 1.7B 模型加载需要较长时间,请耐心等待...")
model = Qwen3TTSModel.from_pretrained(
MODEL_PATH,
tokenizer_path=TOKENIZER_PATH,
device_map='cpu',
dtype=torch.float32
)
print("TTS 模型初始化完成")
@app.get("/")
async def root():
return {
"status": "running",
"mode": "production",
"model": "Qwen3-TTS-24Hz-1.7B-Base-VoiceClone",
"features": ["voice_clone", "high_quality", "24khz"]
}
@app.post("/tts")
async def tts(text: str = Body(..., media_type='application/json')):
"""
注意:必须使用 Body(...) 解析请求体,否则会返回 422 错误
请求格式: POST /ttsBody 为 JSON 字符串 "文本内容"
超时设置:长文本推理可能需要较长时间,建议客户端设置超时时间 > 120 秒
CPU 推理速度:约 0.5-2 秒/字符(1.7B 模型较慢,比 0.6B 模型慢约 2-3 倍)
"""
if not model:
return {"code": 500, "msg": "模型未初始化"}
try:
print(f"收到TTS请求,文本长度: {len(text)} 字符")
wavs, sr = model.generate_custom_voice(text, speaker='serena')
print(f"音频生成完成,采样率: {sr}, 音频时长: {len(wavs[0])/sr:.2f}秒")
# 转换为 WAV 格式
buffer = io.BytesIO()
sf.write(buffer, wavs[0], sr, format='WAV')
buffer.seek(0)
audio_data = buffer.read()
audio_b64 = base64.b64encode(audio_data).decode('utf-8')
print(f"编码完成,base64 长度: {len(audio_b64)}")
return {
"code": 0,
"msg": "success",
"text": text,
"audio": audio_b64
}
except Exception as e:
import traceback
print(f"Error: {traceback.format_exc()}")
return {"code": 500, "msg": f"TTS处理错误: {str(e)}"}
EOF
```
#### 创建 requirements.txt
```bash
cat > requirements.txt << 'EOF'
fastapi>=0.104.0
uvicorn>=0.24.0
numpy>=1.24.0
torch>=2.0.0
librosa>=0.10.0
soundfile>=0.12.0
safetensors>=0.4.0
qwen-tts>=0.1.0
EOF
```
#### 创建 Dockerfile
```bash
cat > Dockerfile.bak << 'EOF'
FROM python:3.12-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
git \
ffmpeg \
libsndfile1 \
&& rm -rf /var/lib/apt/lists/*
# 安装 qwen-tts(包含 qwen_tts 模块)
RUN pip install --no-cache-dir qwen-tts
# 复制应用文件
COPY app.py .
COPY requirements.txt .
# 安装额外依赖
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8000
# 增加超时设置(180秒,1.7B 模型需要更长时间)
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--timeout-keep-alive", "180", "--limit-concurrency", "1"]
EOF
```
### 步骤 3:构建并启动
```bash
cd ~/Qwen3-TTS
# 构建镜像
docker build -t qwen3-tts:latest .
# 启动容器(挂载模型目录,增加内存限制)
docker run -d \
--name tts-service \
-p 8000:8000 \
-v ~/Qwen3-TTS/model:/app/model:ro \
--memory="8g" \
--restart unless-stopped \
qwen3-tts:latest
# 等待服务启动(1.7B 模型需要更长时间,约15-30秒)
sleep 20
# 验证服务
curl http://localhost:8000/
```
**预期输出:**
```json
{
"status": "running",
"mode": "production",
"model": "Qwen3-TTS-24Hz-1.7B-Base-VoiceClone",
"features": ["voice_clone", "high_quality", "24khz"]
}
```
---
## 切换到其他模型版本
### 切换到性能优先模型(12Hz-0.6B-CustomVoice
```bash
# 1. 下载新模型
cd ~/Qwen3-TTS/model
modelscope download --model Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice --local_dir ./Qwen3-TTS-12Hz-0.6B-CustomVoice
modelscope download --model Qwen/Qwen3-TTS-Tokenizer-12Hz --local_dir ./Qwen3-TTS-Tokenizer-12Hz
# 2. 修改 app.py 中的 MODEL_PATH 和 TOKENIZER_PATH
# MODEL_PATH = '/app/model/Qwen3-TTS-12Hz-0.6B-CustomVoice'
# TOKENIZER_PATH = '/app/model/Qwen3-TTS-Tokenizer-12Hz'
# 3. 重新构建并启动
cd ~/Qwen3-TTS
docker stop tts-service
docker build -t qwen3-tts:v12hz .
docker run -d --name tts-service -p 8000:8000 -v ~/Qwen3-TTS/model:/app/model:ro qwen3-tts:v12hz
```
### 切换到最高音质模型(24Hz-1.7B-CustomVoice,无声音克隆)
```bash
# 1. 下载新模型
cd ~/Qwen3-TTS/model
modelscope download --model Qwen/Qwen3-TTS-24Hz-1.7B-CustomVoice --local_dir ./Qwen3-TTS-24Hz-1.7B-CustomVoice
# 2. 修改 app.py 中的路径
# MODEL_PATH = '/app/model/Qwen3-TTS-24Hz-1.7B-CustomVoice'
# TOKENIZER_PATH = '/app/model/Qwen3-TTS-Tokenizer-24Hz'
# 3. 重新构建并启动
cd ~/Qwen3-TTS
docker stop tts-service
docker build -t qwen3-tts:v24hz-noclone .
docker run -d --name tts-service -p 8000:8000 -v ~/Qwen3-TTS/model:/app/model:ro qwen3-tts:v24hz-noclone
```
---
## API 使用
### 健康检查
```bash
curl http://localhost:8000/
```
### 文本转语音
```bash
curl -X POST http://localhost:8000/tts \
-H "Content-Type: application/json" \
-d '"你好,这是一个测试"'
```
**注意:** Body 必须是 JSON 字符串格式,不能是 JSON 对象 `{"text": "..."}`
**响应示例:**
```json
{
"code": 0,
"msg": "success",
"text": "你好,这是一个测试",
"audio": "UklGRiTCAQBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAAZGF0YQD..."
}
```
### 长文本处理
**重要提示:**
- 1.7B 模型推理速度较慢(约 1-3 秒/字符)
- 短文本(< 20 字):约 15-30 秒
- 中等文本(20-50 字):约 50-120 秒
- 长文本(50-100 字):约 120-240 秒
**客户端必须设置超时时间 >= 180 秒**,否则会收到 `EOF` 错误。
### Go 调用示例(带超时设置)
```go
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type TTSResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Text string `json:"text"`
Audio string `json:"audio"`
}
func TTS(text string) ([]byte, error) {
// 必须使用 JSON 字符串格式:`"文本内容"`
jsonText := fmt.Sprintf(`"%s"`, text)
// 创建带超时的 HTTP 客户端(180秒超时,1.7B 模型需要更长时间)
client := &http.Client{
Timeout: 180 * time.Second,
}
resp, err := client.Post(
"http://localhost:8000/tts",
"application/json",
bytes.NewBufferString(jsonText),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result TTSResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
if result.Code != 0 {
return nil, fmt.Errorf("TTS error: %s", result.Msg)
}
return base64.StdEncoding.DecodeString(result.Audio)
}
func main() {
// 长文本测试
longText := "欢迎使用红动未来数字人服务平台,我们将为您提供最优质的AI数字人解决方案。人工智能技术正在改变我们的生活,让我们一起探索未来的无限可能。"
audio, err := TTS(longText)
if err != nil {
panic(err)
}
os.WriteFile("output.wav", audio, 0644)
fmt.Println("音频已保存到 output.wav")
}
```
### Python 调用示例(带超时设置)
```python
import requests
import base64
def tts(text, timeout=180):
"""
TTS 文本转语音
Args:
text: 要转换的文本
timeout: 超时时间(秒),1.7B 模型建议 >= 180 秒
"""
# 必须使用 JSON 字符串格式
response = requests.post(
"http://localhost:8000/tts",
data=f'"{text}"',
headers={"Content-Type": "application/json"},
timeout=timeout # 设置超时
)
result = response.json()
if result["code"] != 0:
raise Exception(f"TTS error: {result['msg']}")
return base64.b64decode(result["audio"])
# 使用
short_text = "你好,这是一个测试"
long_text = "欢迎使用红动未来数字人服务平台,我们将为您提供最优质的AI数字人解决方案。"
# 短文本(30秒超时)
audio_data = tts(short_text, timeout=30)
with open("short_output.wav", "wb") as f:
f.write(audio_data)
# 长文本(180秒超时)
audio_data = tts(long_text, timeout=180)
with open("long_output.wav", "wb") as f:
f.write(audio_data)
```
---
## 服务管理
```bash
# 查看日志
docker logs -f tts-service
# 查看最近50行日志
docker logs --tail 50 tts-service
# 停止服务
docker stop tts-service
# 启动服务
docker start tts-service
# 重启服务
docker restart tts-service
# 删除容器
docker stop tts-service && docker rm tts-service
# 删除镜像
docker rmi qwen3-tts:latest
# 进入容器
docker exec -it tts-service /bin/bash
```
---
## 故障排查
### 1. 端口被占用
```bash
# 查找占用 8000 端口的进程
lsof -ti:8000
# 停止占用端口的进程
lsof -ti:8000 | xargs kill -9
# 或修改映射端口
docker run -d -p 8001:8000 --name tts-service qwen3-tts:latest
```
### 2. API 返回 422 错误
**原因:** 请求格式不正确,必须使用 JSON 字符串格式
**正确请求:**
```bash
curl -X POST http://localhost:8000/tts \
-H "Content-Type: application/json" \
-d '"你好"'
```
**错误请求:**
```bash
# ❌ 错误:这是 JSON 对象,不是字符串
curl -X POST http://localhost:8000/tts \
-d '{"text": "你好"}'
```
### 3. 长文本返回 EOF 错误
**原因:** 1.7B 模型推理更慢,长文本处理时间超过客户端超时时间
**解决方案:**
1. **客户端设置超时 >= 180 秒**
2. **缩短文本长度**(建议单次请求 < 100 字)
3. **使用 GPU 加速**(如果可用)
4. **切换到 0.6B 模型**(更快但质量略低)
**Go 客户端:**
```go
client := &http.Client{
Timeout: 180 * time.Second, // 设置 180 秒超时
}
```
**Python 客户端:**
```python
response = requests.post(
"http://localhost:8000/tts",
data=f'"{text}"',
timeout=180 # 设置 180 秒超时
)
```
### 4. 音频无声音或文件过小
```bash
# 查看日志检查模型是否加载
docker logs tts-service | grep "TTS 模型初始化完成"
# 检查模型文件
docker exec tts-service ls -la /app/model/
# 测试 API 返回的音频数据大小(应该 > 10KB)
curl -s http://localhost:8000/tts -d '"测试"' | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['audio']))"
```
### 5. 内存不足
```bash
# 增加内存限制(1.7B 模型建议 >= 8GB
docker run -d --name tts-service -p 8000:8000 --memory="8g" qwen3-tts:latest
# 或增加更多内存
docker run -d --name tts-service -p 8000:8000 --memory="12g" qwen3-tts:latest
```
### 6. 服务启动后无法访问
```bash
# 检查容器状态
docker ps | grep tts-service
# 检查端口映射
docker port tts-service
# 检查服务是否正常响应
curl http://localhost:8000/
```
### 7. 推理速度过慢
**1.7B 模型优化方案:**
```bash
# 限制并发为 1(避免 CPU 争抢)
docker run -d --name tts-service -p 8000:8000 qwen3-tts:latest \
uvicorn app:app --limit-concurrency 1
# 增加 CPU 资源
docker run -d --name tts-service -p 8000:8000 --cpus="8.0" qwen3-tts:latest
```
**GPU 加速(需要 NVIDIA GPU):**
```bash
# 修改 app.py 中的 device_map='cpu' 为 device_map='cuda:0'
# 重新构建镜像并运行
docker run -d --name tts-service --gpus all -p 8000:8000 qwen3-tts:latest
```
**切换到更快的模型:**
- 如果对速度要求高,可切换到 `Qwen3-TTS-12Hz-0.6B-CustomVoice`
- 推理速度可提升 3-5 倍
### 8. 模型加载失败
**检查模型路径:**
```bash
# 查看容器内模型目录
docker exec tts-service ls -la /app/model/
# 确认 app.py 中的 MODEL_PATH 和 TOKENIZER_PATH 正确
docker exec tts-service cat /app/app.py | grep "MODEL_PATH\|TOKENIZER_PATH"
```
---
## 常见问题 FAQ
**Q: 为什么选择 Qwen3-TTS-24Hz-1.7B-Base-VoiceClone**
A: 这是 Qwen3-TTS 系列中功能最全面、音质最高的模型:
- **24kHz 采样率**:双倍于 12Hz 模型,音质更清晰自然
- **1.7B 参数**:模型表达能力最强
- **声音克隆**:支持自定义声音训练和生成
**Q: 1.7B 模型推理速度慢怎么办?**
A: 可以采取以下措施:
1. 客户端设置超时 >= 180 秒
2. 使用 GPU 加速(速度提升 10-20 倍)
3. 切换到 0.6B 模型(速度提升 3-5 倍)
4. 缩短单次请求文本长度
**Q: 为什么 Body 必须是 JSON 字符串而不是 JSON 对象?**
A: FastAPI 使用 `Body(..., media_type='application/json')` 解析时,直接接收 JSON 字符串。如果使用 `{"text": "..."}` 格式,需要修改 `app.py` 使用 Pydantic 模型。当前实现更简洁,直接传递字符串即可。
**Q: 24Hz 和 12Hz 模型有什么区别?**
A:
- **24Hz**:采样率 24kHz,音质更清晰自然,适合高质量需求
- **12Hz**:采样率 12kHz,推理速度快,适合实时应用
**Q: 1.7B 和 0.6B 模型有什么区别?**
A:
- **1.7B**:参数量更大,音质更高,但推理速度慢,内存占用大(推荐 GPU)
- **0.6B**:参数量小,推理快,内存占用少,适合 CPU 环境
**Q: CustomVoice 和 Base 模型有什么区别?**
A:
- **CustomVoice**:内置 9 种预设声音,开箱即用
- **Base**:基础模型,支持自定义训练和声音克隆
**Q: 长文本推理需要多长时间?**
A: 1.7B 模型 CPU 推理速度约 1-3 秒/字符:
- 短文本(< 20 字):约 15-30 秒
- 中等文本(20-50 字):约 50-120 秒
- 长文本(50-100 字):约 120-240 秒
**Q: 为什么长文本会返回 EOF 错误?**
A: 1.7B 模型推理时间长,如果客户端超时时间设置过短会断开连接。解决方案:
1. 客户端设置超时 >= 180 秒
2. 缩短单次请求文本长度
3. 使用 GPU 加速
4. 切换到 0.6B 模型
**Q: 支持哪些声音?**
A: CustomVoice 模型支持 9 种预设声音:serena, vivian, uncle_fu, ryan, aiden, ono_anna, sohee, eric, dylan
**Q: 可以自定义声音吗?**
A: 可以,Base-VoiceClone 模型支持声音克隆功能,详见 Qwen3-TTS 官方文档
**Q: 支持哪些语言?**
A: 中文、英文、日语、韩语、德语、法语、俄语、葡萄牙语、西班牙语、意大利语
**Q: 音频采样率是多少?**
A: 24kHz (24000 Hz) - 比标准 CD 音质(44.1kHz)略低,但比 12Hz 模型清晰很多
**Q: 生成的音频文件格式是什么?**
A: WAV 格式,Microsoft PCM, 16 bit, mono
**Q: 如何提高推理速度?**
A:
1. 使用 GPU 加速(device_map='cuda:0'
2. 使用更小的模型(0.6B 而非 1.7B)
3. 使用 12Hz 模型而非 24Hz 模型
4. 限制并发请求(limit-concurrency=1
5. 增加 CPU 核心数(--cpus="8.0"
---
## 目录结构
```
~/Qwen3-TTS/
├── app.py # FastAPI 服务代码
├── Dockerfile # Docker 镜像构建文件
├── requirements.txt # Python 依赖
└── model/ # 模型文件目录
├── Qwen3-TTS-Tokenizer-24Hz/ # 24Hz 分词器(651MB
└── Qwen3-TTS-24Hz-1.7B-Base-VoiceClone/ # 24Hz 1.7B 模型(6.8GB
```
---
## 相关链接
- 官方文档:https://github.com/QwenLM/Qwen3-TTS
- ModelScopehttps://modelscope.cn/models?name=Qwen3-TTS
- FastAPI 文档:https://fastapi.tiangolo.com/
+119
View File
@@ -0,0 +1,119 @@
module ai-agent
go 1.26.0
require (
gitea.redpowerfuture.com/red-future/common v0.0.29
github.com/cloudwego/eino v0.9.12
github.com/cloudwego/eino-ext/components/model/qwen v0.1.9
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
github.com/gogf/gf/v2 v2.10.2
github.com/google/uuid v1.6.0
github.com/tidwall/gjson v1.19.0
github.com/tidwall/sjson v1.2.5
go.opentelemetry.io/otel/trace v1.44.0
)
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17 // indirect
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/eino-contrib/jsonschema v1.0.3 // indirect
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
github.com/evanphx/json-patch v0.5.2 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-ego/gse v1.0.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 // indirect
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/glog v1.2.5 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/flatbuffers v1.12.1 // indirect
github.com/goph/emperror v0.17.2 // 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
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/meguminnnnnnnnn/go-openai v0.1.2 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.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.2.4 // indirect
github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f // indirect
github.com/r3labs/diff/v2 v2.15.1 // indirect
github.com/redis/go-redis/v9 v9.17.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tiger1103/gfast-token v1.0.10 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/vcaesar/cedar v0.30.0 // indirect
github.com/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
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/arch v0.19.0 // indirect
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+574
View File
@@ -0,0 +1,574 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.redpowerfuture.com/red-future/common v0.0.29 h1:5McaN5pSewvrLUHQzWMX6EaUvD+B5I5bMYoU+clHJk4=
gitea.redpowerfuture.com/red-future/common v0.0.29/go.mod h1:50U1Xi+Ie56z09S5LQbZvaken0Mxv3OeS9LgR7U/ZRY=
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=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/mockey v1.3.0 h1:ONLRdvhqmCfr9rTasUB8ZKCfvbdD2tohOg4u+4Q/ed0=
github.com/bytedance/mockey v1.3.0/go.mod h1:1BPHF9sol5R1ud/+0VEHGQq/+i2lN+GTsr3O2Q9IENY=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/cloudwego/eino v0.9.12 h1:mHAMo5k7GdvnVD8Lc2sLyfpkxEm0S/y3PkEMhsSYt78=
github.com/cloudwego/eino v0.9.12/go.mod h1:OBD1mrkfkt/pJa4rkg1P0VnaMeOVl7l8IAdEqY//3IQ=
github.com/cloudwego/eino-ext/components/model/qwen v0.1.9 h1:xCz/mp43JeWqupjPR3zLRArmwC6P29/6lTwbwh1yzYM=
github.com/cloudwego/eino-ext/components/model/qwen v0.1.9/go.mod h1:slTGTuhzkzhNavf+1UtUg1FvUSA31iNAF+rq1mT4SnI=
github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17 h1:EeVcR1TslRA2IdNW1h/2LaGbPlffwGhQm99jM3zWZiI=
github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17/go.mod h1:Zkcx6DPTR2NfWmtSXbhItswGw6hqUezNPhNcke0pOG8=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0=
github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4=
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4=
github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2 h1:u8EpP24GkprogROnJ7htMov9Fc66pTP1eVYrWxiCYOs=
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2/go.mod h1:GmvM3r8GVByVMi4RD2+MCs5+CfxVXPMeT8mVDkAaAXE=
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2 h1:iTQegT+lEg/wDKvj2mi3W1wrdrwFarjokf88EXVVgu4=
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2/go.mod h1:ZRw3GNz5cq4uYrW4TPSVyrYWaoqzujKdWro/AOcGBaE=
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 h1:eUqwJ/qNH8lJ6yssiqskazgp1ACQuNU6zXlLOZVuXTQ=
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5/go.mod h1:sjQyMry9+0POYZCA6lHXBxO77WoNKkruJpRB4xKqk5k=
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 h1:tHUEZYB5GTqEYYVDYnlGobf1xISARKDE4KHVlgjwTec=
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5/go.mod h1:cfzTn2HS9RDX8f5pUVkbGxUWcSosouqfNQ1G6cY0V88=
github.com/gogf/gf/v2 v2.10.2 h1:46IO0Uc8e85/FqdftJFskfDejJLBL0JBnGS5qOftUu8=
github.com/gogf/gf/v2 v2.10.2/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18=
github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.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=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/meguminnnnnnnnn/go-openai v0.1.2 h1:iXombGGjqjBrmE9WaSidUhhi3YQhf42QTHvHLMkgvCA=
github.com/meguminnnnnnnnn/go-openai v0.1.2/go.mod h1:qs96ysDmxhE4BZoU45I43zcyfnaYxU3X+aRzLko/htY=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c=
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f h1:lJqhwddJVYAkyp72a4pwzMClI20xTwL7miDdm2W/KBM=
github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
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.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f h1:Z2cODYsUxQPofhpYRMQVwWz4yUVpHF+vPi+eUdruUYI=
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f/go.mod h1:JqzWyvTuI2X4+9wOHmKSQCYxybB/8j6Ko43qVmXDuZg=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y=
github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
github.com/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=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+45
View File
@@ -0,0 +1,45 @@
package main
import (
digitalhumanController "ai-agent/digital-human/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.redpowerfuture.com/red-future/common/http"
"gitea.redpowerfuture.com/red-future/common/jaeger"
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
)
func main() {
ctx := context.Background()
defer jaeger.ShutDown(ctx)
// 注册路由
http.Httpserver.BindHandler("/httpNodeCallback", workflowController.FlowCallBack.HttpNodeCallback)
http.RouteRegister([]interface{}{
//digitalhuman相关接口
digitalhumanController.Audio, // 语音相关接口
digitalhumanController.CustomVoice, // 自定义语音相关接口
digitalhumanController.DigitalHuman, // 数字人相关接口
digitalhumanController.Video, // 视频相关接口
digitalhumanController.AsyncTask, // 异步任务相关接口
workController.CreationInfo,
workflowController.FlowExecution,
workflowController.FlowUser,
workflowController.FlowTemplate,
workflowNodeController.NodeLibrary,
workflowNodeController.NodePrompt,
workflowSkillController.SkillTemplate,
workflowSkillController.SkillUser,
})
//workflow.ExternalInterruptDemo()
//err := activePullService.ActivePullService.AllList(ctx)
//if err != nil {
// g.Log().Error(ctx, "ActivePullService err: %v", err)
//}
// 保持应用运行
select {}
}
+664
View File
@@ -0,0 +1,664 @@
-- -----------------------张斌2025-06-16 15:00:00-----------------------
--------------------pgsql创建digital_human_audio表语句---------------------------
-- 音频表
CREATE TABLE IF NOT EXISTS digital_human_audio (
-- 基础字段(继承 SQLBaseCol 通用字段,与 SQLBaseDO 对齐)
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8类型
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- 音频核心字段
name VARCHAR(128) NOT NULL, -- 音频名称
description TEXT DEFAULT '', -- 音频描述
script_text TEXT NOT NULL, -- 话术文本
audio_url VARCHAR(512) DEFAULT '', -- 音频文件URL
status SMALLINT NOT NULL DEFAULT 0, -- 状态:0生成中/1成功/2失败
error_msg TEXT DEFAULT '', -- 错误信息
duration INT DEFAULT 0, -- 音频时长(秒)
external_id VARCHAR(64) DEFAULT '', -- 外部音频ID
voice VARCHAR(32) DEFAULT 'serena', -- 音色:serena/vivian/uncle_fu/ryan/aiden/ono_anna/sohee/eric/dylan
voice_type VARCHAR(16) DEFAULT 'preset', -- 音色类型:preset/custom(预设/克隆)
custom_voice VARCHAR(64) DEFAULT '' -- 自定义音色ID(用于声音克隆)
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_audio_tenant_id ON digital_human_audio(tenant_id);
CREATE INDEX IF NOT EXISTS idx_audio_status ON digital_human_audio(status);
CREATE INDEX IF NOT EXISTS idx_audio_voice_type ON digital_human_audio(voice_type);
CREATE INDEX IF NOT EXISTS idx_audio_deleted_at ON digital_human_audio(deleted_at);
-- 表和字段注释
COMMENT ON TABLE digital_human_audio IS '音频表';
COMMENT ON COLUMN digital_human_audio.id IS '主键ID(非自增)';
COMMENT ON COLUMN digital_human_audio.tenant_id IS '租户ID';
COMMENT ON COLUMN digital_human_audio.creator IS '创建人';
COMMENT ON COLUMN digital_human_audio.created_at IS '创建时间';
COMMENT ON COLUMN digital_human_audio.updater IS '更新人';
COMMENT ON COLUMN digital_human_audio.updated_at IS '更新时间';
COMMENT ON COLUMN digital_human_audio.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN digital_human_audio.name IS '音频名称';
COMMENT ON COLUMN digital_human_audio.description IS '音频描述';
COMMENT ON COLUMN digital_human_audio.script_text IS '话术文本';
COMMENT ON COLUMN digital_human_audio.audio_url IS '音频文件URL';
COMMENT ON COLUMN digital_human_audio.status IS '状态:0生成中/1成功/2失败';
COMMENT ON COLUMN digital_human_audio.error_msg IS '错误信息';
COMMENT ON COLUMN digital_human_audio.duration IS '音频时长(秒)';
COMMENT ON COLUMN digital_human_audio.external_id IS '外部音频ID';
COMMENT ON COLUMN digital_human_audio.voice IS '音色:serena/vivian/uncle_fu/ryan/aiden/ono_anna/sohee/eric/dylan';
COMMENT ON COLUMN digital_human_audio.voice_type IS '音色类型:preset/custom(预设/克隆)';
COMMENT ON COLUMN digital_human_audio.custom_voice IS '自定义音色ID';
--------------------pgsql创建digital_human_custom_voice表语句---------------------------
-- 自定义音色表
CREATE TABLE IF NOT EXISTS digital_human_custom_voice (
-- 基础字段(继承 SQLBaseCol 通用字段)
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8类型
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- 音色核心字段
name VARCHAR(128) NOT NULL, -- 音色名称
description TEXT DEFAULT '', -- 音色描述
text TEXT DEFAULT '', -- 参考文本
reference_audio BYTEA -- 参考音频数据(二进制)
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_custom_voice_tenant_id ON digital_human_custom_voice(tenant_id);
CREATE INDEX IF NOT EXISTS idx_custom_voice_name ON digital_human_custom_voice(name);
CREATE INDEX IF NOT EXISTS idx_custom_voice_deleted_at ON digital_human_custom_voice(deleted_at);
-- 表和字段注释
COMMENT ON TABLE digital_human_custom_voice IS '自定义音色表';
COMMENT ON COLUMN digital_human_custom_voice.id IS '主键ID(非自增)';
COMMENT ON COLUMN digital_human_custom_voice.tenant_id IS '租户ID';
COMMENT ON COLUMN digital_human_custom_voice.creator IS '创建人';
COMMENT ON COLUMN digital_human_custom_voice.created_at IS '创建时间';
COMMENT ON COLUMN digital_human_custom_voice.updater IS '更新人';
COMMENT ON COLUMN digital_human_custom_voice.updated_at IS '更新时间';
COMMENT ON COLUMN digital_human_custom_voice.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN digital_human_custom_voice.name IS '音色名称';
COMMENT ON COLUMN digital_human_custom_voice.description IS '音色描述';
COMMENT ON COLUMN digital_human_custom_voice.text IS '参考文本';
COMMENT ON COLUMN digital_human_custom_voice.reference_audio IS '参考音频数据(二进制)';
-- 兼容已有库:自定义音色增加状态/结果字段(对接异步模型服务)
ALTER TABLE digital_human_custom_voice ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 0;
ALTER TABLE digital_human_custom_voice ADD COLUMN IF NOT EXISTS error_msg TEXT DEFAULT '';
ALTER TABLE digital_human_custom_voice ADD COLUMN IF NOT EXISTS oss_file VARCHAR(512) DEFAULT '';
COMMENT ON COLUMN digital_human_custom_voice.status IS '状态:0生成中/1成功/2失败';
COMMENT ON COLUMN digital_human_custom_voice.error_msg IS '错误信息';
COMMENT ON COLUMN digital_human_custom_voice.oss_file IS '结果文件URL(如参考音频/特征文件等)';
CREATE INDEX IF NOT EXISTS idx_custom_voice_status ON digital_human_custom_voice(status);
--------------------pgsql创建digital_human_video表语句---------------------------
-- 视频表
CREATE TABLE IF NOT EXISTS digital_human_video (
-- 基础字段
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, -- 视频名称
description TEXT DEFAULT '', -- 视频描述
audio_id BIGINT, -- 关联音频ID
script_text TEXT NOT NULL, -- 话术文本
video_url VARCHAR(512) DEFAULT '', -- 视频文件URL
status SMALLINT NOT NULL DEFAULT 0, -- 状态:0生成中/1成功/2失败
error_msg TEXT DEFAULT '', -- 错误信息
duration INT DEFAULT 0, -- 视频时长(秒)
thumbnail_url VARCHAR(512) DEFAULT '', -- 缩略图URL
external_id VARCHAR(64) DEFAULT '', -- 外部视频ID
digital_human_id BIGINT DEFAULT 0, -- 数字人ID(雪花算法ID
digital_human_name VARCHAR(128) DEFAULT '' -- 数字人名称(冗余字段)
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_video_tenant_id ON digital_human_video(tenant_id);
CREATE INDEX IF NOT EXISTS idx_video_audio_id ON digital_human_video(audio_id);
CREATE INDEX IF NOT EXISTS idx_video_status ON digital_human_video(status);
CREATE INDEX IF NOT EXISTS idx_video_deleted_at ON digital_human_video(deleted_at);
-- 表和字段注释
COMMENT ON TABLE digital_human_video IS '视频表';
COMMENT ON COLUMN digital_human_video.id IS '主键ID(非自增)';
COMMENT ON COLUMN digital_human_video.tenant_id IS '租户ID';
COMMENT ON COLUMN digital_human_video.audio_id IS '关联音频ID';
COMMENT ON COLUMN digital_human_video.name IS '视频名称';
COMMENT ON COLUMN digital_human_video.description IS '视频描述';
COMMENT ON COLUMN digital_human_video.script_text IS '话术文本';
COMMENT ON COLUMN digital_human_video.video_url IS '视频文件URL';
COMMENT ON COLUMN digital_human_video.status IS '状态:0生成中/1成功/2失败';
COMMENT ON COLUMN digital_human_video.error_msg IS '错误信息';
COMMENT ON COLUMN digital_human_video.duration IS '视频时长(秒)';
COMMENT ON COLUMN digital_human_video.thumbnail_url IS '缩略图URL';
COMMENT ON COLUMN digital_human_video.external_id IS '外部视频ID';
COMMENT ON COLUMN digital_human_video.digital_human_id IS '数字人ID';
COMMENT ON COLUMN digital_human_video.digital_human_name IS '数字人名称(冗余字段)';
--------------------pgsql创建digital_human表语句---------------------------
-- 数字人表
CREATE TABLE IF NOT EXISTS digital_human (
-- 基础字段
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, -- 数字人名称
description TEXT DEFAULT '', -- 数字人描述
avatar_url VARCHAR(512) DEFAULT '', -- 头像URL
video_url VARCHAR(512) DEFAULT '', -- 形象视频URL
voice VARCHAR(32) DEFAULT 'serena', -- 默认音色
status SMALLINT NOT NULL DEFAULT 1 -- 状态:1启用/0停用
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_digital_human_tenant_id ON digital_human(tenant_id);
CREATE INDEX IF NOT EXISTS idx_digital_human_name ON digital_human(name);
CREATE INDEX IF NOT EXISTS idx_digital_human_status ON digital_human(status);
CREATE INDEX IF NOT EXISTS idx_digital_human_deleted_at ON digital_human(deleted_at);
-- 表和字段注释
COMMENT ON TABLE digital_human IS '数字人表';
COMMENT ON COLUMN digital_human.id IS '主键ID(非自增)';
COMMENT ON COLUMN digital_human.tenant_id IS '租户ID';
COMMENT ON COLUMN digital_human.creator IS '创建人';
COMMENT ON COLUMN digital_human.created_at IS '创建时间';
COMMENT ON COLUMN digital_human.updater IS '更新人';
COMMENT ON COLUMN digital_human.updated_at IS '更新时间';
COMMENT ON COLUMN digital_human.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN digital_human.name IS '数字人名称';
COMMENT ON COLUMN digital_human.description IS '数字人描述';
COMMENT ON COLUMN digital_human.avatar_url IS '头像URL';
COMMENT ON COLUMN digital_human.video_url IS '形象视频URL';
COMMENT ON COLUMN digital_human.voice IS '默认音色';
COMMENT ON COLUMN digital_human.status IS '状态:1启用/0停用';
--------------------pgsql创建digital_human_async_task_ref表语句---------------------------
-- 异步任务绑定表(task_id -> 业务表+业务ID
CREATE TABLE IF NOT EXISTS digital_human_async_task_ref (
-- 基础字段
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID
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),
-- 绑定字段
task_id VARCHAR(64) NOT NULL, -- 异步任务IDmodel-asynch
state SMALLINT NOT NULL DEFAULT 0, -- 任务状态(与 model-asynch 对齐:0/1/2/3/4
table_name VARCHAR(64) NOT NULL, -- 业务表名:digital_human_audio / digital_human_custom_voice
biz_id BIGINT NOT NULL, -- 业务表主键IDaudio/custom_voice 的 id
oss_file VARCHAR(512) DEFAULT '', -- 已转移后的业务侧OSS地址(可选)
error_msg TEXT DEFAULT '' -- 错误信息(可选)
);
-- 索引
CREATE UNIQUE INDEX IF NOT EXISTS uk_async_task_ref_tenant_task_id ON digital_human_async_task_ref(tenant_id, task_id);
CREATE INDEX IF NOT EXISTS idx_async_task_ref_tenant_state ON digital_human_async_task_ref(tenant_id, state);
CREATE INDEX IF NOT EXISTS idx_async_task_ref_table_biz ON digital_human_async_task_ref(table_name, biz_id);
CREATE INDEX IF NOT EXISTS idx_async_task_ref_deleted_at ON digital_human_async_task_ref(deleted_at);
-- 注释
COMMENT ON TABLE digital_human_async_task_ref IS '异步任务绑定表(task_id -> 业务表+业务ID';
COMMENT ON COLUMN digital_human_async_task_ref.task_id IS '异步任务IDmodel-asynch';
COMMENT ON COLUMN digital_human_async_task_ref.state IS '任务状态(与 model-asynch 对齐:0/1/2/3/4';
COMMENT ON COLUMN digital_human_async_task_ref.table_name IS '业务表名';
COMMENT ON COLUMN digital_human_async_task_ref.biz_id IS '业务表主键ID';
COMMENT ON COLUMN digital_human_async_task_ref.oss_file IS '已转移后的业务侧OSS地址';
COMMENT ON COLUMN digital_human_async_task_ref.error_msg IS '错误信息';
-- =============================================================
-- 低代码流程编排平台 - 数据库表结构
-- Author: AI Assistant
-- =============================================================
-- 素材/创作信息表
CREATE TABLE IF NOT EXISTS black_deacon_creation_info (
-- 基础字段(完全对齐项目规范)
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- 业务字段
html_file_url VARCHAR(512) DEFAULT '', -- HTML文件地址
image_urls TEXT[] DEFAULT '{}', -- 图片地址列表
content_type VARCHAR(255) DEFAULT '', -- 素材类型
theme VARCHAR(255) DEFAULT '', -- 主题
title VARCHAR(255) NOT NULL -- 标题
);
-- 索引(高频查询)
CREATE INDEX idx_creation_tenant_id ON black_deacon_creation_info(tenant_id);
CREATE INDEX idx_creation_content_type ON black_deacon_creation_info(content_type);
CREATE INDEX idx_creation_theme ON black_deacon_creation_info(theme);
CREATE INDEX idx_creation_title ON black_deacon_creation_info(title);
CREATE INDEX idx_creation_deleted_at ON black_deacon_creation_info(deleted_at);
-- 表和字段注释
COMMENT ON TABLE black_deacon_creation_info IS '素材/创作信息表';
COMMENT ON COLUMN black_deacon_creation_info.id IS '主键ID(非自增)';
COMMENT ON COLUMN black_deacon_creation_info.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_creation_info.creator IS '创建人';
COMMENT ON COLUMN black_deacon_creation_info.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_creation_info.updater IS '更新人';
COMMENT ON COLUMN black_deacon_creation_info.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_creation_info.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_creation_info.html_file_url IS 'HTML文件地址';
COMMENT ON COLUMN black_deacon_creation_info.image_urls IS '图片地址列表';
COMMENT ON COLUMN black_deacon_creation_info.content_type IS '素材类型';
COMMENT ON COLUMN black_deacon_creation_info.theme IS '主题';
COMMENT ON COLUMN black_deacon_creation_info.title IS '标题';
--------------------pgsql创建creation_info表语句---------------------------
--------------------pgsql创建black_deacon_file_temp表语句---------------------------
-- 临时文件表
CREATE TABLE IF NOT EXISTS black_deacon_file_temp (
-- 基础字段(完全对齐项目规范)
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),
-- 业务字段
business_id VARCHAR(255) NOT NULL DEFAULT '',
file_url VARCHAR(512) NOT NULL DEFAULT ''
);
-- 索引
CREATE INDEX idx_file_temp_tenant_id ON black_deacon_file_temp(tenant_id);
CREATE INDEX idx_file_temp_business_id ON black_deacon_file_temp(business_id);
CREATE INDEX idx_file_temp_file_url ON black_deacon_file_temp(file_url);
CREATE INDEX idx_file_temp_deleted_at ON black_deacon_file_temp(deleted_at);
-- 注释
COMMENT ON TABLE black_deacon_file_temp IS '临时文件表';
COMMENT ON COLUMN black_deacon_file_temp.id IS '主键ID';
COMMENT ON COLUMN black_deacon_file_temp.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_file_temp.creator IS '创建人';
COMMENT ON COLUMN black_deacon_file_temp.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_file_temp.updater IS '更新人';
COMMENT ON COLUMN black_deacon_file_temp.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_file_temp.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_file_temp.business_id IS '业务ID';
COMMENT ON COLUMN black_deacon_file_temp.file_url IS '文件地址';
--------------------pgsql创建black_deacon_file_temp表语句---------------------------
--------------------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 '',
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_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.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 '',
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_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.file_name IS '文件名称';
COMMENT ON COLUMN black_deacon_skill_user.file_url IS '文件地址';
--------------------pgsql创建black_deacon_skill_user表语句---------------------------
--------------------pgsql创建black_deacon_flow_execution表语句---------------------------
-- 流程执行记录表
CREATE TABLE IF NOT EXISTS black_deacon_flow_execution (
-- 基础字段(完全对齐项目规范)
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- 业务字段
flow_user_id BIGINT NOT NULL, -- 流程ID
flow_name VARCHAR(128) NOT NULL DEFAULT '',
node_group_id varchar(64) NOT NULL DEFAULT '',
total_tokens integer NOT NULL DEFAULT 0,
trigger_type VARCHAR(32) NOT NULL DEFAULT '', -- 触发类型
duration_ms BIGINT NOT NULL DEFAULT 0, -- 执行时长(毫秒)
status SMALLINT NOT NULL DEFAULT 1, -- 状态:1-运行中,2-成功,3-失败
flow_content JSONB DEFAULT '{}', -- 流程模板内容
node_input_params JSONB DEFAULT '[]'::JSONB,
output_params JSONB DEFAULT '[]'::JSONB,
error_message TEXT DEFAULT '', -- 错误信息
trace_id VARCHAR(64) DEFAULT '' -- 跟踪ID
session_id VARCHAR(64) DEFAULT '' -- 会话ID
);
-- 索引(高频查询)
CREATE INDEX idx_bfe_tenant_id ON black_deacon_flow_execution(tenant_id);
CREATE INDEX idx_bfe_flow_user_id ON black_deacon_flow_execution(flow_user_id);
CREATE INDEX idx_bfe_trace_id ON black_deacon_flow_execution(trace_id);
CREATE INDEX idx_bfe_status ON black_deacon_flow_execution(status);
CREATE INDEX idx_bfe_deleted_at ON black_deacon_flow_execution(deleted_at);
-- 表和字段注释
COMMENT ON TABLE black_deacon_flow_execution IS '流程执行记录表';
COMMENT ON COLUMN black_deacon_flow_execution.id IS '主键ID(非自增)';
COMMENT ON COLUMN black_deacon_flow_execution.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_flow_execution.creator IS '创建人';
COMMENT ON COLUMN black_deacon_flow_execution.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_flow_execution.updater IS '更新人';
COMMENT ON COLUMN black_deacon_flow_execution.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_flow_execution.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_flow_execution.flow_user_id IS '流程ID';
COMMENT ON COLUMN black_deacon_flow_execution.flow_name IS '流程名称';
COMMENT ON COLUMN black_deacon_flow_execution.total_tokens IS '总token消耗';
COMMENT ON COLUMN black_deacon_flow_execution.node_group_id IS '节点组ID';
COMMENT ON COLUMN black_deacon_flow_execution.trigger_type IS '触发类型';
COMMENT ON COLUMN black_deacon_flow_execution.duration_ms IS '执行时长(毫秒)';
COMMENT ON COLUMN black_deacon_flow_execution.status IS '状态:1-运行中,2-成功,3-失败';
COMMENT ON COLUMN black_deacon_flow_execution.flow_content IS '流程模板内容';
COMMENT ON COLUMN black_deacon_flow_execution.node_input_params IS '节点输入参数';
COMMENT ON COLUMN black_deacon_flow_execution.output_params IS '输出参数';
COMMENT ON COLUMN black_deacon_flow_execution.error_message IS '错误信息';
COMMENT ON COLUMN black_deacon_flow_execution.trace_id IS '跟踪ID';
COMMENT ON COLUMN black_deacon_flow_execution.session_id IS '会话ID';
--------------------pgsql创建black_deacon_flow_execution表语句---------------------------
--------------------pgsql创建black_deacon_flow_user表语句---------------------------
-- 用户流程表
CREATE TABLE IF NOT EXISTS black_deacon_flow_user (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL DEFAULT 0,
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
flow_name VARCHAR(128) NOT NULL DEFAULT '',
description TEXT DEFAULT '',
flow_content JSONB DEFAULT '{}',
node_input_params JSONB DEFAULT '[]'::JSONB,
access_level VARCHAR(32) NOT NULL DEFAULT '1',
source_flow_template_id BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_flow_user_tenant ON black_deacon_flow_user(tenant_id);
COMMENT ON TABLE black_deacon_flow_user IS '用户流程表';
COMMENT ON COLUMN black_deacon_flow_user.flow_name IS '流程名称';
COMMENT ON COLUMN black_deacon_flow_user.description IS '流程描述';
COMMENT ON COLUMN black_deacon_flow_user.flow_content IS '流程内容';
COMMENT ON COLUMN black_deacon_flow_user.node_input_params IS '节点输入参数';
COMMENT ON COLUMN black_deacon_flow_user.access_level IS '访问权限:1私有,2团队,3公开';
COMMENT ON COLUMN black_deacon_flow_user.source_flow_template_id IS '来源流程模板ID';
--------------------pgsql创建black_deacon_flow_user表语句---------------------------
--------------------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表语句---------------------------
--------------------pgsql创建black_deacon_active_pull表语句---------------------------
-- 主动拉取记录表
CREATE TABLE IF NOT EXISTS black_deacon_active_pull (
-- 基础字段(完全对齐项目规范)
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),
-- 业务字段
type VARCHAR(64) NOT NULL DEFAULT '',
request_parament JSONB DEFAULT '{}',
response_parament JSONB DEFAULT '{}',
extension JSONB DEFAULT '{}'
);
-- 索引
CREATE INDEX idx_active_pull_tenant_id ON black_deacon_active_pull(tenant_id);
CREATE INDEX idx_active_pull_type ON black_deacon_active_pull("type");
CREATE INDEX idx_active_pull_deleted_at ON black_deacon_active_pull(deleted_at);
-- 注释
COMMENT ON TABLE black_deacon_active_pull IS '主动拉取记录表';
COMMENT ON COLUMN black_deacon_active_pull.id IS '主键ID';
COMMENT ON COLUMN black_deacon_active_pull.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_active_pull.creator IS '创建人';
COMMENT ON COLUMN black_deacon_active_pull.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_active_pull.updater IS '更新人';
COMMENT ON COLUMN black_deacon_active_pull.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_active_pull.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_active_pull.type IS '类型';
COMMENT ON COLUMN black_deacon_active_pull.request_parament IS '请求参数';
COMMENT ON COLUMN black_deacon_active_pull.response_parament IS '响应参数';
COMMENT ON COLUMN black_deacon_active_pull.extension IS '扩展信息';
--------------------pgsql创建black_deacon_active_pull表语句---------------------------
--------------------pgsql创建black_deacon_node_prompt表语句---------------------------
-- 节点提示词配置表
CREATE TABLE IF NOT EXISTS black_deacon_node_prompt (
-- 基础字段(完全对齐项目规范)
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),
-- 业务字段
node_type VARCHAR(64) NOT NULL DEFAULT '', -- 节点类型
prompt TEXT NOT NULL DEFAULT '', -- 提示词内容
source_type SMALLINT NOT NULL DEFAULT 1 -- 来源:1=系统初始化,2=用户自定义
);
-- 索引
CREATE INDEX idx_node_prompt_tenant_id ON black_deacon_node_prompt(tenant_id);
CREATE INDEX idx_node_prompt_node_type ON black_deacon_node_prompt(node_type);
CREATE INDEX idx_node_prompt_source_type ON black_deacon_node_prompt(source_type);
CREATE INDEX idx_node_prompt_deleted_at ON black_deacon_node_prompt(deleted_at);
-- 注释
COMMENT ON TABLE black_deacon_node_prompt IS '节点提示词配置表';
COMMENT ON COLUMN black_deacon_node_prompt.id IS '主键ID';
COMMENT ON COLUMN black_deacon_node_prompt.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_node_prompt.creator IS '创建人';
COMMENT ON COLUMN black_deacon_node_prompt.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_node_prompt.updater IS '更新人';
COMMENT ON COLUMN black_deacon_node_prompt.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_node_prompt.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_node_prompt.node_type IS '节点类型';
COMMENT ON COLUMN black_deacon_node_prompt.prompt IS '提示词内容';
COMMENT ON COLUMN black_deacon_node_prompt.source_type IS '数据来源:1=系统初始化,2=用户自定义';
--------------------pgsql创建black_deacon_node_prompt表语句---------------------------
--------------------pgsql创建black_deacon_node_execution表语句---------------------------
-- 节点执行记录表
-- 记录每个节点的入参、出参、token消耗、执行状态等详细信息
CREATE TABLE IF NOT EXISTS black_deacon_node_execution (
-- 基础字段(完全对齐项目规范)
id BIGINT PRIMARY KEY, -- 主键ID(非自增)
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID int8
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- 业务字段
flow_execution_id BIGINT NOT NULL, -- 流程执行ID
node_id VARCHAR(64) NOT NULL DEFAULT '', -- 节点ID
node_name VARCHAR(128) NOT NULL DEFAULT '', -- 节点名称
node_group_id VARCHAR(64) NOT NULL DEFAULT '', -- 节点分组ID
input_params JSONB DEFAULT '{}', -- 节点输入参数
input_params_path VARCHAR(256) DEFAULT '', -- 节点输入参数路径
output_params JSONB DEFAULT '{}', -- 节点输出参数
output_params_path VARCHAR(256) DEFAULT '',
prompt_tokens INTEGER NOT NULL DEFAULT 0, -- 提示词token消耗
completion_tokens INTEGER NOT NULL DEFAULT 0, -- 补全token消耗
total_tokens INTEGER NOT NULL DEFAULT 0, -- 总token消耗
status SMALLINT NOT NULL DEFAULT 1, -- 执行状态:1-运行中,2-成功,3-失败,4-暂停,5-等待执行
duration_ms BIGINT NOT NULL DEFAULT 0, -- 执行时长(毫秒)
error_message TEXT DEFAULT '' -- 错误信息
);
-- 索引(高频查询)
CREATE INDEX IF NOT EXISTS idx_bne_tenant_id ON black_deacon_node_execution(tenant_id);
CREATE INDEX IF NOT EXISTS idx_bne_flow_execution_id ON black_deacon_node_execution(flow_execution_id);
CREATE INDEX IF NOT EXISTS idx_bne_node_id ON black_deacon_node_execution(node_id);
CREATE INDEX IF NOT EXISTS idx_bne_status ON black_deacon_node_execution(status);
CREATE INDEX IF NOT EXISTS idx_bne_deleted_at ON black_deacon_node_execution(deleted_at);
-- 表和字段注释
COMMENT ON TABLE black_deacon_node_execution IS '节点执行记录表';
COMMENT ON COLUMN black_deacon_node_execution.id IS '主键ID(非自增)';
COMMENT ON COLUMN black_deacon_node_execution.tenant_id IS '租户ID';
COMMENT ON COLUMN black_deacon_node_execution.creator IS '创建人';
COMMENT ON COLUMN black_deacon_node_execution.created_at IS '创建时间';
COMMENT ON COLUMN black_deacon_node_execution.updater IS '更新人';
COMMENT ON COLUMN black_deacon_node_execution.updated_at IS '更新时间';
COMMENT ON COLUMN black_deacon_node_execution.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN black_deacon_node_execution.flow_execution_id IS '流程执行ID';
COMMENT ON COLUMN black_deacon_node_execution.node_id IS '节点ID';
COMMENT ON COLUMN black_deacon_node_execution.node_name IS '节点名称';
COMMENT ON COLUMN black_deacon_node_execution.node_group_id IS '节点分组ID';
COMMENT ON COLUMN black_deacon_node_execution.input_params IS '节点输入参数';
COMMENT ON COLUMN black_deacon_node_execution.input_params_path IS '节点输入参数路径';
COMMENT ON COLUMN black_deacon_node_execution.output_params IS '节点输出参数';
COMMENT ON COLUMN black_deacon_node_execution.output_params_path IS '节点输出参数路径';
COMMENT ON COLUMN black_deacon_node_execution.prompt_tokens IS '提示词token消耗';
COMMENT ON COLUMN black_deacon_node_execution.completion_tokens IS '补全token消耗';
COMMENT ON COLUMN black_deacon_node_execution.total_tokens IS '总token消耗';
COMMENT ON COLUMN black_deacon_node_execution.status IS '执行状态:1-运行中,2-成功,3-失败,4-暂停,5-等待执行';
COMMENT ON COLUMN black_deacon_node_execution.duration_ms IS '执行时长(毫秒)';
COMMENT ON COLUMN black_deacon_node_execution.error_message IS '错误信息';
--------------------pgsql创建black_deacon_node_execution表语句---------------------------
@@ -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") // 失败
FlowExecutionStatusCancel = newFlowExecutionStatus(gconv.PtrInt8(4), "cancel") // 取消
)
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}
}
@@ -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}
}
@@ -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}
}
@@ -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}
}
@@ -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}
}
+121
View File
@@ -0,0 +1,121 @@
package node
// ======================== 【常量定义:所有中文文案放这里!】 ========================
// 分组名称
const (
NodeGroupNameComponent = "组件"
NodeGroupNameBase = "基础"
NodeGroupNameCustom = "自定义"
)
// 节点名称
const (
NodeNameTextModel = "生成文案"
NodeNameImageModel = "生成图片"
NodeNameVideoModel = "生成视频"
NodeNameAudioModel = "生成音频"
NodeNameBatchModel = "批量处理一起返回"
NodeNameSenseOptimizeModel = "语义优化"
NodeNameStoryOptimizeModel = "分镜优化"
NodeNameScriptOptimizeModel = "剧本优化"
NodeNameDataConversionModel = "参数转换"
NodeNameModel = "模型"
NodeNameMerge = "结果合并"
NodeNameDataMerge = "结果汇集"
NodeNameJudge = "条件判断"
NodeNameForm = "表单"
NodeNameHttp = "HTTP(S)接口"
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"
NodeTypeBatchModel NodeType = "batch_model"
NodeTypeSenseOptimizeModel NodeType = "sense_optimize_model"
NodeTypeStoryOptimizeModel NodeType = "story_optimize_model"
NodeTypeScriptOptimizeModel NodeType = "script_optimize_model"
// 基础
NodeTypeDataConversionModel NodeType = "data_conversion_model"
NodeTypeModel NodeType = "model"
NodeTypeMerge NodeType = "merge"
NodeTypeDataMerge NodeType = "data_merge"
NodeTypeJudge NodeType = "judge"
NodeTypeForm NodeType = "form"
NodeTypeIntent NodeType = "intent"
NodeTypeHttp NodeType = "http"
// 自定义
NodeTypeCustomNode NodeType = "custom_node"
)
const (
ModelTypeText = 100
ModelTypeImage = 200
ModelTypeAudio = 300
ModelTypeModality = 500
ModelTypeVideo = 600
)
// ======================== 结构定义 ========================
type NodeFormField struct {
Value any `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"`
FieldConstraint any `json:"fieldConstraint"`
}
type SelectOption struct {
Label string `json:"label"`
Value string `json:"value"`
}
type ModelItem struct {
ModelName string `json:"modelName"`
ModelForm []NodeFormField `json:"modelForm"`
}
type NodeItem struct {
NodeId string `json:"nodeId"`
NodeCode NodeType `json:"nodeCode"`
ModelType int `json:"modelType"`
NodeName string `json:"nodeName"` // 从常量来
PatchLayout bool `json:"patchLayout"`
SkillOption bool `json:"skillOption"`
PromptOption bool `json:"promptOption"`
IsSaveFile bool `json:"isSaveFile"`
FormConfig []NodeFormField `json:"formConfig"`
ModelConfig []ModelItem `json:"modelConfig"`
}
type NodeGroupItem struct {
Group NodeGroup `json:"group"`
Label string `json:"label"` // 从常量来
Items []NodeItem `json:"items"`
}
+26
View File
@@ -0,0 +1,26 @@
package node
import "github.com/gogf/gf/v2/util/gconv"
var (
SourceTypeSystem = newSourceType(gconv.PtrInt8(1), "系统初始化")
SourceTypeUser = newSourceType(gconv.PtrInt8(2), "用户自定义")
)
type SourceType *int8
type sourceType struct {
code SourceType
desc string
}
func (s sourceType) Code() SourceType {
return s.code
}
func (s sourceType) Desc() string {
return s.desc
}
func newSourceType(code SourceType, desc string) sourceType {
return sourceType{code: code, desc: desc}
}
+21
View File
@@ -0,0 +1,21 @@
package public
// 数据库名称
const (
DbNameBlackDeacon = "black_deacon"
)
// 数据库表名
const (
TableNameCreationInfo = "creation_info"
TableNameFlowExecution = "flow_execution"
TableNameFlowTemplate = "flow_template"
TableNameFlowUser = "flow_user"
TableNameSkillTemplate = "skill_template"
TableNameSkillUser = "skill_user"
TableNameFileTemp = "file_temp"
TableNameActivePull = "active_pull"
TableNameWorkflowInterrupt = "workflow_interrupt"
TableNameNodePrompt = "node_prompt"
TableNameNodeExecution = "node_execution"
)
@@ -0,0 +1,22 @@
package controller
import (
"ai-agent/workflow/model/dto"
"ai-agent/workflow/service"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type creationInfo struct{}
var CreationInfo = new(creationInfo)
func (c *creationInfo) GraphInvoke(ctx context.Context, req *dto.CreationInput) (res *beans.ResponseEmpty, err error) {
err = service.CreationInfoService.Creation(ctx, req)
return
}
func (c *creationInfo) List(ctx context.Context, req *dto.ListCreationInfoReq) (res *dto.ListCreationInfoRes, err error) {
return service.CreationInfoService.List(ctx, req)
}
@@ -0,0 +1,23 @@
package flow
import (
flowService "ai-agent/workflow/service/flow"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
type flowCallBack struct{}
var FlowCallBack = new(flowCallBack)
func (c *flowCallBack) HttpNodeCallback(r *ghttp.Request) {
ctx := r.Context()
err := flowService.FlowExecutionService.HttpNodeCallback(ctx)
if err != nil {
r.Response.WriteJson(g.Map{"code": 500, "message": err.Error()})
return
}
r.Response.WriteJson(g.Map{"code": 0, "message": "success"})
return
}
@@ -0,0 +1,54 @@
package flow
import (
flowDto "ai-agent/workflow/model/dto/flow"
flowService "ai-agent/workflow/service/flow"
"context"
"gitea.redpowerfuture.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) ComposeCallBack(ctx context.Context, req *flowDto.ComposeCallbackReq) (res *beans.ResponseEmpty, err error) {
err = flowService.FlowExecutionService.ComposeCallback(ctx, req)
return
}
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) VideoCallback(ctx context.Context, req *flowDto.VideoCallbackReq) (res *beans.ResponseEmpty, err error) {
err = flowService.FlowExecutionService.VideoCallback(ctx, req)
return
}
func (c *flowExecution) Get(ctx context.Context, req *flowDto.GetFlowExecutionReq) (res *flowDto.VOFlowExecution, err error) {
return flowService.FlowExecutionService.Get(ctx, req)
}
func (c *flowExecution) List(ctx context.Context, req *flowDto.ListFlowExecutionReq) (res *flowDto.ListFlowExecutionTreeRes, err error) {
return flowService.FlowExecutionService.List(ctx, req)
}
func (c *flowExecution) GetSessionList(ctx context.Context, req *flowDto.GetSessionListReq) (res *flowDto.ListFlowExecutionRes, err error) {
return flowService.FlowExecutionService.GetSessionList(ctx, req)
}
func (c *flowExecution) DeleteResult(ctx context.Context, req *flowDto.DeleteResultReq) (res *beans.ResponseEmpty, err error) {
err = flowService.FlowExecutionService.DeleteResult(ctx, req)
return
}
func (c *flowExecution) DeleteSession(ctx context.Context, req *flowDto.DeleteSessionReq) (res *beans.ResponseEmpty, err error) {
err = flowService.FlowExecutionService.DeleteSession(ctx, req)
return
}
@@ -0,0 +1,41 @@
package flow
import (
flowDto "ai-agent/workflow/model/dto/flow"
flowService "ai-agent/workflow/service/flow"
"context"
"gitea.redpowerfuture.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
}
@@ -0,0 +1,41 @@
package flow
import (
flowDto "ai-agent/workflow/model/dto/flow"
flowService "ai-agent/workflow/service/flow"
"context"
"gitea.redpowerfuture.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
}
@@ -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)
}
@@ -0,0 +1,45 @@
package node
import (
nodeDto "ai-agent/workflow/model/dto/node"
nodeService "ai-agent/workflow/service/node"
"context"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type nodePrompt struct{}
var NodePrompt = new(nodePrompt)
// Create 创建节点提示词
func (c *nodePrompt) Create(ctx context.Context, req *nodeDto.CreateNodePromptReq) (res *nodeDto.CreateNodePromptRes, err error) {
return nodeService.NodePromptService.Create(ctx, req)
}
// Update 更新节点提示词
func (c *nodePrompt) Update(ctx context.Context, req *nodeDto.UpdateNodePromptReq) (res *beans.ResponseEmpty, err error) {
err = nodeService.NodePromptService.Update(ctx, req)
return
}
// Delete 删除节点提示词
func (c *nodePrompt) Delete(ctx context.Context, req *nodeDto.DeleteNodePromptReq) (res *beans.ResponseEmpty, err error) {
err = nodeService.NodePromptService.Delete(ctx, req)
return
}
// Get 根据ID查询节点提示词详情
func (c *nodePrompt) Get(ctx context.Context, req *nodeDto.GetNodePromptReq) (res *nodeDto.NodePromptResp, err error) {
return nodeService.NodePromptService.GetById(ctx, req)
}
// ListMy 查询当前用户自己创建的节点提示词列表
func (c *nodePrompt) ListMy(ctx context.Context, req *nodeDto.ListMyNodePromptReq) (res *nodeDto.ListNodePromptResp, err error) {
return nodeService.NodePromptService.ListMy(ctx, req)
}
// List 查询节点提示词列表,包含系统和当前创建人自定义
func (c *nodePrompt) List(ctx context.Context, req *nodeDto.ListNodePromptReq) (res *nodeDto.ListNodePromptResp, err error) {
return nodeService.NodePromptService.ListWithSystem(ctx, req)
}
@@ -0,0 +1,26 @@
package skill
import (
skillDto "ai-agent/workflow/model/dto/skill"
skillService "ai-agent/workflow/service/skill"
"context"
"gitea.redpowerfuture.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)
}
@@ -0,0 +1,43 @@
package skill
import (
skillDto "ai-agent/workflow/model/dto/skill"
skillService "ai-agent/workflow/service/skill"
"context"
"gitea.redpowerfuture.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) Update(ctx context.Context, req *skillDto.UpdateSkillUserReq) (res *beans.ResponseEmpty, err error) {
err = skillService.SkillUserService.Update(ctx, req)
return
}
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) Get(ctx context.Context, req *skillDto.GetSkillUserReq) (res *skillDto.SkillUserVO, err error) {
return skillService.SkillUserService.Get(ctx, req)
}
func (c *skillUser) GetUserOrTemplate(ctx context.Context, req *skillDto.GetSkillReq) (res *skillDto.SkillUserVO, err error) {
return skillService.SkillUserService.GetUserOrTemplate(ctx, req)
}
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)
}
+43
View File
@@ -0,0 +1,43 @@
package dao
import (
"ai-agent/workflow/consts/public"
"ai-agent/workflow/model/dto"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/util/gconv"
)
var CreationInfoDao = &creationInfoDao{}
type creationInfoDao struct{}
func (d *creationInfoDao) List(ctx context.Context, req *dto.ListCreationInfoReq, fields ...string) (res []*entity.CreationInfo, total int, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameCreationInfo).Fields(fields).OmitEmpty()
model.Where(entity.CreationInfoCol.Creator, req.Creator)
model.OrderDesc(entity.CreationInfoCol.CreatedAt)
if req.Page != nil {
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
}
r, total, err := model.AllAndCount(false)
if err != nil {
return
}
err = r.Structs(&res)
return
}
// Insert 插入
func (d *creationInfoDao) Insert(ctx context.Context, req *dto.Create) (id int64, err error) {
e := &entity.CreationInfo{}
if err = gconv.Struct(req, e); err != nil {
return
}
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameCreationInfo).Insert(e)
if err != nil {
return
}
return r.LastInsertId()
}
+59
View File
@@ -0,0 +1,59 @@
package file
import (
"ai-agent/workflow/consts/public"
fileDto "ai-agent/workflow/model/dto/file"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/util/gconv"
)
var FileTempDao = &fileTempDao{}
type fileTempDao struct{}
func (d *fileTempDao) Insert(ctx context.Context, req *fileDto.CreateFileTempReq) (id int64, err error) {
fileTemp := new(entity.FileTemp)
err = gconv.Struct(req, &fileTemp)
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFileTemp).Insert(&fileTemp)
if err != nil {
return
}
return r.LastInsertId()
}
func (d *fileTempDao) BatchInsert(ctx context.Context, req []*fileDto.CreateFileTempReq) (rows int64, err error) {
var res []*entity.FileTemp
if err = gconv.Structs(req, &res); err != nil {
return
}
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFileTemp).Data(res).Save()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *fileTempDao) Delete(ctx context.Context, req *fileDto.DeleteFileTempReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFileTemp).Where(entity.FileTempCol.Id, req.Id).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *fileTempDao) List(ctx context.Context, req *fileDto.ListFileTempReq, fields ...string) (res []*entity.FileTemp, total int, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFileTemp).NoTenantId(ctx).Fields(fields).OmitEmpty()
model.OrderDesc(entity.FileTempCol.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
}
+74
View File
@@ -0,0 +1,74 @@
package flow
import (
"ai-agent/workflow/consts/public"
flowDto "ai-agent/workflow/model/dto/flow"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/frame/g"
"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)
model.Where(entity.FlowExecutionCol.Creator, req.Creator)
if !g.IsEmpty(req.ResultDel) {
model.Where(entity.FlowExecutionCol.ResultDel, false)
}
if !g.IsEmpty(req.SessionDel) {
model.Where(entity.FlowExecutionCol.SessionDel, false)
}
if req.IsResult {
model.WhereNot(entity.FlowExecutionCol.OutputParams, "[]")
}
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
View 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.redpowerfuture.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
View 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.redpowerfuture.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
}
+100
View File
@@ -0,0 +1,100 @@
package node
import (
"ai-agent/workflow/consts/public"
nodeDto "ai-agent/workflow/model/dto/node"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var NodeExecutionDao = &nodeExecutionDao{}
type nodeExecutionDao struct{}
// Insert 插入节点执行记录
func (d *nodeExecutionDao) Insert(ctx context.Context, req *nodeDto.CreateNodeExecutionReq) (id int64, err error) {
nodeExecution := new(entity.NodeExecution)
err = gconv.Struct(req, &nodeExecution)
if err != nil {
return 0, err
}
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodeExecution).Insert(&nodeExecution)
if err != nil {
return 0, err
}
return r.LastInsertId()
}
// Update 更新节点执行记录
func (d *nodeExecutionDao) Update(ctx context.Context, req *nodeDto.UpdateNodeExecutionReq) (rows int64, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodeExecution).OmitEmpty()
if !g.IsEmpty(req.CompletionTokens) {
model.Data(entity.NodeExecutionCol.CompletionTokens, &gdb.Counter{
Field: entity.NodeExecutionCol.CompletionTokens,
Value: gconv.Float64(req.CompletionTokens),
})
}
if !g.IsEmpty(req.PromptTokens) {
model.Data(entity.NodeExecutionCol.PromptTokens, &gdb.Counter{
Field: entity.NodeExecutionCol.PromptTokens,
Value: gconv.Float64(req.PromptTokens),
})
}
if !g.IsEmpty(req.TotalTokens) {
model.Data(entity.NodeExecutionCol.TotalTokens, &gdb.Counter{
Field: entity.NodeExecutionCol.TotalTokens,
Value: gconv.Float64(req.TotalTokens),
})
}
r, err := model.Data(&req).Where(entity.NodeExecutionCol.Id, req.Id).Update()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
// Delete 删除节点执行记录
func (d *nodeExecutionDao) Delete(ctx context.Context, req *nodeDto.DeleteNodeExecutionReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodeExecution).Where(entity.NodeExecutionCol.Id, req.Id).Delete()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
// Get 根据ID查询节点执行记录
func (d *nodeExecutionDao) Get(ctx context.Context, req *nodeDto.GetNodeExecutionReq, fields ...string) (res *entity.NodeExecution, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodeExecution).NoTenantId(ctx).OmitEmpty().
Where(entity.NodeExecutionCol.Id, req.Id).
Fields(fields).One()
if err != nil {
return nil, err
}
if r.IsEmpty() {
return nil, nil
}
err = r.Struct(&res)
return res, err
}
// ListByFlowExecutionId 查询指定流程执行下的所有节点执行记录
func (d *nodeExecutionDao) ListByFlowExecutionId(ctx context.Context, req *nodeDto.ListNodeExecutionByFlowReq, fields ...string) (res []*entity.NodeExecution, total int, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodeExecution).NoTenantId(ctx).Fields(fields).OmitEmpty()
model.Where(entity.NodeExecutionCol.FlowExecutionId, req.FlowExecutionId)
model.Where(entity.NodeExecutionCol.NodeGroupId, req.NodeGroupId)
model.OrderAsc(entity.NodeExecutionCol.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 nil, 0, err
}
err = r.Structs(&res)
return res, total, err
}
+95
View File
@@ -0,0 +1,95 @@
package node
import (
"ai-agent/workflow/consts/public"
nodeDto "ai-agent/workflow/model/dto/node"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/util/gconv"
)
var NodePromptDao = &nodePromptDao{}
type nodePromptDao struct{}
// Insert 插入节点提示词
func (d *nodePromptDao) Insert(ctx context.Context, req *nodeDto.CreateNodePromptReq) (id int64, err error) {
nodePrompt := new(entity.NodePrompt)
err = gconv.Struct(req, &nodePrompt)
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodePrompt).Insert(&nodePrompt)
if err != nil {
return 0, err
}
return r.LastInsertId()
}
// Update 更新节点提示词
func (d *nodePromptDao) Update(ctx context.Context, req *nodeDto.UpdateNodePromptReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodePrompt).OmitEmpty().Data(&req).Where(entity.NodePromptCol.Id, req.Id).Update()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
// Delete 删除节点提示词
func (d *nodePromptDao) Delete(ctx context.Context, req *nodeDto.DeleteNodePromptReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodePrompt).Where(entity.NodePromptCol.Id, req.Id).Delete()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
// Get 根据ID查询节点提示词
func (d *nodePromptDao) Get(ctx context.Context, req *nodeDto.GetNodePromptReq, fields ...string) (res *entity.NodePrompt, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodePrompt).NoTenantId(ctx).OmitEmpty().
Where(entity.NodePromptCol.Id, req.Id).
Where(entity.NodePromptCol.Prompt, req.Prompt).
Where(entity.NodePromptCol.Creator, req.Creator).
Fields(fields).One()
if err != nil {
return nil, err
}
if r.IsEmpty() {
return nil, nil
}
err = r.Struct(&res)
return res, err
}
// ListByOnlyCreator 查询仅当前创建人自己创建的提示词
func (d *nodePromptDao) ListByOnlyCreator(ctx context.Context, req *nodeDto.ListMyNodePromptReq, fields ...string) (res []*entity.NodePrompt, total int, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameNodePrompt).NoTenantId(ctx).Fields(fields).OmitEmpty()
model.Where(entity.NodePromptCol.Creator, req.Creator)
model.Where(entity.NodePromptCol.NodeType, req.NodeType)
model.OrderDesc(entity.NodePromptCol.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 nil, 0, err
}
err = r.Structs(&res)
return res, total, err
}
// ListByCreator 查询当前创建人的所有提示词(包含系统和用户)
func (d *nodePromptDao) ListByCreator(ctx context.Context, req *nodeDto.ListNodePromptReq, fields ...string) (res []*entity.NodePrompt, total int, err error) {
// 完整 SQL
sql := ` SELECT * FROM black_deacon_node_prompt WHERE (creator=? OR source_type=1) AND node_type=? AND "deleted_at" IS NULL ORDER BY created_at DESC `
queryParams := []interface{}{req.Creator, req.NodeType}
if req.Page != nil {
sql += " LIMIT ?,?"
queryParams = append(queryParams, req.Page.PageNum, req.Page.PageSize)
}
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).GetAll(ctx, sql, queryParams...)
if err != nil {
return nil, 0, err
}
err = r.Structs(&res)
return res, total, err
}
+102
View File
@@ -0,0 +1,102 @@
package pull
import (
"ai-agent/workflow/consts/public"
pullDto "ai-agent/workflow/model/dto/pull"
"ai-agent/workflow/model/entity"
"context"
"fmt"
"strings"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/util/gconv"
)
var ActivePullDao = &activePullDao{}
type activePullDao struct{}
// Insert 创建执行记录
func (d *activePullDao) Insert(ctx context.Context, req *pullDto.CreateActivePullReq) (id int64, err error) {
var activePull = new(entity.ActivePull)
err = gconv.Struct(req, &activePull)
if err != nil {
return
}
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameActivePull).Insert(activePull)
if err != nil {
return
}
return r.LastInsertId()
}
func (d *activePullDao) Update(ctx context.Context, req *pullDto.UpdateActivePullReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameActivePull).OmitEmpty().Data(&req).Where(entity.ActivePullCol.Id, req.Id).Update()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *activePullDao) Delete(ctx context.Context, req *pullDto.DeleteActivePullReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameActivePull).Where(entity.ActivePullCol.Id, req.Id).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *activePullDao) List(ctx context.Context, req *pullDto.ListActivePullReq, fields ...string) (res []*entity.ActivePull, total int, err error) {
model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameActivePull).Fields(fields).OmitEmpty()
model.OrderDesc(entity.ActivePullCol.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
}
func (d *activePullDao) ListNative(ctx context.Context, req *pullDto.ListActivePullReq, fields ...string) (res []*entity.ActivePull, total int, err error) {
db := gfdb.DB(ctx, public.DbNameBlackDeacon)
// Select fields
selectFields := "*"
if len(fields) > 0 {
selectFields = strings.Join(fields, ",")
}
// Build count query first for total
countSql := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE deleted_at is null", "black_deacon_"+public.TableNameActivePull)
countResult, err := db.GetAll(ctx, countSql)
if err != nil {
return nil, 0, err
}
if len(countResult) > 0 {
total = countResult[0]["COUNT(*)"].Int()
}
// Build data query with native SQL
sql := fmt.Sprintf("SELECT %s FROM %s WHERE deleted_at is null ORDER BY created_at DESC", selectFields, "black_deacon_"+public.TableNameActivePull)
if req.Page != nil && req.Page.PageNum > 0 && req.Page.PageSize > 0 {
offset := (req.Page.PageNum - 1) * req.Page.PageSize
sql += fmt.Sprintf(" LIMIT %d OFFSET %d", req.Page.PageSize, offset)
}
// Execute query with GetAll
result, err := db.GetAll(ctx, sql)
if err != nil {
return nil, total, err
}
// Scan to entity slice
var models []*entity.ActivePull
if err = result.Structs(&models); err != nil {
return nil, total, err
}
return models, total, nil
}
+79
View File
@@ -0,0 +1,79 @@
package skill
import (
"ai-agent/workflow/consts/public"
skillDto "ai-agent/workflow/model/dto/skill"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.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) Update(ctx context.Context, req *skillDto.UpdateSkillTemplateReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).OmitEmpty().Data(&req).Where(entity.SkillTemplateCol.Id, req.Id).Update()
if err != nil {
return
}
return r.RowsAffected()
}
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) Count(ctx context.Context, req *skillDto.GetSkillTemplateReq) (count int, err error) {
count, err = gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).NoTenantId(ctx).OmitEmpty().
WhereNot(entity.SkillTemplateCol.Id, req.NotInId).
Where(entity.SkillTemplateCol.Name, req.Name).
Where(entity.SkillTemplateCol.Id, req.Id).Count()
return
}
func (d *skillTemplateDao) Get(ctx context.Context, req *skillDto.GetSkillTemplateReq, fields ...string) (res *entity.SkillTemplate, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillTemplate).NoTenantId(ctx).OmitEmpty().
Where(entity.SkillTemplateCol.Id, req.Id).
Where(entity.SkillTemplateCol.Name, req.Name).
Fields(fields).One()
if err != nil {
return
}
err = r.Struct(&res)
return
}
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
}
+82
View File
@@ -0,0 +1,82 @@
package skill
import (
"ai-agent/workflow/consts/public"
skillDto "ai-agent/workflow/model/dto/skill"
"ai-agent/workflow/model/entity"
"context"
"gitea.redpowerfuture.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) Update(ctx context.Context, req *skillDto.UpdateSkillUserReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).OmitEmpty().Data(&req).Where(entity.SkillUserCol.Id, req.Id).Update()
if err != nil {
return
}
return r.RowsAffected()
}
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) Count(ctx context.Context, req *skillDto.GetSkillUserReq) (count int, err error) {
count, err = gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).NoTenantId(ctx).OmitEmpty().
Where(entity.SkillUserCol.Name, req.Name).
Where(entity.SkillUserCol.Creator, req.Creator).
WhereNot(entity.SkillUserCol.Id, req.NotInId).
Where(entity.SkillUserCol.Id, req.Id).Count()
return
}
func (d *skillUserDao) Get(ctx context.Context, req *skillDto.GetSkillUserReq, fields ...string) (res *entity.SkillUser, err error) {
r, err := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameSkillUser).NoTenantId(ctx).OmitEmpty().
Where(entity.SkillUserCol.Id, req.Id).
Where(entity.SkillUserCol.Name, req.Name).
Where(entity.SkillUserCol.Creator, req.Creator).
Fields(fields).One()
if err != nil {
return
}
err = r.Struct(&res)
return
}
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
}
+115
View File
@@ -0,0 +1,115 @@
package dto
import (
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type CreationInput struct {
g.Meta `path:"/creation" method:"post" tags:"创作作品管理" summary:"作品创作" dc:"作品创作"`
Mode string `json:"mode"`
ContentType string `json:"content_type"`
Theme string `json:"theme"`
Title string `json:"title"`
Style string `json:"style"`
Count int `json:"count"`
ImagePerPost int `json:"image_per_post"`
ImageRatio string `json:"image_ratio"`
Desc string `json:"desc"`
Content string `json:"content"`
}
type ImageUploadItem struct {
Title string `json:"title"` // 内容标题
Index int `json:"index"` // 第几条
ImageUrls []string `json:"image_urls"` // 上传成功的图片URL列表
HtmlFileUrl string `json:"html_file_url"` // 上传成功的HTML文件URL(如有)
Theme string `json:"theme"`
ContentType string `json:"content_type"`
}
// CreationOutput 接口最终返回结构体(你原有dto,这里补充完整)
type CreationOutput struct {
SuccessCount int `json:"success_count"` // 成功条数
Items []ImageUploadItem `json:"items"` // 所有上传成功的详情
}
type Create struct {
HtmlFileUrl string
ImageUrls []string
ContentType string
Theme string
Title string
}
// UploadFileBytesReq 上传文件请求(字节流)
type UploadFileBytesReq struct {
FileName string `json:"fileName" dc:"文件名"`
FileBytes []byte `json:"fileBytes" dc:"文件字节流"`
FileStoreURL string `json:"fileStoreURL" dc:"文件存储URL"`
}
type UploadFileBytesRes struct {
FileURL string `json:"fileURL" dc:"上传地址"`
FileSize int `json:"fileSize" dc:"文件大小"`
FileName string `json:"fileName" dc:"文件名称"`
FileFormat string `json:"fileFormat" dc:"文件格式"`
FileAddressPrefix string `json:"fileAddressPrefix"`
}
type ListCreationInfoReq struct {
g.Meta `path:"/list" method:"get" tags:"创作作品管理" summary:"作品列表" dc:"作品列表"`
Page *beans.Page `json:"page"`
Creator string `json:"creator"`
}
type ListCreationInfoRes struct {
List []*CreationInfoVO `json:"list"`
Total int `json:"total"`
Tree []TimeNode
ImgAddressPrefix string `json:"imgAddressPrefix"`
}
type CreationInfoVO struct {
Id int64 `json:"id,string" dc:"id"`
HtmlFileUrl string `json:"htmlFileUrl"`
ImageUrls []string `json:"imageUrls"`
Theme string `json:"theme"`
Title string `json:"title"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
// 第一层:日期
type TimeNode struct {
CreatedDate string `json:"createdDate"`
ContentTypes []ContentTypeNode `json:"contentTypes"`
}
// 第二层:ContentType
type ContentTypeNode struct {
ContentType string `json:"contentType"`
Themes []ThemeNode `json:"themes"`
}
// 第三层:Theme
type ThemeNode struct {
Theme string `json:"theme"`
Titles []TitleNode `json:"titles"`
}
// Title 节点:Title-1、Title-2...
type TitleNode struct {
Title string `json:"title"` // 标题+编号:如 通勤-1
HtmlFileUrl string `json:"htmlFileUrl"` // html地址
ImageUrls []ImgNode `json:"imageUrls"` // 图片列表
}
// 图片
type ImgNode struct {
Name string `json:"name"` // img+1
Url string `json:"url"`
}
+45
View File
@@ -0,0 +1,45 @@
package file
import (
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
type CreateFileTempReq struct {
g.Meta `path:"/create" method:"post" tags:"临时文件管理" summary:"创建临时文件" dc:"创建临时文件"`
BusinessId string `json:"businessId"`
FileUrl string `json:"fileUrl"`
}
type CreateFileTempRes struct {
Id int64 `json:"id,string"`
}
type DeleteFileTempReq struct {
g.Meta `path:"/delete" method:"delete" tags:"临时文件管理" summary:"删除临时文件" dc:"删除临时文件"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
type ListFileTempReq struct {
g.Meta `path:"/list" method:"get" tags:"临时文件管理" summary:"临时文件列表" dc:"临时文件列表"`
Page *beans.Page `json:"page"`
BusinessId string `json:"businessId"`
CreatedAt *gtime.Time `json:"createdAt"`
}
type ListFileTempRes struct {
List []*FileTempVO `json:"list"`
Total int `json:"total"`
}
type FileTempVO struct {
Id int64 `json:"id,string" dc:"id"`
BusinessId string `json:"businessId"`
FileUrl string `json:"fileUrl"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
@@ -0,0 +1,319 @@
package flow
import (
"ai-agent/workflow/consts/flow"
"ai-agent/workflow/consts/node"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.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 `json:"config"` // 节点配置
Global *FlowExecutionInput `json:"global"`
NodeExecutionId int64 `json:"nodeExecutionId"`
}
// ExecutedNode 已执行节点记录,包含节点ID和执行状态
type ExecutedNode struct {
NodeId string `json:"nodeId"`
Status node.NodeExecutionStatus `json:"status"` // 执行状态:成功/失败
}
// FlowExecutionInput 工作流执行入参(全程不变)
type FlowExecutionInput struct {
NodeGroupId string `json:"nodeGroupId"`
IsDialogue bool `json:"isDialogue"`
ExecutionId int64 `json:"executionId"`
ConfigMap map[string]*entity.FlowNode `json:"configMap"`
SessionId string `json:"sessionId" dc:"会话ID"`
Templates []map[string]any `json:"templates"`
Desc string `json:"desc"`
SkillName string `json:"skillName"`
FileUrl []string `json:"fileUrl"`
ExecutedNodes []ExecutedNode `json:"executedNodes"` // 已执行节点列表,包含执行状态
}
type GetIsChatModelRes struct {
Model struct {
ModelName string `json:"modelName"`
ResponseBody map[string]any `json:"responseBody"`
}
}
type GetModelInfoReq struct {
ModelName string `json:"modelName"`
}
type GetModelInfoRes struct {
Model struct {
FirstFrame string `json:"firstFrame"`
LastFrame string `json:"lastFrame"`
ResponseTokenField string `json:"responseTokenField"`
ResponseMapping map[string]any `json:"responseMapping"`
ResponseBody string `json:"responseBody"`
//QueryConfig struct {
// ResponseType string `json:"responseType"`
// CallbackUrl string `json:"callbackUrl"`
// Method string `json:"method"`
// Url string `json:"url"`
// Headers map[string]any `json:"headers"`
// Body map[string]any `json:"body"`
// Response []map[string]any `json:"response"`
// ResponseBody string `json:"responseBody"`
// ResponseTokenField string `json:"responseTokenField"`
//} `json:"queryConfig"`
} `json:"model"`
}
type ComposeMessagesReq struct {
BuildType int `json:"buildType"`
ModelName string `json:"modelName"`
SkillName string `json:"skillName"`
CallbackUrl string `json:"callbackUrl"`
Form []map[string]any `json:"form"`
UserForm []map[string]any `json:"userForm"`
UserPrompt string `json:"userPrompt" dc:"用户提示词"`
Consult []Consult `json:"consult"`
SessionId string `json:"sessionId" dc:"会话ID"`
NodeId string `json:"nodeId"`
Cause string `json:"cause"`
}
type Consult struct {
Type string `json:"type"`
Url string `json:"url"`
}
type ComposeMessagesRes struct {
TaskId string `json:"taskId"`
}
type VideoConcatReq struct {
VideoUrls []string `json:"video_urls"`
Method string `json:"method"`
Upload bool `json:"upload"`
CallbackUrl string `json:"callback_url"`
}
type VideoConcatRes struct {
TaskId string `json:"taskId"`
}
type ModelGatewayReq 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 ModelGatewayRes struct {
TaskId string `json:"taskId"`
}
type ComposeCallbackReq struct {
g.Meta `path:"/composeCallBack" method:"post" tags:"提示词处理" summary:"提示词 回调" dc:"提示词 成功后 GET 回调:callbackUrl/{bizName}"`
TaskId string `json:"taskId"`
Status string `json:"status"`
Messages struct {
TotalRounds int `json:"total_rounds"` // 总轮数
Rounds []map[string]any `json:"rounds"` // 每轮详情(动态类型)
} `json:"messages,omitempty"`
EpicycleId int64 `json:"epicycleId"`
ErrorMsg string `json:"errorMsg,omitempty"`
BillingData []map[string]any `json:"billing_data"`
}
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:"结果文件类型"`
ErrorMsg string `json:"error_msg"`
BillingData []map[string]any `json:"billing_data"`
}
type VideoCallbackReq struct {
g.Meta `path:"/videoCallback" method:"post" tags:"视频处理" summary:"media 回调" dc:"media 成功后 GET 回调:callbackUrl/{bizName}"`
TaskId string `json:"taskId"`
FileURL string `json:"fileUrl"`
}
//=============================================================================
// 原始入参结构体
type Word struct {
Confidence float64 `json:"confidence"`
StartTime float64 `json:"startTime"`
EndTime float64 `json:"endTime"`
Word string `json:"word"`
}
type Sentence struct {
EndTime float64 `json:"endTime"`
StartTime float64 `json:"startTime"`
Text string `json:"text"`
Words []Word `json:"words"`
}
type InputData struct {
Data struct {
Sentences []Sentence `json:"sentences"`
} `json:"data"`
}
// 输出目标结构体(对应截图subtitles格式)
type Subtitle struct {
Start float64 `json:"start"`
End float64 `json:"end"`
Text string `json:"text"`
}
//==============================================================================
type ExecuteReq struct {
g.Meta `path:"/execute" method:"post" tags:"任务管理" summary:"执行任务" dc:"执行任务"`
FlowId int64 `json:"flowId" dc:"用户流程ID"`
FlowName string `json:"flowName"`
NodeGroupId string `json:"nodeGroupId"`
FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"`
NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"`
SessionId string `json:"sessionId" dc:"会话ID"`
Templates []map[string]any `json:"templates"`
Desc string `json:"desc"`
SkillName string `json:"skillName"`
FileUrl []string `json:"fileUrl"`
ResultUrl string `json:"resultUrl"`
}
type ExecuteRes struct {
Id int64 `json:"id,string" dc:"执行记录ID,用于查询执行状态和结果"`
}
type CancelReq struct {
g.Meta `path:"/cancel" method:"post" tags:"任务管理" summary:"取消任务" dc:"取消任务"`
SessionId string `json:"sessionId" dc:"会话ID"`
}
type CreateFlowExecutionReq struct {
FlowUserId int64 `json:"flowUserId" description:"流程ID"`
FlowName string `json:"flowName"`
NodeGroupId string `json:"nodeGroupId"`
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"`
Extension map[string]interface{} `json:"extension"`
}
type CreateFlowExecutionRes struct {
Id int64 `json:"id,string"`
}
type UpdateFlowExecutionReq struct {
Id int64 `json:"id" v:"required#ID不能为空"`
NodeGroupId string `json:"nodeGroupId"`
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"`
TotalTokens int `json:"totalTokens" description:"总token"`
TotalFee float64 `json:"totalFee" description:"总费用"`
SessionDel bool `json:"sessionDel" description:"会话是否删除"`
ResultDel bool `json:"resultDel" description:"结果是否删除"`
Extension map[string]interface{} `json:"extension"`
}
type DeleteResultReq struct {
g.Meta `path:"/deleteResult" method:"delete" tags:"任务管理" summary:"删除结果" dc:"删除结果"`
Id int64 `json:"id" v:"required#ID不能为空"`
Content string `json:"content"`
}
type DeleteSessionReq struct {
g.Meta `path:"/deleteSession" method:"delete" tags:"任务管理" summary:"删除会话" dc:"删除会话"`
Id int64 `json:"id" v:"required#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 GetSessionListReq struct {
g.Meta `path:"/sessionList" method:"get" tags:"任务管理" summary:"会话列表" dc:"会话列表"`
*beans.Page `json:"page"`
}
type ListFlowExecutionReq struct {
g.Meta `path:"/list" method:"get" tags:"任务管理" summary:"任务列表" dc:"任务列表"`
*beans.Page `json:"page"`
Creator string `json:"creator"`
SessionDel *bool `json:"sessionDel"`
ResultDel *bool `json:"resultDel"`
IsResult bool `json:"isResult"`
}
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:"错误信息"`
Extension map[string]interface{} `json:"extension"`
TraceId string `json:"traceId" description:"跟踪ID"`
SessionId string `json:"sessionId" dc:"会话ID"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
ImgAddressPrefix string `json:"imgAddressPrefix"`
}
// ========== 核心:构建树状结构 ==========
type OutputItem struct {
Id int64 `json:"id,string" description:"ID"`
Timestamp string `json:"timestamp" description:"时间戳key"`
Content string `json:"content" description:"内容值"`
Type string `json:"type" description:"类型"`
Label string `json:"label" description:"后缀+数字标号"`
}
type DateNode struct {
CreateDate string `json:"createDate" description:"创建日期"`
Items []OutputItem `json:"items" description:"直接是结果项列表"`
}
type ListFlowExecutionTreeRes struct {
Tree []DateNode `json:"tree"`
ImgAddressPrefix string `json:"imgAddressPrefix"`
}
@@ -0,0 +1,76 @@
package flow
import (
"ai-agent/workflow/consts/flow"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.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
View File
@@ -0,0 +1,80 @@
package flow
import (
"ai-agent/workflow/consts/flow"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.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"`
}
@@ -0,0 +1,76 @@
package node
import (
"ai-agent/workflow/consts/node"
flowDto "ai-agent/workflow/model/dto/flow"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
)
// CreateNodeExecutionReq 创建节点执行记录请求
type CreateNodeExecutionReq struct {
g.Meta `path:"/create" method:"post" tags:"节点执行记录" summary:"创建节点执行记录" dc:"创建节点执行记录"`
FlowExecutionId int64 `json:"flowExecutionId" v:"required#流程执行ID不能为空"`
NodeId string `json:"nodeId" v:"required#节点ID不能为空"`
NodeName string `json:"nodeName"`
NodeGroupId string `json:"nodeGroupId"`
Status node.NodeExecutionStatus `json:"status"`
InputParams *flowDto.NodeExecutionInput `json:"inputParams"`
InputParamsPath string
OutputParams *flowDto.NodeExecutionInput `json:"outputParams"`
OutputParamsPath string
}
type CreateNodeExecutionRes struct {
Id int64 `json:"id,string"`
}
// UpdateNodeExecutionReq 更新节点执行记录请求
type UpdateNodeExecutionReq struct {
g.Meta `path:"/update" method:"put" tags:"节点执行记录" summary:"更新节点执行记录" dc:"更新节点执行记录状态和结果"`
Id int64 `json:"id" v:"required#ID不能为空"`
InputParams *flowDto.NodeExecutionInput `json:"inputParams"`
InputParamsPath string
OutputParams *flowDto.NodeExecutionInput `json:"outputParams"`
OutputParamsPath string
PromptTokens int `json:"promptTokens"`
CompletionTokens int `json:"completionTokens"`
TotalTokens int `json:"totalTokens"`
TokenInfo []map[string]any `json:"tokenInfo"`
Status node.NodeExecutionStatus `json:"status"`
DurationMs int64 `json:"durationMs"`
ErrorMessage string `json:"errorMessage"`
}
// DeleteNodeExecutionReq 删除节点执行记录请求
type DeleteNodeExecutionReq struct {
g.Meta `path:"/delete" method:"delete" tags:"节点执行记录" summary:"删除节点执行记录" dc:"删除节点执行记录"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
// GetNodeExecutionReq 根据ID查询节点执行记录请求
type GetNodeExecutionReq struct {
g.Meta `path:"/get" method:"get" tags:"节点执行记录" summary:"查询节点执行记录详情" dc:"根据ID查询节点执行记录详情"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
// ListNodeExecutionByFlowReq 查询流程下所有节点执行记录请求
type ListNodeExecutionByFlowReq struct {
g.Meta `path:"/listByFlow" method:"get" tags:"节点执行记录" summary:"查询流程节点执行列表" dc:"查询指定流程执行下的所有节点执行记录"`
Page *beans.Page `json:"page"`
FlowExecutionId int64 `json:"flowExecutionId" v:"required#流程执行ID不能为空"`
NodeGroupId string `json:"nodeGroupId"`
}
// NodeExecutionResp 节点执行记录响应
type NodeExecutionResp struct {
*entity.NodeExecution
}
// ListNodeExecutionResp 节点执行记录列表响应
type ListNodeExecutionResp struct {
List []*entity.NodeExecution `json:"list"`
Total int `json:"total"`
}
@@ -0,0 +1,33 @@
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"`
}
type ModelTypeResponse struct {
Type map[int]string `json:"type"` // key 自动解析为整数 100/200/300...
}
@@ -0,0 +1,70 @@
package node
import (
"ai-agent/workflow/consts/node"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
)
// CreateNodePromptReq 创建节点提示词请求
type CreateNodePromptReq struct {
g.Meta `path:"/create" method:"post" tags:"节点提示词管理" summary:"创建节点提示词" dc:"创建用户自定义节点提示词"`
NodeType node.NodeType `json:"nodeType" v:"required#节点类型不能为空"`
Prompt string `json:"prompt" v:"required#提示词不能为空"`
SourceType node.SourceType `json:"sourceType"`
}
type CreateNodePromptRes struct {
Id int64 `json:"id,string"`
}
// UpdateNodePromptReq 更新节点提示词请求
type UpdateNodePromptReq struct {
g.Meta `path:"/update" method:"put" tags:"节点提示词管理" summary:"更新节点提示词" dc:"更新用户自定义节点提示词"`
Id int64 `json:"id" v:"required#ID不能为空"`
NodeType node.NodeType `json:"nodeType"`
Prompt string `json:"prompt"`
}
// DeleteNodePromptReq 删除节点提示词请求
type DeleteNodePromptReq struct {
g.Meta `path:"/delete" method:"delete" tags:"节点提示词管理" summary:"删除节点提示词" dc:"删除用户自定义节点提示词"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
// GetNodePromptReq 根据ID查询节点提示词请求
type GetNodePromptReq struct {
g.Meta `path:"/get" method:"get" tags:"节点提示词管理" summary:"查询节点提示词详情" dc:"根据ID查询节点提示词详情"`
Id int64 `json:"id"`
Prompt string `json:"prompt"`
Creator string `json:"creator"`
}
// ListNodePromptReq 查询节点提示词列表请求
type ListNodePromptReq struct {
g.Meta `path:"/list" method:"get" tags:"节点提示词管理" summary:"查询节点提示词列表" dc:"查询当前创建人的节点提示词,包含系统和用户自定义"`
Page *beans.Page `json:"page"`
Creator string `json:"creator"`
NodeType node.NodeType `json:"nodeType"`
}
// ListMyNodePromptReq 查询当前用户节点提示词列表请求
type ListMyNodePromptReq struct {
g.Meta `path:"/listMy" method:"get" tags:"节点提示词管理" summary:"查询当前用户节点提示词列表" dc:"查询当前创建人自己创建的节点提示词列表"`
Page *beans.Page `json:"page"`
NodeType node.NodeType `json:"nodeType"`
Creator string `json:"creator"`
}
// NodePromptResp 节点提示词响应
type NodePromptResp struct {
*entity.NodePrompt
}
// ListNodePromptResp 节点提示词列表响应
type ListNodePromptResp struct {
List []*entity.NodePrompt `json:"list"`
Total int `json:"total"`
}
@@ -0,0 +1,54 @@
package pull
import (
"ai-agent/workflow/consts/flow"
"ai-agent/workflow/model/entity"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/os/gtime"
)
type CreateActivePullReq struct {
Type string `json:"type"`
RequestParament map[string]any `json:"requestParament"`
ResponseParament map[string]any `json:"responseParament"`
Extension map[string]any `json:"extension"`
}
type CreateActivePullRes struct {
Id int64 `json:"id,string"`
}
type UpdateActivePullReq struct {
Id int64 `json:"id" v:"required#ID不能为空"`
Type string `json:"type"`
RequestParament map[string]any `json:"requestParament"`
ResponseParament map[string]any `json:"responseParament"`
Extension map[string]any `json:"extension"`
}
type DeleteActivePullReq struct {
Id int64 `json:"id" v:"required#ID不能为空"`
}
type ListActivePullReq struct {
Page *beans.Page `json:"page"`
Type string `json:"type"`
}
type ListActivePullRes struct {
List []*ActivePullVO `json:"list"`
Total int `json:"total"`
}
type ActivePullVO 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:"更新时间"`
}
@@ -0,0 +1,66 @@
package skill
import (
"gitea.redpowerfuture.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"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
}
type CreateSkillTemplateRes struct {
Id int64 `json:"id,string"`
}
type UpdateSkillTemplateReq struct {
g.Meta `path:"/update" method:"put" tags:"Skill技能管理" summary:"修改Skill用户技能" dc:"修改Skill用户技能"`
Id int64 `json:"id" v:"required#ID不能为空"`
Name string `json:"name"`
Description string `json:"description"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
}
type DeleteSkillTemplateReq struct {
g.Meta `path:"/delete" method:"delete" tags:"Skill技能管理" summary:"删除Skill技能" dc:"删除Skill技能"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
type GetSkillTemplateReq struct {
g.Meta `path:"/get" method:"get" tags:"Skill技能管理" summary:"Skill技能详情" dc:"Skill技能详情"`
Id int64 `json:"id"`
Name string `json:"name"`
NotInId int64 `json:"notInId"`
}
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"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
@@ -0,0 +1,86 @@
package skill
import (
"gitea.redpowerfuture.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"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
}
type CreateSkillUserRes struct {
Id int64 `json:"id,string"`
}
type UpdateSkillUserReq struct {
g.Meta `path:"/update" method:"put" tags:"Skill用户技能管理" summary:"修改Skill用户技能" dc:"修改Skill用户技能"`
Id int64 `json:"id" v:"required#ID不能为空"`
Name string `json:"name"`
Description string `json:"description"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
}
type DeleteSkillUserReq struct {
g.Meta `path:"/delete" method:"delete" tags:"Skill用户技能管理" summary:"删除Skill用户技能" dc:"删除Skill用户技能"`
Id int64 `json:"id" v:"required#ID不能为空"`
}
type GetSkillUserReq struct {
g.Meta `path:"/get" method:"get" tags:"Skill用户技能管理" summary:"Skill用户技能详情" dc:"Skill用户技能详情"`
Id int64 `json:"id"`
Name string `json:"name"`
Creator string `json:"creator"`
NotInId int64 `json:"notInId"`
}
type GetSkillReq struct {
g.Meta `path:"/getUserOrTemplate" method:"get" tags:"Skill用户技能管理" summary:"Skill技能详情" dc:"Skill技能详情"`
Id int64 `json:"id"`
Name string `json:"name"`
Creator string `json:"creator"`
NotInId int64 `json:"notInId"`
}
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"`
FileName string `json:"fileName"`
FileUrl string `json:"fileUrl"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
ImgAddressPrefix string `json:"imgAddressPrefix"`
}
+28
View File
@@ -0,0 +1,28 @@
package entity
import "gitea.redpowerfuture.com/red-future/common/beans"
type ActivePull struct {
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
Type string `orm:"type" json:"type"`
RequestParament map[string]any `orm:"request_parament" json:"requestParament"`
ResponseParament map[string]any `orm:"response_parament" json:"responseParament"`
Extension map[string]any `orm:"extension" json:"extension"`
}
type activePullCol struct {
beans.SQLBaseCol
Type string
RequestParament string
ResponseParament string
Extension string
}
var ActivePullCol = activePullCol{
SQLBaseCol: beans.DefSQLBaseCol,
Type: "type",
RequestParament: "request_parament",
ResponseParament: "response_parament",
Extension: "extension",
}
+31
View File
@@ -0,0 +1,31 @@
package entity
import "gitea.redpowerfuture.com/red-future/common/beans"
type CreationInfo struct {
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
// 业务字段
HtmlFileUrl string `orm:"html_file_url" json:"htmlFileUrl"`
ImageUrls []string `orm:"image_urls" json:"imageUrls"`
ContentType string `orm:"content_type" json:"contentType"`
Theme string `orm:"theme" json:"theme"`
Title string `orm:"title" json:"title"`
}
type creationInfoCol struct {
beans.SQLBaseCol
HtmlFileUrl string
ImageUrls string
ContentType string
Theme string
Title string
}
var CreationInfoCol = creationInfoCol{
SQLBaseCol: beans.DefSQLBaseCol,
HtmlFileUrl: "html_file_url",
ImageUrls: "image_urls",
ContentType: "content_type",
Theme: "theme",
Title: "title",
}
+24
View File
@@ -0,0 +1,24 @@
package entity
import (
"gitea.redpowerfuture.com/red-future/common/beans"
)
type FileTemp struct {
beans.SQLBaseDO `orm:",inherit"` // 嵌入基础字段:Id, TenantId, Creator, CreatedAt, Updater, UpdatedAt, DeletedAt
BusinessId string `orm:"business_id" json:"businessId"`
FileUrl string `orm:"file_url" json:"fileUrl"`
}
type fileTempCol struct {
beans.SQLBaseCol
BusinessId string
FileUrl string
}
var FileTempCol = fileTempCol{
SQLBaseCol: beans.DefSQLBaseCol,
BusinessId: "business_id",
FileUrl: "file_url",
}
+71
View File
@@ -0,0 +1,71 @@
package entity
import (
"ai-agent/workflow/consts/flow"
"gitea.redpowerfuture.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:"流程名称"`
NodeGroupId string `orm:"node_group_id" json:"nodeGroupId" description:"节点组ID"`
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"`
TotalTokens int `orm:"total_tokens" json:"totalTokens" description:"总token消耗"`
TotalFee int `orm:"total_fee" json:"totalFee" description:"总费用"`
SessionDel bool `orm:"session_del" json:"sessionDel" description:"会话是否删除"`
ResultDel bool `orm:"result_del" json:"resultDel" description:"结果是否删除"`
Extension map[string]interface{} `orm:"extension" json:"extension" description:"扩展字段"`
}
type flowExecutionCol struct {
beans.SQLBaseCol
FlowUserId string
FlowName string
NodeGroupId string
TriggerType string
DurationMs string
Status string
FlowContent string
NodeInputParams string
OutputParams string
ErrorMessage string
TraceId string
SessionId string
TotalTokens string
TotalFee string
SessionDel string
ResultDel string
Extension string
}
var FlowExecutionCol = flowExecutionCol{
SQLBaseCol: beans.DefSQLBaseCol,
FlowUserId: "flow_user_id",
FlowName: "flow_name",
NodeGroupId: "node_group_id",
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",
TotalTokens: "total_tokens",
TotalFee: "total_fee",
SessionDel: "session_del",
ResultDel: "result_del",
Extension: "extension",
}
+41
View File
@@ -0,0 +1,41 @@
package entity
import (
"ai-agent/workflow/consts/flow"
"gitea.redpowerfuture.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",
}

Some files were not shown because too many files have changed in this diff Show More