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

@@ -1,138 +1,163 @@
-- -----------------------王立钊2026-04-22 10:00:00-----------------------
-- model-asynch 三张核心表pgsql
-- 1) asynch_models模型配置
-- 2) asynch_task异步任务
-- 3) asynch_op_log操作日志统计用
--------------------pgsql创建 asynch_models / asynch_task ---------------------------
-- 异步模型表
-- =========================
-- 1) asynch_models
-- =========================
CREATE TABLE IF NOT EXISTS asynch_models (
-- 基础字段(与现有表保持一致)
id BIGINT PRIMARY KEY, -- 主键ID非自增
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID
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),
deleted_at TIMESTAMP(6),
-- 业务字段
model_name VARCHAR(128) NOT NULL, -- 模型名称(路由键)
base_url VARCHAR(256) NOT NULL, -- 模型服务基础地址(如 http://1.2.3.4:8080
route VARCHAR(256) NOT NULL DEFAULT '', -- 模型服务路由(如 /v1/infer
model_name VARCHAR(128) NOT NULL, -- 模型名称(路由键)
base_url VARCHAR(256) NOT NULL, -- 模型服务基础地址(如 http://1.2.3.4:8080
route VARCHAR(256) NOT NULL DEFAULT '',-- 模型服务路由(如 /v1/infer
http_method VARCHAR(8) NOT NULL DEFAULT 'POST', -- 请求方式GET/POST
api_key VARCHAR(256) DEFAULT '', -- 请求密钥绑定(请求头示例TTS_API_KEY:your-key
enabled SMALLINT NOT NULL DEFAULT 1, -- 是否启用1启用/0停用
max_concurrency INT NOT NULL DEFAULT 10, -- 单模型最大并发worker/队列消费侧应遵守)
queue_limit INT NOT NULL DEFAULT 1000, -- 单模型排队上限(防堆积,可选)
timeout_ms INT NOT NULL DEFAULT 60000, -- 调用模型服务超时(毫秒
retry_times SMALLINT NOT NULL DEFAULT 0, -- 失败重试次数0表示不重试
auto_clean_seconds INT NOT NULL DEFAULT 86400, -- 已下载(state=4)后的保留时间(秒),到期后清理
remark TEXT DEFAULT '' -- 备注
api_key VARCHAR(1024) DEFAULT '', -- 请求绑定(支持多个逗号分隔X-API-Key:xxx,operation:true
enabled SMALLINT NOT NULL DEFAULT 1, -- 是否启用1启用/0停用
max_concurrency INT NOT NULL DEFAULT 10, -- 单模型最大并发
queue_limit INT NOT NULL DEFAULT 1000, -- 排队上限(近似控制
timeout_seconds INT NOT NULL DEFAULT 60, -- 调用模型服务超时(秒
retry_times SMALLINT NOT NULL DEFAULT 0, -- 失败后最多再重试 N 次(不含首次)
retry_queue_max_seconds INT NOT NULL DEFAULT 0, -- 失败重试最大排队时间0=插队到队首;>0=排队超过该时间后插队,否则仍到队尾
auto_clean_seconds INT NOT NULL DEFAULT 86400, -- 已下载(state=4)后的保留时间(秒)
remark TEXT DEFAULT '' -- 备注
);
-- 索引/约束
CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_models_tenant_model_name ON asynch_models(tenant_id, model_name);
CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_models_tenant_model_name
ON asynch_models(tenant_id, model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_models_tenant_id ON asynch_models(tenant_id);
CREATE INDEX IF NOT EXISTS idx_asynch_models_model_name ON asynch_models(model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_models_enabled ON asynch_models(enabled);
CREATE INDEX IF NOT EXISTS idx_asynch_models_deleted_at ON asynch_models(deleted_at);
-- 表和字段注释
COMMENT ON TABLE asynch_models IS '异步模型表(模型服务配置)';
COMMENT ON COLUMN asynch_models.id IS '主键ID非自增';
COMMENT ON COLUMN asynch_models.tenant_id IS '租户ID';
COMMENT ON COLUMN asynch_models.creator IS '创建人';
COMMENT ON COLUMN asynch_models.created_at IS '创建时间';
COMMENT ON COLUMN asynch_models.updater IS '更新人';
COMMENT ON COLUMN asynch_models.updated_at IS '更新时间';
COMMENT ON COLUMN asynch_models.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN asynch_models.model_name IS '模型名称(路由键)';
COMMENT ON COLUMN asynch_models.base_url IS '模型服务基础地址';
COMMENT ON COLUMN asynch_models.route IS '模型服务路由';
COMMENT ON COLUMN asynch_models.http_method IS '请求方式GET/POST';
COMMENT ON COLUMN asynch_models.api_key IS '请求密钥绑定请求头示例TTS_API_KEY:your-key';
COMMENT ON COLUMN asynch_models.enabled IS '是否启用1启用/0停用';
COMMENT ON COLUMN asynch_models.max_concurrency IS '单模型最大并发';
COMMENT ON COLUMN asynch_models.queue_limit IS '单模型排队上限';
COMMENT ON COLUMN asynch_models.timeout_ms IS '调用模型服务超时(毫秒)';
COMMENT ON COLUMN asynch_models.retry_times IS '失败重试次数';
COMMENT ON COLUMN asynch_models.auto_clean_seconds IS '已下载(state=4)后的保留时间(秒),到期后清理';
COMMENT ON COLUMN asynch_models.remark IS '备注';
-- 兼容已有库:更新字段注释
COMMENT ON COLUMN asynch_models.auto_clean_seconds IS '已下载(state=4)后的保留时间(秒),到期后清理';
COMMENT ON COLUMN asynch_models.api_key IS '请求头绑定支持多个逗号分隔X-API-Key:xxx,operation:true';
COMMENT ON COLUMN asynch_models.retry_times IS '失败后最多再重试 N 次(不含首次)';
COMMENT ON COLUMN asynch_models.retry_queue_max_seconds IS '失败重试最大排队时间0=插队到队首;>0=排队超过该时间后插队,否则仍到队尾';
COMMENT ON COLUMN asynch_models.auto_clean_seconds IS '已下载(state=4)后的保留时间(秒),到期清理';
-- 异步任务表
-- =========================
-- 2) asynch_task
-- =========================
CREATE TABLE IF NOT EXISTS asynch_task (
-- 基础字段(与现有表保持一致)
id BIGINT PRIMARY KEY, -- 主键ID非自增
tenant_id BIGINT NOT NULL DEFAULT 0, -- 租户ID
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),
deleted_at TIMESTAMP(6),
-- 任务核心字段
model_name VARCHAR(128) NOT NULL, -- 模型名称
task_id VARCHAR(64) NOT NULL, -- 任务ID对外返回
state SMALLINT NOT NULL DEFAULT 0, -- 状态:0排队中/1执行中/2成功/3失败/4已下载
oss_file VARCHAR(512) DEFAULT '', -- OSS文件URL/Key
file_type VARCHAR(32) DEFAULT '', -- 文件类型(如 mp3/mp4/png/pdf/...
file_size BIGINT NOT NULL DEFAULT 0, -- 文件大小(字节)
error_msg TEXT DEFAULT '', -- 错误信息
model_name VARCHAR(128) NOT NULL, -- 模型名称
task_id VARCHAR(64) NOT NULL, -- 任务ID对外返回
state SMALLINT NOT NULL DEFAULT 0, -- 0排队中/1执行中/2成功/3失败/4已下载
-- 执行与清理字段
started_at TIMESTAMP, -- 开始执行时间
finished_at TIMESTAMP, -- 执行结束时间
expire_at TIMESTAMP, -- 过期清理时间已下载后写入updated_at + auto_clean_seconds
retry_count INT NOT NULL DEFAULT 0, -- 已重试次数(不含首次)
enqueue_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 入队时间(用于排队顺序,重试会更新时间到队尾)
oss_file VARCHAR(512) DEFAULT '', -- 结果文件OSS地址
file_type VARCHAR(32) DEFAULT '', -- 文件类型mp3/mp4/png/...
file_size BIGINT NOT NULL DEFAULT 0, -- 文件大小(字节)
error_msg TEXT DEFAULT '', -- 错误信息
-- 输入信息(用于排障/重放;如不需要可不写入或置空)
input_ref TEXT DEFAULT '', -- 输入引用如上传文件URL/OSS key/业务侧资源ID
request_payload JSONB -- 请求参数(可选)
started_at TIMESTAMP, -- 开始执行时间
finished_at TIMESTAMP, -- 执行结束时间
expire_at TIMESTAMP, -- state=4 后写入,用于清理
-- 重试/排队
retry_count INT NOT NULL DEFAULT 0, -- 已重试次数(不含首次)
enqueue_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 入队时间(用于排队顺序)
-- 任务执行阶段:用于区分“重试模型”与“仅重试 OSS”
phase SMALLINT NOT NULL DEFAULT 0, -- 0模型阶段/1OSS阶段
tmp_file TEXT DEFAULT '', -- 临时结果文件路径phase=1 时仅重试 OSS 上传)
-- 输入信息(可选)
input_ref TEXT DEFAULT '', -- 输入引用如OSS/业务资源ID等
request_payload JSONB -- 请求参数(可选)
);
-- 索引/约束
CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_task_tenant_task_id ON asynch_task(tenant_id, task_id);
CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_task_tenant_task_id
ON asynch_task(tenant_id, task_id);
CREATE INDEX IF NOT EXISTS idx_asynch_task_tenant_id ON asynch_task(tenant_id);
CREATE INDEX IF NOT EXISTS idx_asynch_task_model_name ON asynch_task(model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_task_state ON asynch_task(state);
CREATE INDEX IF NOT EXISTS idx_asynch_task_enqueue_at ON asynch_task(enqueue_at);
CREATE INDEX IF NOT EXISTS idx_asynch_task_updated_at ON asynch_task(updated_at);
CREATE INDEX IF NOT EXISTS idx_asynch_task_expire_at ON asynch_task(expire_at);
CREATE INDEX IF NOT EXISTS idx_asynch_task_deleted_at ON asynch_task(deleted_at);
-- 表和字段注释
COMMENT ON TABLE asynch_task IS '异步任务表';
COMMENT ON COLUMN asynch_task.id IS '主键ID非自增';
COMMENT ON COLUMN asynch_task.tenant_id IS '租户ID';
COMMENT ON COLUMN asynch_task.creator IS '创建人';
COMMENT ON COLUMN asynch_task.created_at IS '创建时间';
COMMENT ON COLUMN asynch_task.updater IS '更新人';
COMMENT ON COLUMN asynch_task.updated_at IS '更新时间';
COMMENT ON COLUMN asynch_task.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN asynch_task.model_name IS '模型名称';
COMMENT ON COLUMN asynch_task.task_id IS '任务ID对外返回';
COMMENT ON COLUMN asynch_task.state IS '状态0排队中/1执行中/2成功/3失败/4已下载';
COMMENT ON COLUMN asynch_task.oss_file IS 'OSS文件URL/Key';
COMMENT ON COLUMN asynch_task.file_type IS '文件类型';
COMMENT ON COLUMN asynch_task.file_size IS '文件大小(字节)';
COMMENT ON COLUMN asynch_task.error_msg IS '错误信息';
COMMENT ON COLUMN asynch_task.started_at IS '开始执行时间';
COMMENT ON COLUMN asynch_task.finished_at IS '执行结束时间';
COMMENT ON COLUMN asynch_task.expire_at IS '过期清理时间(已下载后保留到期)';
COMMENT ON COLUMN asynch_task.state IS '0排队中/1执行中/2成功/3失败/4已下载';
COMMENT ON COLUMN asynch_task.retry_count IS '已重试次数(不含首次)';
COMMENT ON COLUMN asynch_task.enqueue_at IS '入队时间(用于排队顺序,重试会更新时间到队尾';
COMMENT ON COLUMN asynch_task.input_ref IS '输入引用如上传文件URL/OSS key/业务侧资源ID';
COMMENT ON COLUMN asynch_task.request_payload IS '请求参数JSON';
COMMENT ON COLUMN asynch_task.enqueue_at IS '入队时间(用于排队顺序)';
COMMENT ON COLUMN asynch_task.phase IS '执行阶段0模型阶段/1OSS阶段模型已成功等待上传OSS';
COMMENT ON COLUMN asynch_task.tmp_file IS '临时结果文件路径phase=1 时仅重试 OSS 上传';
-- 兼容已有库增量加字段PostgreSQL 支持 IF NOT EXISTS
ALTER TABLE asynch_task ADD COLUMN IF NOT EXISTS retry_count INT NOT NULL DEFAULT 0;
ALTER TABLE asynch_task ADD COLUMN IF NOT EXISTS enqueue_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
COMMENT ON COLUMN asynch_task.retry_count IS '已重试次数(不含首次)';
COMMENT ON COLUMN asynch_task.enqueue_at IS '入队时间(用于排队顺序,重试会更新时间到队尾)';
COMMENT ON COLUMN asynch_task.state IS '状态0排队中/1执行中/2成功/3失败/4已下载';
COMMENT ON COLUMN asynch_task.expire_at IS '过期清理时间(已下载后保留到期)';
-- 对存量数据进行入队时间回填(只在 enqueue_at 为空时)
UPDATE asynch_task SET enqueue_at = created_at WHERE enqueue_at IS NULL;
-- =========================
-- 3) asynch_op_log
-- =========================
CREATE TABLE IF NOT EXISTS asynch_op_log (
-- 基础字段(与现有表保持一致)
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),
-- 基础审计信息
ip VARCHAR(64) DEFAULT '',
user_agent VARCHAR(256) DEFAULT '',
api_path VARCHAR(256) DEFAULT '',
http_method VARCHAR(16) DEFAULT '',
-- 业务信息
biz_name VARCHAR(128) NOT NULL DEFAULT '', -- 调用方业务模块/系统
model_name VARCHAR(128) NOT NULL DEFAULT '',
task_id VARCHAR(64) NOT NULL DEFAULT '',
-- 统计字段
op_type VARCHAR(64) NOT NULL DEFAULT 'createTask', -- 操作类型(默认创建任务)
success SMALLINT NOT NULL DEFAULT 1, -- 1成功/0失败
error_msg TEXT DEFAULT '',
cost_ms BIGINT NOT NULL DEFAULT 0, -- 耗时(毫秒)
-- 请求/响应 JSON用于后期统计分析
request_payload JSONB,
response_payload JSONB
);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_tenant_time ON asynch_op_log(tenant_id, created_at);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_model_name ON asynch_op_log(model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_biz_name ON asynch_op_log(biz_name);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_task_id ON asynch_op_log(task_id);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_op_type ON asynch_op_log(op_type);
CREATE INDEX IF NOT EXISTS idx_asynch_op_log_deleted_at ON asynch_op_log(deleted_at);
COMMENT ON TABLE asynch_op_log IS '操作记录日志表(创建任务等,用于统计)';
COMMENT ON COLUMN asynch_op_log.biz_name IS '业务名称(调用方模块/系统)';
COMMENT ON COLUMN asynch_op_log.model_name IS '模型名称';
COMMENT ON COLUMN asynch_op_log.task_id IS '任务ID';
COMMENT ON COLUMN asynch_op_log.op_type IS '操作类型(如 createTask/getTaskResult/getTaskBatch 等)';
COMMENT ON COLUMN asynch_op_log.success IS '是否成功1成功/0失败';
COMMENT ON COLUMN asynch_op_log.error_msg IS '错误信息(失败时)';
COMMENT ON COLUMN asynch_op_log.cost_ms IS '耗时(毫秒)';
COMMENT ON COLUMN asynch_op_log.request_payload IS '请求 JSON';
COMMENT ON COLUMN asynch_op_log.response_payload IS '响应 JSON';