409 lines
19 KiB
Go
409 lines
19 KiB
Go
package dto
|
||
|
||
import (
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/net/ghttp"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
)
|
||
|
||
// 模型训练体系接口(数据集 → 标注 → 训练 → 模型版本),组前缀 /api/v1/admin。
|
||
// 图片/标注/模型文件均在服务器磁盘(app.datasetDir),DB 只存元数据。
|
||
// 图片访问为直接写响应体场景,由 controller 以 *ghttp.Request 方法实现
|
||
// (见 controller/admin.go 的 Image 方法)。
|
||
|
||
// ---------- 数据集管理 ----------
|
||
|
||
// AdminDatasetListReq 数据集列表(创建时间倒序)
|
||
type AdminDatasetListReq struct {
|
||
g.Meta `path:"/datasets" method:"get" summary:"数据集列表" tags:"管理端"`
|
||
Keyword string `json:"keyword" v:"length:0,50" dc:"名称模糊匹配"`
|
||
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
|
||
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
|
||
}
|
||
|
||
// AdminDatasetItem 数据集条目(卡片展示用;AI 端点/训练机 SSH 走 config.yml 全局配置(localAi / training.ssh))
|
||
type AdminDatasetItem struct {
|
||
Id int64 `json:"id"`
|
||
Name string `json:"name"`
|
||
Source string `json:"source"` // manual | ai
|
||
ImageCount int64 `json:"imageCount"`
|
||
LabeledCount int64 `json:"labeledCount"`
|
||
Status string `json:"status"` // building | labeled | synced
|
||
Cover string `json:"cover"` // 封面文件名
|
||
Description string `json:"description"` // 描述
|
||
NamePrefix string `json:"namePrefix"` // AI 生成图文件名前缀
|
||
TrainingId int64 `json:"trainingId"` // 最新训练记录 id(发布/详情用)
|
||
TrainingStatus string `json:"trainingStatus"` // 最新训练记录状态 running|success|failed|空
|
||
TrainingError string `json:"trainingError"` // 最新训练记录失败原因(failed 时展示用)
|
||
TrainingPublished bool `json:"trainingPublished"` // 该训练是否已发布过版本(已发布不再显示发布按钮)
|
||
TrainingCurrentEpoch int `json:"trainingCurrentEpoch"`
|
||
TrainingTotalEpochs int `json:"trainingTotalEpochs"`
|
||
CreatedAt *gtime.Time `json:"createdAt"`
|
||
UpdatedAt *gtime.Time `json:"updatedAt"`
|
||
}
|
||
|
||
type AdminDatasetListRes struct {
|
||
Total int64 `json:"total"`
|
||
List []*AdminDatasetItem `json:"list"`
|
||
}
|
||
|
||
// AdminDatasetUpdateReq 更新数据集配置(空值字段不覆盖原值,保留现有配置;改名时同步迁移
|
||
// 图片目录与模型文件,名称唯一且同是磁盘目录名;
|
||
// AI 端点/训练机 SSH 走 config.yml 全局配置(localAi / training.ssh))
|
||
type AdminDatasetUpdateReq struct {
|
||
g.Meta `path:"/datasets/update" method:"post" summary:"更新数据集配置" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"数据集 id"`
|
||
Name string `json:"name" v:"length:0,50" dc:"数据集名称(非空且变化才更新;同是目录名)"`
|
||
NamePrefix string `json:"namePrefix" v:"length:0,50" dc:"AI 生成图文件名前缀(非空才更新)"`
|
||
Description string `json:"description" v:"length:0,500" dc:"描述(非空才更新)"`
|
||
Cover string `json:"cover" v:"length:0,200" dc:"封面文件名(非空才更新)"`
|
||
}
|
||
|
||
type AdminDatasetUpdateRes struct {
|
||
Id int64 `json:"id"`
|
||
}
|
||
|
||
// AdminDatasetCreateReq 新建数据集(名称同时是磁盘目录名:中文/字母/数字/下划线/短横线,唯一)
|
||
type AdminDatasetCreateReq struct {
|
||
g.Meta `path:"/datasets" method:"post" summary:"新建数据集" tags:"管理端"`
|
||
Name string `json:"name" v:"required|regex:^[a-zA-Z0-9_一-龥-]+$|length:1,50" dc:"数据集名称(同是目录名,唯一)"`
|
||
Source string `json:"source" v:"required|in:manual,ai" dc:"图片来源 manual|ai"`
|
||
NamePrefix string `json:"namePrefix" v:"length:0,50" dc:"AI 生成图文件名前缀(可选,字母/数字/下划线/短横线)"`
|
||
}
|
||
|
||
type AdminDatasetCreateRes struct {
|
||
Id int64 `json:"id"`
|
||
}
|
||
|
||
// AdminDatasetDeleteReq 删除数据集(删除图片文件+标注目录+记录;AI 生成图为付费资产,前端须带确认文案)
|
||
type AdminDatasetDeleteReq struct {
|
||
g.Meta `path:"/datasets/delete" method:"post" summary:"删除数据集" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
type AdminDatasetDeleteRes struct{}
|
||
|
||
// AdminDatasetUploadReq 上传图片(multipart 多文件;重名跳过并计数)
|
||
type AdminDatasetUploadReq struct {
|
||
g.Meta `path:"/datasets/upload" method:"post" summary:"上传图片" tags:"管理端" mime:"multipart/form-data"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Files []*ghttp.UploadFile `json:"files" dc:"图片文件(jpg/jpeg/png,多选)"`
|
||
}
|
||
|
||
type AdminDatasetUploadRes struct {
|
||
Added int `json:"added"`
|
||
Skipped int `json:"skipped"` // 重名跳过数
|
||
}
|
||
|
||
// AdminDatasetGenerateReq AI 生成图片(同步执行;prompt 可手填覆盖模板,禁止含目标位置描述;
|
||
// 未手填走 imageGen.promptTemplate 组装:species/scene 用表单选择,数量词=animalCount。
|
||
// distanceMin/distanceMax(米)与 animalCount 填写后逐张校验:RF-DETR 检测目标框(去重口径),
|
||
// 数量精确匹配、估算距离(物理公式)越界自动重新生成(上限 consts.GenValidateMaxAttempts 次)。
|
||
// 文件名前缀为数据集属性(datasets 表 name_prefix),不再随生成请求传入
|
||
type AdminDatasetGenerateReq struct {
|
||
g.Meta `path:"/datasets/generate" method:"post" summary:"AI 生成图片" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Species string `json:"species" v:"length:0,50" dc:"动物名称(如家鸽/山斑鸠;留空按数据集名取物种池随机)"`
|
||
Prompt string `json:"prompt" v:"length:0,500" dc:"完整提示词(可选,填写则覆盖模板;禁止含位置描述)"`
|
||
Count int `json:"count" v:"required|integer|min:1|max:8" dc:"生成张数 1-8"`
|
||
Size string `json:"size" v:"required|in:704x1248" dc:"图片尺寸"`
|
||
DistanceMin int `json:"distanceMin" v:"integer|min:0" dc:"距离范围下限(米,0=不校验距离)"`
|
||
DistanceMax int `json:"distanceMax" v:"integer|min:0" dc:"距离范围上限(米,0=不校验距离)"`
|
||
AnimalCount int `json:"animalCount" v:"integer|min:0|max:20" dc:"画面目标数量(精确匹配,0=不校验;标注框数不超过该值)"`
|
||
}
|
||
|
||
// AdminDatasetGenOptionsReq 生成表单选项(物种池/场景池,来自 config imageGen 配置)
|
||
type AdminDatasetGenOptionsReq struct {
|
||
g.Meta `path:"/datasets/gen-options" method:"get" summary:"生成表单选项" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
type AdminDatasetGenOptionsRes struct {
|
||
Species []string `json:"species"` // 该数据集的物种池(未配置则为空,表单可自由输入)
|
||
Scenes []string `json:"scenes"` // 环境场景池
|
||
}
|
||
|
||
type AdminDatasetGenerateRes struct {
|
||
Generated int `json:"generated"` // 成功张数(失败时可能 < count,已成功的图保留)
|
||
}
|
||
|
||
// AdminDatasetImagesReq 数据集图片列表(标注工作台/网格预览用)
|
||
type AdminDatasetImagesReq struct {
|
||
g.Meta `path:"/datasets/images" method:"get" summary:"数据集图片列表" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
// AdminImageItem 图片条目(url 为管理端图片访问地址,前端拼 apiBaseUrl 使用)
|
||
type AdminImageItem struct {
|
||
Id int64 `json:"id"`
|
||
Filename string `json:"filename"`
|
||
Source string `json:"source"` // manual | ai
|
||
Prompt string `json:"prompt"`
|
||
Url string `json:"url"`
|
||
CreatedAt *gtime.Time `json:"createdAt"`
|
||
}
|
||
|
||
type AdminDatasetImagesRes struct {
|
||
List []*AdminImageItem `json:"list"`
|
||
}
|
||
|
||
// AdminDatasetImagesDeleteReq 删除图片(删文件+删记录;付费资产,前端须带确认文案)
|
||
type AdminDatasetImagesDeleteReq struct {
|
||
g.Meta `path:"/datasets/images/delete" method:"post" summary:"删除图片" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Ids []int64 `json:"ids" v:"required|min-length:1" dc:"图片记录 id 列表"`
|
||
}
|
||
|
||
type AdminDatasetImagesDeleteRes struct{}
|
||
|
||
// AdminDatasetImageReq 图片访问(直写响应体,例外场景:controller 经 service 校验归属后输出二进制)
|
||
type AdminDatasetImageReq struct {
|
||
g.Meta `path:"/datasets/image" method:"get" summary:"图片访问" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Filename string `json:"filename" v:"required" dc:"图片文件名"`
|
||
}
|
||
|
||
type AdminDatasetImageRes struct{}
|
||
|
||
// AdminDatasetCoverUploadReq 上传数据集封面(multipart 单文件;覆盖旧封面,UUID 命名 jpg)
|
||
type AdminDatasetCoverUploadReq struct {
|
||
g.Meta `path:"/datasets/cover" method:"post" summary:"上传数据集封面" tags:"管理端" mime:"multipart/form-data"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
File *ghttp.UploadFile `json:"file" dc:"封面图片(jpg/jpeg/png,≤2MB)"`
|
||
}
|
||
|
||
type AdminDatasetCoverUploadRes struct{}
|
||
|
||
// AdminDatasetCoverReq 封面访问(直写响应体,与 Image 同模式)
|
||
type AdminDatasetCoverReq struct {
|
||
g.Meta `path:"/datasets/cover" method:"get" summary:"数据集封面访问" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
type AdminDatasetCoverRes struct{}
|
||
|
||
// AdminDatasetCoverDeleteReq 删除数据集封面(删文件+清字段;列表卡片恢复占位图)
|
||
type AdminDatasetCoverDeleteReq struct {
|
||
g.Meta `path:"/datasets/cover/delete" method:"post" summary:"删除数据集封面" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
type AdminDatasetCoverDeleteRes struct{}
|
||
|
||
// ---------- 训练编排 ----------
|
||
|
||
// AdminTrainingListReq 训练任务列表(创建时间倒序)
|
||
type AdminTrainingListReq struct {
|
||
g.Meta `path:"/trainings" method:"get" summary:"训练任务列表" tags:"管理端"`
|
||
Status string `json:"status" v:"in:running,success,failed" dc:"状态筛选"`
|
||
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
|
||
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
|
||
}
|
||
|
||
// AdminTrainingItem 训练任务条目(列表不含日志尾部,详情接口返回)
|
||
type AdminTrainingItem struct {
|
||
Id int64 `json:"id"`
|
||
Name string `json:"name"`
|
||
DatasetId int64 `json:"datasetId"`
|
||
DatasetName string `json:"datasetName"`
|
||
Status string `json:"status"` // running | success | failed
|
||
Imgsz int `json:"imgsz"`
|
||
Epochs int `json:"epochs"`
|
||
Batch int `json:"batch"`
|
||
Device string `json:"device"`
|
||
CurrentEpoch int `json:"currentEpoch"`
|
||
TotalEpochs int `json:"totalEpochs"`
|
||
Metrics string `json:"metrics"`
|
||
Error string `json:"error"`
|
||
StartedAt *gtime.Time `json:"startedAt"`
|
||
FinishedAt *gtime.Time `json:"finishedAt"`
|
||
CreatedAt *gtime.Time `json:"createdAt"`
|
||
}
|
||
|
||
type AdminTrainingListRes struct {
|
||
Total int64 `json:"total"`
|
||
List []*AdminTrainingItem `json:"list"`
|
||
}
|
||
|
||
// AdminTrainingStartReq 发起训练(并发度 1:已有 running 任务时拒绝;训练参数走 config.yml training 节点,name 空则自动生成)
|
||
type AdminTrainingStartReq struct {
|
||
g.Meta `path:"/trainings" method:"post" summary:"发起训练" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Name string `json:"name" v:"length:0,50" dc:"任务名称,空自动生成"`
|
||
}
|
||
|
||
type AdminTrainingStartRes struct {
|
||
Id int64 `json:"id"`
|
||
}
|
||
|
||
// AdminTrainingDetailReq 训练任务详情(含日志尾部)
|
||
type AdminTrainingDetailReq struct {
|
||
g.Meta `path:"/trainings/detail" method:"get" summary:"训练任务详情" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
|
||
}
|
||
|
||
// AdminTrainingDetailRes 详情 = 条目 + 日志尾部
|
||
type AdminTrainingDetailRes struct {
|
||
AdminTrainingItem
|
||
LogTail string `json:"logTail"`
|
||
}
|
||
|
||
// AdminTrainingCancelReq 取消训练(杀进程,任务置 failed)
|
||
type AdminTrainingCancelReq struct {
|
||
g.Meta `path:"/trainings/cancel" method:"post" summary:"取消训练" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
|
||
}
|
||
|
||
type AdminTrainingCancelRes struct{}
|
||
|
||
// AdminTrainingPublishReq 发布模型版本(仅 success 任务;按数据集版本号 m<major>.<minor>.<patch> 自增)
|
||
type AdminTrainingPublishReq struct {
|
||
g.Meta `path:"/trainings/publish" method:"post" summary:"发布模型版本" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"训练任务 id"`
|
||
}
|
||
|
||
type AdminTrainingPublishRes struct {
|
||
Version string `json:"version"`
|
||
}
|
||
|
||
// ---------- 模型版本 ----------
|
||
|
||
// ---------- 预标注 ----------
|
||
|
||
// AdminLabelTaskListReq 预标注任务列表(创建时间倒序)
|
||
type AdminLabelTaskListReq struct {
|
||
g.Meta `path:"/label-tasks" method:"get" summary:"预标注任务列表" tags:"管理端"`
|
||
Page int `json:"page" v:"integer|min:1" dc:"页码,默认 1"`
|
||
Size int `json:"size" v:"integer|min:1|max:100" dc:"每页条数,默认 20"`
|
||
}
|
||
|
||
type AdminLabelTaskItem struct {
|
||
Id int64 `json:"id"`
|
||
DatasetId int64 `json:"datasetId"`
|
||
DatasetName string `json:"datasetName"`
|
||
Status string `json:"status"` // running | done
|
||
Total int `json:"total"`
|
||
Done int `json:"done"`
|
||
Error string `json:"error"`
|
||
CreatedAt *gtime.Time `json:"createdAt"`
|
||
FinishedAt *gtime.Time `json:"finishedAt"`
|
||
}
|
||
|
||
type AdminLabelTaskListRes struct {
|
||
Total int64 `json:"total"`
|
||
List []*AdminLabelTaskItem `json:"list"`
|
||
}
|
||
|
||
// AdminLabelTaskStartReq 发起预标注(RF-DETR 扫描,候选框合并入 datasets/<name>/boxes.json)
|
||
type AdminLabelTaskStartReq struct {
|
||
g.Meta `path:"/label-tasks" method:"post" summary:"发起预标注" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Filenames []string `json:"filenames" v:"length:0,500" dc:"选中图片列表(多选批量);缺省=全量扫描"`
|
||
}
|
||
|
||
type AdminLabelTaskStartRes struct {
|
||
Id int64 `json:"id"`
|
||
}
|
||
|
||
// AdminImageVlmReviewReq 单图 VLM 补检:qwen3.8-9b 按「环境/光线/习性」推理 RF-DETR 之外
|
||
// 可能藏匿目标的位置(排除已确认坐标),最多补 3 个疑似框(class 1)追加到 labels_json。
|
||
// 依赖 localAi.vlmModel(默认 qwen3.8-9b);VL 与 z-image 生成不能同时驻留显存,批量补检
|
||
// 应在生成队列空闲时执行
|
||
type AdminImageVlmReviewReq struct {
|
||
g.Meta `path:"/datasets/images/vlm-review" method:"post" summary:"VLM 藏匿位补检" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
ImageId int64 `json:"imageId" v:"required|min:1" dc:"图片 id"`
|
||
}
|
||
|
||
type AdminImageVlmReviewRes struct {
|
||
Added int `json:"added"` // 新增疑似框数(≤3)
|
||
Boxes []*AdminLabelBox `json:"boxes"` // VLM 新增框(归一化坐标,class=1)
|
||
Note string `json:"note"` // 未补检原因(如已确认框为空/VL 无输出)
|
||
}
|
||
|
||
// AdminLabelTaskDetailReq 预标注任务详情(每张图候选框 + 已确认标注,供工作台 canvas 叠框)
|
||
type AdminLabelTaskDetailReq struct {
|
||
g.Meta `path:"/label-tasks/detail" method:"get" summary:"预标注任务详情" tags:"管理端"`
|
||
Id int64 `json:"id" v:"required|min:1" dc:"标注任务 id"`
|
||
}
|
||
|
||
// AdminLabelBox 单框(归一化 xywh,坐标 0~1)
|
||
type AdminLabelBox struct {
|
||
Cx float64 `json:"cx"`
|
||
Cy float64 `json:"cy"`
|
||
W float64 `json:"w"`
|
||
H float64 `json:"h"`
|
||
Confidence float64 `json:"confidence"` // 人工框为 1
|
||
Class int `json:"class"` // 0 确认 | 1 疑似
|
||
}
|
||
|
||
// AdminLabelImageItem 工作台单张图:全部标注(labels_json,AI 自动标注与人工框同层,人工可修改/清理)
|
||
type AdminLabelImageItem struct {
|
||
Filename string `json:"filename"`
|
||
Url string `json:"url"`
|
||
Width int `json:"width"`
|
||
Height int `json:"height"`
|
||
Boxes []*AdminLabelBox `json:"boxes"`
|
||
Labeled bool `json:"labeled"` // 有标注(AI 自动标注或人工保存)
|
||
}
|
||
|
||
type AdminLabelTaskDetailRes struct {
|
||
TaskId int64 `json:"taskId"`
|
||
DatasetId int64 `json:"datasetId"`
|
||
DatasetName string `json:"datasetName"`
|
||
Status string `json:"status"`
|
||
Total int `json:"total"`
|
||
Done int `json:"done"`
|
||
Error string `json:"error"`
|
||
Images []*AdminLabelImageItem `json:"images"`
|
||
}
|
||
|
||
// AdminLabelWorkbenchReq 标注工作台数据(无历史任务时直接用数据集图片 + 已确认标注,
|
||
// 候选框读数据集目录 boxes.json)
|
||
type AdminLabelWorkbenchReq struct {
|
||
g.Meta `path:"/label-workbench" method:"get" summary:"标注工作台数据" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
}
|
||
|
||
type AdminLabelWorkbenchRes struct {
|
||
DatasetId int64 `json:"datasetId"`
|
||
DatasetName string `json:"datasetName"`
|
||
Images []*AdminLabelImageItem `json:"images"`
|
||
}
|
||
|
||
// AdminLabelSaveReq 保存单张图标注(工作台「保存本张」;覆写 dataset_image.labels_json)
|
||
type AdminLabelSaveReq struct {
|
||
g.Meta `path:"/label-tasks/save" method:"post" summary:"保存图片标注" tags:"管理端"`
|
||
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
|
||
Filename string `json:"filename" v:"required|length:1,200" dc:"图片文件名"`
|
||
Boxes []*AdminLabelBox `json:"boxes" dc:"确认后的框列表(空=清空标注)"`
|
||
}
|
||
|
||
type AdminLabelSaveRes struct {
|
||
LabeledCount int64 `json:"labeledCount"` // 该数据集当前已标注图数
|
||
}
|
||
|
||
// ---------- 客户端模型目录 ----------
|
||
|
||
// ModelCatalogReq 模型目录(客户端登录态):全部数据集的当前生效模型
|
||
type ModelCatalogReq struct {
|
||
g.Meta `path:"/models" method:"get" summary:"模型目录" tags:"客户端"`
|
||
}
|
||
|
||
// ModelCatalogItem 客户端模型条目(App 按需下载,多模型并行推理合并)
|
||
type ModelCatalogItem struct {
|
||
DatasetId int64 `json:"datasetId"`
|
||
DatasetName string `json:"datasetName"`
|
||
Version string `json:"version"`
|
||
Labels []string `json:"labels"` // 类别名,App 推理结果展示用
|
||
SizeBytes int64 `json:"sizeBytes"`
|
||
Sha256 string `json:"sha256"`
|
||
Notes string `json:"notes"`
|
||
PublishedAt *gtime.Time `json:"publishedAt"`
|
||
DownloadUrl string `json:"downloadUrl"` // /download/trainings/<name>.tflite
|
||
}
|
||
|
||
type ModelCatalogRes struct {
|
||
List []*ModelCatalogItem `json:"list"`
|
||
}
|