yao/openapi/agent/robot/utils.go
Max 36ac190637 Implement Auto-Generation of Member ID in Robot API
- Updated the CreateRobot API to auto-generate the member_id if not provided, enhancing usability and ensuring unique identifiers.
- Revised CreateRobotRequest structure to make member_id optional, aligning with the new auto-generation logic.
- Added a new function for generating unique member IDs with collision detection, ensuring compliance with existing ID patterns.
- Enhanced unit tests to validate the new behavior, ensuring robust error handling and proper ID generation.
- Updated related OpenAPI documentation to reflect changes in request structure and behavior.
2026-01-22 17:31:36 +08:00

101 lines
2.5 KiB
Go

package robot
import (
"context"
"fmt"
"strings"
"github.com/gin-gonic/gin"
gonanoid "github.com/matoous/go-nanoid/v2"
"github.com/yaoapp/gou/model"
)
// GetLocale extracts locale from request
// Priority: query param > Accept-Language header > default
func GetLocale(c *gin.Context) string {
// Check query param first
if locale := c.Query("locale"); locale != "" {
return strings.ToLower(strings.TrimSpace(locale))
}
// Check Accept-Language header
if acceptLang := c.GetHeader("Accept-Language"); acceptLang != "" {
// Parse first language from header (e.g., "en-US,en;q=0.9" -> "en-us")
parts := strings.Split(acceptLang, ",")
if len(parts) > 0 {
lang := strings.Split(parts[0], ";")[0]
return strings.ToLower(strings.TrimSpace(lang))
}
}
// Default locale
return "en-us"
}
// ParseBoolValue parses various string formats into a boolean pointer
func ParseBoolValue(value string) *bool {
value = strings.ToLower(strings.TrimSpace(value))
switch value {
case "1", "true", "yes", "on":
v := true
return &v
case "0", "false", "no", "off":
v := false
return &v
}
return nil
}
// ==================== Member ID Generation ====================
// Follows the same pattern as openapi/oauth/providers/user/utils.go
const memberModel = "__yao.member"
// GenerateMemberID generates a new unique member_id for robot creation
// Uses numeric ID (12 characters) with collision detection
func GenerateMemberID(ctx context.Context) (string, error) {
const maxRetries = 10
for i := 0; i < maxRetries; i++ {
// Generate 12-digit numeric ID (matches existing pattern)
id, err := gonanoid.Generate("0123456789", 12)
if err != nil {
return "", fmt.Errorf("failed to generate member_id: %w", err)
}
// Check if ID already exists
exists, err := memberIDExists(ctx, id)
if err != nil {
return "", fmt.Errorf("failed to check member_id existence: %w", err)
}
if !exists {
return id, nil
}
// ID exists, retry
}
return "", fmt.Errorf("failed to generate unique member_id after %d retries", maxRetries)
}
// memberIDExists checks if a member_id already exists in the database
func memberIDExists(ctx context.Context, memberID string) (bool, error) {
m := model.Select(memberModel)
if m == nil {
return false, fmt.Errorf("model %s not found", memberModel)
}
members, err := m.Get(model.QueryParam{
Select: []interface{}{"id"},
Wheres: []model.QueryWhere{
{Column: "member_id", Value: memberID},
},
Limit: 1,
})
if err != nil {
return false, err
}
return len(members) > 0, nil
}