feat:模型分组添加默认字段

This commit is contained in:
chengqian committed 2026-06-11 16:10:54 +08:00
1 parent 5f72629835
commit 6dc69944d1
10 files changed
+95 -14

No files matched your search

+4
View File
@@ -28,6 +28,7 @@ type ModelGroupItem struct {
Code string `json:"code"`
Description string `json:"description"`
Status string `json:"status"`
IsDefault bool `json:"is_default"`
ModelCount int `json:"model_count"`
TenantCount int `json:"tenant_count"`
CreatedAt string `json:"created_at"`
@@ -40,6 +41,7 @@ type ModelGroupCreateReq struct {
Name string `json:"name" v:"required|length:1,100#请输入分组名称|分组名称长度1-100" dc:"分组名称"`
Code string `json:"code" v:"required|length:1,50#请输入分组标识|分组标识长度1-50" dc:"分组唯一标识"`
Description string `json:"description" dc:"分组描述"`
IsDefault *bool `json:"is_default" dc:"是否为新租户默认模型组"`
ModelIds []int64 `json:"model_ids" dc:"初始包含的模型ID列表"`
}
@@ -55,6 +57,7 @@ type ModelGroupUpdateReq struct {
Name string `json:"name" dc:"分组名称"`
Description string `json:"description" dc:"分组描述"`
Status string `json:"status" v:"in:active,disabled" dc:"状态:active/disabled"`
IsDefault *bool `json:"is_default" dc:"是否为新租户默认模型组"`
}
// ModelGroupUpdateRes 更新模型分组响应
@@ -150,5 +153,6 @@ type ModelGroupOptionItem struct {
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
IsDefault bool `json:"is_default"`
ModelCount int `json:"model_count"`
}
@@ -26,6 +26,7 @@ type MdlModelGroupsColumns struct {
Code string // 分组唯一标识(如 full_access、basic_chat)
Description string // 分组描述
Status string // 状态:active(启用)/ disabled(禁用)
IsDefault string // 是否为新租户默认模型组,注册时自动关联
CreatedAt string // 创建时间
UpdatedAt string // 更新时间
}
@@ -37,6 +38,7 @@ var mdlModelGroupsColumns = MdlModelGroupsColumns{
Code: "code",
Description: "description",
Status: "status",
IsDefault: "is_default",
CreatedAt: "created_at",
UpdatedAt: "updated_at",
}
+10 -2
View File
@@ -44,6 +44,7 @@ func (s *sAdmin) ListModelGroups(ctx context.Context, req *v1.ModelGroupListReq)
Code string `json:"code"`
Description string `json:"description"`
Status string `json:"status"`
IsDefault bool `json:"is_default"`
ModelCount int `json:"model_count"`
TenantCount int `json:"tenant_count"`
CreatedAt string `json:"created_at"`
@@ -51,7 +52,7 @@ func (s *sAdmin) ListModelGroups(ctx context.Context, req *v1.ModelGroupListReq)
}
err := query.
Fields("mg.id, mg.name, mg.code, mg.description, mg.status, COALESCE(mc.model_count, 0) AS model_count, COALESCE(tc.tenant_count, 0) AS tenant_count, mg.created_at, mg.updated_at").
Fields("mg.id, mg.name, mg.code, mg.description, mg.status, mg.is_default, COALESCE(mc.model_count, 0) AS model_count, COALESCE(tc.tenant_count, 0) AS tenant_count, mg.created_at, mg.updated_at").
OrderAsc("mg.id").
Page(req.Page, req.PageSize).
Scan(&results)
@@ -67,6 +68,7 @@ func (s *sAdmin) ListModelGroups(ctx context.Context, req *v1.ModelGroupListReq)
Code: r.Code,
Description: r.Description,
Status: r.Status,
IsDefault: r.IsDefault,
ModelCount: r.ModelCount,
TenantCount: r.TenantCount,
CreatedAt: r.CreatedAt,
@@ -93,6 +95,7 @@ func (s *sAdmin) CreateModelGroup(ctx context.Context, req *v1.ModelGroupCreateR
Name: req.Name,
Code: req.Code,
Description: req.Description,
IsDefault: req.IsDefault,
})
if err != nil {
return nil, err
@@ -131,6 +134,9 @@ func (s *sAdmin) UpdateModelGroup(ctx context.Context, req *v1.ModelGroupUpdateR
if req.Status != "" {
data.Status = req.Status
}
if req.IsDefault != nil {
data.IsDefault = *req.IsDefault
}
_, err := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Data(data).Update()
if err != nil {
@@ -182,11 +188,12 @@ func (s *sAdmin) ListModelGroupOptions(ctx context.Context, req *v1.ModelGroupOp
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
IsDefault bool `json:"is_default"`
ModelCount int `json:"model_count"`
}
err := query.
Fields("mg.id, mg.name, mg.code, COALESCE(mc.model_count, 0) AS model_count").
Fields("mg.id, mg.name, mg.code, mg.is_default, COALESCE(mc.model_count, 0) AS model_count").
OrderAsc("mg.id").
Scan(&results)
if err != nil {
@@ -199,6 +206,7 @@ func (s *sAdmin) ListModelGroupOptions(ctx context.Context, req *v1.ModelGroupOp
ID: r.ID,
Name: r.Name,
Code: r.Code,
IsDefault: r.IsDefault,
ModelCount: r.ModelCount,
})
}
+21
View File
@@ -7,6 +7,7 @@ import (
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
v1 "github.com/qianfree/team-api/api/admin/v1"
@@ -171,6 +172,26 @@ func (s *sAdmin) CreateTenant(ctx context.Context, req *v1.TenantCreateReq) (*v1
return gerror.Wrapf(err, "create wallet")
}
// Assign default model groups
var defaultGroups []struct {
Id int64
}
if err := tx.Model("mdl_model_groups").Ctx(ctx).
Where("is_default", true).Where("status", "active").
Fields("id").Scan(&defaultGroups); err == nil && len(defaultGroups) > 0 {
insertData := make([]do.MdlTenantGroups, 0, len(defaultGroups))
for _, dg := range defaultGroups {
insertData = append(insertData, do.MdlTenantGroups{
TenantId: tenantID,
GroupId: dg.Id,
})
}
if _, err := tx.Model("mdl_tenant_groups").Ctx(ctx).
Batch(len(insertData)).Insert(insertData); err != nil {
g.Log().Warningf(ctx, "assign default model groups failed: %v", err)
}
}
return nil
})
if err != nil {
+4 -11
View File
@@ -258,8 +258,7 @@ func checkGroupModelAccess(ctx context.Context, tenantID int64, modelName string
cachedSet, found := lcommon.TenantGroupModelCache.Get(ctx, cacheKey)
if found {
if cachedSet == nil {
// nil 表示无分组 → 向后兼容,允许所有模型
return true, nil, nil
return false, nil, nil
}
if modelSet, ok := cachedSet.(map[string]bool); ok {
_, inGroup := modelSet[modelName]
@@ -273,9 +272,8 @@ func checkGroupModelAccess(ctx context.Context, tenantID int64, modelName string
Count()
if groupCount == 0 {
// 无分组 → 向后兼容,允许所有模型
lcommon.TenantGroupModelCache.Set(ctx, cacheKey, nil)
return true, nil, nil
return false, nil, nil
}
// 查询租户所有活跃分组中的可用模型
@@ -747,13 +745,8 @@ func (p *DataProviderImpl) GetAvailableModels(ctx context.Context, tenantID, api
hasGroup := len(groupModelIDs) > 0
if !hasExplicit && !hasGroup {
// 无分配记录且无分组:返回所有活跃模型(向后兼容)
err = dao.MdlModels.Ctx(ctx).
Where("status", "active").
Fields(fieldsNoAlias).
OrderAsc("category").
OrderAsc("model_id").
Scan(&models)
// 无分配记录且无分组:返回空列表
models = make([]modelRow, 0)
} else {
// 构建合并的模型 ID 集合
allModelIDs := make([]int64, 0)
+20
View File
@@ -148,6 +148,26 @@ func (s *sTenant) Register(ctx context.Context, req *v1.TenantRegisterReq) (*v1.
return gerror.Wrapf(err, "create wallet")
}
// Assign default model groups
var defaultGroups []struct {
Id int64
}
if err := tx.Model("mdl_model_groups").Ctx(ctx).
Where("is_default", true).Where("status", "active").
Fields("id").Scan(&defaultGroups); err == nil && len(defaultGroups) > 0 {
insertData := make([]do.MdlTenantGroups, 0, len(defaultGroups))
for _, dg := range defaultGroups {
insertData = append(insertData, do.MdlTenantGroups{
TenantId: tenantID,
GroupId: dg.Id,
})
}
if _, err := tx.Model("mdl_tenant_groups").Ctx(ctx).
Batch(len(insertData)).Insert(insertData); err != nil {
g.Log().Warningf(ctx, "assign default model groups failed: %v", err)
}
}
return nil
})
if err != nil {
+1
View File
@@ -17,6 +17,7 @@ type MdlModelGroups struct {
Code any // 分组唯一标识(如 full_access、basic_chat)
Description any // 分组描述
Status any // 状态:active(启用)/ disabled(禁用)
IsDefault any // 是否为新租户默认模型组,注册时自动关联
CreatedAt *gtime.Time // 创建时间
UpdatedAt *gtime.Time // 更新时间
}
@@ -15,6 +15,7 @@ type MdlModelGroups struct {
Code string `json:"code" orm:"code" description:"分组唯一标识(如 full_access、basic_chat)"` // 分组唯一标识(如 full_access、basic_chat)
Description string `json:"description" orm:"description" description:"分组描述"` // 分组描述
Status string `json:"status" orm:"status" description:"状态:active(启用)/ disabled(禁用)"` // 状态:active(启用)/ disabled(禁用)
IsDefault bool `json:"is_default" orm:"is_default" description:"是否为新租户默认模型组,注册时自动关联"` // 是否为新租户默认模型组,注册时自动关联
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
}
@@ -0,0 +1,9 @@
-- +goose Up
-- 模型分组增加"默认分组"标记,标记为默认的分组在新租户注册时自动关联
ALTER TABLE mdl_model_groups ADD COLUMN is_default BOOLEAN NOT NULL DEFAULT FALSE;
COMMENT ON COLUMN mdl_model_groups.is_default IS '是否为新租户默认模型组,注册时自动关联';
-- +goose Down
ALTER TABLE mdl_model_groups DROP COLUMN IF EXISTS is_default;
+23 -1
View File
@@ -18,6 +18,10 @@ const pagination = reactive({
const filterSearch = ref('')
const filterStatus = ref<string | null>(null)
const hasDefaultGroup = computed(() => {
return data.value.some((item: any) => item.is_default && item.status === 'active')
})
async function fetchData() {
loading.value = true
try {
@@ -67,6 +71,12 @@ const columns: TableColumnData[] = [
return h(Tag, { color, size: 'small' }, () => label)
},
},
{
title: '默认', dataIndex: 'is_default', width: 70,
render({ record }) {
return record.is_default ? h(Tag, { color: 'blue', size: 'small' }, () => '默认') : '-'
},
},
{ title: '模型数', dataIndex: 'model_count', width: 80 },
{ title: '租户数', dataIndex: 'tenant_count', width: 80 },
{
@@ -100,12 +110,14 @@ const form = reactive({
name: '',
code: '',
description: '',
is_default: false,
})
function resetForm() {
form.name = ''
form.code = ''
form.description = ''
form.is_default = false
}
function openCreate() {
@@ -119,6 +131,7 @@ function openEdit(row: any) {
form.name = row.name || ''
form.code = row.code || ''
form.description = row.description || ''
form.is_default = !!row.is_default
editingId.value = row.id
modalTitle.value = '编辑分组'
showModal.value = true
@@ -131,7 +144,7 @@ async function handleSubmit(done: () => void) {
}
formLoading.value = true
try {
const payload = { name: form.name, code: form.code, description: form.description }
const payload: any = { name: form.name, description: form.description, is_default: form.is_default }
if (editingId.value) {
await request.put(`/admin/model-groups/${editingId.value}`, payload)
Message.success('更新成功')
@@ -141,6 +154,7 @@ async function handleSubmit(done: () => void) {
formLoading.value = false
return
}
payload.code = form.code
await request.post('/admin/model-groups', payload)
Message.success('创建成功')
}
@@ -232,6 +246,10 @@ onMounted(() => {
<PageHeader title="模型分组" description="通过分组批量管理租户可用的模型,新增模型加入分组后自动对组内租户生效">
</PageHeader>
<AAlert v-if="!loading && !hasDefaultGroup" type="warning" class="mb-4" closable>
当前未设置默认模型组,新注册租户将无法使用任何模型。请创建一个分组并标记为「默认」。
</AAlert>
<ACard :bordered="false" class="mb-4">
<ARow :gutter="16" align="center">
<ACol :span="8">
@@ -294,6 +312,10 @@ onMounted(() => {
<AFormItem label="描述">
<ATextarea v-model="form.description" placeholder="可选,描述分组用途" :auto-size="{ minRows: 2, maxRows: 4 }" />
</AFormItem>
<AFormItem label="设为新租户默认分组">
<ASwitch v-model="form.is_default" />
<span style="margin-left: 8px; color: var(--color-text-3); font-size: 12px">开启后,新注册的租户将自动关联此分组</span>
</AFormItem>
</AForm>
</AModal>