feat(portal): add logical group packaging config
This commit is contained in:
@@ -13,6 +13,8 @@ const (
|
||||
defaultUserModelTTLSeconds = 1800
|
||||
defaultFailoverThreshold = 2
|
||||
defaultCooldownSeconds = 600
|
||||
defaultLogicalGroupVisibility = "public"
|
||||
defaultLogicalGroupPackageTier = "standard"
|
||||
)
|
||||
|
||||
type LogicalGroup struct {
|
||||
@@ -24,6 +26,10 @@ type LogicalGroup struct {
|
||||
UsageScenario string
|
||||
Recommendation string
|
||||
NextStepHint string
|
||||
VisibilityScope string
|
||||
PackageTier string
|
||||
PurchaseCTALabel string
|
||||
PurchaseCTAURL string
|
||||
RoutePolicy string
|
||||
StickyMode string
|
||||
ConversationTTLSeconds int
|
||||
@@ -58,13 +64,17 @@ func (r *LogicalGroupsRepo) Create(ctx context.Context, group LogicalGroup) (int
|
||||
usage_scenario,
|
||||
recommendation,
|
||||
next_step_hint,
|
||||
visibility_scope,
|
||||
package_tier,
|
||||
purchase_cta_label,
|
||||
purchase_cta_url,
|
||||
route_policy,
|
||||
sticky_mode,
|
||||
conversation_ttl_seconds,
|
||||
user_model_ttl_seconds,
|
||||
failover_threshold,
|
||||
cooldown_seconds
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
group.LogicalGroupID,
|
||||
group.DisplayName,
|
||||
group.Status,
|
||||
@@ -72,6 +82,10 @@ func (r *LogicalGroupsRepo) Create(ctx context.Context, group LogicalGroup) (int
|
||||
group.UsageScenario,
|
||||
group.Recommendation,
|
||||
group.NextStepHint,
|
||||
group.VisibilityScope,
|
||||
group.PackageTier,
|
||||
group.PurchaseCTALabel,
|
||||
group.PurchaseCTAURL,
|
||||
group.RoutePolicy,
|
||||
group.StickyMode,
|
||||
group.ConversationTTLSeconds,
|
||||
@@ -99,7 +113,7 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
var group LogicalGroup
|
||||
if err := r.db.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, visibility_scope, package_tier, purchase_cta_label, purchase_cta_url, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
FROM logical_groups
|
||||
WHERE logical_group_id = ?`,
|
||||
logicalGroupID,
|
||||
@@ -112,6 +126,10 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
&group.UsageScenario,
|
||||
&group.Recommendation,
|
||||
&group.NextStepHint,
|
||||
&group.VisibilityScope,
|
||||
&group.PackageTier,
|
||||
&group.PurchaseCTALabel,
|
||||
&group.PurchaseCTAURL,
|
||||
&group.RoutePolicy,
|
||||
&group.StickyMode,
|
||||
&group.ConversationTTLSeconds,
|
||||
@@ -129,7 +147,7 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
func (r *LogicalGroupsRepo) List(ctx context.Context) ([]LogicalGroup, error) {
|
||||
rows, err := r.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, visibility_scope, package_tier, purchase_cta_label, purchase_cta_url, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
FROM logical_groups
|
||||
ORDER BY id ASC`,
|
||||
)
|
||||
@@ -150,6 +168,10 @@ func (r *LogicalGroupsRepo) List(ctx context.Context) ([]LogicalGroup, error) {
|
||||
&group.UsageScenario,
|
||||
&group.Recommendation,
|
||||
&group.NextStepHint,
|
||||
&group.VisibilityScope,
|
||||
&group.PackageTier,
|
||||
&group.PurchaseCTALabel,
|
||||
&group.PurchaseCTAURL,
|
||||
&group.RoutePolicy,
|
||||
&group.StickyMode,
|
||||
&group.ConversationTTLSeconds,
|
||||
@@ -178,7 +200,7 @@ func (r *LogicalGroupsRepo) UpdateByLogicalGroupID(ctx context.Context, group Lo
|
||||
result, err := r.db.ExecContext(
|
||||
ctx,
|
||||
`UPDATE logical_groups
|
||||
SET display_name = ?, status = ?, description = ?, usage_scenario = ?, recommendation = ?, next_step_hint = ?, route_policy = ?, sticky_mode = ?, conversation_ttl_seconds = ?, user_model_ttl_seconds = ?, failover_threshold = ?, cooldown_seconds = ?, updated_at = CURRENT_TIMESTAMP
|
||||
SET display_name = ?, status = ?, description = ?, usage_scenario = ?, recommendation = ?, next_step_hint = ?, visibility_scope = ?, package_tier = ?, purchase_cta_label = ?, purchase_cta_url = ?, route_policy = ?, sticky_mode = ?, conversation_ttl_seconds = ?, user_model_ttl_seconds = ?, failover_threshold = ?, cooldown_seconds = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE logical_group_id = ?`,
|
||||
group.DisplayName,
|
||||
group.Status,
|
||||
@@ -186,6 +208,10 @@ func (r *LogicalGroupsRepo) UpdateByLogicalGroupID(ctx context.Context, group Lo
|
||||
group.UsageScenario,
|
||||
group.Recommendation,
|
||||
group.NextStepHint,
|
||||
group.VisibilityScope,
|
||||
group.PackageTier,
|
||||
group.PurchaseCTALabel,
|
||||
group.PurchaseCTAURL,
|
||||
group.RoutePolicy,
|
||||
group.StickyMode,
|
||||
group.ConversationTTLSeconds,
|
||||
@@ -235,6 +261,10 @@ func normalizeLogicalGroup(group LogicalGroup) (LogicalGroup, error) {
|
||||
group.UsageScenario = strings.TrimSpace(group.UsageScenario)
|
||||
group.Recommendation = strings.TrimSpace(group.Recommendation)
|
||||
group.NextStepHint = strings.TrimSpace(group.NextStepHint)
|
||||
group.VisibilityScope = strings.TrimSpace(group.VisibilityScope)
|
||||
group.PackageTier = strings.TrimSpace(group.PackageTier)
|
||||
group.PurchaseCTALabel = strings.TrimSpace(group.PurchaseCTALabel)
|
||||
group.PurchaseCTAURL = strings.TrimSpace(group.PurchaseCTAURL)
|
||||
group.RoutePolicy = strings.TrimSpace(group.RoutePolicy)
|
||||
group.StickyMode = strings.TrimSpace(group.StickyMode)
|
||||
|
||||
@@ -250,6 +280,12 @@ func normalizeLogicalGroup(group LogicalGroup) (LogicalGroup, error) {
|
||||
if group.RoutePolicy == "" {
|
||||
group.RoutePolicy = defaultLogicalGroupRoutePolicy
|
||||
}
|
||||
if group.VisibilityScope == "" {
|
||||
group.VisibilityScope = defaultLogicalGroupVisibility
|
||||
}
|
||||
if group.PackageTier == "" {
|
||||
group.PackageTier = defaultLogicalGroupPackageTier
|
||||
}
|
||||
if group.StickyMode == "" {
|
||||
group.StickyMode = defaultLogicalGroupStickyMode
|
||||
}
|
||||
|
||||
@@ -12,13 +12,17 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
id, err := store.LogicalGroups().Create(ctx, LogicalGroup{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
Description: "shared group",
|
||||
UsageScenario: "适合统一 GPT 产品入口。",
|
||||
Recommendation: "优先使用 gpt-5.4。",
|
||||
NextStepHint: "先创建测试 Key。",
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
Description: "shared group",
|
||||
UsageScenario: "适合统一 GPT 产品入口。",
|
||||
Recommendation: "优先使用 gpt-5.4。",
|
||||
NextStepHint: "先创建测试 Key。",
|
||||
VisibilityScope: "login_required",
|
||||
PackageTier: "pro",
|
||||
PurchaseCTALabel: "升级到 Pro",
|
||||
PurchaseCTAURL: "https://sub.tksea.top/portal/upgrade/pro",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
@@ -40,6 +44,9 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
if group.UsageScenario != "适合统一 GPT 产品入口。" || group.Recommendation != "优先使用 gpt-5.4。" || group.NextStepHint != "先创建测试 Key。" {
|
||||
t.Fatalf("guidance fields = %+v, want persisted guidance", group)
|
||||
}
|
||||
if group.VisibilityScope != "login_required" || group.PackageTier != "pro" || group.PurchaseCTALabel != "升级到 Pro" || group.PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" {
|
||||
t.Fatalf("packaging fields = %+v, want persisted packaging", group)
|
||||
}
|
||||
|
||||
if err := store.LogicalGroups().UpdateByLogicalGroupID(ctx, LogicalGroup{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
@@ -49,6 +56,10 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
UsageScenario: "适合更新后的产品入口。",
|
||||
Recommendation: "优先做连通性验证。",
|
||||
NextStepHint: "先确认订阅再调用。",
|
||||
VisibilityScope: "entitled_only",
|
||||
PackageTier: "enterprise",
|
||||
PurchaseCTALabel: "联系销售升级",
|
||||
PurchaseCTAURL: "https://sub.tksea.top/portal/contact-sales",
|
||||
RoutePolicy: "priority",
|
||||
StickyMode: "user_preferred",
|
||||
ConversationTTLSeconds: 3600,
|
||||
@@ -69,6 +80,9 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
if updated.UsageScenario != "适合更新后的产品入口。" || updated.Recommendation != "优先做连通性验证。" || updated.NextStepHint != "先确认订阅再调用。" {
|
||||
t.Fatalf("updated guidance = %+v, want updated guidance fields", updated)
|
||||
}
|
||||
if updated.VisibilityScope != "entitled_only" || updated.PackageTier != "enterprise" || updated.PurchaseCTALabel != "联系销售升级" || updated.PurchaseCTAURL != "https://sub.tksea.top/portal/contact-sales" {
|
||||
t.Fatalf("updated packaging = %+v, want updated packaging fields", updated)
|
||||
}
|
||||
|
||||
if err := store.LogicalGroups().DeleteByLogicalGroupID(ctx, "gpt-shared"); err != nil {
|
||||
t.Fatalf("DeleteByLogicalGroupID() error = %v", err)
|
||||
|
||||
Reference in New Issue
Block a user