Files
model-gateway/service/model_manage_service.go
T
19904408334 1ca25abca0 refactor: 模型解析/价格工具拆分至 service/utils,调用方适配(WIP)
- price.go -> service/utils/media_type.go(媒体类型检测)
- model_resolve.go -> service/utils/model_resolve.go(模型解析)
- 删除 service/parse_error_test.go(逻辑迁往 utils 后无对应测试)
- session_sync/session_stream/model_*_service/pricing_client 等调用方适配
- schema_mapping/model_call_dto 补字段路径
2026-09-01 10:18:23 +08:00

385 lines
11 KiB
Go

package service
import (
"context"
"fmt"
"model-gateway/consts/model"
"model-gateway/consts/public"
"model-gateway/dao"
"model-gateway/model/dto"
"model-gateway/model/entity"
modelUtils "model-gateway/service/utils"
"gitea.redpowerfuture.com/red-future/common/beans"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"gitea.redpowerfuture.com/red-future/common/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var ModelManage = &modelManageService{}
type modelManageService struct{}
// Create 创建模型
func (s *modelManageService) Create(ctx context.Context, req *dto.CreateModelManageReq) (res *dto.CreateModelManageRes, err error) {
err = gfdb.DB(ctx, public.DbNameModelGateway).Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
// 1)检查是否是超管
var isSuperAdmin bool
isSuperAdmin, err = IsSuperAdmin(ctx)
if err != nil {
return
}
req.SystemModel = &isSuperAdmin
// 1)如果设为会话模型,先把该用户旧会话模型取消
err = s.CancelChatModel(ctx, req.ModelType, req.ChatModel, isSuperAdmin)
if err != nil {
return
}
// 2)模型名称唯一性:同一用户下不允许同名模型
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
if !g.IsEmpty(req.ModelName) {
var exist *entity.ModelManage
exist, err = dao.ModelManage.GetByCreatorAndName(ctx, user.UserName, req.ModelName)
if err != nil {
return
}
if exist != nil {
return fmt.Errorf("模型名称 [%s] 已存在,同一用户下不能重复添加同名模型", req.ModelName)
}
}
// 3)插入数据
id, err := dao.ModelManage.Insert(ctx, req)
if err != nil {
return
}
res = &dto.CreateModelManageRes{Id: id}
return
})
return
}
// Update 更新模型配置
// 引用行:只改 apiKey/enabled/chatModel,配置锁定跟随系统模型(其余字段忽略);
// 系统模型:仅创建者(超管)可改配置,改名时同步引用行 model_name;
// 非超管编辑系统模型 → 建引用行(不拷贝配置,apiKey 必填);用户自有模型:创建者可改全配置。非创建者操作他人行 → 无权限。
func (s *modelManageService) Update(ctx context.Context, req *dto.UpdateModelManageReq) (res *dto.GetModelManageRes, err error) {
err = gfdb.DB(ctx, public.DbNameModelGateway).Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
var get *entity.ModelManage
get, err = dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{
Id: req.Id,
})
if err != nil {
return
}
if get == nil {
return fmt.Errorf("模型不存在")
}
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
// 引用行:只改 apiKey/enabled/chatModel,配置锁定跟随系统模型(其余字段忽略;isSuperAdmin=false=个人会话模型开关)
if get.RefSystemModelId > 0 {
if get.Creator != user.UserName {
return fmt.Errorf("无权限操作")
}
if err = s.CancelChatModel(ctx, get.ModelType, req.ChatModel, false); err != nil {
return err
}
_, err = dao.ModelManage.Update(ctx, &dto.UpdateModelManageReq{
Id: req.Id,
ApiKey: req.ApiKey,
Enabled: req.Enabled,
ChatModel: req.ChatModel,
})
return
}
// 同名唯一性:同一用户下不允许同名模型(排除自身,允许不改名编辑)
if !g.IsEmpty(req.ModelName) {
var exist *entity.ModelManage
exist, err = dao.ModelManage.GetByCreatorAndName(ctx, user.UserName, req.ModelName)
if err != nil {
return
}
if exist != nil && exist.Id != req.Id {
return fmt.Errorf("模型名称 [%s] 已存在,同一用户下不能重复添加同名模型", req.ModelName)
}
}
// 1)检查是否是超管
var isSuperAdmin bool
isSuperAdmin, err = IsSuperAdmin(ctx)
if err != nil {
return
}
if err = s.CancelChatModel(ctx, get.ModelType, req.ChatModel, isSuperAdmin); err != nil {
return err
}
if isSuperAdmin {
if get.Creator != user.UserName {
return fmt.Errorf("无权限操作")
}
// 3)系统模型改名 → 同步引用行 model_name(保列表 DISTINCT ON 去重正确)
if get.SystemModel != nil && *get.SystemModel && req.ModelName != "" && req.ModelName != get.ModelName {
if _, err = dao.ModelManage.UpdateReferencesName(ctx, get.Id, req.ModelName); err != nil {
return err
}
}
} else {
if get.SystemModel != nil && *get.SystemModel {
// 用户引用系统模型:apiKey 必填(与旧拷贝分支一致,空 key 引用行无意义)
if g.IsEmpty(req.ApiKey) {
return fmt.Errorf("模型apiKey不能为空")
}
var exist *entity.ModelManage
exist, err = dao.ModelManage.GetByCreatorAndName(ctx, user.UserName, get.ModelName)
if err != nil {
return
}
if exist != nil {
return fmt.Errorf("模型名称 [%s] 已存在,同一用户下不能重复添加同名模型", get.ModelName)
}
// 4)插引用行(配置列零值即可,解析层只读系统行配置)
enabled := true
if req.Enabled != nil {
enabled = *req.Enabled
}
var id int64
id, err = dao.ModelManage.Insert(ctx, &dto.CreateModelManageReq{
ModelSupplier: get.ModelSupplier,
ModelName: get.ModelName,
ModelType: get.ModelType,
SystemModel: gconv.PtrBool(false),
ChatModel: req.ChatModel,
ApiKey: req.ApiKey,
Enabled: gconv.PtrBool(enabled),
RefSystemModelId: get.Id,
})
if err != nil {
return err
}
res, err = s.Get(ctx, &dto.GetModelManageReq{
Id: id,
})
return
}
if get.Creator != user.UserName {
return fmt.Errorf("无权限操作")
}
}
// 4)更新数据
_, err = dao.ModelManage.Update(ctx, req)
return
})
return
}
func (s *modelManageService) CancelChatModel(ctx context.Context, modelType model.ModelType, chatModel *bool, isSuperAdmin bool) (err error) {
if !g.IsEmpty(chatModel) && *chatModel {
if !g.IsEmpty(modelType) && *modelType == *model.ModelTypeInference.Code {
if isSuperAdmin {
return fmt.Errorf("超级管理员不能设置会话模型")
}
// 2)获取该用户信息
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
// 3)取消该用户之前的会话模型
var get *entity.ModelManage
get, err = dao.ModelManage.Get(ctx, &dto.GetModelManage{
Creator: user.UserName,
ChatModel: chatModel,
})
if err != nil {
return
}
if g.IsEmpty(get) {
return
}
_, err = dao.ModelManage.Update(ctx, &dto.UpdateModelManageReq{
Id: get.Id,
ChatModel: gconv.PtrBool(false),
})
if err != nil {
return
}
} else {
return fmt.Errorf("只有推理模型可以设置成会话模型")
}
}
return
}
// Delete 删除模型:系统模型被引用 → 拒绝;引用行/自有行仅创建者可删
func (s *modelManageService) Delete(ctx context.Context, req *dto.DeleteModelManageReq) error {
user, err := utils.GetUserInfo(ctx)
if err != nil {
return err
}
get, err := dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{Id: req.Id})
if err != nil {
return err
}
if get == nil {
return nil
}
// 系统模型被引用 → 拒绝删除(防悬挂)
if get.SystemModel != nil && *get.SystemModel {
n, err := dao.ModelManage.CountReferences(ctx, get.Id)
if err != nil {
return err
}
if n > 0 {
return fmt.Errorf("系统模型已被 %d 个用户引用,不能删除", n)
}
_, err = dao.ModelManage.Delete(ctx, req)
return err
}
// 引用行/自有行:仅创建者可删(删引用行即解除引用)
if get.Creator != user.UserName {
return fmt.Errorf("无权限操作")
}
_, err = dao.ModelManage.Delete(ctx, req)
return err
}
func (s *modelManageService) Get(ctx context.Context, req *dto.GetModelManageReq) (res *dto.GetModelManageRes, err error) {
get, err := dao.ModelManage.GetNotTenantId(ctx, req)
if err != nil {
return
}
if get == nil {
return new(dto.GetModelManageRes), nil
}
// 引用行 → 合入系统模型配置返回(前端展示/编辑需要完整配置;保留引用行自身 id 供更新/删除)
if get.RefSystemModelId > 0 {
sys, e := dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{Id: get.RefSystemModelId})
if e != nil {
return nil, e
}
if sys != nil {
get = modelUtils.MergeReferenceConfigForQuery(get, sys)
}
}
// 系统模型 apiKey 对非创建者脱敏(引用行/自有行仅本人可见)
user, err := utils.GetUserInfo(ctx)
if err != nil {
return
}
if get.SystemModel != nil && *get.SystemModel && get.Creator != user.UserName {
get.ApiKey = ""
}
res = new(dto.GetModelManageRes)
err = gconv.Struct(get, &res.ModelManage)
return
}
func (s *modelManageService) GetChatModel(ctx context.Context, req *dto.GetChatModelReq) (res *dto.GetChatModelRes, err error) {
user, err := utils.GetUserInfo(ctx)
if err != nil {
return
}
get, err := dao.ModelManage.Get(ctx, &dto.GetModelManage{
Creator: user.UserName,
ChatModel: gconv.PtrBool(true),
})
if err != nil {
return
}
res = &dto.GetChatModelRes{
ModelManage: get,
}
return
}
// List 获取模型列表
func (s *modelManageService) List(ctx context.Context, req *dto.ListModelManageReq) (res *dto.ListModelManageRes, err error) {
if req.IsSameType && !g.IsEmpty(req.Id) {
var get *entity.ModelManage
get, err = dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{
Id: req.Id,
})
if err != nil {
return
}
req.ModelType = get.ModelType
}
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
req.Creator = user.UserName
list, total, err := dao.ModelManage.ListNotTenantId(ctx, req)
if err != nil {
return
}
// 引用行 → 合入系统模型配置返回(与 Get 展示一致;保留引用行自身 Id/SystemModel 供更新/脱敏判断)
sysCache := make(map[int64]*entity.ModelManage)
for _, row := range list {
if row.RefSystemModelId <= 0 {
continue
}
sys, ok := sysCache[row.RefSystemModelId]
if !ok {
sys, err = dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{Id: row.RefSystemModelId})
if err != nil {
return
}
if sys == nil {
continue
}
sysCache[row.RefSystemModelId] = sys
}
*row = *modelUtils.MergeReferenceConfigForQuery(row, sys)
}
// 系统模型 apiKey 对非创建者脱敏(引用行/自有行仅本人可见)
for _, row := range list {
if row.SystemModel != nil && *row.SystemModel && row.Creator != user.UserName {
row.ApiKey = ""
}
}
res = &dto.ListModelManageRes{
Total: total,
}
err = gconv.Struct(list, &res.List)
return
}
func (s *modelManageService) CheckChatModel(ctx context.Context, req *dto.CheckChatModelReq) (res *dto.CheckChatModelRes, err error) {
get, err := s.GetChatModel(ctx, &dto.GetChatModelReq{})
if err != nil {
return
}
res = &dto.CheckChatModelRes{
IsChatModel: !g.IsEmpty(get),
}
return
}
// GetModelType 获取模型类型
func (s *modelManageService) GetModelType(ctx context.Context, req *dto.ModelTypeReq) (res *dto.ModelTypeRes, err error) {
res = &dto.ModelTypeRes{
List: model.GetTypeTreeList(),
}
return res, nil
}
// GetModelSupplier 获取运营商列表
func (s *modelManageService) GetModelSupplier(ctx context.Context, req *dto.ModelSupplierReq) (res *dto.ModelSupplierRes, err error) {
return &dto.ModelSupplierRes{
List: model.GetSupplierOptionList(),
}, nil
}