feat:模型分组添加默认字段
This commit is contained in:
1 parent
5f72629835
commit
6dc69944d1
10 files changed
+95
-14
No files matched your search
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in new issue
Block a user