feat: Backend part translation

This commit is contained in:
tbphp committed 2025-09-08 23:29:29 +08:00
1 parent a540d5e2fd
commit 519f6cfc8b
12 files changed
+147 -74

No files matched your search

+8 -8
View File
@@ -20,7 +20,7 @@ func (s *Server) Stats(c *gin.Context) {
now := time.Now()
rpmStats, err := s.getRPMStats(now)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get rpm stats"))
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.rpm_stats_failed")
return
}
twentyFourHoursAgo := now.Add(-24 * time.Hour)
@@ -28,12 +28,12 @@ func (s *Server) Stats(c *gin.Context) {
currentPeriod, err := s.getHourlyStats(twentyFourHoursAgo, now)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get current period stats"))
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.current_stats_failed")
return
}
previousPeriod, err := s.getHourlyStats(fortyEightHoursAgo, twentyFourHoursAgo)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get previous period stats"))
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.previous_stats_failed")
return
}
@@ -125,7 +125,7 @@ func (s *Server) Chart(c *gin.Context) {
query = query.Where("group_id = ?", groupID)
}
if err := query.Order("time asc").Find(&hourlyStats).Error; err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get chart data"))
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.chart_data_failed")
return
}
@@ -272,14 +272,14 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
if len(password) < 16 {
warnings = append(warnings, models.SecurityWarning{
Type: keyType,
Message: i18n.Message(c, "security.password_too_short", map[string]interface{}{"keyType": keyType, "length": len(password)}),
Message: i18n.Message(c, "security.password_too_short", map[string]any{"keyType": keyType, "length": len(password)}),
Severity: "high", // 长度不足是高风险
Suggestion: i18n.Message(c, "security.password_recommendation_16"),
})
} else if len(password) < 32 {
warnings = append(warnings, models.SecurityWarning{
Type: keyType,
Message: i18n.Message(c, "security.password_short", map[string]interface{}{"keyType": keyType, "length": len(password)}),
Message: i18n.Message(c, "security.password_short", map[string]any{"keyType": keyType, "length": len(password)}),
Severity: "medium",
Suggestion: i18n.Message(c, "security.password_recommendation_32"),
})
@@ -297,7 +297,7 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
if strings.Contains(lower, pattern) {
warnings = append(warnings, models.SecurityWarning{
Type: keyType,
Message: i18n.Message(c, "security.password_weak_pattern", map[string]interface{}{"keyType": keyType, "pattern": pattern}),
Message: i18n.Message(c, "security.password_weak_pattern", map[string]any{"keyType": keyType, "pattern": pattern}),
Severity: "high",
Suggestion: i18n.Message(c, "security.password_avoid_common"),
})
@@ -309,7 +309,7 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
if len(password) >= 16 && !hasGoodComplexity(password) {
warnings = append(warnings, models.SecurityWarning{
Type: keyType,
Message: i18n.Message(c, "security.password_low_complexity", map[string]interface{}{"keyType": keyType}),
Message: i18n.Message(c, "security.password_low_complexity", map[string]any{"keyType": keyType}),
Severity: "medium",
Suggestion: i18n.Message(c, "security.password_complexity"),
})
+11 -12
View File
@@ -190,7 +190,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
channelType := strings.TrimSpace(req.ChannelType)
if !isValidChannelType(channelType) {
supported := strings.Join(channel.GetChannels(), ", ")
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]interface{}{"types": supported})
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]any{"types": supported})
return
}
@@ -202,13 +202,13 @@ func (s *Server) CreateGroup(c *gin.Context) {
cleanedUpstreams, err := validateAndCleanUpstreams(req.Upstreams)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_upstreams", map[string]any{"error": err.Error()})
return
}
cleanedConfig, err := s.validateAndCleanConfig(req.Config)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, fmt.Sprintf("Invalid config format: %v", err)))
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "error.invalid_config_format", map[string]any{"error": err.Error()})
return
}
@@ -235,7 +235,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
// Check for duplicate keys
if seenKeys[canonicalKey] {
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]interface{}{"key": canonicalKey})
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]any{"key": canonicalKey})
return
}
seenKeys[canonicalKey] = true
@@ -250,7 +250,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
if len(normalizedHeaderRules) > 0 {
headerRulesBytes, err := json.Marshal(normalizedHeaderRules)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrInternalServer, fmt.Sprintf("Failed to process header rules: %v", err)))
response.ErrorI18nFromAPIError(c, app_errors.ErrInternalServer, "error.process_header_rules", map[string]any{"error": err.Error()})
return
}
headerRulesJSON = headerRulesBytes
@@ -378,7 +378,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
cleanedChannelType := strings.TrimSpace(*req.ChannelType)
if !isValidChannelType(cleanedChannelType) {
supported := strings.Join(channel.GetChannels(), ", ")
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]interface{}{"types": supported})
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]any{"types": supported})
return
}
group.ChannelType = cleanedChannelType
@@ -409,7 +409,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
if req.Config != nil {
cleanedConfig, err := s.validateAndCleanConfig(req.Config)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, fmt.Sprintf("Invalid config format: %v", err)))
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "error.invalid_config_format", map[string]any{"error": err.Error()})
return
}
group.Config = cleanedConfig
@@ -436,7 +436,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
// Check for duplicate keys
if seenKeys[canonicalKey] {
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]interface{}{"key": canonicalKey})
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]any{"key": canonicalKey})
return
}
seenKeys[canonicalKey] = true
@@ -451,7 +451,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
if len(normalizedHeaderRules) > 0 {
headerRulesBytes, err := json.Marshal(normalizedHeaderRules)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrInternalServer, fmt.Sprintf("Failed to process header rules: %v", err)))
response.ErrorI18nFromAPIError(c, app_errors.ErrInternalServer, "error.process_header_rules", map[string]any{"error": err.Error()})
return
}
headerRulesJSON = headerRulesBytes
@@ -607,8 +607,7 @@ func (s *Server) DeleteGroup(c *gin.Context) {
"error": err,
}).Error("Failed to remove keys from memory store, rolling back transaction")
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase,
"Failed to delete group: unable to clean up cache"))
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "error.delete_group_cache")
return
}
}
@@ -623,7 +622,7 @@ func (s *Server) DeleteGroup(c *gin.Context) {
if err := s.GroupManager.Invalidate(); err != nil {
logrus.WithContext(c.Request.Context()).WithError(err).Error("failed to invalidate group cache")
}
response.Success(c, gin.H{"message": "Group and associated keys deleted successfully"})
response.SuccessI18n(c, "success.group_deleted", nil)
}
// ConfigOption represents a single configurable option for a group.
+4 -3
View File
@@ -8,6 +8,7 @@ import (
"gpt-load/internal/config"
"gpt-load/internal/encryption"
"gpt-load/internal/i18n"
"gpt-load/internal/services"
"gpt-load/internal/types"
@@ -84,7 +85,7 @@ func (s *Server) Login(c *gin.Context) {
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "Invalid request format",
"message": i18n.Message(c, "auth.invalid_request"),
})
return
}
@@ -96,12 +97,12 @@ func (s *Server) Login(c *gin.Context) {
if isValid {
c.JSON(http.StatusOK, LoginResponse{
Success: true,
Message: "Authentication successful",
Message: i18n.Message(c, "auth.authentication_successful"),
})
} else {
c.JSON(http.StatusUnauthorized, LoginResponse{
Success: false,
Message: "Authentication failed",
Message: i18n.Message(c, "auth.authentication_failed"),
})
}
}
+26 -30
View File
@@ -16,27 +16,32 @@ import (
)
// validateGroupIDFromQuery validates and parses group ID from a query parameter.
func validateGroupIDFromQuery(c *gin.Context) (uint, error) {
// Returns 0 and false if validation fails (error is already sent to client)
func validateGroupIDFromQuery(c *gin.Context) (uint, bool) {
groupIDStr := c.Query("group_id")
if groupIDStr == "" {
return 0, fmt.Errorf("group_id query parameter is required")
response.ErrorI18nFromAPIError(c, app_errors.ErrBadRequest, "validation.group_id_required")
return 0, false
}
groupID, err := strconv.Atoi(groupIDStr)
if err != nil || groupID <= 0 {
return 0, fmt.Errorf("invalid group_id format")
response.ErrorI18nFromAPIError(c, app_errors.ErrBadRequest, "validation.invalid_group_id_format")
return 0, false
}
return uint(groupID), nil
return uint(groupID), true
}
// validateKeysText validates the keys text input
func validateKeysText(keysText string) error {
// Returns false if validation fails (error is already sent to client)
func validateKeysText(c *gin.Context, keysText string) bool {
if strings.TrimSpace(keysText) == "" {
return fmt.Errorf("keys text cannot be empty")
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.keys_text_empty")
return false
}
return nil
return true
}
// findGroupByID is a helper function to find a group by its ID.
@@ -82,8 +87,7 @@ func (s *Server) AddMultipleKeys(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -115,8 +119,7 @@ func (s *Server) AddMultipleKeysAsync(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -131,9 +134,8 @@ func (s *Server) AddMultipleKeysAsync(c *gin.Context) {
// ListKeysInGroup handles listing all keys within a specific group with pagination.
func (s *Server) ListKeysInGroup(c *gin.Context) {
groupID, err := validateGroupIDFromQuery(c)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrBadRequest, err.Error()))
groupID, ok := validateGroupIDFromQuery(c)
if !ok {
return
}
@@ -189,8 +191,7 @@ func (s *Server) DeleteMultipleKeys(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -222,8 +223,7 @@ func (s *Server) DeleteMultipleKeysAsync(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -248,8 +248,7 @@ func (s *Server) RestoreMultipleKeys(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -287,8 +286,7 @@ func (s *Server) TestMultipleKeys(c *gin.Context) {
return
}
if err := validateKeysText(req.KeysText); err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
if !validateKeysText(c, req.KeysText) {
return
}
@@ -364,7 +362,7 @@ func (s *Server) RestoreAllInvalidKeys(c *gin.Context) {
return
}
response.Success(c, gin.H{"message": fmt.Sprintf("%d keys restored.", rowsAffected)})
response.SuccessI18n(c, "success.keys_restored", nil, map[string]any{"count": rowsAffected})
}
// ClearAllInvalidKeys deletes all 'inactive' keys from a group.
@@ -385,7 +383,7 @@ func (s *Server) ClearAllInvalidKeys(c *gin.Context) {
return
}
response.Success(c, gin.H{"message": fmt.Sprintf("%d invalid keys cleared.", rowsAffected)})
response.SuccessI18n(c, "success.invalid_keys_cleared", nil, map[string]any{"count": rowsAffected})
}
// ClearAllKeys deletes all keys from a group.
@@ -406,14 +404,13 @@ func (s *Server) ClearAllKeys(c *gin.Context) {
return
}
response.Success(c, gin.H{"message": fmt.Sprintf("%d keys cleared.", rowsAffected)})
response.SuccessI18n(c, "success.all_keys_cleared", nil, map[string]any{"count": rowsAffected})
}
// ExportKeys handles exporting keys to a text file.
func (s *Server) ExportKeys(c *gin.Context) {
groupID, err := validateGroupIDFromQuery(c)
if err != nil {
response.Error(c, app_errors.NewAPIError(app_errors.ErrBadRequest, err.Error()))
groupID, ok := validateGroupIDFromQuery(c)
if !ok {
return
}
@@ -438,8 +435,7 @@ func (s *Server) ExportKeys(c *gin.Context) {
c.Header("Content-Disposition", "attachment; filename="+filename)
c.Header("Content-Type", "text/plain; charset=utf-8")
err = s.KeyService.StreamKeysToWriter(groupID, statusFilter, c.Writer)
if err != nil {
if err := s.KeyService.StreamKeysToWriter(groupID, statusFilter, c.Writer); err != nil {
log.Printf("Failed to stream keys: %v", err)
}
}
+2 -1
View File
@@ -3,6 +3,7 @@ package handler
import (
"fmt"
app_errors "gpt-load/internal/errors"
"gpt-load/internal/i18n"
"gpt-load/internal/models"
"gpt-load/internal/response"
"log"
@@ -56,7 +57,7 @@ func (s *Server) ExportLogs(c *gin.Context) {
err := s.LogService.StreamLogKeysToCSV(c, c.Writer)
if err != nil {
log.Printf("Failed to stream log keys to CSV: %v", err)
c.JSON(500, gin.H{"error": "Failed to export logs"})
c.JSON(500, gin.H{"error": i18n.Message(c, "error.export_logs")})
return
}
}
+1 -3
View File
@@ -85,7 +85,5 @@ func (s *Server) UpdateSettings(c *gin.Context) {
time.Sleep(100 * time.Millisecond) // 等待异步更新配置
response.Success(c, gin.H{
"message": "Settings updated successfully. Configuration will be reloaded in the background across all instances.",
})
response.SuccessI18n(c, "settings.update_success", nil)
}
+1 -1
View File
@@ -110,7 +110,7 @@ func normalizeLanguageCode(lang string) string {
}
// T 翻译消息
func T(localizer *i18n.Localizer, msgID string, data ...map[string]interface{}) string {
func T(localizer *i18n.Localizer, msgID string, data ...map[string]any) string {
config := &i18n.LocalizeConfig{
MessageID: msgID,
}
+26
View File
@@ -55,6 +55,10 @@ var MessagesEnUS = map[string]string{
"validation.invalid_channel_type": "Invalid channel type. Supported types: {types}",
"validation.test_model_empty": "Test model cannot be empty or contain only spaces",
"validation.invalid_status_value": "Invalid status value",
"validation.invalid_upstreams": "Invalid upstreams configuration: {error}",
"validation.group_id_required": "group_id query parameter is required",
"validation.invalid_group_id_format": "Invalid group_id format",
"validation.keys_text_empty": "Keys text cannot be empty",
// Task related
"task.validation_started": "Key validation task started",
@@ -151,4 +155,26 @@ var MessagesEnUS = map[string]string{
"error.upstream_weight_positive": "upstream weight must be a positive integer",
"error.marshal_upstreams_failed": "failed to marshal cleaned upstreams",
"error.unknown_config_field": "unknown config field: '{field}'",
"error.invalid_config_format": "Invalid config format: {error}",
"error.process_header_rules": "Failed to process header rules: {error}",
"error.invalidate_group_cache": "failed to invalidate group cache",
"error.unmarshal_header_rules": "Failed to unmarshal header rules",
"error.delete_group_cache": "Failed to delete group: unable to clean up cache",
"error.get_total_keys": "failed to get total keys: {error}",
"error.get_active_keys": "failed to get active keys: {error}",
"error.get_hourly_requests": "failed to get hourly total requests: {error}",
"error.get_hourly_failures": "failed to get hourly failed requests: {error}",
"error.get_daily_stats": "failed to get daily stats: {error}",
"error.get_weekly_stats": "failed to get weekly stats: {error}",
"error.decrypt_key_copy": "Failed to decrypt key during group copy, skipping",
"error.start_import_task": "Failed to start async key import task for group copy",
"error.export_logs": "Failed to export logs",
// Login related
"auth.invalid_request": "Invalid request format",
"auth.authentication_successful": "Authentication successful",
"auth.authentication_failed": "Authentication failed",
// Settings success message
"settings.update_success": "Settings updated successfully. Configuration will be reloaded in the background across all instances.",
}
+26
View File
@@ -55,6 +55,10 @@ var MessagesJaJP = map[string]string{
"validation.invalid_channel_type": "無効なチャンネルタイプ。サポートされるタイプ: {types}",
"validation.test_model_empty": "テストモデルは空またはスペースのみにできません",
"validation.invalid_status_value": "無効なステータス値",
"validation.invalid_upstreams": "無効なupstreams設定: {error}",
"validation.group_id_required": "group_idクエリパラメータが必要です",
"validation.invalid_group_id_format": "無効なgroup_id形式",
"validation.keys_text_empty": "キーテキストは空にできません",
// タスク関連
"task.validation_started": "キー検証タスクが開始されました",
@@ -151,4 +155,26 @@ var MessagesJaJP = map[string]string{
"error.upstream_weight_positive": "upstreamの重みは正の整数である必要があります",
"error.marshal_upstreams_failed": "クリーンアップされたupstreamsのシリアル化に失敗しました",
"error.unknown_config_field": "未知の設定フィールド: '{field}'",
"error.invalid_config_format": "無効な設定形式: {error}",
"error.process_header_rules": "ヘッダールールの処理に失敗しました: {error}",
"error.invalidate_group_cache": "グループキャッシュの無効化に失敗しました",
"error.unmarshal_header_rules": "ヘッダールールのアンマーシャルに失敗しました",
"error.delete_group_cache": "グループの削除に失敗: キャッシュをクリーンアップできません",
"error.get_total_keys": "合計キー数の取得に失敗: {error}",
"error.get_active_keys": "アクティブキー数の取得に失敗: {error}",
"error.get_hourly_requests": "時間別リクエスト数の取得に失敗: {error}",
"error.get_hourly_failures": "時間別失敗数の取得に失敗: {error}",
"error.get_daily_stats": "日別統計の取得に失敗: {error}",
"error.get_weekly_stats": "週別統計の取得に失敗: {error}",
"error.decrypt_key_copy": "グループコピー中のキー復号化に失敗、スキップします",
"error.start_import_task": "グループコピー用の非同期キーインポートタスクの開始に失敗しました",
"error.export_logs": "ログのエクスポートに失敗しました",
// ログイン関連
"auth.invalid_request": "無効なリクエスト形式",
"auth.authentication_successful": "認証成功",
"auth.authentication_failed": "認証失敗",
// 設定成功メッセージ
"settings.update_success": "設定が正常に更新されました。設定はすべてのインスタンスでバックグラウンドで再読み込みされます。",
}
+26
View File
@@ -55,6 +55,10 @@ var MessagesZhCN = map[string]string{
"validation.invalid_channel_type": "无效的通道类型。支持的类型有: {types}",
"validation.test_model_empty": "测试模型不能为空或只有空格",
"validation.invalid_status_value": "无效的状态值",
"validation.invalid_upstreams": "upstreams配置错误: {error}",
"validation.group_id_required": "需要提供group_id参数",
"validation.invalid_group_id_format": "无效的group_id格式",
"validation.keys_text_empty": "密钥文本不能为空",
// 任务相关
"task.validation_started": "密钥验证任务已开始",
@@ -151,4 +155,26 @@ var MessagesZhCN = map[string]string{
"error.upstream_weight_positive": "upstream权重必须是正整数",
"error.marshal_upstreams_failed": "序列化清理后的upstreams失败",
"error.unknown_config_field": "未知的配置字段: '{field}'",
"error.invalid_config_format": "无效的配置格式: {error}",
"error.process_header_rules": "处理请求头规则失败: {error}",
"error.invalidate_group_cache": "刷新分组缓存失败",
"error.unmarshal_header_rules": "解析请求头规则失败",
"error.delete_group_cache": "删除分组失败: 无法清理缓存",
"error.get_total_keys": "获取密钥总数失败: {error}",
"error.get_active_keys": "获取活跃密钥数失败: {error}",
"error.get_hourly_requests": "获取每小时请求数失败: {error}",
"error.get_hourly_failures": "获取每小时失败数失败: {error}",
"error.get_daily_stats": "获取每日统计失败: {error}",
"error.get_weekly_stats": "获取每周统计失败: {error}",
"error.decrypt_key_copy": "解密密钥时失败,跳过该密钥",
"error.start_import_task": "启动异步密钥导入任务失败",
"error.export_logs": "导出日志失败",
// 登录相关
"auth.invalid_request": "无效的请求格式",
"auth.authentication_successful": "认证成功",
"auth.authentication_failed": "认证失败",
// 设置成功消息
"settings.update_success": "设置更新成功。配置将在后台在所有实例间重新加载。",
}
+13 -13
View File
@@ -17,17 +17,17 @@ func Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 获取 Accept-Language 头
acceptLang := c.GetHeader("Accept-Language")
// 获取 Localizer
localizer := GetLocalizer(acceptLang)
// 将 Localizer 存储到 Context 中
c.Set(LocalizerKey, localizer)
// 存储当前语言
lang := normalizeLanguageCode(acceptLang)
c.Set(LangKey, lang)
c.Next()
}
}
@@ -54,10 +54,10 @@ func GetLangFromContext(c *gin.Context) string {
}
// Success 返回成功响应(带国际化消息)
func Success(c *gin.Context, msgID string, data interface{}) {
func Success(c *gin.Context, msgID string, data any) {
localizer := GetLocalizerFromContext(c)
message := T(localizer, msgID)
c.JSON(200, gin.H{
"success": true,
"message": message,
@@ -67,10 +67,10 @@ func Success(c *gin.Context, msgID string, data interface{}) {
}
// SuccessWithData 返回成功响应(带模板数据)
func SuccessWithData(c *gin.Context, msgID string, templateData map[string]interface{}, data interface{}) {
func SuccessWithData(c *gin.Context, msgID string, templateData map[string]any, data any) {
localizer := GetLocalizerFromContext(c)
message := T(localizer, msgID, templateData)
c.JSON(200, gin.H{
"success": true,
"message": message,
@@ -83,7 +83,7 @@ func SuccessWithData(c *gin.Context, msgID string, templateData map[string]inter
func Error(c *gin.Context, code int, msgID string) {
localizer := GetLocalizerFromContext(c)
message := T(localizer, msgID)
c.JSON(code, gin.H{
"success": false,
"message": message,
@@ -92,10 +92,10 @@ func Error(c *gin.Context, code int, msgID string) {
}
// ErrorWithData 返回错误响应(带模板数据)
func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[string]interface{}) {
func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[string]any) {
localizer := GetLocalizerFromContext(c)
message := T(localizer, msgID, templateData)
c.JSON(code, gin.H{
"success": false,
"message": message,
@@ -104,7 +104,7 @@ func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[stri
}
// Message 获取国际化消息
func Message(c *gin.Context, msgID string, templateData ...map[string]interface{}) string {
func Message(c *gin.Context, msgID string, templateData ...map[string]any) string {
localizer := GetLocalizerFromContext(c)
return T(localizer, msgID, templateData...)
}
}
+3 -3
View File
@@ -40,7 +40,7 @@ func Error(c *gin.Context, apiErr *app_errors.APIError) {
}
// SuccessI18n sends a standardized success response with i18n message.
func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[string]interface{}) {
func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[string]any) {
message := i18n.Message(c, msgID, templateData...)
c.JSON(http.StatusOK, SuccessResponse{
Code: 0,
@@ -50,7 +50,7 @@ func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[str
}
// ErrorI18n sends a standardized error response with i18n message.
func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templateData ...map[string]interface{}) {
func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templateData ...map[string]any) {
message := i18n.Message(c, msgID, templateData...)
c.JSON(httpStatus, ErrorResponse{
Code: code,
@@ -59,7 +59,7 @@ func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templa
}
// ErrorI18nFromAPIError sends a standardized error response using an APIError with i18n message.
func ErrorI18nFromAPIError(c *gin.Context, apiErr *app_errors.APIError, msgID string, templateData ...map[string]interface{}) {
func ErrorI18nFromAPIError(c *gin.Context, apiErr *app_errors.APIError, msgID string, templateData ...map[string]any) {
message := i18n.Message(c, msgID, templateData...)
c.JSON(apiErr.HTTPStatus, ErrorResponse{
Code: apiErr.Code,