357 lines
10 KiB
Go
357 lines
10 KiB
Go
package admin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
v1 "github.com/qianfree/team-api/api/admin/v1"
|
|
"github.com/qianfree/team-api/internal/consts"
|
|
"github.com/qianfree/team-api/internal/dao"
|
|
"github.com/qianfree/team-api/internal/logic/billing"
|
|
"github.com/qianfree/team-api/internal/logic/common"
|
|
do "github.com/qianfree/team-api/internal/model/do"
|
|
)
|
|
|
|
// ==================== 分组 CRUD ====================
|
|
|
|
// ListModelGroups 模型分组列表
|
|
func (s *sAdmin) ListModelGroups(ctx context.Context, req *v1.ModelGroupListReq) (*v1.ModelGroupListRes, error) {
|
|
query := g.DB().Model("mdl_model_groups mg").Ctx(ctx).
|
|
LeftJoin("(SELECT group_id, COUNT(*) AS model_count FROM mdl_group_models GROUP BY group_id) mc ON mc.group_id = mg.id").
|
|
LeftJoin("(SELECT group_id, COUNT(*) AS tenant_count FROM mdl_tenant_groups GROUP BY group_id) tc ON tc.group_id = mg.id")
|
|
|
|
if req.Status != "" {
|
|
query = query.Where("mg.status", req.Status)
|
|
}
|
|
if req.Search != "" {
|
|
query = query.Where("mg.name LIKE ? OR mg.code LIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
|
|
}
|
|
|
|
countQuery := g.DB().Model("mdl_model_groups mg").Ctx(ctx)
|
|
if req.Status != "" {
|
|
countQuery = countQuery.Where("mg.status", req.Status)
|
|
}
|
|
if req.Search != "" {
|
|
countQuery = countQuery.Where("mg.name LIKE ? OR mg.code LIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
|
|
}
|
|
total, _ := countQuery.Count()
|
|
|
|
var results []struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Code string `json:"code"`
|
|
Description string `json:"description"`
|
|
Status string `json:"status"`
|
|
ModelCount int `json:"model_count"`
|
|
TenantCount int `json:"tenant_count"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
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").
|
|
OrderAsc("mg.id").
|
|
Page(req.Page, req.PageSize).
|
|
Scan(&results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
list := make([]v1.ModelGroupItem, 0, len(results))
|
|
for _, r := range results {
|
|
list = append(list, v1.ModelGroupItem{
|
|
ID: r.ID,
|
|
Name: r.Name,
|
|
Code: r.Code,
|
|
Description: r.Description,
|
|
Status: r.Status,
|
|
ModelCount: r.ModelCount,
|
|
TenantCount: r.TenantCount,
|
|
CreatedAt: r.CreatedAt,
|
|
UpdatedAt: r.UpdatedAt,
|
|
})
|
|
}
|
|
|
|
return &v1.ModelGroupListRes{
|
|
List: list,
|
|
Total: total,
|
|
Page: req.Page,
|
|
PageSize: req.PageSize,
|
|
}, nil
|
|
}
|
|
|
|
// CreateModelGroup 创建模型分组
|
|
func (s *sAdmin) CreateModelGroup(ctx context.Context, req *v1.ModelGroupCreateReq) (*v1.ModelGroupCreateRes, error) {
|
|
count, _ := dao.MdlModelGroups.Ctx(ctx).Where("code", req.Code).Count()
|
|
if count > 0 {
|
|
return nil, common.NewBusinessError(consts.CodeModelGroupCodeExists, consts.MsgModelGroupCodeExists)
|
|
}
|
|
|
|
result, err := dao.MdlModelGroups.Ctx(ctx).Insert(do.MdlModelGroups{
|
|
Name: req.Name,
|
|
Code: req.Code,
|
|
Description: req.Description,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, _ := result.LastInsertId()
|
|
|
|
if len(req.ModelIds) > 0 {
|
|
for _, modelID := range req.ModelIds {
|
|
_, _ = dao.MdlGroupModels.Ctx(ctx).Insert(do.MdlGroupModels{
|
|
GroupId: id,
|
|
ModelId: modelID,
|
|
})
|
|
}
|
|
}
|
|
|
|
return &v1.ModelGroupCreateRes{ID: id}, nil
|
|
}
|
|
|
|
// UpdateModelGroup 更新模型分组
|
|
func (s *sAdmin) UpdateModelGroup(ctx context.Context, req *v1.ModelGroupUpdateReq) (*v1.ModelGroupUpdateRes, error) {
|
|
count, _ := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Count()
|
|
if count == 0 {
|
|
return nil, common.NewBusinessError(consts.CodeModelGroupNotFound, consts.MsgModelGroupNotFound)
|
|
}
|
|
|
|
data := do.MdlModelGroups{}
|
|
if req.Name != "" {
|
|
data.Name = req.Name
|
|
}
|
|
if req.Description != "" {
|
|
data.Description = req.Description
|
|
}
|
|
if req.Status != "" {
|
|
data.Status = req.Status
|
|
}
|
|
|
|
_, err := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Data(data).Update()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if req.Status != "" {
|
|
invalidateTenantsInGroup(ctx, req.ID)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// DeleteModelGroup 删除模型分组
|
|
func (s *sAdmin) DeleteModelGroup(ctx context.Context, req *v1.ModelGroupDeleteReq) (*v1.ModelGroupDeleteRes, error) {
|
|
count, _ := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Count()
|
|
if count == 0 {
|
|
return nil, common.NewBusinessError(consts.CodeModelGroupNotFound, consts.MsgModelGroupNotFound)
|
|
}
|
|
|
|
tenantCount, _ := dao.MdlTenantGroups.Ctx(ctx).Where("group_id", req.ID).Count()
|
|
if tenantCount > 0 {
|
|
return nil, common.NewBusinessError(consts.CodeModelGroupHasTenants, consts.MsgModelGroupHasTenants)
|
|
}
|
|
|
|
_, _ = dao.MdlGroupModels.Ctx(ctx).Where("group_id", req.ID).Delete()
|
|
_, err := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Delete()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// ListModelGroupOptions 分组选项列表(不分页,用于下拉选择)
|
|
func (s *sAdmin) ListModelGroupOptions(ctx context.Context, req *v1.ModelGroupOptionsReq) (*v1.ModelGroupOptionsRes, error) {
|
|
query := g.DB().Model("mdl_model_groups mg").Ctx(ctx).
|
|
LeftJoin("(SELECT group_id, COUNT(*) AS model_count FROM mdl_group_models GROUP BY group_id) mc ON mc.group_id = mg.id")
|
|
|
|
if req.Status != "" {
|
|
query = query.Where("mg.status", req.Status)
|
|
} else {
|
|
query = query.Where("mg.status", "active")
|
|
}
|
|
|
|
var results []struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Code string `json:"code"`
|
|
ModelCount int `json:"model_count"`
|
|
}
|
|
|
|
err := query.
|
|
Fields("mg.id, mg.name, mg.code, COALESCE(mc.model_count, 0) AS model_count").
|
|
OrderAsc("mg.id").
|
|
Scan(&results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
list := make([]v1.ModelGroupOptionItem, 0, len(results))
|
|
for _, r := range results {
|
|
list = append(list, v1.ModelGroupOptionItem{
|
|
ID: r.ID,
|
|
Name: r.Name,
|
|
Code: r.Code,
|
|
ModelCount: r.ModelCount,
|
|
})
|
|
}
|
|
|
|
return &v1.ModelGroupOptionsRes{List: list}, nil
|
|
}
|
|
|
|
// ==================== 分组模型管理 ====================
|
|
|
|
// ListGroupModels 查看分组内模型列表
|
|
func (s *sAdmin) ListGroupModels(ctx context.Context, req *v1.GroupModelsListReq) (*v1.GroupModelsListRes, error) {
|
|
var results []struct {
|
|
ModelId string `json:"model_id"`
|
|
ModelName string `json:"model_name"`
|
|
Category string `json:"category"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
err := dao.MdlGroupModels.Ctx(ctx).As("gm").
|
|
InnerJoin("mdl_models m ON gm.model_id = m.id").
|
|
Where("gm.group_id", req.ID).
|
|
Fields("m.model_id, m.model_name, m.category, m.status").
|
|
OrderAsc("m.category").
|
|
OrderAsc("m.model_id").
|
|
Scan(&results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
list := make([]v1.GroupModelItem, 0, len(results))
|
|
for _, r := range results {
|
|
list = append(list, v1.GroupModelItem{
|
|
ModelId: r.ModelId,
|
|
ModelName: r.ModelName,
|
|
Category: r.Category,
|
|
Status: r.Status,
|
|
})
|
|
}
|
|
|
|
return &v1.GroupModelsListRes{List: list}, nil
|
|
}
|
|
|
|
// SetGroupModels 设置分组内模型(全量替换)
|
|
func (s *sAdmin) SetGroupModels(ctx context.Context, req *v1.GroupModelsSetReq) (*v1.GroupModelsSetRes, error) {
|
|
count, _ := dao.MdlModelGroups.Ctx(ctx).Where("id", req.ID).Count()
|
|
if count == 0 {
|
|
return nil, common.NewBusinessError(consts.CodeModelGroupNotFound, consts.MsgModelGroupNotFound)
|
|
}
|
|
|
|
// 删除旧关联
|
|
_, err := dao.MdlGroupModels.Ctx(ctx).Where("group_id", req.ID).Delete()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 批量插入新关联
|
|
if len(req.ModelIds) > 0 {
|
|
insertData := make([]do.MdlGroupModels, 0, len(req.ModelIds))
|
|
for _, modelID := range req.ModelIds {
|
|
insertData = append(insertData, do.MdlGroupModels{
|
|
GroupId: req.ID,
|
|
ModelId: modelID,
|
|
})
|
|
}
|
|
_, err = dao.MdlGroupModels.Ctx(ctx).Batch(len(insertData)).Insert(insertData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
invalidateTenantsInGroup(ctx, req.ID)
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// ==================== 租户分组管理 ====================
|
|
|
|
// ListTenantGroups 查看租户关联的分组列表
|
|
func (s *sAdmin) ListTenantGroups(ctx context.Context, req *v1.TenantGroupsListReq) (*v1.TenantGroupsListRes, error) {
|
|
var results []struct {
|
|
GroupID int64 `json:"group_id"`
|
|
Name string `json:"name"`
|
|
Code string `json:"code"`
|
|
Status string `json:"status"`
|
|
ModelCount int `json:"model_count"`
|
|
}
|
|
|
|
err := dao.MdlTenantGroups.Ctx(ctx).As("tg").
|
|
InnerJoin("mdl_model_groups g ON tg.group_id = g.id").
|
|
LeftJoin("(SELECT group_id, COUNT(*) AS model_count FROM mdl_group_models GROUP BY group_id) mc ON mc.group_id = g.id").
|
|
Where("tg.tenant_id", req.TenantID).
|
|
Fields("g.id AS group_id, g.name, g.code, g.status, COALESCE(mc.model_count, 0) AS model_count").
|
|
OrderAsc("g.id").
|
|
Scan(&results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
list := make([]v1.TenantGroupItem, 0, len(results))
|
|
for _, r := range results {
|
|
list = append(list, v1.TenantGroupItem{
|
|
GroupID: r.GroupID,
|
|
Name: r.Name,
|
|
Code: r.Code,
|
|
Status: r.Status,
|
|
ModelCount: r.ModelCount,
|
|
})
|
|
}
|
|
|
|
return &v1.TenantGroupsListRes{List: list}, nil
|
|
}
|
|
|
|
// SetTenantGroups 设置租户关联的分组(全量替换)
|
|
func (s *sAdmin) SetTenantGroups(ctx context.Context, req *v1.TenantGroupsSetReq) (*v1.TenantGroupsSetRes, error) {
|
|
// 删除旧关联
|
|
_, err := dao.MdlTenantGroups.Ctx(ctx).Where("tenant_id", req.TenantID).Delete()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 批量插入新关联
|
|
if len(req.GroupIds) > 0 {
|
|
insertData := make([]do.MdlTenantGroups, 0, len(req.GroupIds))
|
|
for _, groupID := range req.GroupIds {
|
|
insertData = append(insertData, do.MdlTenantGroups{
|
|
TenantId: req.TenantID,
|
|
GroupId: groupID,
|
|
})
|
|
}
|
|
_, err = dao.MdlTenantGroups.Ctx(ctx).Batch(len(insertData)).Insert(insertData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
invalidateTenantGroupCache(ctx, req.TenantID)
|
|
billing.ClearTenantPriceCache(ctx, req.TenantID)
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// ==================== 缓存失效辅助函数 ====================
|
|
|
|
func invalidateTenantGroupCache(ctx context.Context, tenantID int64) {
|
|
common.TenantGroupModelCache.Delete(ctx, fmt.Sprintf("%d", tenantID))
|
|
}
|
|
|
|
func invalidateTenantsInGroup(ctx context.Context, groupID int64) {
|
|
var tenants []struct {
|
|
TenantId int64 `json:"tenant_id"`
|
|
}
|
|
dao.MdlTenantGroups.Ctx(ctx).
|
|
Where("group_id", groupID).
|
|
Fields("tenant_id").
|
|
Scan(&tenants)
|
|
|
|
for _, t := range tenants {
|
|
invalidateTenantGroupCache(ctx, t.TenantId)
|
|
}
|
|
}
|