feat: model_manage 增加 ref_system_model_id 引用列与引用 DTO

- entity/dto 落地 ref_system_model_id 字段 + ReferenceSystemModelReq/Res
- DAO 新增 CountReferences / UpdateReferencesName 辅助方法
- roundtrip 单测验证 DTO→entity 映射

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-29 14:35:12 +08:00
co-authored by Claude
parent 928682f121
commit 4afb920456
5 changed files with 75 additions and 1 deletions
+21
View File
@@ -8,6 +8,7 @@ import (
"model-gateway/model/entity"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -77,6 +78,26 @@ func (d *modelManageDao) GetByCreatorAndName(ctx context.Context, creator, model
return
}
// CountReferences 统计引用某系统模型的引用行数(Model 链自动过滤软删)
func (d *modelManageDao) CountReferences(ctx context.Context, systemModelId int64) (count int, err error) {
count, err = gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).
Where(entity.ModelManageCol.RefSystemModelId, systemModelId).
Count()
return
}
// UpdateReferencesName 系统模型改名时同步引用行的 model_name(保列表 DISTINCT ON 去重正确)
func (d *modelManageDao) UpdateReferencesName(ctx context.Context, systemModelId int64, newName string) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).
Data(gdb.Map{entity.ModelManageCol.ModelName: newName}).
Where(entity.ModelManageCol.RefSystemModelId, systemModelId).
Update()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *modelManageDao) GetNotTenantId(ctx context.Context, req *dto.GetModelManageReq, fields ...string) (res *entity.ModelManage, err error) {
// 获取表前缀
prefix := g.Cfg().MustGet(ctx, fmt.Sprintf("database.%s.0.prefix", public.DbNameModelGateway)).String()
+15
View File
@@ -17,6 +17,7 @@ type CreateModelManageReq struct {
ModelType model.ModelType `json:"modelType" v:"required#模型类型不能为空" dc:"模型类型"`
BaseURL string `json:"baseUrl" v:"required#模型服务地址不能为空" dc:"模型服务地址"`
SystemModel *bool `json:"systemModel" dc:"系统模型"`
RefSystemModelId int64 `json:"refSystemModelId" dc:"引用的系统模型ID(引用创建时填,普通创建留空)"`
HttpMethod string `json:"httpMethod" dc:"请求方式:GET/POST" d:"POST"`
ChatModel *bool `json:"chatModel" dc:"对话模型"`
ResponseType model.ResponseType `json:"responseType" v:"required#调用模式不能为空" dc:"调用模式:0-同步 1-异步 2-流式"`
@@ -51,6 +52,7 @@ type UpdateModelManageReq struct {
ModelType model.ModelType `json:"modelType" dc:"模型类型"`
BaseURL string `json:"baseUrl" dc:"模型服务地址"`
SystemModel *bool `json:"systemModel" dc:"系统模型"`
RefSystemModelId int64 `json:"refSystemModelId" dc:"引用的系统模型ID(引用行只改个人字段,本字段无效)"`
HttpMethod string `json:"httpMethod" dc:"请求方式:GET/POST"`
ChatModel *bool `json:"chatModel" dc:"对话模型"`
ResponseType model.ResponseType `json:"responseType" dc:"调用模式:0-同步 1-异步 2-流式"`
@@ -152,3 +154,16 @@ type BuildSchemaMappingReq struct {
type BuildSchemaMappingRes struct {
SchemaMapping map[string]any `json:"schemaMapping" dc:"生成的 Schema 映射 JSON"`
}
// ReferenceSystemModelReq 引用系统模型请求:用户只填 apiKey + 个人开关,配置实时跟随系统模型
type ReferenceSystemModelReq struct {
g.Meta `path:"/referenceSystemModel" method:"post" tags:"模型管理" summary:"引用系统模型" dc:"用户引用系统模型,只填 apiKey 等个人字段,配置实时跟随系统模型(不再全量拷贝)"`
SystemModelId int64 `json:"systemModelId" v:"required#系统模型ID不能为空" dc:"被引用的系统模型ID"`
ApiKey string `json:"apiKey" v:"required#apiKey不能为空" dc:"调用凭证/密钥"`
ChatModel *bool `json:"chatModel" dc:"设为我的会话模型"`
Enabled *bool `json:"enabled" dc:"启用(默认启用)"`
}
type ReferenceSystemModelRes struct {
Id int64 `json:"id,string" dc:"引用行ID"`
}
+24
View File
@@ -0,0 +1,24 @@
package dto
import (
"testing"
"model-gateway/model/entity"
"github.com/gogf/gf/v2/util/gconv"
)
// 引用行字段落地验证:DTO → entity 映射(CreateModelManageReq.RefSystemModelId → entity.RefSystemModelId
func TestRefSystemModelIdRoundtrip(t *testing.T) {
req := CreateModelManageReq{ModelName: "gpt-4o", RefSystemModelId: 100}
var e entity.ModelManage
if err := gconv.Struct(&req, &e); err != nil {
t.Fatalf("gconv dto->entity: %v", err)
}
if e.RefSystemModelId != 100 {
t.Fatalf("refSystemModelId mismatch: got %d want 100", e.RefSystemModelId)
}
if e.ModelName != "gpt-4o" {
t.Fatalf("modelName mismatch: got %s", e.ModelName)
}
}
+3
View File
@@ -13,6 +13,7 @@ type modelManageCol struct {
ModelType string
BaseURL string
SystemModel string
RefSystemModelId string
HttpMethod string
ChatModel string
ResponseType string
@@ -40,6 +41,7 @@ var ModelManageCol = modelManageCol{
ModelType: "model_type",
BaseURL: "base_url",
SystemModel: "system_model",
RefSystemModelId: "ref_system_model_id",
HttpMethod: "http_method",
ChatModel: "chat_model",
ResponseType: "response_type",
@@ -67,6 +69,7 @@ type ModelManage struct {
ModelType model.ModelType `orm:"model_type" json:"modelType" description:"模型类型"`
BaseURL string `orm:"base_url" json:"baseUrl" description:"模型地址"`
SystemModel *bool `orm:"system_model" json:"systemModel" description:"系统模型"`
RefSystemModelId int64 `orm:"ref_system_model_id" json:"refSystemModelId" description:"引用的系统模型IDNULL=非引用行)"`
HttpMethod string `orm:"http_method" json:"httpMethod" description:"http方法"`
ChatModel *bool `orm:"chat_model" json:"chatModel" description:"是否聊天模型"`
ResponseType model.ResponseType `orm:"response_type" json:"responseType" description:"返回类型:1同步,2异步,3流"`
+12 -1
View File
@@ -317,4 +317,15 @@ COMMENT ON COLUMN model_gateway_model_task_start.media_type
ALTER TABLE model_gateway_model_task_end
ADD COLUMN IF NOT EXISTS total_cost NUMERIC DEFAULT 0;
COMMENT ON COLUMN model_gateway_model_task_end.total_cost
IS '本次调用总费用(元),未配置计费规则为0';
IS '本次调用总费用(元),未配置计费规则为0';
-- =========================
-- 模型引用化:model_manage 新增 ref_system_model_id(引用行指向系统模型)
-- 系统模型 = 配置唯一来源;引用行只存 apiKey/enabled/chatModel,配置列留空
-- =========================
ALTER TABLE model_gateway_model_manage
ADD COLUMN IF NOT EXISTS ref_system_model_id BIGINT DEFAULT NULL;
COMMENT ON COLUMN model_gateway_model_manage.ref_system_model_id
IS '引用的系统模型IDNULL=非引用行;system_model=false 且该列非空=引用行)';
CREATE INDEX IF NOT EXISTS idx_model_manage_ref_system_model_id
ON model_gateway_model_manage(ref_system_model_id);