fix:修复管理端多个自动化测试bug
This commit is contained in:
1 parent
2ec452b883
commit
706b2da5b4
12 files changed
+101
-29
No files matched your search
@@ -107,7 +107,7 @@ type MessageSendRes struct{}
|
||||
|
||||
type MessageBroadcastReq struct {
|
||||
g.Meta `path:"/notification/messages/broadcast" method:"post" mime:"json" tags:"管理后台-通知" summary:"广播消息"`
|
||||
TenantID int64 `json:"tenant_id" v:"required|min:1"`
|
||||
TenantID *int64 `json:"tenant_id" dc:"目标租户ID,不传或为空表示广播到所有租户"`
|
||||
Title string `json:"title" v:"required"`
|
||||
Content string `json:"content" v:"required"`
|
||||
TargetRoles string `json:"target_roles" d:"" dc:"目标角色,逗号分隔如 owner,admin;留空表示全部角色"`
|
||||
|
||||
@@ -44,9 +44,14 @@ type TenantLevelConfigCreateRes struct {
|
||||
}
|
||||
|
||||
type TenantLevelConfigUpdateReq struct {
|
||||
g.Meta `path:"/tenant-level-configs/{id}" method:"put" mime:"json" tags:"管理后台-租户等级" summary:"更新等级配置"`
|
||||
Id int64 `json:"id" in:"path" v:"required|min:1"`
|
||||
Update map[string]interface{} `json:"update" v:"required"`
|
||||
g.Meta `path:"/tenant-level-configs/{id}" method:"put" mime:"json" tags:"管理后台-租户等级" summary:"更新等级配置"`
|
||||
Id int64 `json:"id" in:"path" v:"required|min:1"`
|
||||
Name *string `json:"name" dc:"等级名称"`
|
||||
CumulativeRechargeThreshold *float64 `json:"cumulative_recharge_threshold" dc:"累计充值阈值"`
|
||||
MaxMembers *int `json:"max_members" dc:"最大成员数"`
|
||||
MaxConcurrency *int `json:"max_concurrency" dc:"最大并发数"`
|
||||
PriceMultiplier *float64 `json:"price_multiplier" dc:"价格乘数"`
|
||||
SortOrder *int `json:"sort_order" dc:"排序权重"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigUpdateRes struct{}
|
||||
|
||||
@@ -72,7 +72,7 @@ require (
|
||||
github.com/olekukonko/tablewriter v1.1.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/pressly/goose/v3 v3.27.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.19.0 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.6 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.6 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
@@ -92,6 +92,7 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/image v0.25.0 // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
|
||||
@@ -134,6 +134,8 @@ github.com/pressly/goose/v3 v3.27.1 h1:6uEvcprBybDmW4hcz3gYujhARhye+GoWKhEWyzD5s
|
||||
github.com/pressly/goose/v3 v3.27.1/go.mod h1:maruOxsPnIG2yHHyo8UqKWXYKFcH7Q76csUV7+7KYoM=
|
||||
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
|
||||
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k=
|
||||
github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=
|
||||
github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
|
||||
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
|
||||
@@ -211,6 +213,8 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
|
||||
@@ -152,9 +152,13 @@ func (s *sAdmin) CreateModel(ctx context.Context, req *v1.ModelCreateReq) (*v1.M
|
||||
"max_context_tokens": req.MaxContext,
|
||||
"max_output_tokens": req.MaxOutput,
|
||||
"description": req.Description,
|
||||
"tags": req.Tags,
|
||||
"status": "active",
|
||||
}
|
||||
if req.Tags != nil {
|
||||
insertData["tags"] = req.Tags
|
||||
} else {
|
||||
insertData["tags"] = []string{}
|
||||
}
|
||||
if req.Capabilities != nil {
|
||||
capJson, _ := json.Marshal(req.Capabilities)
|
||||
insertData["capabilities"] = string(capJson)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
v1 "github.com/qianfree/team-api/api/admin/v1"
|
||||
@@ -39,17 +40,16 @@ func (s *sAdmin) ListTemplates(ctx context.Context, req *v1.TemplateListReq) (*v
|
||||
|
||||
// GetTemplate 获取单个通知模板
|
||||
func (s *sAdmin) GetTemplate(ctx context.Context, req *v1.TemplateGetReq) (*v1.TemplateGetRes, error) {
|
||||
var tpl map[string]any
|
||||
err := dao.NtfTemplates.Ctx(ctx).
|
||||
record, err := dao.NtfTemplates.Ctx(ctx).
|
||||
Where("code", req.Code).
|
||||
Scan(&tpl)
|
||||
One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tpl == nil {
|
||||
if record == nil {
|
||||
return nil, common.NewNotFoundError("template")
|
||||
}
|
||||
return &v1.TemplateGetRes{Data: tpl}, nil
|
||||
return &v1.TemplateGetRes{Data: record.Map()}, nil
|
||||
}
|
||||
|
||||
// UpdateTemplate 更新通知模板
|
||||
@@ -150,7 +150,25 @@ func (s *sAdmin) SendBroadcast(ctx context.Context, req *v1.MessageBroadcastReq)
|
||||
}
|
||||
|
||||
engine := common.NewNotificationEngine()
|
||||
return nil, engine.SendBroadcastMessage(ctx, req.TenantID, "system", req.Title, req.Content, req.TargetRoles)
|
||||
|
||||
if req.TenantID != nil && *req.TenantID > 0 {
|
||||
return nil, engine.SendBroadcastMessage(ctx, *req.TenantID, "system", req.Title, req.Content, req.TargetRoles)
|
||||
}
|
||||
|
||||
// 广播到所有活跃租户
|
||||
var tenants []struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
err := dao.TntTenants.Ctx(ctx).Where("status", "active").Fields("id").Scan(&tenants)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range tenants {
|
||||
if e := engine.SendBroadcastMessage(ctx, t.Id, "system", req.Title, req.Content, req.TargetRoles); e != nil {
|
||||
g.Log().Warningf(ctx, "SendBroadcast to tenant %d failed: %v", t.Id, e)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ListMessages 获取所有消息列表(管理后台,支持过滤)
|
||||
|
||||
@@ -112,15 +112,14 @@ func (s *sAdmin) ArchivePlan(ctx context.Context, req *v1.PlanArchiveReq) (*v1.P
|
||||
|
||||
// ToggleRecommend 切换推荐标记
|
||||
func (s *sAdmin) ToggleRecommend(ctx context.Context, req *v1.PlanToggleRecommendReq) (*v1.PlanToggleRecommendRes, error) {
|
||||
// 先查当前值
|
||||
var isRecommended bool
|
||||
err := dao.PlnPlans.Ctx(ctx).
|
||||
val, err := dao.PlnPlans.Ctx(ctx).
|
||||
Where("id", req.Id).
|
||||
Fields("is_recommended").
|
||||
Scan(&isRecommended)
|
||||
Value()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isRecommended := val.Bool()
|
||||
|
||||
_, err = dao.PlnPlans.Ctx(ctx).
|
||||
Where("id", req.Id).
|
||||
|
||||
@@ -2,10 +2,12 @@ package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
@@ -431,9 +433,18 @@ func (s *sAdmin) UpdateTenant(ctx context.Context, req *v1.TenantUpdateReq) (*v1
|
||||
|
||||
// UpdateTenantChannelScope 更新租户默认渠道范围
|
||||
func (s *sAdmin) UpdateTenantChannelScope(ctx context.Context, req *v1.TenantChannelScopeUpdateReq) (*v1.TenantChannelScopeUpdateRes, error) {
|
||||
_, err := dao.TntTenants.Ctx(ctx).Where("id", req.Id).Update(do.TntTenants{
|
||||
DefaultChannelScope: req.DefaultChannelScope,
|
||||
})
|
||||
var scopeValue any
|
||||
if req.DefaultChannelScope == nil || *req.DefaultChannelScope == "" || *req.DefaultChannelScope == "all" {
|
||||
scopeValue = nil
|
||||
} else if json.Valid([]byte(*req.DefaultChannelScope)) {
|
||||
scopeValue = *req.DefaultChannelScope
|
||||
} else {
|
||||
return nil, gerror.New("default_channel_scope 必须是有效的 JSON(如 [1,5,12])或 null")
|
||||
}
|
||||
|
||||
_, err := dao.TntTenants.Ctx(ctx).Where("id", req.Id).Data(g.Map{
|
||||
"default_channel_scope": scopeValue,
|
||||
}).Update()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/dao"
|
||||
@@ -64,9 +65,33 @@ func (s *sAdmin) CreateTenantLevelConfig(ctx context.Context, req *v1.TenantLeve
|
||||
}
|
||||
|
||||
func (s *sAdmin) UpdateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (*v1.TenantLevelConfigUpdateRes, error) {
|
||||
data := g.Map{}
|
||||
if req.Name != nil {
|
||||
data["name"] = *req.Name
|
||||
}
|
||||
if req.CumulativeRechargeThreshold != nil {
|
||||
data["cumulative_recharge_threshold"] = *req.CumulativeRechargeThreshold
|
||||
}
|
||||
if req.MaxMembers != nil {
|
||||
data["max_members"] = *req.MaxMembers
|
||||
}
|
||||
if req.MaxConcurrency != nil {
|
||||
data["max_concurrency"] = *req.MaxConcurrency
|
||||
}
|
||||
if req.PriceMultiplier != nil {
|
||||
data["price_multiplier"] = *req.PriceMultiplier
|
||||
}
|
||||
if req.SortOrder != nil {
|
||||
data["sort_order"] = *req.SortOrder
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return &v1.TenantLevelConfigUpdateRes{}, nil
|
||||
}
|
||||
|
||||
_, err := dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
Where("id", req.Id).
|
||||
Data(req.Update).
|
||||
Data(data).
|
||||
Update()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -75,20 +100,16 @@ func (s *sAdmin) UpdateTenantLevelConfig(ctx context.Context, req *v1.TenantLeve
|
||||
}
|
||||
|
||||
func (s *sAdmin) DeleteTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigDeleteReq) (*v1.TenantLevelConfigDeleteRes, error) {
|
||||
// 不允许删除 level=1(默认等级)
|
||||
var config entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Scan(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Scan(&config)
|
||||
if config.Id == 0 {
|
||||
return nil, gerror.New("等级配置不存在")
|
||||
return &v1.TenantLevelConfigDeleteRes{}, nil
|
||||
}
|
||||
if config.Level == 1 {
|
||||
return nil, gerror.New("不允许删除默认等级 LV1")
|
||||
}
|
||||
|
||||
_, err = dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Delete()
|
||||
_, err := dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func (s *sAdmin) BatchAssignModels(ctx context.Context, req *v1.TenantModelBatch
|
||||
CustomPricingTiers: tiersJSON,
|
||||
Multiplier: 1.0,
|
||||
}
|
||||
g.Log().Debugf(ctx, "BatchAssignModels: inserting data tenant_id=%v, model_id=%v, enabled=%v, max_concurrency=%v", insertData.TenantId, insertData.ModelId, insertData.Enabled, insertData.MaxConcurrency)
|
||||
g.Log().Debugf(ctx, "BatchAssignModels: inserting data tenant_id=%v, model_id=%v, enabled=%v, max_concurrency=%v", insertData.TenantId, insertData.ModelId, insertData.Enabled, *maxConc)
|
||||
_, err = dao.MdlTenantModels.Ctx(ctx).Insert(insertData)
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "BatchAssignModels: insert failed, model_id=%d, err=%v", a.ModelID, err)
|
||||
|
||||
@@ -116,7 +116,7 @@ func sendAlertInAppToAdmins(ctx context.Context, adminIDs []int64, title, conten
|
||||
Type: "alert",
|
||||
Title: title,
|
||||
Content: content,
|
||||
IsRead: false,
|
||||
IsRead: 0,
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "create in-app alert for admin %d: %v", adminID, err)
|
||||
|
||||
@@ -81,6 +81,11 @@ func CreateAlertRule(ctx context.Context, data do.OpsAlertRules) (int64, error)
|
||||
return 0, common.NewBadRequestError("规则名称不能为空")
|
||||
}
|
||||
metricType := gconv.String(data.MetricType)
|
||||
// Auto-prefix: if no namespace (dot) found, try prepending "api."
|
||||
if !strings.Contains(metricType, ".") {
|
||||
metricType = "api." + metricType
|
||||
data.MetricType = metricType
|
||||
}
|
||||
if _, ok := validMetricTypes[metricType]; !ok {
|
||||
return 0, gerror.Newf("不支持的指标类型: %s", metricType)
|
||||
}
|
||||
@@ -116,6 +121,10 @@ func UpdateAlertRule(ctx context.Context, id int64, updates do.OpsAlertRules) er
|
||||
// Validate metric_type if provided
|
||||
if updates.MetricType != nil {
|
||||
metricType := gconv.String(updates.MetricType)
|
||||
if !strings.Contains(metricType, ".") {
|
||||
metricType = "api." + metricType
|
||||
updates.MetricType = metricType
|
||||
}
|
||||
if _, valid := validMetricTypes[metricType]; !valid {
|
||||
return gerror.Newf("不支持的指标类型: %s", metricType)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user