feat: 新增操作日志、任务分页查询与模型失败重试优化

- 新增操作日志表(asynch_op_log)及对应DAO,记录任务创建等操作的审计信息
- 新增任务分页查询接口(ListTask)及对应DTO、Service和DAO方法
- 优化模型调用失败重试逻辑:支持配置重试排队策略(插队到队首或队尾)
- 新增临时文件存储机制,当模型调用成功但OSS上传失败时,下次仅重试OSS上传
- 模型配置新增retry_queue_max_seconds字段,控制失败重试排队策略
- 更新数据库表结构(asynch_models、asynch_task、新增asynch_op_log)及同步更新SQL
- 配置文件调整:超时单位改为秒,更新服务地址和轮询间隔
- 修复模型列表查询支持按名称模糊搜索
This commit is contained in:
2026-04-25 10:42:21 +08:00
parent 23b83cae39
commit f6c70a451e
22 changed files with 573 additions and 214 deletions

View File

@@ -7,19 +7,20 @@ import (
// CreateModelReq 添加模型配置
type CreateModelReq struct {
g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"`
ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"模型名称(唯一标识)"`
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#baseUrl不能为空" dc:"模型服务基础地址(如 http(s)://host:port"`
Route string `p:"route" json:"route" dc:"路由/路径(拼接到 BaseURL 之后的可选路径)"`
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST默认POST"`
APIKey string `p:"apiKey" json:"apiKey" dc:"请求密钥绑定请求头示例TTS_API_KEY:your-key"`
Enabled int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用"`
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数"`
QueueLimit int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(超过则拒绝/限流)"`
TimeoutMs int `p:"timeoutMs" json:"timeoutMs" dc:"请求超时时间(秒)"`
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数"`
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"自动清理间隔(秒)(如清理超时任务/队列)"`
Remark string `p:"remark" json:"remark" dc:"备注说明"`
g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"`
ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"模型名称(唯一标识)"`
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#baseUrl不能为空" dc:"模型服务基础地址(如 http(s)://host:port"`
Route string `p:"route" json:"route" dc:"路由/路径(拼接到 BaseURL 之后的可选路径)"`
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST默认POST"`
APIKey string `p:"apiKey" json:"apiKey" dc:"请求密钥绑定请求头示例TTS_API_KEY:your-key"`
Enabled int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用"`
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数"`
QueueLimit int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(超过则拒绝/限流)"`
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒)"`
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数"`
RetryQueueMaxSeconds int `p:"retryQueueMaxSeconds" json:"retryQueueMaxSeconds" dc:"失败重试最大排队时间0表示失败重试插队到队首>0表示排队超过该时间后插队否则仍到队尾"`
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"自动清理间隔(秒)(如清理超时任务/队列)"`
Remark string `p:"remark" json:"remark" dc:"备注说明"`
}
type CreateModelRes struct {
@@ -28,19 +29,20 @@ type CreateModelRes struct {
// UpdateModelReq 更新模型配置
type UpdateModelReq struct {
g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"`
ID int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"`
BaseURL string `p:"baseUrl" json:"baseUrl" dc:"模型服务基础地址"`
Route string `p:"route" json:"route" dc:"路由/路径"`
HttpMethod *string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST可选更新"`
APIKey *string `p:"apiKey" json:"apiKey" dc:"请求密钥绑定(请求头)(可选更新)"`
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用(可选更新)"`
MaxConcurrency *int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(可选更新)"`
QueueLimit *int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(可选更新)"`
TimeoutMs *int `p:"timeoutMs" json:"timeoutMs" dc:"请求超时时间(秒)(可选更新)"`
RetryTimes *int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数(可选更新)"`
AutoCleanSeconds *int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"自动清理间隔(秒)(可选更新)"`
Remark *string `p:"remark" json:"remark" dc:"备注说明(可选更新)"`
g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"`
ID int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"`
BaseURL string `p:"baseUrl" json:"baseUrl" dc:"模型服务基础地址"`
Route string `p:"route" json:"route" dc:"路由/路径"`
HttpMethod *string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST可选更新"`
APIKey *string `p:"apiKey" json:"apiKey" dc:"请求密钥绑定(请求头)(可选更新)"`
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用(可选更新)"`
MaxConcurrency *int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(可选更新)"`
QueueLimit *int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(可选更新)"`
TimeoutSeconds *int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒)(可选更新)"`
RetryTimes *int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数(可选更新)"`
RetryQueueMaxSeconds *int `p:"retryQueueMaxSeconds" json:"retryQueueMaxSeconds" dc:"失败重试最大排队时间(秒)(可选更新)"`
AutoCleanSeconds *int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"自动清理间隔(秒)(可选更新)"`
Remark *string `p:"remark" json:"remark" dc:"备注说明(可选更新)"`
}
// DeleteModelReq 删除模型配置
@@ -61,8 +63,9 @@ type GetModelRes struct {
// ListModelReq 配置列表
type ListModelReq struct {
g.Meta `path:"/listModel" method:"post" tags:"模型管理" summary:"模型配置列表" dc:"分页获取模型配置列表"`
Page *beans.Page `p:"page" json:"page" dc:"分页参数"`
g.Meta `path:"/listModel" method:"post" tags:"模型管理" summary:"模型配置列表" dc:"分页获取模型配置列表"`
Page *beans.Page `p:"page" json:"page" dc:"分页参数"`
ModelName string `p:"modelName" json:"modelName" dc:"模型名称(模糊查询,可选)"`
}
type ListModelRes struct {

View File

@@ -1,11 +1,15 @@
package dto
import "github.com/gogf/gf/v2/frame/g"
import (
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
)
// CreateTaskReq 创建异步任务
type CreateTaskReq struct {
g.Meta `path:"/createTask" method:"post" tags:"任务管理" summary:"创建异步任务" dc:"创建异步任务并返回任务ID"`
ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"模型名称"`
BizName string `p:"bizName" json:"bizName" dc:"业务名称(调用方模块/系统,用于统计)"`
InputRef string `p:"inputRef" json:"inputRef" dc:"输入引用如OSS/文件引用等)"`
RequestPayload any `p:"requestPayload" json:"requestPayload" dc:"请求负载(透传给模型服务)"`
}
@@ -40,3 +44,17 @@ type GetTaskBatchItem struct {
type GetTaskBatchRes struct {
List []GetTaskBatchItem `json:"list" dc:"任务列表"`
}
// ListTaskReq 任务列表分页查询
type ListTaskReq struct {
g.Meta `path:"/listTask" method:"post" tags:"任务管理" summary:"任务列表" dc:"分页查询任务列表,支持按状态/模型名称/task_id过滤"`
Page *beans.Page `p:"page" json:"page" dc:"分页参数"`
ModelName string `p:"modelName" json:"modelName" dc:"模型名称(模糊匹配)"`
TaskID string `p:"taskId" json:"taskId" dc:"任务ID模糊匹配"`
State *int `p:"state" json:"state" dc:"任务状态0/1/2/3/4可选"`
}
type ListTaskRes struct {
List any `json:"list" dc:"列表数据"`
Total int64 `json:"total" dc:"总数"`
}

View File

@@ -12,8 +12,9 @@ type asynchModelCol struct {
Enabled string
MaxConcurrency string
QueueLimit string
TimeoutMs string
TimeoutSeconds string
RetryTimes string
RetryQueueMaxSecs string
AutoCleanSeconds string
Remark string
}
@@ -28,25 +29,27 @@ var AsynchModelCol = asynchModelCol{
Enabled: "enabled",
MaxConcurrency: "max_concurrency",
QueueLimit: "queue_limit",
TimeoutMs: "timeout_ms",
TimeoutSeconds: "timeout_seconds",
RetryTimes: "retry_times",
RetryQueueMaxSecs: "retry_queue_max_seconds",
AutoCleanSeconds: "auto_clean_seconds",
Remark: "remark",
}
// AsynchModel 异步模型配置
type AsynchModel struct {
beans.SQLBaseDO `orm:",inline"`
ModelName string `orm:"model_name" json:"modelName"`
BaseURL string `orm:"base_url" json:"baseUrl"`
Route string `orm:"route" json:"route"`
HttpMethod string `orm:"http_method" json:"httpMethod"`
APIKey string `orm:"api_key" json:"apiKey"`
Enabled int `orm:"enabled" json:"enabled"`
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
QueueLimit int `orm:"queue_limit" json:"queueLimit"`
TimeoutMs int `orm:"timeout_ms" json:"timeoutMs"`
RetryTimes int `orm:"retry_times" json:"retryTimes"`
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
Remark string `orm:"remark" json:"remark"`
beans.SQLBaseDO `orm:",inline"`
ModelName string `orm:"model_name" json:"modelName"`
BaseURL string `orm:"base_url" json:"baseUrl"`
Route string `orm:"route" json:"route"`
HttpMethod string `orm:"http_method" json:"httpMethod"`
APIKey string `orm:"api_key" json:"apiKey"`
Enabled int `orm:"enabled" json:"enabled"`
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
QueueLimit int `orm:"queue_limit" json:"queueLimit"`
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
RetryTimes int `orm:"retry_times" json:"retryTimes"`
RetryQueueMaxSecs int `orm:"retry_queue_max_seconds" json:"retryQueueMaxSeconds"`
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
Remark string `orm:"remark" json:"remark"`
}

View File

@@ -0,0 +1,57 @@
package entity
import (
"gitea.com/red-future/common/beans"
)
type asynchOpLogCol struct {
beans.SQLBaseCol
IP string
UserAgent string
APIPath string
HttpMethod string
BizName string
ModelName string
TaskID string
OpType string
Success string
ErrorMsg string
CostMs string
RequestPayload string
ResponsePayload string
}
var AsynchOpLogCol = asynchOpLogCol{
SQLBaseCol: beans.DefSQLBaseCol,
IP: "ip",
UserAgent: "user_agent",
APIPath: "api_path",
HttpMethod: "http_method",
BizName: "biz_name",
ModelName: "model_name",
TaskID: "task_id",
OpType: "op_type",
Success: "success",
ErrorMsg: "error_msg",
CostMs: "cost_ms",
RequestPayload: "request_payload",
ResponsePayload: "response_payload",
}
// AsynchOpLog 操作日志(创建任务等)
type AsynchOpLog struct {
beans.SQLBaseDO `orm:",inline"`
IP string `orm:"ip" json:"ip"`
UserAgent string `orm:"user_agent" json:"userAgent"`
APIPath string `orm:"api_path" json:"apiPath"`
HttpMethod string `orm:"http_method" json:"httpMethod"`
BizName string `orm:"biz_name" json:"bizName"`
ModelName string `orm:"model_name" json:"modelName"`
TaskID string `orm:"task_id" json:"taskId"`
OpType string `orm:"op_type" json:"opType"`
Success int `orm:"success" json:"success"`
ErrorMsg string `orm:"error_msg" json:"errorMsg"`
CostMs int64 `orm:"cost_ms" json:"costMs"`
RequestPayload any `orm:"request_payload" json:"requestPayload"`
ResponsePayload any `orm:"response_payload" json:"responsePayload"`
}

View File

@@ -19,6 +19,8 @@ type asynchTaskCol struct {
ExpireAt string
RetryCount string
EnqueueAt string
Phase string
TmpFile string
InputRef string
RequestPayload string
}
@@ -37,6 +39,8 @@ var AsynchTaskCol = asynchTaskCol{
ExpireAt: "expire_at",
RetryCount: "retry_count",
EnqueueAt: "enqueue_at",
Phase: "phase",
TmpFile: "tmp_file",
InputRef: "input_ref",
RequestPayload: "request_payload",
}
@@ -56,6 +60,10 @@ type AsynchTask struct {
ExpireAt *gtime.Time `orm:"expire_at" json:"expireAt"` // 已下载(state=4)后的过期时间
RetryCount int `orm:"retry_count" json:"retryCount"`
EnqueueAt *gtime.Time `orm:"enqueue_at" json:"enqueueAt"`
Phase int `orm:"phase" json:"phase"` // 0模型阶段/1OSS阶段
TmpFile string `orm:"tmp_file" json:"tmpFile"` // 临时结果文件路径
// RetryQueueMaxSeconds 为 ListFailedRetryableGlobal 的 join 字段(非任务表字段)
RetryQueueMaxSeconds int `orm:"retry_queue_max_seconds" json:"-"`
InputRef string `orm:"input_ref" json:"inputRef"`
RequestPayload any `orm:"request_payload" json:"requestPayload"`
}