docs(readme): 更新项目文档为智能模型网关

chore(database): 优化模型配置表结构和索引设计

- 重新设计 model_gateway_models 表的字段结构和约束
- 新增 billing_config 和 special_params 配置字段
- 优化索引策略,包括复合索引和条件索引的创建
- 统一表注释格式和字段说明文档
- 移除过时的配置字段和冗余索引
This commit is contained in:
WangLiZhao
2026-07-07 09:47:30 +08:00
parent d0a8d6889c
commit 6a48edb742
2 changed files with 578 additions and 381 deletions
+394 -166
View File
@@ -1,206 +1,434 @@
# model-asynch(模型异步中间件)[2026.5.12前,暂时弃置]
# Model Gateway — 智能模型网关
一个独立的异步中间件服务:按模型配置路由调用不同模型服务,统一生成 `task_id`,后台异步执行,结果上传 OSS,并提供查询/批量领取/自动重试/自动清理能力,便于业务方“拿走结果并转移”
> 分支约定:`dev` 为开发分支;`main`(或 master)为线上主分支。
统一的 AI 模型网关服务,负责多模态(文本、图像、音频、向量、视频、全模态)模型调用的路由、执行与管理。基于 Go (GoFrame v2) 构建,使用 PostgreSQL、Redis 和 Consul 服务发现
---
## 1. 核心功能
## 架构概览
### 1.1 模型配置(asynch_models
- 增删改查模型服务配置(`model_name` 唯一标识)
- 支持配置:
- 请求地址:`base_url + route`
- 请求方式:`http_method`GET/POST
- 请求头:`head_msg`(以请求头注入,支持多个 header)
- 超时:`timeout_seconds`
- 并发:`max_concurrency`(按租户+模型的 Redis 分布式信号量限流
- 重试:`retry_times`(失败后最多再重试 N 次)
- 保留:`auto_clean_seconds`(任务被业务领取到 `state=4` 后的保留秒数,到期清理)
```
客户端 ──> HTTP API ──> Controller ──> Service ──> DAO ──> PostgreSQL
│ └─> Redis(信号量/队列)
├──> AI 模型服务商(OpenAI、阿里云、火山引擎等
├──> OSS 文件服务
├──> Admin-go(租户/余额)
├──> Prompts-core(会话回调
└──> Skill 技能服务
```
### 1.2 异步任务(asynch_task
- 创建任务:生成 `task_id`,入库排队
- 后台 Worker
- PostgreSQL `FOR UPDATE SKIP LOCKED` 抢占任务,支持多实例不重复消费
- 调用模型服务(GET/POST
- 结果上传 OSS(调用你们的 OSS 文件服务 `oss/file/uploadFile`,透传 `Authorization/X-User-Info`
- 批量领取结果:批量查询 `task_id` 列表,返回 `task_id/state/oss_file`,并把成功的任务从 `state=2` 更新为 `state=4`
- 自动重试:失败 `state=3` 会由清理器按 `retry_times` 重新入队到队尾
- 自动清理:
- `state=4``expire_at` 到期 → 硬删除任务
- 失败重试耗尽仍失败 → 硬删除任务
- `state=0/1` 超时 → 标记失败(防止卡死)
系统分为六个层次:
### 1.3 统计(asynch_model_stat
- 按天统计:`day + tenant_id + creator + model_name -> request_count`
- 统计口径:仅在 Worker 真正调用模型服务时计数(OSS 重试不计数)
- 用途:给其他服务提供全局限流/监控依据
| 层次 | 目录 | 职责 |
|---|---|---|
| **控制器层** | `controller/` | HTTP 路由注册与请求绑定 |
| **业务逻辑层** | `service/` | 模型配置 CRUD、异步任务执行、提示词构建、队列管理、统计 |
| **数据访问层** | `dao/` | PostgreSQL 数据访问(GoFrame ORM |
| **模型层** | `model/` | DTO 请求/响应结构与数据库实体定义 |
| **通用工具层** | `common/` | 计费、类型转换、文件处理、请求头、映射、提示词、流式解析等工具 |
| **常量层** | `consts/` | 公共常量和表名定义 |
---
## 2. 使用流程(业务方如何接入)
## 核心功能
### 第一步:创建模型配置
业务方(或运维)先在中间件里创建/更新模型配置(`model_name` 为唯一键),例如:
- `POST /model/createModel`(或 `/model/updateModel`
### 1. 模型配置管理 (`model_gateway_models`)
提供 AI 模型服务配置的完整增删改查:
| 字段 | 说明 |
|---|---|
| `model_name` | 模型名称(唯一标识) |
| `model_type` | 模型分类(100=推理, 200=图像, 300=音频, 400=向量, 500=全模态, 600=视频) |
| `operator_name` | 运营商标识(OpenAI、阿里云、火山引擎等) |
| `base_url` | 模型服务地址 |
| `http_method` | GET / POST |
| `head_msg` | 每次调用注入的请求头 |
| `form_json` | 动态表单定义(用于前端按模型渲染参数表单) |
| `request_mapping` / `response_mapping` | 标准格式与提供商 API 之间的参数映射 |
| `call_mode` | 0=同步, 1=异步, 2=流式 |
| `max_concurrency` | 单模型最大并发数(按租户维度限流) |
| `timeout_seconds` | 请求超时时间(秒) |
| `retry_times` | 失败重试次数 |
| `billing_config` | 计费规则(推理阶梯计价 / 视频分辨率计价) |
| `stream_config` | SSE 流式解析配置 |
| `query_config` | 异步任务轮询/查询配置 |
**支持的模型类型:**
| 类型码 | 类别 | 子类型 |
|---|---|---|
| 100 | 推理模型 | 文本生成、对话 |
| 200-205 | 图像模型 | 文生图、图生图、图片编辑、图片变体、图文生图 |
| 300-303 | 音频模型 | 文生语音、语音转文字、语音转语音 |
| 400-402 | 向量模型 | 文本嵌入、重排序 |
| 500-502 | 全模态模型 | 文图音频、视觉理解 |
| 600-604 | 视频模型 | 文生视频、图生视频、图文生视频、视频生视频 |
### 2. 异步任务执行 (`model_gateway_task`)
任务生命周期如下:
```
CreateTask ──> state=0(排队中)
Worker 抢占(state=1,执行中)
├── 同步模式:直接调用模型
├── 异步模式:提交任务,通过 QueryConfig 轮询
└── 流式模式:通过 StreamConfig 解析 SSE 事件
├── 成功(state=2)──> 上传结果到 OSS
│ └──> 触发回调(如有配置)
└── 失败(state=3)──> 重试(最多 retry_times 次)
客户端下载(state=4,已下载)
```
**关键特性:**
- **任务创建**:立即返回 `taskId`,执行在 goroutine 中异步完成
- **并发控制**:基于 Redis 的分布式信号量,按模型维度限制并发数
- **队列门控**:基于 Redis Lua 脚本的严格队列插槽机制,防止分布式创建下超限
- **自动重试**:临时性错误(超时、内部错误)自动重试;硬错误直接失败
- **OSS 上传**:结果以 JSON 格式上传到 OSS 文件服务,OSS URL 存入任务记录
- **回调通知**:任务完成时可选触发 HTTP 回调(`TriggerCallback``TriggerPromptsCallback``CallbackBuildResult`
- **阶梯计费**:支持推理 token 阶梯计价和视频分辨率计价两种模型
### 3. 提示词构建 (`/buildMessages`)
异步构建推理模型的结构化消息:
- 从配置合并系统提示词(`modelPrompts.types`
- 拉取技能 Markdown 内容(`SkillMdContent`
- 通过 `GetSessionHistory` 注入会话历史
- 根据附件数量自动拆分多轮(`SplitByAttachment`
- 支持视频模型通过对话模型编排的多轮生成
### 4. 动态调参 (`/autoTune`)
周期性自动调参(建议每小时触发),基于近期 P90 执行耗时和到达率动态调整 `max_concurrency` 和队列上限:
- 读取模型配置(数据库中的上限值 cap)
- 根据时间窗口内已完成任务统计 P90 执行耗时
- 使用 Little 定律(到达率 × P90 / 利用率)计算新的并发数
- 单次调整幅度限制在 ±50%
- 运行时参数写入 Redis(2 小时 TTL),不修改数据库中的上限值
### 5. 日统计 (`model_gateway_log_stat`)
按天、租户、创建人、模型维度的请求计数,使用原子 upsert(`ON DUPLICATE KEY UPDATE`)。
按天、租户、创建人、模型维度的请求计数,使用原子 upsert(`ON DUPLICATE KEY UPDATE`)。
### 6. 核心接口详细说明
#### 6.1 构建消息结构 `/buildMessages`
构建推理模型的提示词消息体,将系统提示词、技能知识、会话历史、用户自定义提示词合并为最终的消息结构。
**请求示例(JSON):**
请求示例(JSON):
```json
{
"modelName": "model-service",
"modelsType": "1,2,3",
"baseUrl": "http://127.0.0.1:8000",
"route": "/api/v1/chat",
"httpMethod": "POST",
"headMsg": "API_KEY:model-key,API_STATE:true,API_NUM:123",
"enabled": 1,
"maxConcurrency": 5,
"queueLimit": 20,
"timeoutSeconds": 1800,
"expectedSeconds": 600,
"retryTimes": 3,
"retryQueueMaxSeconds": 600,
"autoCleanSeconds": 3600,
"remark": "Model-Service 模型服务"
"modelName": "gpt-4o",
"buildType": 1,
"skillName": "code-review",
"callbackUrl": "http://callback.example.com/result",
"nodeId": "node_001",
"sessionId": "session_abc",
"customPrompt": "请用中文回答",
"messages": {
"model": "gpt-4o",
"max_tokens": 4096,
"messages": [
{"role": "system", "content": [{"type": "text", "text": "你是一名助手"}]},
{"role": "user", "content": [{"type": "text", "text": "写一封邮件"}]}
]
}
}
```
参数说明:
- `modelName`:模型名称(唯一标识/路由键)
- `modelsType`:模型类型ID列表(逗号分隔),示例:`1,2,3`(关联 `asynch_models_type.type_id`
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `modelName` | string | 是 | 网关模型名称 |
| `buildType` | int | 是 | 构建类型:1=单轮构建, 2=多轮构建 |
| `skillName` | string | 否 | 技能名称,用于拉取技能 MD 知识 |
| `callbackUrl` | string | 否 | 构建完成后的回调地址 |
| `nodeId` | string | 否 | 节点 ID(用于查询会话历史) |
| `sessionId` | string | 否 | 会话 ID |
| `messages` | object | 是 | 前端构建的消息结构 |
| `customPrompt` | string | 否 | 用户自定义提示词 |
### 模型类型同步
- `POST /model/type/createModelType` 创建成功后,会同步 `POST``prompts-core``/prompt/createPrompt`
- 同步字段映射:
- `typeId` -> `modelTypeId`
- `type` -> `modelType`
- `promptInfo` -> `promptInfo`
- `responseJsonSchema` -> `responseJsonSchema`
- `version` -> `version`
-`prompts-core` 同步失败,`model-gateway` 会回滚本地新建的模型类型,避免两边数据不一致
- `form`:动态表单配置(JSON数组),用于前端按模型渲染参数表单(字段示例:field/label/type/required
- `baseUrl`:模型服务地址(Base URL
- `route`:模型服务路由(拼接到 baseUrl 后)
- `httpMethod`:请求方式(GET/POST
- `headMsg`:请求头绑定(支持多个 header,逗号分隔,格式 `Key:Value`;布尔/数字也会以字符串形式注入 header)
- `enabled`:是否启用(0禁用/1启用)
- `maxConcurrency`:单模型最大并发(按租户+模型维度限流)
- `queueLimit`:排队上限(严格控制)。创建任务时通过 Redis Lua 原子闸门校验并占位,保证分布式并发创建不会超限;任务进入成功/失败态后释放占位,失败重试重新入队时会再次占位。
- `timeoutSeconds`:调用模型服务超时(秒)
- `expectedSeconds`:模型预计执行时间(秒,用于超时判定/排队策略等)
- `retryTimes`:失败后最多再重试 N 次(不含首次)
- `retryQueueMaxSeconds`:失败重试最大排队时间(秒);0 表示重试插队到队首;>0 表示排队超过该时间后插队,否则仍到队尾
- `autoCleanSeconds`:任务被领取到 `state=4` 后的保留时间(秒),到期清理
- `remark`:备注说明
**响应:**
### 第二步:创建任务拿到 task_id
业务方发起推理请求时调用:
- `POST /task/createTask`(传 `modelName + requestPayload + bizName + callbackUrl(可选) + modelKey(可选)`
- 中间件返回 `task_id`
- 业务方将 `task_id` 落到自己的业务表,并把业务状态置为「生成中」
> `modelKey` 用于“动态覆盖/补充”模型配置中的 `head_msg`(例如每次请求携带不同的 `X-API-Key:xxx`)。
>
> `callbackUrl` 用于任务成功后的回调通知:当任务 `state=2` 成功时,中间件会发起一次 GET 请求:
> - 实际回调地址:`callbackUrl/{bizName}`
> - query 参数:`task_id/state/oss_file/file_type/text(可选,最多2000字符)`
### 第三步:同步任务进度(推荐批量)
业务方通过轮询/定时任务同步进度:
- 推荐:`POST /task/getTaskBatch`(批量传 `taskIds`,返回每个任务的 `state + oss_file`
- 或单条:`GET /task/getTaskResult?taskId=...`
业务侧拿到 `oss_file` 后自行做资源处理(直接保存或转存),并把业务状态更新为「成功/失败」。
> 说明:批量接口对 `state=2(成功)` 的任务会自动标记为 `state=4(已下载)` 并写入 `expire_at`,用于后续清理。
### 后台执行(由上层定时任务控制)
本项目不再在服务进程内常驻轮询 worker/cleaner,而是提供两个接口供上层定时任务触发:
- `POST /task/runWork`:执行一次 Worker(抢占并处理一批排队任务;适合处理 createTask 立即执行时未处理到的任务和积压队列)
- `POST /task/cleanWork`:执行一次 Cleaner(清理过期任务、失败重试、超时任务失败等)
创建任务执行策略:
- `POST /task/createTask` 成功入库后,会立即异步尝试执行当前任务。
- 若当前模型并发已满,或当前任务未成功抢占,则会按 `asynch.worker.intervalSeconds` 对当前任务做轻量级定向轮询;只要任务仍为 `state=0` 就继续尝试,一旦进入 `state=1/2/3/4` 就立即停止,不会一直轮询。
- 若任务执行成功且配置了 `callbackUrl + bizName`,会在成功落库后异步触发回调钩子。
本地调试(可选):
可在 `config.yml` 中开启自动执行,避免手工频繁调用接口:
```yml
asynch:
worker:
enabled: true
intervalSeconds: 5
batchSize: 10
goroutines: 1
cleaner:
enabled: true
intervalSeconds: 30
```
### 动态并发/队列调参(接口请求控制)
为支持根据最近一段时间的耗时与吞吐对 `max_concurrency/queue_limit` 做动态调整,本项目提供接口供上层定时任务触发(建议每小时一次):
- `POST /model/autoTune`
请求参数(JSON,可选):
```json
{
"windowSeconds": 3600
"taskId": "550e8400-e29b-41d4-a716-446655440000"
}
```
> `windowSeconds` 不传/<=0 默认 36001小时)。
动态调参口径(默认近 1 小时窗口,按 `model_name` 维度):
- 执行耗时:`finished_at - started_at`(取 P90
- 吞吐:近 1 小时完成数 / 3600
**处理流程:**
调参结果不会覆盖 `asynch_models` 中配置的最大上限(cap),而是写入 Redis 运行时参数(带 TTL,默认 2 小时):
- `asynch:runtime:max_concurrency:{model_name}`
- `asynch:runtime:queue_limit:{model_name}`
1. **查模型配置** — 根据 `modelName` 查询 `model_gateway_models` 获取模型定义
2. **创建构建记录** — 写入 `model_gateway_build_record`,状态为处理中
3. **异步执行构建**goroutine):
- **推理模型(type=100**
- 从配置读取系统提示词(`modelPrompts.types.100`
- 若有 `skillName`,通过 `GetSkillUser` 拉取技能 ZIP,提取 MD 内容
- 合并系统提示词 + 技能知识 + 用户自定义提示词到 `messages`
- 通过 `GetSessionHistory` 注入会话历史
- 按附件数量自动拆分多轮(`SplitByAttachment`,按 video/image/audio 的 `maxCount` 约束)
- 返回单轮或多轮消息结构
- **视频模型(type=600-699**
- 查找当前用户的对话模型(`is_chat_model=1`
- 获取该对话模型的协议模板(`prompts_provider_protocol`
- 构建对话模型请求体,调用对话模型生成视频分镜的 JSON rounds
- 将 rounds 上传 OSS,存入构建记录
4. **回写结果** — 更新构建记录状态(成功/失败 + 耗时)
5. **触发回调** — 若有 `callbackUrl`POST 回调通知任务完成
生效位置:
- CreateTask 入队时,严格 queue_limit 闸门会优先使用运行时 `queue_limit`(若无运行时值则回退 cap)。
- Worker 获取并发令牌时,优先使用运行时 `max_concurrency`(若无运行时值则回退 cap)。
**对应源码:**
- Controller[controller/model_gateway_task_controller.go]`ModelGatewayTask.BuildMessages`
- Service[service/task/task_service.go]`taskService.BuildMessages``executeBuild``buildResult`
- Util(提示词合并):[common/util/prompt.go]`MergePrompt``InjectHistory``SplitByAttachment`
- Util(技能知识):[service/prompt/prompt_files_handle_service.go]`SkillMdContent`
#### 6.2 创建异步任务 `/createTask`
创建异步模型调用任务,立即返回 `taskId`,后端 goroutine 异步执行模型调用、结果上传与计费。
**请求示例(JSON):**
```json
{
"modelName": "dall-e-3",
"bizName": "image-generator",
"callbackUrl": "http://callback.example.com/result",
"epicycleId": 12345,
"buildModelName": "gpt-4o",
"requestPayload": {
"model": "dall-e-3",
"prompt": "一只站在树枝上的猫头鹰,水墨风格",
"n": 1,
"size": "1024x1024"
}
}
```
| 请求字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `modelName` | string | 是 | 模型名称 |
| `bizName` | string | 否 | 业务名称,用于统计区分 |
| `callbackUrl` | string | 否 | 任务完成后的回调地址 |
| `requestPayload` | object | 是 | 透传给模型服务的请求参数 |
| `epicycleId` | int64 | 否 | 轮次 IDprompts-core 场景) |
| `buildModelName` | string | 否 | 构建阶段使用的模型名(prompts-core 场景) |
**响应:**
```json
{
"taskId": "550e8400-e29b-41d4-a716-446655440000"
}
```
**处理流程:**
1. **鉴权与参数校验** — 获取用户信息,校验模型配置是否存在且已启用
2. **队列门控(可选)** — 通过 Redis Lua 脚本做严格的分布式队列插槽校验,避免并发超限
3. **写入任务记录** — 状态为 `1(执行中)`,记录 `requestPayload``callbackUrl``bizName`
4. **操作日志** — 记录创建任务的审计日志到 `model_gateway_logs_op`
5. **计费预处理** — 若模型配置了 `billing_config`,从请求参数中提取计费维度数据
6. **异步执行**goroutine `AsyncWorker.handleOne`):
```
检查租户余额 ──> 调用模型服务 ──> 解析响应映射 ──> 计费计算并扣费
┌── 同步模式(call_mode=0):直接调用,等待 HTTP 响应
├── 异步模式(call_mode=1):提交任务后通过 QueryConfig 轮询结果
└── 流式模式(call_mode=2):通过 StreamConfig 解析 SSE 事件流
┌── prompts-core 场景:对模型输出做 ParseAndValidate + 重试机制
└── 普通场景:直接映射响应
上传 OSS ──> 更新任务为成功 ──> 触发回调
```
7. **重试机制**:超时、内部错误等可重试错误会按 `retry_times` 重试;硬错误(参数错误等)直接失败
8. **回调通知**:任务成功或失败后,若有 `callbackUrl` 则 POST 回调;prompts-core 场景额外触发 `TriggerPromptsCallback`
**调用模式对比:**
| 模式 | 值 | 行为 |
|---|---|---|
| 同步 | 0 | 直接 HTTP 调用模型,等待完整响应后返回 |
| 异步 | 1 | 提交任务后通过 `QueryConfig` 配置的轮询地址定时查询结果 |
| 流式 | 2 | 对 SSE 事件流按 `StreamConfig` 规则解析(支持 concat/base64_concat/collect/final |
**对应源码:**
- Controller[controller/model_gateway_task_controller.go]`ModelGatewayTask.CreateTask`
- Service[service/task/task_service.go]`taskService.Create`
- Worker[service/task/worker.go]`asyncWorker.handleOne``InvokeModel`
- 队列门控:[service/queue/queue_gate.go]`AcquireQueueSlot``ReleaseQueueSlot`
- 运行时调参:[service/queue/runtime_tune.go]`GetRuntimeMaxConcurrency``GetRuntimeQueueLimit`
- 异步轮询:[common/util/pull_task.go]`PullTaskResult`
- 流式解析:[common/util/streaming.go]`ParseStreamResponse`
- 计费:[common/util/billing.go]`CalculateBilling``ExtractRequestBilling`
### 7. 全量 API 接口一览
| 分类 | 接口路径 | 方法 | 说明 |
|---|---|---|---|
| **模型配置** | `/createModel` | POST | 创建模型配置 |
| | `/updateModel` | PUT | 更新模型配置 |
| | `/deleteModel` | DELETE | 删除模型配置 |
| | `/getModel` | GET | 获取模型详情 |
| | `/listModel` | GET | 模型列表(分页+筛选) |
| | `/listType` | GET | 模型类型列表 |
| | `/listOperator` | GET | 运营商列表 |
| | `/updateChatModel` | POST | 设置当前用户的对话模型 |
| | `/getIsChatModel` | GET | 获取当前对话模型 |
| | `/autoTune` | POST | 触发动态调参 |
| **任务管理** | `/createTask` | POST | 创建异步任务 |
| | `/jobTask` | POST | 定时批量任务处理器 |
| | `/getTaskResult` | GET | 获取单条任务结果 |
| | `/getTaskBatch` | POST | 批量查询任务(成功任务自动标记为已下载) |
| | `/listTask` | GET | 任务列表分页查询 |
| | `/modelCallback` | POST | 接收异步模型回调通知 |
| | `/queryPending` | GET | 轮询进行中的异步任务 |
| **提示词** | `/buildMessages` | POST | 构建结构化提示词消息(异步) |
| **统计** | `/listModelStat` | GET | 日使用量统计 |
--
## 技术栈
| 组件 | 技术选型 |
|---|---|
| 语言 | Go 1.26 |
| 框架 | GoFrame v2`github.com/gogf/gf/v2` |
| 数据库 | PostgreSQLGoFrame pgsql 驱动) |
| 缓存/队列 | Redis(分布式信号量、队列门控、运行时调参存储) |
| 服务发现 | Consul |
| 链路追踪 | JaegerOTLP HTTP |
| 模型调用 | 原生 HTTP(可定制请求头、鉴权、超时) |
| 文件存储 | OSS 文件上传服务(multipart 表单) |
## 数据库表
| 表名 | 说明 |
|---|---|
| `model_gateway_models` | 模型服务配置(动态表单、请求/响应映射、计费规则) |
| `model_gateway_task` | 异步任务记录(状态机、重试、OSS 结果文件) |
| `model_gateway_build_record` | 提示词构建记录 |
| `model_gateway_logs_op` | 操作审计日志 |
| `model_gateway_logs_stat` | 日维度请求量统计 |
| `prompts_provider_protocol` | 视频模型编排的提供商协议模板 |
## 配置说明
详见 `config.yml`。主要配置块:
- `database` — PostgreSQL 连接(双数据源:`default` + `model_gateway`
- `redis` — Redis 连接
- `consul` — Consul 地址
- `jaeger` — Jaeger OTLP HTTP 端点
- `queryPending` — 异步任务自动轮询配置(调试开关)
- `jobTask` — 批量处理器间隔、批大小、协程池大小
- `modelPrompts.types` — 各模型类型的系统提示词(100/200/300/400/500
- `nodePrompts` — 节点路由提示词模板
## 快速开始
### 环境要求
- Go 1.26+
- PostgreSQL
- Redis
- Consul(可选,用于服务发现)
### 本地开发
1. 克隆仓库
2. 在 PostgreSQL 中执行 `update.sql` 创建所有表
3. 修改 `config.yml` 中的数据库、Redis 和 Consul 配置
4. 启动服务:
```bash
go run main.go
```
5. 服务默认监听 `3004` 端口(可在 `config.yml` 中修改)
### Docker 构建
```bash
docker build -t model-gateway .
docker run -p 3004:3004 model-gateway
```
Dockerfile 使用多阶段构建,基于 `golang:alpine` 镜像,配置了国内 Go 代理,输出剥离调试信息的精简二进制。
---
## 3. 状态机说明(asynch_task.state
## 项目目录结构
| state | 含义 | 产生方 |
|---:|---|---|
| 0 | 排队中 | 创建任务/重试入队 |
| 1 | 执行中 | Worker 抢占后 |
| 2 | 成功(已上传 OSS | Worker |
| 3 | 失败 | Worker / 超时处理 |
| 4 | 已下载(已领取) | 批量领取接口(2→4) |
字段补充:
- `retry_count`:已重试次数(不含首次)
- `enqueue_at`:入队时间(用于排队顺序,重试会更新为 NOW() 放到队尾)
- `expire_at`:仅对 `state=4` 生效,表示保留到期时间
```
.
├── main.go # 入口:路由注册、自动执行器启动
├── config.yml # 应用配置
├── Dockerfile # 多阶段 Docker 构建
├── go.mod / go.sum # Go 模块依赖
├── update.sql # 数据库 DDL
├── controller/ # HTTP 接口控制器
│ ├── model_gateway_models_controller.go
│ ├── model_gateway_task_controller.go
│ └── model_gateway_logs_stat_controller.go
├── service/ # 业务逻辑层
│ ├── gateway/ # OSS 上传、回调、余额、技能/会话查询
│ ├── model/ # 模型 CRUD、对话模型管理
│ ├── prompt/ # 文件拉取、技能 Markdown、提示词构建
│ ├── queue/ # 动态调参、信号量、队列门控、运行时调参
│ ├── stat/ # 使用量统计
│ └── task/ # 任务创建、Worker 执行、异步结果处理
├── dao/ # 数据访问层
│ ├── model_gateway_models_dao.go
│ ├── model_gateway_task_dao.go
│ ├── model_gateway_build_record_dao.go
│ ├── model_gateway_logs_stat_dao.go
│ ├── model_gateway_logs_op_dao.go
│ └── provider_protocol_dao.go
├── model/ # 数据模型
│ ├── dto/ # 请求/响应结构体
│ └── entity/ # 数据库实体
├── common/ # 通用工具
│ └── util/ # 计费、类型转换、文件处理、请求头、映射、提示词、流式、异步轮询
└── consts/ # 常量定义
└── public/ # 模型类型、任务状态、运营商列表、表名
```
---
## 4. 配置说明(config.yml
## 任务状态机
关键配置:
- `database.default`: PostgreSQL 连接
- `redis.default`: Redis 连接(并发令牌、可扩展用途)
| 状态码 | 含义 | 转换来源 |
|---|---:|---|
| 0 | 排队中 | 创建任务或重试入队 |
| 1 | 执行中 | Worker 抢占成功 |
| 2 | 成功(已上传 OSS | Worker 执行完成 |
| 3 | 失败 | Worker 执行出错或超时 |
| 4 | 已下载 | 批量查询接口标记 |
---
## 5. 数据库初始化
## License
项目根目录提供 `update.sql`:首次部署执行建表 SQL。
内部项目 — 红未来科技
---
## 6. 开发与发布建议(Git
- `dev`:日常开发与联调
- `main`:线上稳定分支
- 推荐流程:
1) 从 `main` 拉出 `dev`
2) 功能完成后提 MR/PR 合并回 `main`
3) 打 tag / 发布镜像
+184 -215
View File
@@ -1,233 +1,202 @@
-- =========================
-- model_gateway_models
-- =========================
CREATE TABLE IF NOT EXISTS model_gateway_models (
id int8 PRIMARY KEY,
tenant_id int8 NOT NULL DEFAULT 0,
creator varchar(64) NOT NULL,
created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater varchar(64) NOT NULL,
updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
model_name varchar(128) NOT NULL,
model_type int2 NOT NULL DEFAULT 0,
operator_name varchar(64) NOT NULL DEFAULT '',
base_url varchar(256) NOT NULL,
http_method varchar(8) NOT NULL DEFAULT 'POST',
head_msg jsonb NOT NULL DEFAULT '{}',
api_key varchar(256) NOT NULL DEFAULT '',
is_private int2 NOT NULL DEFAULT 0,
enabled int2 NOT NULL DEFAULT 1,
is_chat_model int2 NOT NULL DEFAULT 0,
is_owner int2 NOT NULL DEFAULT 99,
form_json jsonb NOT NULL DEFAULT '{}',
request_mapping jsonb NOT NULL DEFAULT '{}',
response_mapping jsonb NOT NULL DEFAULT '{}',
response_body varchar(128) NOT NULL DEFAULT '',
token_config jsonb NOT NULL DEFAULT '{}',
extend_mapping jsonb NOT NULL DEFAULT '{}',
query_config jsonb NOT NULL DEFAULT '{}',
stream_config jsonb NOT NULL DEFAULT '{}',
first_frame varchar(128) NOT NULL DEFAULT '',
last_frame varchar(128) NOT NULL DEFAULT '',
max_concurrency int4 NOT NULL DEFAULT 10,
timeout_seconds int4 NOT NULL DEFAULT 600,
retry_times int2 NOT NULL DEFAULT 3,
auto_clean_seconds int4 NOT NULL DEFAULT 86400,
response_token_field varchar(128) NOT NULL DEFAULT '',
call_mode int2 NOT NULL DEFAULT 0,
required_fields jsonb NOT NULL DEFAULT '[]',
max_tokens int4 DEFAULT 0
);
-- ============================================
-- 模型网关 (model-gateway) 建表语句
-- ============================================
CREATE UNIQUE INDEX IF NOT EXISTS uk_model_gateway_models_tenant_creator_model ON model_gateway_models (tenant_id, creator, model_name);
CREATE INDEX IF NOT EXISTS idx_model_gateway_models_model_name ON model_gateway_models (model_name);
CREATE INDEX IF NOT EXISTS idx_model_gateway_models_model_type ON model_gateway_models (model_type);
CREATE INDEX IF NOT EXISTS idx_model_gateway_models_tenant_id ON model_gateway_models (tenant_id);
CREATE INDEX IF NOT EXISTS idx_model_gateway_models_deleted_at ON model_gateway_models (deleted_at);
CREATE INDEX IF NOT EXISTS idx_model_gateway_models_enabled ON model_gateway_models (enabled);
-- 1. 模型配置表
CREATE TABLE "public"."model_gateway_models" (
"id" int8 NOT NULL,
"tenant_id" int8 NOT NULL DEFAULT 0,
"creator" varchar(64) NOT NULL,
"created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) NOT NULL,
"updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp(6),
"model_name" varchar(128) NOT NULL,
"model_type" int2 NOT NULL DEFAULT 0,
"operator_name" varchar(64) NOT NULL DEFAULT '',
"base_url" varchar(256) NOT NULL,
"http_method" varchar(8) NOT NULL DEFAULT 'POST',
"head_msg" jsonb NOT NULL DEFAULT '{}',
"api_key" varchar(256) NOT NULL DEFAULT '',
"is_private" int2 NOT NULL DEFAULT 0,
"enabled" int2 NOT NULL DEFAULT 1,
"is_chat_model" int2 NOT NULL DEFAULT 0,
"form_json" jsonb NOT NULL DEFAULT '{}',
"request_mapping" jsonb NOT NULL DEFAULT '{}',
"response_mapping" jsonb NOT NULL DEFAULT '{}',
"extend_mapping" jsonb NOT NULL DEFAULT '{}',
"query_config" jsonb NOT NULL DEFAULT '{}',
"stream_config" jsonb NOT NULL DEFAULT '{}',
"max_concurrency" int4 NOT NULL DEFAULT 10,
"timeout_seconds" int4 NOT NULL DEFAULT 600,
"retry_times" int2 NOT NULL DEFAULT 3,
"call_mode" int2 NOT NULL DEFAULT 0,
"required_fields" jsonb NOT NULL DEFAULT '[]',
"billing_config" jsonb NOT NULL DEFAULT '{}',
"special_params" jsonb NOT NULL DEFAULT '{}',
CONSTRAINT "model_gateway_models_pkey" PRIMARY KEY ("id")
);
COMMENT ON TABLE model_gateway_models IS '模型配置表';
COMMENT ON COLUMN model_gateway_models.id IS '主键ID(非自增)';
COMMENT ON COLUMN model_gateway_models.tenant_id IS '租户ID';
COMMENT ON COLUMN model_gateway_models.creator IS '创建人';
COMMENT ON COLUMN model_gateway_models.created_at IS '创建时间';
COMMENT ON COLUMN model_gateway_models.updater IS '更新人';
COMMENT ON COLUMN model_gateway_models.updated_at IS '更新时间';
COMMENT ON COLUMN model_gateway_models.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN model_gateway_models.model_name IS '模型名称';
COMMENT ON COLUMN model_gateway_models.model_type IS '模型类型';
COMMENT ON COLUMN model_gateway_models.operator_name IS '运营商名称';
COMMENT ON COLUMN model_gateway_models.base_url IS '模型地址';
COMMENT ON COLUMN model_gateway_models.http_method IS '请求方式 GET/POST';
COMMENT ON COLUMN model_gateway_models.head_msg IS '请求头信息';
COMMENT ON COLUMN model_gateway_models.api_key IS '调用凭证/密钥';
ALTER TABLE "public"."model_gateway_models" OWNER TO "postgres";
COMMENT ON COLUMN model_gateway_models.is_private IS '是否私有化:0-私有 1-公共';
COMMENT ON COLUMN model_gateway_models.enabled IS '是否启用:0-停用 1-启用';
COMMENT ON COLUMN model_gateway_models.is_chat_model IS '是否为对话模型:0-否 1-是';
COMMENT ON COLUMN model_gateway_models.is_owner IS '1=当前用户创建 0=超级管理员';
CREATE INDEX "idx_models_deleted_at" ON "public"."model_gateway_models" ("deleted_at");
CREATE INDEX "idx_models_enabled" ON "public"."model_gateway_models" ("enabled");
CREATE INDEX "idx_models_model_name" ON "public"."model_gateway_models" ("model_name");
CREATE INDEX "idx_models_model_type" ON "public"."model_gateway_models" ("model_type");
CREATE INDEX "idx_models_tenant_id" ON "public"."model_gateway_models" ("tenant_id");
CREATE INDEX "idx_models_tenant_creator" ON "public"."model_gateway_models" ("tenant_id", "creator");
CREATE INDEX "idx_models_tenant_creator_model_deleted" ON "public"."model_gateway_models" ("tenant_id", "creator", "model_name") WHERE deleted_at IS NULL;
CREATE UNIQUE INDEX "uk_models_tenant_creator_model" ON "public"."model_gateway_models" ("tenant_id", "creator", "model_name");
COMMENT ON COLUMN model_gateway_models.form_json IS '动态表单结构';
COMMENT ON COLUMN model_gateway_models.request_mapping IS '请求映射';
COMMENT ON COLUMN model_gateway_models.response_mapping IS '返回映射';
COMMENT ON COLUMN model_gateway_models.response_body IS '返回主体';
COMMENT ON COLUMN model_gateway_models.token_config IS 'Token计算配置';
COMMENT ON COLUMN model_gateway_models.extend_mapping IS '附加映射';
COMMENT ON COLUMN model_gateway_models.query_config IS '查询/回调配置';
COMMENT ON COLUMN model_gateway_models.stream_config IS '流式输出配置';
COMMENT ON COLUMN model_gateway_models.first_frame IS '首帧图片参数';
COMMENT ON COLUMN model_gateway_models.last_frame IS '尾帧图片参数';
COMMENT ON COLUMN model_gateway_models.max_concurrency IS '最大并发数';
COMMENT ON COLUMN model_gateway_models.timeout_seconds IS '调用模型超时(秒)';
COMMENT ON COLUMN model_gateway_models.retry_times IS '失败重试次数';
COMMENT ON COLUMN model_gateway_models.auto_clean_seconds IS '任务完成后自动清理时间(秒)';
COMMENT ON COLUMN model_gateway_models.response_token_field IS '响应中消耗token的字段映射';
COMMENT ON COLUMN model_gateway_models.call_mode IS '调用模式:0-同步 1-异步 2-流式';
COMMENT ON COLUMN model_gateway_models.required_fields IS '必选字段列表';
COMMENT ON COLUMN model_gateway_models.max_tokens IS '最大 token 数,0 表示不传';
COMMENT ON TABLE "public"."model_gateway_models" IS '模型配置表';
COMMENT ON COLUMN "public"."model_gateway_models"."model_name" IS '模型名称';
COMMENT ON COLUMN "public"."model_gateway_models"."model_type" IS '模型类型';
COMMENT ON COLUMN "public"."model_gateway_models"."operator_name" IS '运营商名称';
COMMENT ON COLUMN "public"."model_gateway_models"."base_url" IS '模型地址';
COMMENT ON COLUMN "public"."model_gateway_models"."http_method" IS '请求方式 GET/POST';
COMMENT ON COLUMN "public"."model_gateway_models"."head_msg" IS '请求头信息';
COMMENT ON COLUMN "public"."model_gateway_models"."api_key" IS '调用凭证/密钥';
COMMENT ON COLUMN "public"."model_gateway_models"."is_private" IS '是否私有化:0-私有 1-公共';
COMMENT ON COLUMN "public"."model_gateway_models"."enabled" IS '是否启用:0-停用 1-启用';
COMMENT ON COLUMN "public"."model_gateway_models"."is_chat_model" IS '是否为对话模型:0-否 1-是';
COMMENT ON COLUMN "public"."model_gateway_models"."form_json" IS '动态表单结构';
COMMENT ON COLUMN "public"."model_gateway_models"."request_mapping" IS '请求映射';
COMMENT ON COLUMN "public"."model_gateway_models"."response_mapping" IS '返回映射';
COMMENT ON COLUMN "public"."model_gateway_models"."extend_mapping" IS '附加映射';
COMMENT ON COLUMN "public"."model_gateway_models"."query_config" IS '查询/回调配置';
COMMENT ON COLUMN "public"."model_gateway_models"."stream_config" IS '流式输出配置';
COMMENT ON COLUMN "public"."model_gateway_models"."max_concurrency" IS '最大并发数';
COMMENT ON COLUMN "public"."model_gateway_models"."timeout_seconds" IS '调用模型超时(秒)';
COMMENT ON COLUMN "public"."model_gateway_models"."retry_times" IS '失败重试次数';
COMMENT ON COLUMN "public"."model_gateway_models"."call_mode" IS '调用模式:0-同步 1-异步 2-流式';
COMMENT ON COLUMN "public"."model_gateway_models"."required_fields" IS '必选字段列表';
COMMENT ON COLUMN "public"."model_gateway_models"."billing_config" IS '计费配置';
COMMENT ON COLUMN "public"."model_gateway_models"."special_params" IS '请求特殊参数';
-- 2. 模型网关任务表
CREATE TABLE "public"."model_gateway_task" (
"id" int8 NOT NULL,
"tenant_id" int8 NOT NULL DEFAULT 0,
"creator" varchar(64) NOT NULL,
"created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) NOT NULL,
"updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp(6),
"model_name" varchar(128) NOT NULL,
"task_id" varchar(64) NOT NULL,
"biz_name" varchar(128) NOT NULL DEFAULT '',
"callback_url" varchar(512) DEFAULT '',
"state" int2 NOT NULL DEFAULT 0,
"retry_count" int4 NOT NULL DEFAULT 0,
"error_msg" text DEFAULT '',
"result_file" jsonb NOT NULL DEFAULT '{}',
"request_payload" jsonb NOT NULL DEFAULT '{}',
"duration_seconds" int8 NOT NULL DEFAULT 0,
"epicycle_id" varchar(64) NOT NULL DEFAULT '',
"billing_data" jsonb NOT NULL DEFAULT '[]',
"build_model_name" varchar(128) NOT NULL DEFAULT '',
CONSTRAINT "model_gateway_task_pkey" PRIMARY KEY ("id")
);
-- =========================
-- model_gateway_task
-- =========================
CREATE TABLE IF NOT EXISTS model_gateway_task (
id int8 PRIMARY KEY,
tenant_id int8 NOT NULL DEFAULT 0,
creator varchar(64) NOT NULL,
created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater varchar(64) NOT NULL,
updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
model_name varchar(128) NOT NULL,
task_id varchar(64) NOT NULL,
biz_name varchar(128) NOT NULL DEFAULT '',
callback_url varchar(512) DEFAULT '',
state int2 NOT NULL DEFAULT 0,
retry_count int4 NOT NULL DEFAULT 0,
phase int2 NOT NULL DEFAULT 0,
tmp_file text DEFAULT '',
error_msg text DEFAULT '',
result_file jsonb NOT NULL DEFAULT '{}',
request_payload jsonb NOT NULL DEFAULT '{}',
text_result jsonb NOT NULL DEFAULT '{}',
expend_tokens int8 NOT NULL DEFAULT 0,
duration_seconds int8 NOT NULL DEFAULT 0,
epicycle_id varchar(64) NOT NULL DEFAULT ''
);
ALTER TABLE "public"."model_gateway_task" OWNER TO "postgres";
CREATE UNIQUE INDEX IF NOT EXISTS uk_model_gateway_task_tenant_creator_task_id ON model_gateway_task (tenant_id, creator, task_id);
CREATE INDEX IF NOT EXISTS idx_model_gateway_task_task_id ON model_gateway_task (task_id);
CREATE INDEX IF NOT EXISTS idx_model_gateway_task_state ON model_gateway_task (state);
CREATE INDEX IF NOT EXISTS idx_model_gateway_task_deleted_at ON model_gateway_task (deleted_at);
CREATE INDEX "idx_task_creator_created" ON "public"."model_gateway_task" ("creator", "created_at" DESC);
CREATE INDEX "idx_task_model_state" ON "public"."model_gateway_task" ("model_name", "state");
CREATE INDEX "idx_task_state" ON "public"."model_gateway_task" ("state");
CREATE INDEX "idx_task_state_created" ON "public"."model_gateway_task" ("state", "created_at");
CREATE INDEX "idx_task_task_id" ON "public"."model_gateway_task" ("task_id");
CREATE INDEX "idx_task_tenant_model" ON "public"."model_gateway_task" ("tenant_id", "model_name");
CREATE UNIQUE INDEX "uk_task_task_id" ON "public"."model_gateway_task" ("task_id");
COMMENT ON TABLE model_gateway_task IS '模型网关任务表';
COMMENT ON COLUMN model_gateway_task.id IS '主键ID';
COMMENT ON COLUMN model_gateway_task.tenant_id IS '租户ID';
COMMENT ON COLUMN model_gateway_task.creator IS '创建人';
COMMENT ON COLUMN model_gateway_task.created_at IS '创建时间';
COMMENT ON COLUMN model_gateway_task.updater IS '更新人';
COMMENT ON COLUMN model_gateway_task.updated_at IS '更新时间';
COMMENT ON COLUMN model_gateway_task.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN model_gateway_task.model_name IS '模型名称';
COMMENT ON COLUMN model_gateway_task.task_id IS '任务ID(对外返回';
COMMENT ON COLUMN model_gateway_task.biz_name IS '业务名称(调用方模块/系统';
COMMENT ON COLUMN model_gateway_task.callback_url IS '回调地址';
COMMENT ON COLUMN model_gateway_task.state IS '0排队中/1执行中/2成功/3失败/4已下载';
COMMENT ON COLUMN model_gateway_task.retry_count IS '已重试次数';
COMMENT ON COLUMN model_gateway_task.phase IS '执行阶段:0模型阶段/1OSS阶段';
COMMENT ON COLUMN model_gateway_task.tmp_file IS '临时结果文件路径';
COMMENT ON COLUMN model_gateway_task.error_msg IS '错误信息';
COMMENT ON COLUMN model_gateway_task.result_file IS '结果文件:{oss_file, file_type, file_size}';
COMMENT ON COLUMN model_gateway_task.request_payload IS '请求参数(JSON';
COMMENT ON COLUMN model_gateway_task.text_result IS '文本类结果';
COMMENT ON COLUMN model_gateway_task.expend_tokens IS '消耗token数';
COMMENT ON COLUMN model_gateway_task.duration_seconds IS '耗时(秒)';
COMMENT ON COLUMN model_gateway_task.epicycle_id IS '轮次ID';
COMMENT ON TABLE "public"."model_gateway_task" IS '模型网关任务表';
COMMENT ON COLUMN "public"."model_gateway_task"."model_name" IS '模型名称';
COMMENT ON COLUMN "public"."model_gateway_task"."task_id" IS '任务ID(对外返回)';
COMMENT ON COLUMN "public"."model_gateway_task"."biz_name" IS '业务名称';
COMMENT ON COLUMN "public"."model_gateway_task"."callback_url" IS '回调地址';
COMMENT ON COLUMN "public"."model_gateway_task"."state" IS '0排队中/1执行中/2成功/3失败/4已下载';
COMMENT ON COLUMN "public"."model_gateway_task"."retry_count" IS '已重试次数';
COMMENT ON COLUMN "public"."model_gateway_task"."error_msg" IS '错误信息';
COMMENT ON COLUMN "public"."model_gateway_task"."result_file" IS '结果文件:{oss_file, file_type, file_size}';
COMMENT ON COLUMN "public"."model_gateway_task"."request_payload" IS '请求参数(JSON';
COMMENT ON COLUMN "public"."model_gateway_task"."duration_seconds" IS '耗时(秒';
COMMENT ON COLUMN "public"."model_gateway_task"."epicycle_id" IS '轮次ID';
COMMENT ON COLUMN "public"."model_gateway_task"."billing_data" IS '计费数据';
COMMENT ON COLUMN "public"."model_gateway_task"."build_model_name" IS '构建模型名称';
-- 3. 操作日志表
CREATE TABLE "public"."model_gateway_logs_op" (
"id" int8 NOT NULL,
"tenant_id" int8 NOT NULL DEFAULT 0,
"creator" varchar(64) NOT NULL,
"created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) NOT NULL,
"updated_at" timestamp(6) 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" int2 NOT NULL DEFAULT 1,
"error_msg" text DEFAULT '',
"cost_ms" int8 NOT NULL DEFAULT 0,
"request_payload" jsonb,
"response_payload" jsonb,
CONSTRAINT "model_gateway_logs_op_pkey" PRIMARY KEY ("id")
);
-- =========================
-- model_gateway_log_stat
-- =========================
CREATE TABLE IF NOT EXISTS model_gateway_log_stat (
day date NOT NULL,
tenant_id int8 NOT NULL DEFAULT 0,
creator varchar(64) NOT NULL DEFAULT '',
model_name varchar(128) NOT NULL DEFAULT '',
request_count int8 NOT NULL DEFAULT 0,
created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (day, tenant_id, creator, model_name)
);
ALTER TABLE "public"."model_gateway_logs_op" OWNER TO "postgres";
CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_day ON model_gateway_log_stat (day);
CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_creator ON model_gateway_log_stat (creator);
CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_model_name ON model_gateway_log_stat (model_name);
CREATE INDEX IF NOT EXISTS idx_model_gateway_log_stat_tenant_day ON model_gateway_log_stat (tenant_id, day);
CREATE INDEX "idx_op_log_biz_name" ON "public"."model_gateway_logs_op" ("biz_name");
CREATE INDEX "idx_op_log_deleted_at" ON "public"."model_gateway_logs_op" ("deleted_at");
CREATE INDEX "idx_op_log_model_name" ON "public"."model_gateway_logs_op" ("model_name");
CREATE INDEX "idx_op_log_op_type" ON "public"."model_gateway_logs_op" ("op_type");
CREATE INDEX "idx_op_log_task_id" ON "public"."model_gateway_logs_op" ("task_id");
CREATE INDEX "idx_op_log_tenant_time" ON "public"."model_gateway_logs_op" ("tenant_id", "created_at");
CREATE INDEX "idx_op_log_model_time" ON "public"."model_gateway_logs_op" ("model_name", "created_at");
COMMENT ON TABLE model_gateway_log_stat IS '按天统计';
COMMENT ON COLUMN model_gateway_log_stat.day IS '天(YYYY-MM-DD';
COMMENT ON COLUMN model_gateway_log_stat.tenant_id IS '租户ID';
COMMENT ON COLUMN model_gateway_log_stat.creator IS '创建人';
COMMENT ON COLUMN model_gateway_log_stat.model_name IS '模型名称';
COMMENT ON COLUMN model_gateway_log_stat.request_count IS '请求次数';
COMMENT ON COLUMN model_gateway_log_stat.created_at IS '创建时间';
COMMENT ON COLUMN model_gateway_log_stat.updated_at IS '更新时间';
COMMENT ON TABLE "public"."model_gateway_logs_op" IS '操作日志';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."ip" IS '客户端IP';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."user_agent" IS 'User-Agent';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."api_path" IS '接口路径';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."http_method" IS 'HTTP方法';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."biz_name" IS '业务名称';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."model_name" IS '模型名称';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."task_id" IS '任务ID';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."op_type" IS '操作类型';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."success" IS '是否成功:1成功/0失败';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."error_msg" IS '错误信息';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."cost_ms" IS '耗时(毫秒)';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."request_payload" IS '请求 JSON';
COMMENT ON COLUMN "public"."model_gateway_logs_op"."response_payload" IS '响应 JSON';
-- =========================
-- model_gateway_logs_op
-- =========================
CREATE TABLE IF NOT EXISTS model_gateway_logs_op (
id int8 PRIMARY KEY,
tenant_id int8 NOT NULL DEFAULT 0,
creator varchar(64) NOT NULL,
created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater varchar(64) NOT NULL,
updated_at timestamp(6) 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 int2 NOT NULL DEFAULT 1,
error_msg text DEFAULT '',
cost_ms int8 NOT NULL DEFAULT 0,
request_payload jsonb,
response_payload jsonb
);
-- 4. 按天统计表
CREATE TABLE "public"."model_gateway_logs_stat" (
"day" date NOT NULL,
"tenant_id" int8 NOT NULL DEFAULT 0,
"creator" varchar(64) NOT NULL DEFAULT '',
"model_name" varchar(128) NOT NULL DEFAULT '',
"request_count" int8 NOT NULL DEFAULT 0,
"created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_task_id ON model_gateway_logs_op (task_id);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_biz_name ON model_gateway_logs_op (biz_name);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_model_name ON model_gateway_logs_op (model_name);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_op_type ON model_gateway_logs_op (op_type);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_deleted_at ON model_gateway_logs_op (deleted_at);
CREATE INDEX IF NOT EXISTS idx_model_gateway_logs_op_tenant_time ON model_gateway_logs_op (tenant_id, created_at);
ALTER TABLE "public"."model_gateway_logs_stat" OWNER TO "postgres";
COMMENT ON TABLE model_gateway_logs_op IS '操作日志表';
COMMENT ON COLUMN model_gateway_logs_op.id IS '主键ID(非自增)';
COMMENT ON COLUMN model_gateway_logs_op.tenant_id IS '租户ID';
COMMENT ON COLUMN model_gateway_logs_op.creator IS '创建人';
COMMENT ON COLUMN model_gateway_logs_op.created_at IS '创建时间';
COMMENT ON COLUMN model_gateway_logs_op.updater IS '更新人';
COMMENT ON COLUMN model_gateway_logs_op.updated_at IS '更新时间';
COMMENT ON COLUMN model_gateway_logs_op.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN model_gateway_logs_op.ip IS '客户端IP';
COMMENT ON COLUMN model_gateway_logs_op.user_agent IS 'User-Agent';
COMMENT ON COLUMN model_gateway_logs_op.api_path IS '接口路径';
COMMENT ON COLUMN model_gateway_logs_op.http_method IS 'HTTP方法';
COMMENT ON COLUMN model_gateway_logs_op.biz_name IS '业务名称(调用方模块/系统)';
COMMENT ON COLUMN model_gateway_logs_op.model_name IS '模型名称';
COMMENT ON COLUMN model_gateway_logs_op.task_id IS '任务ID';
COMMENT ON COLUMN model_gateway_logs_op.op_type IS '操作类型';
COMMENT ON COLUMN model_gateway_logs_op.success IS '是否成功:1成功/0失败';
COMMENT ON COLUMN model_gateway_logs_op.error_msg IS '错误信息(失败时)';
COMMENT ON COLUMN model_gateway_logs_op.cost_ms IS '耗时(毫秒)';
COMMENT ON COLUMN model_gateway_logs_op.request_payload IS '请求 JSON';
COMMENT ON COLUMN model_gateway_logs_op.response_payload IS '响应 JSON';
CREATE INDEX "idx_stat_creator" ON "public"."model_gateway_logs_stat" ("creator");
CREATE INDEX "idx_stat_day" ON "public"."model_gateway_logs_stat" ("day");
CREATE INDEX "idx_stat_model_name" ON "public"."model_gateway_logs_stat" ("model_name");
CREATE INDEX "idx_stat_tenant_day" ON "public"."model_gateway_logs_stat" ("tenant_id", "day");
COMMENT ON TABLE "public"."model_gateway_logs_stat" IS '按天统计表';
COMMENT ON COLUMN "public"."model_gateway_logs_stat"."day" IS '天(YYYY-MM-DD';
COMMENT ON COLUMN "public"."model_gateway_logs_stat"."tenant_id" IS '租户ID';
COMMENT ON COLUMN "public"."model_gateway_logs_stat"."creator" IS '创建人';
COMMENT ON COLUMN "public"."model_gateway_logs_stat"."model_name" IS '模型名称';
COMMENT ON COLUMN "public"."model_gateway_logs_stat"."request_count" IS '请求次数';