Files
observer/server/biz/model/dto/training.go
T
2026-09-07 10:11:36 +08:00

551 lines
28 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"`
}
// AdminDatasetTrainBrief 数据集卡片训练状态:各档位(s/n)最新一条训练记录
type AdminDatasetTrainBrief struct {
TrainingId int64 `json:"trainingId"`
Variant string `json:"variant"` // s(高识别) | n(高性能)
Status string `json:"status"` // queued | running | success | failed
Error string `json:"error"`
Published bool `json:"published"` // 该训练是否已发布过版本(已发布不再显示发布按钮)
CurrentEpoch int `json:"currentEpoch"`
TotalEpochs int `json:"totalEpochs"`
}
// AdminDatasetItem 数据集条目(卡片展示用;AI 端点/训练机 SSH 走 config.yml 全局配置(localAi / training.ssh);
// 双档位训练后每数据集最多两条训练状态(Trains,s 前 n 后),无记录档位不在列)
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 生成图文件名前缀
GenSpecies string `json:"genSpecies"` // 生成参数池:物种(单值)
GenTone string `json:"genTone"` // 轮廓色词(单值)
GenHeights float64 `json:"genHeights"` // 站高cm(数值)
GenScenes string `json:"genScenes"` // 场景池(JSON 数组)
GenActions string `json:"genActions"` // 动作池(JSON 数组)
GenOcclusions string `json:"genOcclusions"`
GenClasses string `json:"genClasses"` // 第二标注类别名(单值)
SortOrder int64 `json:"sortOrder"` // 序号(列表排序主键,升序)
Trains []*AdminDatasetTrainBrief `json:"trains"` // 各档位最新训练(无记录为空数组)
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)。
// gen_* 7 列(species/tone/heights/scenes/actions/occlusions/classes)不在此维护——
// 创建数据集时 VLM 生成,重新生成走 gen-pools 接口
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:"封面文件名(非空才更新)"`
SortOrder *int64 `json:"sortOrder" dc:"序号(升序排列,缺省不更新)"`
}
type AdminDatasetUpdateRes struct {
Id int64 `json:"id"`
}
// AdminDatasetCreateReq 新建数据集(名称同时是磁盘目录名:中文/字母/数字/下划线/短横线,唯一;
// 单物种规则:数据集名即物种,VLM 按数据集名自动生成生成参数池,表单无动物名称输入)
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 生成图文件名前缀(可选,字母/数字/下划线/短横线)"`
Cover string `json:"cover" v:"length:0,200" dc:"封面文件名(新建对话框预生成封面后回传;非空则跳过自动生成)"`
SortOrder *int64 `json:"sortOrder" dc:"序号(列表排序主键,升序;缺省 0)"`
}
type AdminDatasetCreateRes struct {
Id int64 `json:"id"`
PoolsGenerated bool `json:"poolsGenerated"` // 是否已用 VLM 生成生成参数池
PoolError string `json:"poolError"` // 参数池生成失败原因(可用 gen-pools 补生成)
CoverGenerated bool `json:"coverGenerated"` // 是否已生成封面(16:9、1雄1雌)
CoverError string `json:"coverError"` // 封面生成失败原因(可编辑模式重新生成)
}
// AdminGenPoolsReq 重新生成生成参数池(VLM 覆盖写;编辑对话框「VLM 重新生成参数」入口,
// 按数据集名生成——单物种规则,无动物名称输入)
type AdminGenPoolsReq struct {
g.Meta `path:"/datasets/gen-pools" method:"post" summary:"重新生成生成参数池" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminGenPoolsRes struct {
GenSpecies string `json:"genSpecies"`
GenTone string `json:"genTone"`
GenHeights float64 `json:"genHeights"`
GenScenes string `json:"genScenes"`
GenActions string `json:"genActions"`
GenOcclusions string `json:"genOcclusions"`
GenClasses string `json:"genClasses"`
}
// 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{}
// AdminDatasetNegativeReq 获取负样本库(技术设计.md「负样本库」):固定保留名特殊数据集,
// 不存在则自动创建;管理端「负样本」tab 进入时调用,返回 id 供既有上传/图片列表/删除接口复用
type AdminDatasetNegativeReq struct {
g.Meta `path:"/datasets/negative" method:"post" summary:"获取负样本库(无则创建)" tags:"管理端"`
}
type AdminDatasetNegativeRes struct {
Id int64 `json:"id"`
Name string `json:"name"`
ImageCount int64 `json:"imageCount"`
}
// 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 组装(单物种规则:物种固定取数据集 gen_species[0]/数据集名,
// 表单无物种输入),scene/action/occlusion 从数据集表池随机。
// animalCount 为生成提示词中的声明目标数量,仅信息性存储(自动标注上限为固定 3 个,
// 与声明数无关——生成图可能实际含多个目标,检出超 3 个按置信度取前 3,2026-09-02);
// 距离校验已取消(2026-08-28)。文件名前缀为数据集属性(datasets 表 name_prefix)。
// 生成耗时约 40s/张(localai),批量请用返回的 TaskId 轮询 /datasets/gen-task 进度。
type AdminDatasetGenerateReq struct {
g.Meta `path:"/datasets/generate" method:"post" summary:"AI 生成图片(异步任务)" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Prompt string `json:"prompt" v:"length:0,500" dc:"完整提示词(可选,填写则覆盖模板;禁止含位置描述)"`
Count int `json:"count" v:"required|integer|min:1|max:1000" dc:"生成张数 1-1000"`
Size string `json:"size" v:"required|in:704x1248" dc:"图片尺寸"`
Distance int `json:"distance" v:"required|min:1" dc:"目标距离(米,固定值,注入提示词)"`
}
type AdminDatasetGenerateRes struct {
TaskId int64 `json:"taskId"` // 生成任务 id(前端轮询 /datasets/gen-task 进度)
Total int `json:"total"` // 计划生成张数
}
// AdminGenTaskQueryReq 生成任务进度查询(前端轮询;任务完成后错误信息在 Error)
type AdminGenTaskQueryReq struct {
g.Meta `path:"/datasets/gen-task" method:"get" summary:"生成任务进度" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
}
type AdminGenTaskQueryRes struct {
Id int64 `json:"id"`
DatasetId int64 `json:"datasetId"`
Status string `json:"status"` // running | done | failed
Total int `json:"total"`
Done int `json:"done"`
Error string `json:"error"`
CreatedAt *gtime.Time `json:"createdAt"`
FinishedAt *gtime.Time `json:"finishedAt"`
}
// 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 使用;
// cleanExcluded=1 的图归「已清洗」tab——数据清洗排除,训练集打包跳过,可恢复;
// annotateTaskId>0 的图已下发给众包任务,不进「未标注」tab)
type AdminImageItem struct {
Id int64 `json:"id"`
Filename string `json:"filename"`
Source string `json:"source"` // manual | ai
Url string `json:"url"`
CleanExcluded int `json:"cleanExcluded"` // 1=已清洗排除
AnnotateTaskId int64 `json:"annotateTaskId"` // 众包任务占用标记(0=未下发)
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 同模式;
// datasetId=0 时按 name+filename 直读——新建对话框预生成封面回显用)
type AdminDatasetCoverReq struct {
g.Meta `path:"/datasets/cover" method:"get" summary:"数据集封面访问" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"min:0" dc:"数据集 id"`
Name string `json:"name" v:"length:0,50" dc:"数据集名称(datasetId=0 时必填)"`
Filename string `json:"filename" v:"length:0,200" dc:"封面文件名(datasetId=0 时必填)"`
}
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{}
// AdminGenCoverReq 生成数据集封面(z-image 文生图:16:9、1 雄 1 雌;覆盖旧封面;编辑对话框按钮入口)。
// datasetId=0 时按 name 预生成(新建对话框——数据集未创建,封面仅落盘不写库,
// 创建请求带 cover 回传写库),物种取数据集名。
type AdminGenCoverReq struct {
g.Meta `path:"/datasets/cover/generate" method:"post" summary:"生成数据集封面" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"min:0" dc:"数据集 id(0=新建预生成)"`
Name string `json:"name" v:"length:0,50" dc:"数据集名称(datasetId=0 时必填)"`
}
type AdminGenCoverRes struct {
Cover string `json:"cover" dc:"新封面文件名"`
}
// ---------- 训练编排 ----------
// AdminTrainingListReq 训练任务列表(创建时间倒序)
type AdminTrainingListReq struct {
g.Meta `path:"/trainings" method:"get" summary:"训练任务列表" tags:"管理端"`
Status string `json:"status" v:"in:queued,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"`
Variant string `json:"variant"` // s(高识别) | n(高性能)
Status string `json:"status"` // queued | 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 发起训练(GPU 独占排队:忙时新任务落 queued,running 结束自动晋级;
// 同 (数据集,档位) 已有 running/queued 任务时拒绝;训练参数走 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:"任务名称,空自动生成"`
Variants []string `json:"variants" dc:"限定档位 s/n(省略=双档各建一条任务;只补跑高性能档传 [\"n\"];含未知档位/未配置档位报错)"`
}
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 取消训练(running=杀进程,queued=无进程直接置 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 任务;按任务档位发布——s/n 各自的
// is_latest 互不影响,版本号数据集内共用 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" dc:"选中图片列表(多选批量);缺省=全量扫描"`
}
type AdminLabelTaskStartRes struct {
Id int64 `json:"id"`
}
// AdminImageVlmReviewReq 单图 VLM 补检:qwen3.6-35b-a3b 按「环境/季节/时间/天气/光线/地形/习性」
// 综合判读画面,推理 RF-DETR 之外可能藏身的位置(排除已确认坐标),最多补 3 个疑似框(class 1)
// 追加到 labels_json。
// 依赖 localAi.vlmModel(默认 qwen3.6-35b-a3b);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 预标或人工保存)
ReviewStatus int `json:"reviewStatus"` // 审核三态 0=未标注,1=待审核,2=已审核
}
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"` // 该数据集当前已标注图数
}
// ---------- 数据清洗 ----------
// AdminDatasetCleanPreviewReq 数据清洗预览(训练集去冗余候选清单,语义见技术设计.md「数据清洗」):
// 返回桶分布 + 超配桶候选清单(dHash 贪心挑保留)+ 已排除清单(可恢复)。
// Quotas 与档位一一对应覆盖「保留张数」,缺省用 consts.CleanBuckets 默认;<2% 豁免档无视。
type AdminDatasetCleanPreviewReq struct {
g.Meta `path:"/datasets/clean/preview" method:"post" summary:"数据清洗预览" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
Quotas []int `json:"quotas" v:"length:0,8" dc:"各档配额覆盖(与档位一一对应,可选)"`
}
// AdminCleanBucketRes 单档分布(档位定义与顺序见 consts.CleanBuckets
type AdminCleanBucketRes struct {
Label string `json:"label"`
Exempt bool `json:"exempt"` // 豁免档(<2% 极远,不进候选)
Total int `json:"total"` // 该档有确认目标(class 0)标注图总数
Excluded int `json:"excluded"` // 其中已清洗排除数
Quota int `json:"quota"` // 生效保留张数(请求覆盖或默认)
Over int `json:"over"` // 超配需排除数 = max(0, 未排除-配额);豁免档恒 0
}
// AdminCleanImage 候选/已排除单图(两个清单共用)
type AdminCleanImage struct {
ImageId int64 `json:"imageId"`
Filename string `json:"filename"`
Source string `json:"source"` // manual | ai
BucketLabel string `json:"bucketLabel"` // 所在档(无 class 0 标注的已排除图为空)
BoxHeightPct float64 `json:"boxHeightPct"` // 主目标框高占图高百分比(取最大 class 0 框,保留 1 位小数)
MinHeightPct float64 `json:"minHeightPct"` // 图内最小确认目标占比(档内优先保留依据,保留 1 位小数;单目标图 = 主尺寸)
TargetCount int `json:"targetCount"` // class 0 目标总数(多目标图里的小目标样本稀缺,看主尺寸+最小占比判断取舍)
}
type AdminDatasetCleanPreviewRes struct {
Total int `json:"total"` // 参与统计图数(有 class 0 标注)
Excluded int `json:"excluded"` // 已排除总数(恢复入口角标)
Buckets []*AdminCleanBucketRes `json:"buckets"`
Candidates []*AdminCleanImage `json:"candidates"` // 建议排除候选(默认全选即执行)
ExcludedImages []*AdminCleanImage `json:"excludedImages"` // 已排除清单(勾选恢复)
}
// AdminDatasetCleanApplyReq 数据清洗执行:批量置/清 clean_excluded(排除不删文件,
// 只影响训练集打包;恢复 = 同接口 exclude=false 重提该清单)。imageIds 须全属该数据集。
type AdminDatasetCleanApplyReq struct {
g.Meta `path:"/datasets/clean/apply" method:"post" summary:"数据清洗执行(排除/恢复)" tags:"管理端"`
DatasetId int64 `json:"datasetId" v:"required|min:1" dc:"数据集 id"`
ImageIds []int64 `json:"imageIds" v:"required|min-length:1" dc:"图片记录 id 列表"`
Exclude bool `json:"exclude" dc:"true=置排除标记(出训练集);false=清标记(恢复)"`
}
type AdminDatasetCleanApplyRes struct {
Applied int `json:"applied"` // 处理图片数(幂等)
Excluded int64 `json:"excluded"` // 该数据集当前排除总数(角标/计数刷新)
}
// ---------- 客户端模型目录 ----------
// ModelCatalogReq 模型目录(客户端登录态):全部数据集的当前生效模型
type ModelCatalogReq struct {
g.Meta `path:"/models" method:"get" summary:"模型目录" tags:"客户端"`
}
// ModelCatalogItem 客户端模型条目(App 按需下载,多模型并行推理合并;variant 区分档位,
// s/n 两档同数据集各一条 is_latest,App 按识别档位筛选加载)
type ModelCatalogItem struct {
DatasetId int64 `json:"datasetId"`
DatasetName string `json:"datasetName"`
Variant string `json:"variant"` // s(高识别) | n(高性能)
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"` // s 档 /download/trainings/<文件名前缀>.tfliten 档 <文件名前缀>_n.tflite
CoverUrl string `json:"coverUrl"` // /api/v1/app/cover?namePrefix=<前缀>App 模型清单缩略图)
}
type ModelCatalogRes struct {
List []*ModelCatalogItem `json:"list"`
}